fuse-3.17.2/0000755000175000017500000000000015002273413011511 5ustar berndberndfuse-3.17.2/.ackrc0000644000175000017500000000005115002272303012566 0ustar berndbernd--ignore-dir=build --ignore-dir=doc/html fuse-3.17.2/.clang-format0000644000175000017500000000636415002272303014072 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.17.2/.codespellrc0000644000175000017500000000101515002272303014003 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.17.2/.dir-locals.el0000644000175000017500000000340215002272303014136 0ustar berndbernd((python-mode . ((indent-tabs-mode . nil))) (autoconf-mode . ((indent-tabs-mode . t))) (c++-mode . ((c-file-style . "k&r") (indent-tabs-mode . nil) (c-basic-offset . 4) (c-file-offsets . ((block-close . 0) (brace-list-close . 0) (brace-list-entry . 0) (brace-list-intro . +) (case-label . 0) (class-close . 0) (defun-block-intro . +) (defun-close . 0) (defun-open . 0) (else-clause . 0) (inclass . +) (label . 0) (statement . 0) (statement-block-intro . +) (statement-case-intro . +) (statement-cont . +) (substatement . +) (topmost-intro . 0))))) (c-mode . ((c-file-style . "stroustrup") (indent-tabs-mode . t) (tab-width . 8) (c-basic-offset . 8) (c-file-offsets . ((block-close . 0) (brace-list-close . 0) (brace-list-entry . 0) (brace-list-intro . +) (case-label . 0) (class-close . 0) (defun-block-intro . +) (defun-close . 0) (defun-open . 0) (else-clause . 0) (inclass . +) (label . 0) (statement . 0) (statement-block-intro . +) (statement-case-intro . +) (statement-cont . +) (substatement . +) (topmost-intro . 0)))))) fuse-3.17.2/AUTHORS0000644000175000017500000002400315002272303012555 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.0 Luis Henriques Zegang # New authors since fuse-3.17.1-rc0 Maksim Harbachou Vassili Tchersky # New authors since fuse-3.17.1-rc1 jnr0006 Vassili Tchersky # New authors since fuse-3.17.1 swj <1186093704@qq.com> Ben Dooks fuse-3.17.2/ChangeLog.rst0000644000175000017500000007665615002272303014113 0ustar berndberndlibfuse 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.17.2/GPL2.txt0000644000175000017500000004325415002272303012763 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.17.2/LGPL2.txt0000644000175000017500000006364215002272303013102 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.17.2/LICENSE0000644000175000017500000000066315002272303012520 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.17.2/README.md0000644000175000017500000001446015002272303012772 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.17.2/SECURITY.md0000644000175000017500000000155615002272303013306 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.17.2/checkpatch.pl0000755000175000017500000072310015002272303014146 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.17.2/dev-docs/�������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�15002272303�013212� 5����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.17.2/dev-docs/extend-authors.sh��������������������������������������������������������������0000755�0001750�0001750�00000002222�15002272303�016521� 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.17.2/dev-docs/release-process.md�������������������������������������������������������������0000644�0001750�0001750�00000003472�15002272303�016636� 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.17.2/doc/������������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�15002273413�012256� 5����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.17.2/doc/Doxyfile����������������������������������������������������������������������������0000644�0001750�0001750�00000352153�15002272303�013772� 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.17.2/doc/README.NFS��������������������������������������������������������������������������0000644�0001750�0001750�00000004154�15002272303�013564� 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.17.2/doc/fast17-vangoor.pdf������������������������������������������������������������������0000644�0001750�0001750�00002222165�15002272303�015536� 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.17.2/doc/fusermount3.1�����������������������������������������������������������������������0000644�0001750�0001750�00000002530�15002272303�014627� 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.17.2/doc/kernel.txt��������������������������������������������������������������������������0000644�0001750�0001750�00000034724�15002272303�014306� 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.17.2/doc/libfuse-operations.txt��������������������������������������������������������������0000644�0001750�0001750�00000031352�15002272303�016632� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������List of libfuse operations with their in/out arguments, created with help of chatgpt. As of kernel 6.9 (protocol 7.40). 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]: Size of fuse_statfs_in (16 bytes) - 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 (48 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 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.17.2/doc/mainpage.dox������������������������������������������������������������������������0000644�0001750�0001750�00000004147�15002272303�014556� 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.17.2/doc/meson.build�������������������������������������������������������������������������0000644�0001750�0001750�00000000163�15002272303�014415� 0����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������if not platform.endswith('bsd') and platform != 'dragonfly' install_man('fusermount3.1', 'mount.fuse3.8') endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.17.2/doc/mount.fuse3.8�����������������������������������������������������������������������0000644�0001750�0001750�00000031425�15002272303�014537� 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.17.2/doc/html/�������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�15002273247�013227� 5����������������������������������������������������������������������������������������������������ustar �bernd���������������������������bernd������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fuse-3.17.2/doc/html/annotated.html�����������������������������������������������������������������0000644�0001750�0001750�00000015450�15002273413�016072� 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: Data Structures
libfuse
Data Structures
fuse-3.17.2/doc/html/bc_s.png0000644000175000017500000000124415002273413014637 0ustar berndberndPNG  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*ʒJJˢou_~ r]%%mnu]zr5[ưXeI+ Kch` ^ލnIENDB`fuse-3.17.2/doc/html/buffer_8c_source.html0000644000175000017500000020266315002273413017344 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 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
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
@ FUSE_BUF_SPLICE_NONBLOCK
@ FUSE_BUF_FORCE_SPLICE
@ FUSE_BUF_NO_SPLICE
@ FUSE_BUF_SPLICE_MOVE
enum fuse_buf_flags flags
void * mem
off_t pos
size_t size
struct fuse_buf buf[1]
fuse-3.17.2/doc/html/build-debian_2config_8h_source.html0000644000175000017500000001530714240475423022033 0ustar berndbernd libfuse: build-debian/config.h Source File
libfuse
config.h
1 /*
2  * Autogenerated by the Meson build system.
3  * Do not edit, your changes will be lost.
4  */
5 
6 #pragma once
7 
8 #define HAVE_COPY_FILE_RANGE
9 
10 #define HAVE_FALLOCATE
11 
12 #define HAVE_FDATASYNC
13 
14 #define HAVE_FORK
15 
16 #define HAVE_FSTATAT
17 
18 #define HAVE_ICONV
19 
20 #define HAVE_OPENAT
21 
22 #define HAVE_PIPE2
23 
24 #define HAVE_POSIX_FALLOCATE
25 
26 #define HAVE_READLINKAT
27 
28 #define HAVE_SETXATTR
29 
30 #define HAVE_SPLICE
31 
32 #define HAVE_STRUCT_STAT_ST_ATIM
33 
34 #undef HAVE_STRUCT_STAT_ST_ATIMESPEC
35 
36 #define HAVE_UTIMENSAT
37 
38 #define HAVE_VMSPLICE
39 
40 #define PACKAGE_VERSION "3.10.5"
41 
fuse-3.17.2/doc/html/build-debian_2meson-private_2sanitycheckc_8c_source.html0000644000175000017500000000500414240475423026155 0ustar berndbernd libfuse: build-debian/meson-private/sanitycheckc.c Source File
libfuse
sanitycheckc.c
1 int main(void) { int class=0; return class; }
fuse-3.17.2/doc/html/build-rhel8_2config_8h_source.html0000644000175000017500000001530514240475423021631 0ustar berndbernd libfuse: build-rhel8/config.h Source File
libfuse
config.h
1 /*
2  * Autogenerated by the Meson build system.
3  * Do not edit, your changes will be lost.
4  */
5 
6 #pragma once
7 
8 #define HAVE_COPY_FILE_RANGE
9 
10 #define HAVE_FALLOCATE
11 
12 #define HAVE_FDATASYNC
13 
14 #define HAVE_FORK
15 
16 #define HAVE_FSTATAT
17 
18 #define HAVE_ICONV
19 
20 #define HAVE_OPENAT
21 
22 #define HAVE_PIPE2
23 
24 #define HAVE_POSIX_FALLOCATE
25 
26 #define HAVE_READLINKAT
27 
28 #define HAVE_SETXATTR
29 
30 #define HAVE_SPLICE
31 
32 #define HAVE_STRUCT_STAT_ST_ATIM
33 
34 #undef HAVE_STRUCT_STAT_ST_ATIMESPEC
35 
36 #define HAVE_UTIMENSAT
37 
38 #define HAVE_VMSPLICE
39 
40 #define PACKAGE_VERSION "3.11.0"
41 
fuse-3.17.2/doc/html/build-rhel8_2meson-private_2sanitycheckc_8c_source.html0000644000175000017500000000500214240475423025753 0ustar berndbernd libfuse: build-rhel8/meson-private/sanitycheckc.c Source File
libfuse
sanitycheckc.c
1 int main(void) { int class=0; return class; }
fuse-3.17.2/doc/html/build-ubuntu_2config_8h_source.html0000644000175000017500000001530714240475423022133 0ustar berndbernd libfuse: build-ubuntu/config.h Source File
libfuse
config.h
1 /*
2  * Autogenerated by the Meson build system.
3  * Do not edit, your changes will be lost.
4  */
5 
6 #pragma once
7 
8 #define HAVE_COPY_FILE_RANGE
9 
10 #define HAVE_FALLOCATE
11 
12 #define HAVE_FDATASYNC
13 
14 #define HAVE_FORK
15 
16 #define HAVE_FSTATAT
17 
18 #define HAVE_ICONV
19 
20 #define HAVE_OPENAT
21 
22 #define HAVE_PIPE2
23 
24 #define HAVE_POSIX_FALLOCATE
25 
26 #define HAVE_READLINKAT
27 
28 #define HAVE_SETXATTR
29 
30 #define HAVE_SPLICE
31 
32 #define HAVE_STRUCT_STAT_ST_ATIM
33 
34 #undef HAVE_STRUCT_STAT_ST_ATIMESPEC
35 
36 #define HAVE_UTIMENSAT
37 
38 #define HAVE_VMSPLICE
39 
40 #define PACKAGE_VERSION "3.12.0"
41 
fuse-3.17.2/doc/html/build-ubuntu_2meson-private_2sanitycheckc_8c_source.html0000644000175000017500000000530115002273247026252 0ustar berndbernd libfuse: build-ubuntu/meson-private/sanitycheckc.c Source File
libfuse
sanitycheckc.c
1int main(void) { int class=0; return class; }
fuse-3.17.2/doc/html/classes.html0000644000175000017500000000673615002273413015561 0ustar berndbernd libfuse: Data Structure Index
libfuse
Data Structure Index
fuse-3.17.2/doc/html/closed.png0000644000175000017500000000020415002273413015175 0ustar berndberndPNG  IHDR KIDATxm @!Gk7-`&sts@k}2 P%_N .:0Dk›x" ֛)x5IENDB`fuse-3.17.2/doc/html/cuse_8c.html0000644000175000017500000011451015002273413015443 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 COPYING.
*/
#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.17.2/doc/html/cuse_8c_source.html0000644000175000017500000016233515002273413017033 0ustar berndbernd 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 COPYING.
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.17.2/doc/html/cuse__client_8c.html0000644000175000017500000003230715002273413017143 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 COPYING.
*/
#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.17.2/doc/html/cuse__client_8c_source.html0000644000175000017500000005025615002273413020526 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 COPYING.
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.17.2/doc/html/cuse__lowlevel_8c_source.html0000644000175000017500000020574515002273413021106 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 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.17.2/doc/html/cuse__lowlevel_8h_source.html0000644000175000017500000004505515002273413021107 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 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.17.2/doc/html/dir_13e138d54eb8818da29c3992edef070a.html0000644000175000017500000001162115002273413021336 0ustar berndbernd libfuse: test Directory Reference
libfuse
test Directory Reference

Files

 hello.c
 
 readdir_inode.c
 
 release_unlink_race.c
 
 stracedecode.c
 
 test_setattr.c
 
 test_syscalls.c
 
 test_want_conversion.c
 
 test_write_cache.c
 
 wrong_command.c
 
fuse-3.17.2/doc/html/dir_23ec12649285f9fabf3a6b7380226c28.html0000644000175000017500000000534615002273413021202 0ustar berndbernd libfuse: util Directory Reference
libfuse
util Directory Reference

Files

 fusermount.c
 
 mount.fuse.c
 
fuse-3.17.2/doc/html/dir_812ebf512ecd184ed1d39ebdcddff7ee.html0000644000175000017500000000635515002273413022064 0ustar berndbernd libfuse: build-ubuntu Directory Reference
libfuse
build-ubuntu Directory Reference

Directories

 meson-private
 

Files

 fuse_config.h
 
 libfuse_config.h
 
fuse-3.17.2/doc/html/dir_97aefd0d527b934f1d99a682da8fe6a9.html0000644000175000017500000002016115002273413021517 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
 
 helper.c
 
 mount.c
 
 mount_bsd.c
 
 mount_util.c
 
 mount_util.h
 
 util.c
 
 util.h
 
fuse-3.17.2/doc/html/dir_9bd412e6c6ed29227c4172ad8b62b221.html0000644000175000017500000000511515002273413021237 0ustar berndbernd libfuse: build-ubuntu/meson-private Directory Reference
libfuse
meson-private Directory Reference

Files

 sanitycheckc.c
 
fuse-3.17.2/doc/html/dir_bc297b8ee9a3081b188fc7e3fa93504a.html0000644000175000017500000000424114240475424021430 0ustar berndbernd libfuse: build-rhel8/meson-private Directory Reference
libfuse
meson-private Directory Reference
fuse-3.17.2/doc/html/dir_bf10854db571cf772d4a6211be1232f2.html0000644000175000017500000000424314240475424021241 0ustar berndbernd libfuse: build-debian/meson-private Directory Reference
libfuse
meson-private Directory Reference
fuse-3.17.2/doc/html/dir_cd1412f659f324388a4b25bf5c39d8d8.html0000644000175000017500000000427314240475424021301 0ustar berndbernd libfuse: build-rhel8 Directory Reference
libfuse
build-rhel8 Directory Reference

Directories

fuse-3.17.2/doc/html/dir_cfafba98a580ce4b62f8a6fa96d7cbb0.html0000644000175000017500000002144615002273413022001 0ustar berndbernd libfuse: example Directory Reference
libfuse
example Directory Reference
fuse-3.17.2/doc/html/dir_d44c64559bbebec7f509842c48db8b23.html0000644000175000017500000001127715002273413021431 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.17.2/doc/html/dir_d709b210fbffdc23d8da2b53f1d32b88.html0000644000175000017500000000427614240475424021557 0ustar berndbernd libfuse: build-debian Directory Reference
libfuse
build-debian Directory Reference

Directories

fuse-3.17.2/doc/html/dir_e1dbc8ba94a86723d4c32227b7c46099.html0000644000175000017500000000547715002273413021267 0ustar berndbernd libfuse: lib/modules Directory Reference
libfuse
modules Directory Reference

Files

 iconv.c
 
 subdir.c
 
fuse-3.17.2/doc/html/dir_e68e8157741866f444e17edd764ebbae.html0000644000175000017500000000376415002273413021370 0ustar berndbernd libfuse: doc Directory Reference
libfuse
doc Directory Reference
fuse-3.17.2/doc/html/doc.png0000644000175000017500000000135214240475424014507 0ustar berndberndPNG  IHDR}\IDATxMOS[sa?-XZ(PD4 AWbu`b 77wHFCԁ/`voAPqP@ 980 +y^Z9SW\83g3'Nçl_bpV"ֆXd]3xM[1W *PGz/Eg{ aoV:这1$RW,@56-,m/蹖 r5T*S(Vf89u գwa=<{ҡUr+dDF$`zNܮ0Q3~_^N=vpTLT}kqm<?ZhX_ݥ[) `ga_*2`'=F2EP l=8Wv%THqɿ<"GxH{#֫aJmKsVءM^ T ݛr߽m_?Wİ#uIENDB`fuse-3.17.2/doc/html/doxygen.css0000644000175000017500000013107715002273413015422 0ustar berndbernd/* 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,&%238595;"); /** 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,&%238595;"); /** 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.17.2/doc/html/doxygen.svg0000644000175000017500000003614515002273413015431 0ustar berndbernd fuse-3.17.2/doc/html/dynsections.js0000644000175000017500000001756615002273413016141 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 */ 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'); // add vertical lines to other rows $('span[class=lineno]').not(':eq(0)').append(''); // 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(''); // append div for folded (closed) representation $(this).after(''); // 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+''+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.17.2/doc/html/fast17-vangoor.pdf0000644000175000017500000222216515002273413016505 0ustar berndbernd%PDF-1.5 % 2 0 obj <>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-MMfNv&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&<)İ>*Z1mZm=%_[L]Bx`\ G1+qì$!`Vkd"@ٕ>Զ,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ł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^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>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+N05zs֝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)`waFte!UhZ K԰[; ?w%{b`a@'7Dgv洴FP'iD}LLSiERAƎ+ptZHMJ(Gs*ă!8𹤘([qTܱ;9Xaˁ 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}GVp&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ӯjUlnq5vk>~[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!>݉->4CBB9C"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}Gj1z?ę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\ZM|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\LoO[ux; Mg[Ҹ/QJ hDJc>4 MʭKcf;N/hA` TcОJX LwO~h bIa)N3viE ^\7%E6}兴=nSRR4Pדt?tE²4J 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>=q6ކbqz$ Kƶu CE? k#O=MVU2̬Q_ -^'2˲Pf*oV!hYc.N4 S#>Dמ+ ZrA-?o@5bT|XW#j/.K#Hd&P{Ɲgya3WRpqŢ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(+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% 瓶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[;CZnؾ)| 3gJXc? ̃NM eU#T0w bL۬9kS&DJvnr:!<‡ &-Ъ"4HxElJ@? ~Ɏ?E|4X|jfsfִxKhryj Afm}m3Ռߖbc7 06õ߮.1aD,GE[`T dҰtmd\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{!Tz= !aH|t7,T#pb LVKmHًDtu^Y?+1q#Q =?#' u]V}YHPy`@+Ɵnщ{pכ0i] Lq)_qVj?QxMQdm}D1W輲dJhl,C[Cb,yI`iVRAM$V[q #iU wxJE AqhꝘ9`?P~(܏d7)QYˏč2#2 .?Y!]Ӈ Xf)`Nu:{& KlFRTlmJ&>`ɋ֙A6WF7џ_G쌦:Il;!y^Ui 41(-PPj~rQ?^a|cxT!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 wzzHyp7Wڼ$R75?"3nXQBވ-E yYh`=H D>BؼKL_qhKvu-f-59ezf _ղ &vыuPCփ#)'NzlSTEYgrqbgw݅Rv mjgQ +Ἆ) mmi/2% a}$Pa1fa[<zA]?GVlCnY$O#g`>#75!Chsׂ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? UI'OSv Aff`2U*jߨ]Fx(F#'F8 A; 0S3ȱ@'7?s|&TN\RfU mȯ+,,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=~ny.xL\6 (.y??='A욾. â43S]Cxۙ.{{)ow*}eSG=vkd/Ʈ{ӔD}zԿ(šW{[\q*?Ň4a6+B>SLJizpQ< T9)vr-# $MZH%;zaa%yj?+]4(.&<岯 (~Dq>Hduij|%T+QGvLew[mQ:uy][]@aŰ n i(% .3( -1ZGm.+XP3mKg?zR-z"V|_f\v*Bf69 ;{k覊1v+p',m4ߺ`M f)o0^0A' zU-岙{IӮb(ԅ\mR9Tz({\eޯ>W3XsV~g*[EʈzP}ũNH>*x4̚\2tuRX:\-b5 %׼"Оze}q6g3nEDqIM {yPB=lF2Ƒ_~GICVn8Y[;"zDdoaAq˭$g~`=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-I84K6N[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劌73b.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';\2ikm(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⹿@nKWopUEU'Pj2:vB {U{XfYqs&Uĩf{v(/cMݒt w*MQۻbܶYC4ϔ6@L윰)DئPÔUYcͅmg~ȧxsygBȔ. Q̵<n0j{Y0a?:ƕ'J9 .KTxuOq[tؐYգ'G2aHKl"${yzF_۸]~DdsWOJn+g EA7{yU#, >+'&Յp¹}oUlMhwU}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)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 }i3NYJށ|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` FU\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^{bTUv: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ͰNPvYX8jMb.P܆&wJL), LKϙ݅Py5yQEr׆ijر) CT)nױzY۵754f?lDB7z4)龻BpF<쵻[r Fr j# C75&U |=z4 )NKCM/c<~Q8eZS`[8 BWj`hfK^ďz¡a DLou JPA-UJ`+aEȵKln@3ָ,kH` [Ԡf"AoeytExxFz_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 #ɮstytsvmLf5}|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[.$roS5I\dH>'î/c{NUt?%8&!ݼޓrMcܼY(h@:|ߐ's韉ZT-xCޟ)eqozBi.cynu:c1Wn_  ; hûmD,T[:S$;VK2J01Aewt6|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}%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``]8MQҟ 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<ݥ !Ջ`qWnZ B5\|l5$9)2\ 6d9J?$i^(:UnPđ;l-L92  =&=aOeDa\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ݗ[!fD-"93=s(Zy % "Nn$DI}FOi'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+֧YcUT1M5>; vh/5i1 b{KE<\c{>zWcQ<}  dp&9Pig=ܭ-EN=ìJS:|!X:pǨ^suڏ^bu}Ѹ1̫6Bv-!2I:Fz?){@)ƋDZ[C7Ues`̣b[z `s6nfr/y9R,&%3=phdyA%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ĆŷKRGAXok.5|QlV.1ݿAd lΩ#;Ja E&:@شu G0䧖ek]챲;f&t#6B TIAiu)`Ŗ\]*hVYOB\W8<· 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"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}wNBYVqCgW6GfŢHYԉ9;2Cgq.`%{H9,[fTM@eSt@ڲ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Ɋ3hho.ERk$r\n `";bX7 2fSnuu ԂU+jQ_ )Z1뎨yGJ jG~z=ed֞ˡId]ŵm!547^qy+8v]8K]LJ'^Bܢ|Dăr.Ā Shf5kctH2 +,2wCoduab1*}Z zTMhF4Q>kګQn ya] ١{`Q ƅ{]]ѭ1:a6߁ݫksH{9]KB iN[r;_^L%r%ZgU>5omGPqQ/OLC!!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 <> endobj 1 0 obj <> endobj 4 0 obj <> endobj 5 0 obj <>stream x+| endstream endobj 6 0 obj <>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 <>stream x+| endstream endobj 8 0 obj <>stream xm @D)\SDBNH^roC,fyQC IB"`nڠf<uy978w 8YB3 wQzܩ4TrtOt=ŏT`1i4 endstream endobj 9 0 obj <>stream x+| endstream endobj 10 0 obj <>stream xeA @W̱.ʚy0x{V?-ѡh8|3C IB"`m]jGʖצ[[{x "$ymTgto_γ[/>yaxRkK!0٘4U endstream endobj 11 0 obj <>stream x+| endstream endobj 12 0 obj <>stream xeA @W̱.- ^OKt(93ߠgAS!ڶV66ܢgmXqk}JSۍse(]F|F {v=CNvbtf d򌠴B7N& }Ʉ%47 endstream endobj 13 0 obj <>stream x+| endstream endobj 14 0 obj <>stream xe @})ܙR%rB7IKP)oP3`H! V4tmc5 V׺Y:.Vܩ4Tvumv_%ˈQ\Ϟ]^ķp7:d<#(U_q83!844 endstream endobj 15 0 obj <>stream x+| endstream endobj 16 0 obj <>stream xeA @W̱.y0x{V?-ѡh8|3C):C>stream x+| endstream endobj 18 0 obj <>stream xe @})$T`#6$P)o3(FHdPvD5֗s`zoĮ՝jEo7)ͮǙ9ٱbq237^\,BEoN)Ҽ P44F endstream endobj 19 0 obj <>stream x+| endstream endobj 20 0 obj <>stream xeA @W̱.j- ^OKt(9̠gAS(8F ImU (Nצ[z8 "$yTigTo_γ[/>z~pR)l< iM/Q4C endstream endobj 21 0 obj <>stream x+| endstream endobj 22 0 obj <>stream xeA @W̱.ʪy0x{V?-ѡh8|3C IB"`mB ci'R2Tsxˁ}Y$tp?zdp<͝*CΊMYO},'Op(:ŦdcZ %4= endstream endobj 23 0 obj <>stream x+| endstream endobj 24 0 obj <>stream xm @})ѻR%rB7IKPpfAE! Em V׺Y:.Vܩ4T\mvٟ,#>+Gq={vCNvbz d򌠴b7n14Tԇk4 endstream endobj 25 0 obj <>stream x+| endstream endobj 26 0 obj <>stream xeO @S̱.Xa%7*\7l~ZCq jz4ňc mk3Mu9ϳ[S&EX-#>#Gq{v=CNvbtf~t2<#(S_4EꙆP4@ endstream endobj 27 0 obj <>stream x+| endstream endobj 28 0 obj <>stream xm1 0+nE:TpJ#t->stream x+| endstream endobj 30 0 obj <>stream xeA @W̱.y0x{V?-ѡh8|3C)ELyu'o8X'w)0 1}4I endstream endobj 31 0 obj <>stream x+| endstream endobj 32 0 obj <>stream xmA @W̱. K nUV?-ѡ0 *zF,)$ &E>h82 T(cMP/ځaL28Nꗣ3P̳t]śZCq*01fe4 endstream endobj 33 0 obj <>/ProcSet[/PDF/Text]>> endobj 39 0 obj <>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 40 0 obj <>stream xڍ:vFw/݆1[שU^=Kyuj'1U5{f6}}deCcyhME<7>ps?qaZ(Z:?nLlNkitz?nRA{ & 4R8UvGSʻ05f]ݮV&ɢ6? \gQ-@<- 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=Q@bZS~D!6:똑} 7N tcp{*ƑɡQ.G7s}[K/jXUGI 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^ %V6>~TJ3M_Íٽ˲8/ffוQ{?g?.v(7~vϒ.R#_WN endstream endobj 34 0 obj <> endobj 35 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 38 0 obj <> endobj 41 0 obj <> endobj 59 0 obj <>/Font<>>>/Length 1096/Filter/FlateDecode>>stream xWKo9 ϯm3qlv@=8$vkg;i$H]ɏDQ`r\}pFzT6U;~4d>95DQ8Μ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 #$'|H1h_߾ia`[CsYhg6OikhƂR BRIޏ(wVtJR(nFYSNCYSqWKL׿uع/>:tJC:u1\]slw <8<γur竤@Qp:!ƙ3@l%ǜ !o߶q= M8{n7۾=<<;Zف H sqtًN{&[ID3W=Ͽ*ÓJXŖZ߯nq++*SsCqʪ~93ҪTY&eug_f~tpJ endstream endobj 64 0 obj <>/XObject<>/ProcSet[/PDF/Text]>> endobj 54 0 obj <>/XObject<>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 66 0 obj <>stream xڭ%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&Xmil7XvS`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?\QMf8dE|4&&VQQOxU [cH \`:rK.rs4,5H3#q/Ԝ` !.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&CaX|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+zily{,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/kuO^ڃ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*Ld7z8A7=&jE`~(Q[y\^9&Q}_<Μ21uDΦv]w;{ݾKM0JbW+ uˌ1v1t@<‘rq@<$̐$O|c;Cy }cx>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@Tug0JϘ&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 <> endobj 60 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 63 0 obj <> endobj 69 0 obj <> endobj 70 0 obj <> endobj 71 0 obj <> endobj 72 0 obj <>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 +_('+ ^i60oVUk$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 mcOnҺ\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/=1GYgn7kZZ@""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=j8PI endstream endobj 73 0 obj <>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%/:^ԙ>/Font<>>>/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ۘtDI*hR*Tmڐւ(ʈ +<"/>("`PDR9Ʃʱ^`QkY’ c|:G/K*o<$Q6ZW"Q>h[!ST^82k'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Ū"ԑ3zRX [1Hm^\z x b6e`^^ƥ.̊u+kv,xL/Cv-,c2Fx >tp ,h4=Ie{<`񽡐(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٤ono;%*w}{߾;t=f?X)x~0g>ݗ~|]´ U_de}u9̠\@wߦ/ۯQK|if%>FE6ȇ(>O-%D->/XObject<>/ProcSet[/PDF/Text]>> endobj 55 0 obj <>/XObject<>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 81 0 obj <>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!LtebtbSfIciC`8 VpJ(NgX%<Q9ʣ)*GHB+'/]HgӉ8T>ܯ5^XgPݥԝ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@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/P5Tzm3fo55z9l(Ґ3Zb)˽i~܋waAP,P ٰ# OL;GR̸o܈t* NkGf&ɃqK3/Ue`׃2oQ'45rG !Lb!"l4,LZC_">fκ p^Xu?:i3ZN| _ra`פ Sv48g 6=\zS؁70 Bz\c}xas7bxhp }-]5>VX*I QPǛŷQ&`qQLW=bgyQ&T~Wt <~ *Sq˭c҆\W/:p6[58uZaj EP@KswBXQ*ن͝1x߂*-( {^a !-,8U#K^+~ajjM< dؿ=Je`hI أ>-e)/Qn̈́6Ю%53B֗GІSs$QMҤ{LM&`y[7o.l6oySw.$3{u5bxc)Tg4$B,AυxAmOAy2؍h}b8. yU%N Cj$K/_ .Y{#s2B,|n|\[tq }ԳQǘ<)'7åv-f` ,f`S̓<09@:JfxIpg3ҽ0qg|Vxx=|Ei1Wh}y\qdl3Ղq{WaJX.`eB GbJ+Fo{Rr`АU|ÎwJQZ~J6a:Ox7o.k dPl7ҁj,i TZCv`t<2SyCi" YdJ Pl$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{0alŵLRۤs)`Mt"> endobj 76 0 obj <> endobj 77 0 obj <> endobj 78 0 obj <> endobj 79 0 obj <> endobj 82 0 obj <> endobj 83 0 obj <> endobj 84 0 obj <> endobj 85 0 obj <>stream xuWyTS׺?1sTL(Mx8:80 `&B 0(" (uXmުKk}>]wVI} < cAa@a`(s=AϛBC7x/&^7ރA3 &h[f 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 cn7) 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 ]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:v5&\0}%I0"V1B;ɸh<38ߓ?uFPm:.VO:-kT\F58 z])Nuwz̎' qp' 'kkQ$Nv \UIts6WV=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ךZk[;lg9Lh=)Tj 2ϝ*mU#WHl7{(D ,u&ތgq B<~v~j=Y8 #\}.CUZMzKS',-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 <>stream xeTiP[}BۢQ,ՎҴ$&񸩱.b1HbI.}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|.|rfOX ]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~)>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 <>/ProcSet[/PDF/Text]>> endobj 56 0 obj <>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 91 0 obj <>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_'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]^M6OQ 콅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ڳ42mۊ 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 0plG >ʁ;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 ;3Ch1iL: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.yHc-h3I!F/a}nyҷي4VjPMԼD?3 ~?C]D@T'ZZ*7ET=@X/yq%8&#y | )wDPV(CC_ fhRa9GmNioqI?dOރ t!ZtvN].WMP"i#)8tJ' B'l0Ԋu~PEEkwٞ Ƞ5ݮ-Hjg`/8Dԡ:r)bG t>$MG8Ik|"NJ :tTUO·H(Ҽߨ*cN5ST,YhHA,R@| q2qv^]or)ֿ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 <> endobj 90 0 obj <> endobj 96 0 obj <>/ProcSet[/PDF/Text]>> endobj 57 0 obj <>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 99 0 obj <>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/!hwsxoX_`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%\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; 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ђ֥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 <> endobj 98 0 obj <> endobj 104 0 obj <>/ProcSet[/PDF/Text]>> endobj 58 0 obj <>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 41 0 R>> endobj 107 0 obj <>stream xڭ;ۖ۸ Yvbq 8n;dֳn${<~(t73T_WAv/"Xq)ԽJnm|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 y2@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 TvdHjH؄<' 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 jXu27/(@"BxӑXh Y3l4&KrH3:D2 c<4'C[G^tk] ڙ@>C @ohVU1L!_Qi8 أBѫ[C %>!>bK%\>8'n(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)wl`}$ [ޮ@\R$<3+4DFubZ;_ o,kiex%v kOH[9?-Ɇq}T܁z;çe@"I`7ӑc,Q&yTݸF -9-`2dD$*RN_cZrEp`K$Ga$&z 955D9 0x5)ѣ@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%ЦoGGEyU9Q9{PY ]XcO-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(8eSKhaH G%N`nt#kރt.Kބ¿%E2 ')yՖ7,X O=6.B&[,͢l ǹ/j_`)g: 0"xun#nڧA=l:V ͩֈñE!_*Ε{sM7XY0m>SE֘03t({'(u%)C\3!T|Q~AzF_8Ul2q_3@g)|'/(4ZW.O.N2eak* &sX 5] .X5U'5j8zOhAvH-`X2OT`)[:mlGfL_BnMxSk&BZc9\.UITF}MXrBu k:ix.46S*S[CCM}D w൲sM]=}!4UVv˕u5C~6r׾b<5΋};.M̆?3 ݗW8WXigR$I(TqGC[Zf> endobj 106 0 obj <> endobj 112 0 obj <>/Font<>>>/Length 8953/Filter/FlateDecode>>stream x\Mdqݿ_Kдc),K F` L(}ɺ6oGeeeՇܻ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\0L!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}P7 iRZ{VX'NRud%;=* mI47]P!{ri91F'ݦ⪁,سڬ@{ P}Cbǻ(WzGN T6 1cVܞ6Lt MƷ ,CiGC9W `?L0\*C7GM.qqQgښyT2@ 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 6a;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ᶱh1oaR6F[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 ;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 +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~AK/ 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ֿǗء}[5xK#4 SlM, 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(uWTuo&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 CX wx>AA0s ݊yk*^o%\`6IRW/o]ݳV%񸩬*ڵ1 7Dhݬ~N>0o&JD&#QnA)5LUiuxjB:F}Sga[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~JK-|D͏;N2wC]2.d/zB枺 .dKېeݚulck[]ԦYVW{}(@1L0p-/˶ұOH}& u +tօ}_grq endstream endobj 116 0 obj <>stream x+2T0B˥kJU endstream endobj 118 0 obj <>/XObject<>/ProcSet[/PDF/Text]>> endobj 119 0 obj <>/XObject<>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 120 0 obj <>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~Rtx # ݀)`;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=G1TD96m0 P w2;&gpQ>d*CBDU,Vi|^%\s~yy I2dvL½Ϡ=Y&~2!_0DRJX6_"_eSG8}O{T豮V+̹Q5p3)۩ՊϲjEi*4%`2g&IϫHQ2 MIOAO= 05RGHD|D<$>dXuT`9P6^γ4< O6jL84D<Dž_iP>f6 ̳Isq(ĺ\d*CB/%)CY,uy[6a"ƹnvFgz ;_ֆʵޅ`16rz2C< sgtW29ycM} 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-V4favd`𾩌l<|rϳ@\x7V#PNiGL k0 XTvժÛ"]ġYr=zdyMH3v\F``۴nnMjYء A|{XHA/x[\Y"ܭ[4·˯Wc56R[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텢!nMKB=$kܬ~hB)d!݁ )Tmw3o ;%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^\Nczua7G'}_ 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 <> endobj 117 0 obj <>/ProcSet[/PDF]>> endobj 113 0 obj <> endobj 114 0 obj <> endobj 115 0 obj <> endobj 127 0 obj <> endobj 128 0 obj <>stream xmWPg^YHn$FHIpq .M3S :B"Q?(H4ӻq&qlI0r-7Hշoyy^fcXK<#bD1A Jj Pl3g5R^ U6ep}cXy'q\ZBDXxܽZ~ÿٲk.?;#bRDQln{'saiqA!!o^AѢ(#qq7޲e#DDLpr|'Ļ]EaA a[Ɗ'IUrlB OL: x8Tohc{B$BbO+`icǏ ڂi*t>Dja5bB*DqXh7k Pm7 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%tNWNC } }ߡƁ}E*𐀱 SP#ӿKXS|uDm2)m.O]6{. WqT m^^6guՔ&'ůvp<#&D>ѕA "9TruEtux.e*fBMŜ]rO.%"#q<0{[-7Z"60R = '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%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!))@TJ'ƵV聧OxZ}/ĸf`:Y8H65gZ@㭶c/k>7ݲFE WCKSS4?TOh*ʬәʉ3Ty~AҼySlaY>f Ȟ}x*K頓YqP&̊U-EKu׳s~4i(7Is@f׌ 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:.ՊcGtcHZ{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犸|%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 <>/ProcSet[/PDF/Text]>> endobj 122 0 obj <>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 130 0 obj <>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-TyOi{w|Twt[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̳/N87lNIr_^?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| 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%wJ2vu"v-_P: 55B XDCUŮhfrg+IpWj]¡C.Z^p vb:)h+*bW';_ TBvEuq&]Ղ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(AV ؗ,ڍWݕlTX(_)&8nb3WE;wp& QwUb*PTŗ; %B- TSPPj 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& 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* TSOR dĝ@*T>ݰj,Xm LU7^L߇;xn8MȢMrP6kvE3;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ּWL9q_5`'8h;pm?j W]PW S|0yrcgh^<<2 Vj?Gf'7e~دbe?%P2ϓ42?-`ͤa=\t}[znbk6`YP#Lְgj5HcX: )9(,ְRdE@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=MlFX2hRaNk^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` 6qQ,?r.C6"} w]aBp !q!諰tE6r(:( *>|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*00sfbg3fFhRGw2O`@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`!*0y 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;%,%2PNIKc{1pX._ c\5!CW;ujx^&cǪȞ7Ո=X VUq+_X<Ű`daQ V.bX=,aQI7=B%rBJta I[11z;LPMiтӢdfeBA2@{Z|KeP,PM?%*ϟ`f )f<{bBUn@&5|(qd j}.‡&GKmUqA+_(wM|ڮ{6D%}aLr&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 .3TX'D 02at\nl"atU"A"lmO31A3H):E"Lhx0 rws-b~ ~9<ȡΩ- Nq^ΰ2C!؍m<.ЕV=wOhq`/ZOhľ,Υpz\hktʈ} ha%\ac2^? k" @|?K0uEpwt|su^?ޏ"-`݂Hkb w*ʂ^ ) dH W+Pk]>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ºH7rPk 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+L~Mޱ%A )9WL.LG?dG1#^ȶ0!(FG/ !HFݝdD g)$#eEɠQ F;%}dF}Fj& h%ԵC IXWOD Dj<0r ^<$ M̃Y&by)M|hUC@%_^hCDW$Q TL /\پ_?}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>/Font<>>>/Length 9243/Filter/FlateDecode>>stream x\M$q_1G)7dْe#,$jEGJ {gfgfy;nUʪ_n=7|n}Tn}J~哾}v[R}~gOy y߾;͟V 5ۛݎ?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 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@R5GESI0D1ZRFKYBTb3m `#1 ?76`q"#7ZY1Ue,ԅ~rLx&}4 k `15 ])rp9j63h\Y(&qӘ^d{D7#Zo 89BZ؉pW> jaCgY98 <Պi0:4ZўiB pV+a;fΚт :9߳3ZSvF+$ $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/(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+*%"\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{a0Hn_ GC a$iR"1cT[ 0h #H^pJ2Iua$` Ma4FPa]FJZH ֓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.^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$VcZ1E={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=B4y:B`tj&IQ5B#dކr0Fhvu(]#4Ab <]#t Mu#`zt/\sr^u]?fʆ]+v )Ɏm:H2VUF]2#WT]-iHDIEB+뼩8FIZ&ϩxePfe^zpa e67d ,_mI^3$17_߿}R{bpjO>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.lf@~Y`u1Efx`Y]3jк׬.qʙ/K\a.񄢻K7yj]]"uեDyW祳]]"$ `~//Ο=qXbg>b_obKdb]l麄]la'=bwqb|[lK ^l ?b8 endstream endobj 135 0 obj <>stream x+2T0B˥kJ\ endstream endobj 137 0 obj <>/XObject<>/ProcSet[/PDF/Text]>> endobj 123 0 obj <>/XObject<>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 138 0 obj <>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ﻟ>jQng^ 5VF+>DͨI܁0*T^弹TSV}ҿ´U 3 Q<6qLcW$x-bkΏp|]lhŪ!`"i8 VboP! #731n5ڄoPǎ~g0Ό"ڕCW m l\KOJ#;ȉhNqW"yfDzX٨CWnBZx'G;6e`۞j;nKPÙIz=;Odէ δ(u_S]Yoj ֱI20&]JM;0pnft6^GqN7@ve /&9XUK׸újUni&ù+x@Dн#W>XC(O0[,j ikV_?As#Iu~zCFJԔ]DNժ(K+EUxߗ6x %X魛-va?sM ɈRI6 id)m-#kZ?Ta<:О)/ySO7wHttyK"?m!౾]{BnN߲]v57jتsK Aʀ;gh)Ms˨\AlLs:j>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*UT~_7MAlZE*…^/[މ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ߴaQMsQB 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+\sw| %Fp0p[~Y}C~[4%S6 ޡ鿍p>/ProcSet[/PDF]>> endobj 132 0 obj <> endobj 133 0 obj <> endobj 134 0 obj <> endobj 139 0 obj <> endobj 140 0 obj <>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\bbYaB1+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=|tDArPYNxA/I$/(PS#A$%"nϙ?|ynt6?&Я&J ,0j٥$^B@BY9)SR0Jp>FVwAؒТHl.$AcQޖ\"y"H}t"VqLAz\+cu)ٵ + UGn =z] n6\n _S+DMj8m{Nb ޗUU6%@}DDqB@m?t6]MZ\ C9xX"O*/1a*y^rNF\)ְYubhEr'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^>?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"D}IYΐaΈ3yRE6}m !@8T[` se: ֪@^qPn&͋ST.$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 <>/ProcSet[/PDF/Text]>> endobj 124 0 obj <>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 144 0 obj <>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{}vo(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<*OrAyBLz4WѶ+֧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 +ӯ_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 Xfz4/X:aQ䱵'z b VEǃUD 矃 -,=,3b/jSH 4?rUd}\hm<8iYrQi H4 Xb K5uk[Wc`rl +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'κ;] '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#?;ө,,@0Hp@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ǰ(k4@{%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 <> endobj 143 0 obj <> endobj 149 0 obj <>/Font<>>>/Length 12549/Filter/FlateDecode>>stream x}]lmTJnRDzdT![$<%FBZX;sgGW/ |g~$ۧw_>|'~_RuۧOo|Wdȏ&ϩ||=ҿ}Ocp=~?s~%/$Qasovn[Q(kJУ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:䂮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!\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"JeB@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䩂,DT! M*?TAP!M -uSATU'QA-ZvT!/U~Sַ S;TA݋F 0vki8 #3Lwn6y+т>0E26UQPM, U݊npUUW}S+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(X A"{KA?(XHİ4g,E@,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-+,ᗕnL*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: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 ؏xeʹ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$ hyDij6<4]Gx`d#<b 2C"L$4!+N4c>ͼs}sۢ[4c=.ϗ-QAo`ь?F&Hhl-,-~,[Rh,EfWp= ò׳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  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_d>\uG}/ DF#cSid7ҕ*m, =.Kw;GƲ1頫>2Mk,):?ʱTfoLNӏ@g`Ј5ָո*\K[d5!B{G U,R7y c{l endstream endobj 153 0 obj <>stream x+2T0B˥kJc endstream endobj 155 0 obj <>/Font<>>>/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*ҧ<߰+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_쮌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|]LAJk ;?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 HNH{yNkFJԆa謡 5k>ag CcK9c0H=c0jyg v AEt3/k| W2f=0qo6dƟk "x͠^3"l<3xq_NxƇƻJH\OA'|vqM'4{뙇5= <p@dj#ʙgmЖ)bGyyM%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ʘ:;/VaC?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笺ҿ|+)~Cy硱nN $IA@l<4;4x;4<CcCc֡sx(i;4<=C3A<ZO?44B%֡vwhh:4a?Б}MUl{)QhCC#.`*e[}UWx~s\×n|~f~=+x50FJ 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ՁHp=ldU"\ R/3kf! YE@~QI%zA!:xz 8-+.ֳ}Upw7 2 2÷D ~,djQCPT#x@o o+qyqHɤɽ;%1g2Iv, _gw*`/5%t-vYeW>aGȍ2m#0|õ((>OdY,m#Q œpeYIUt7Tkcͳ%9*2C>v5, "+dz)-+age](BL8yfOo*X0#N# DnTeup* }4D*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*(,BITIU1RأlS, vpm)te`)|*@RT!B hB XiNUFHHb1 UM i͓:'D'oA4`'ZuR=S"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~* 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<,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< 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`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\"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+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ìTs“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-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~HPqED/,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]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]ň5tn0tsКԲ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=@@`0U]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 9y 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!̓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/'&uDwrb6PKzYCiz?]{>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ރBGGFKllk; N|?`Sov'h퇸AJ=N%mE@KmEt;?Ͼ"?QPG_c+FK^#+%qkV>@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*ѧ,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 4jvixuU WjX-#;ufմY iu9Ă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[},ɡ'3WZC<6xqdnk g2xt<<r>stream x+2T0B˥kJj endstream endobj 161 0 obj <>/Font<>>>/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&QO7F|pB֡|_;rIIe*N9ԃ ǔX Y#/OR~L/)EaeWJxB!նY51SU3Izoy CRmO(#®<5zpn S(͟z__`8eFzҪòQڙ\yFs%NyUo  \Az敓4c_G iGOSg4Mf q8z$qqi/M=U3BTV )Dƛ¯OZ^xF7;5z3X{͏=B  1[FE><YDD)!M6QwQ?ӄFGhiDa*%}ϖK*OiE=JV5k -fV'E;zlqd,Imzg&nN,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 E Z"f|BWP4kP EQh(# (FUP"N}W#bz4E 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]Ӟhx9NCln8G0;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<&$D3>!{PcñZ!Ǥ^}BwcPJ4oԗȪgz2w5?YPߨw`WLckA ц>l?yp S-6FӋ17'-eN_ gQ\(`6FEϳ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λf>f 3$`m/0zqv%t X@,qˆx²>#m,j0f2|YGf 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(nHei:|;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ć-޲;dB%ң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䈌P9zTnQyH>%HfBLؔOoӃOi0':9@i\: ЁG6TA6@Y n}Tӆ^ +j 4TDB[*?QwBmuQRBQQ $w/j#EBSS &R(qj0<8B,":҃S(袷rۃS w`( ŔM667< 8j)n]sz[,҄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>xjn6_- F@Svhk('y#/;DfܯH|F*#(\xޏ AD̰_;k҃mhHg̭ć )~vIQ xfp҅(yL~ArPH`tKFZԕ1DeQd18Ɖ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>@OfD`Ll}Lb!1ͯR0˜Y`Y47i3/*d=8=MjpB#N2x3o)[za8>b$K@@o[8iE(닓2RS.椈Vչ rYJ)Y]-@caY5]ә 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?<{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*& TJPV(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'6erRDiN[Cd>|RHB ;vIUHSMJfSd."+ TnB)u1*L`IS/B+"u@ ʩ-2J@g$n"o|i>B) A;մ#6GPX\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} ,NtFKsO5޴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ؾ!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{>bU %|ՊP%OHJ|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埽~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\o59k1> 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;/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篕\|[غ 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 jiװ]VB-c k8]emmB#ΥLOě>ulA>7CnީKyyxЖ>wn1~c%1u F5J"뛙]fj-9Lo"I#Yp 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 mkr :]C15];nv~nva[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/\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᫄|PE>\ 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盥 ;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/>stream x+2T0B˥kJq endstream endobj 167 0 obj <>/XObject<>/ProcSet[/PDF/Text]>> endobj 125 0 obj <>/XObject<>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 168 0 obj <>stream xZ[~_a*cEIbtw4MN$+[$f{.n΢PC;=ſ/ mъ_Tئ^W\*VwՏZ|4kEnQFGO6zY"O{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`xn*"P^8G1a =,N!zv e"UG臨$ 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 Wsme#],y/),x}T͆=n]S:;t&KOLM)t p 8I4kS[QLݚ_! e&iy" sUojvuX;^07 k<JҍKL\/*T2@-y]W ̐<Z? \r8ͳqζ45Kzk9/_'wDF vn`5bzV ʰ;ˊb >'}784<"\1$ γj&.Or{~γDl6gjL_܃, 7P^"T[%'qL-m [fX-ùM ,\l8f-q b-:8KGQ*HU'6;czqy\q g?W^qq%l PmꙠ65DuR-0 endstream endobj 154 0 obj <>/ProcSet[/PDF]>> endobj 150 0 obj <> endobj 151 0 obj <> endobj 152 0 obj <> endobj 169 0 obj <> endobj 170 0 obj <>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 =FbTWH{2PUgJa%fF³ąs5@|,BbxMaV"@c$L% ػ[0S#i:;i Ņ&8 т@'?[-zSR7Ewlu]"{>B,[JfSǣ0il?^D@-MFVA(`&?VElSHhN+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ڳ݄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|dSC5$ EEw4M ۿC՗VѩVv|'量8e${;Uk@cq85#&󦢛p; >J>a$Õp|Rp'6W 1)fevC9rg!DY_oSׅc>?? Pk 9t;Ǽlm4B#줐c(EP>Sij[rƙ3_V͜ab endstream endobj 160 0 obj <>/ProcSet[/PDF]>> endobj 156 0 obj <> endobj 157 0 obj <> endobj 158 0 obj <> endobj 171 0 obj <> endobj 172 0 obj <>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(PIq3Jwă Gɣ+ ׂ -C} ?Z|ҷ2 8 C(TRb଑*FlL7 8423h*_hڼWh./3=vy%zN ɿ \˵X+~f 3'0ʛSYiK6+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?l71LPv 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<!<` ؏@vnc|;OU3'~䏣I5RjOR5MLjo$rPwXSD.*,WLͼniٽ©/`:=ÒvRQ7hDXVU(:"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-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Ƭ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{("vG9W6rCu}4;EPxӦ?qy N{443& $V}X$KY]"ёY,;*-P &cgJs(9([/ǜ?{w0nt(ZTk;|>yѵ7ʒP ,BgޥؗA6zJ;C~~`G|zq]^*Ey4o~C~i6ۃh/(d/\qtkҕuD[_-8wrp_唗3ͬ/((Xf,4fa endstream endobj 166 0 obj <>/ProcSet[/PDF]>> endobj 162 0 obj <> endobj 163 0 obj <> endobj 164 0 obj <> endobj 173 0 obj <> endobj 174 0 obj <>stream xmX TS־1D+-DmBU['ZhT@ a&$y !L28"UV<}=wx뽵J7'{6S7;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| >&*HJmJt( COAXL%m\3(g(BC wLy:`2"cM8P@J.<'hs4\Θ)"eYpqNʮ򚾂_a>Ġ#1h~Cߣ$jb ܶHH}ODv.cpԵbyoc=PLL$Id"*8G޲K )`Y^Wv|#@y'tJ&uyE~ } /K:#n0;h5UIQV'00})->Q/>#.P Jxژ.p~^h8h_/n=q{%Tzޤ0B4w ڀh߫yN1<{aWzC-N k/w~ ~_E=uWH\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 062Y\C }߄>зPY5JpuPxHW,Zנ%&d¹-fJ"J 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:V8mdw(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?gprVL$.+˕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 }и8ITL-~R#,p: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"gmohch9Ee94}4yzϋ52g@rf6Mp+$ՖMj{i̴ӘibC [8da_Аx.a7/㧍׬ך;>B7Dbla},B6O{:dƌ53fI endstream endobj 175 0 obj <>/Font<>>>/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?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Қ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ԌɦG+/jB{VUՔJpyQ+(%x`7'HSwNE]>u D 19/`[L\M9u17[oZl۶8-ۖr t%0M-&ȃ8ʂqv yve5t2恉<ڥ]AڮlJY ە]W`RmۖWۗSX[n[f@^mKmK1Pή Km]x%^]x׺+ߛgWf nWۖbŐT-Lږۖjdql-L9\~ݳ7+anWRnW M+ەyڭJdr3`Y]Y*_ .P^壩YȊ]W #nXiE[Qku[` 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_ ؜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Ӿĥ/}&9T/Χ _7%e䠆T<'MDqޓ@wO0L#z7!!VO߀x=\!y [&@eG<ꈕHo"$ ^M}!@Q ^K xYoI=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% М|Vn}2t.g!= B/]9뿕Xv`<4 tAKVW:?~`|q*ġp1Wëb.CcuoO!P" UZrObЫ/.A㲑rP js|R!'gz)xvh\Jm.ϸ$[Y5Ԏ9t gTp’5]Ompgq"^72)&+:Ř eogxpy!fEeNv4y՘WgȊBE1+$g\j*:}TXj°kE-)1l[ .єU A:%jp܀I`J&Bj2Spg.P.\b @&;(ހW9Nz^fmOxULv'+3f~xy/*C$5wy3 d^;YsxɂwbމZLq#f>ť~.7Ķ^;y;1kݼO 4x!JzoJ x eD#^BRl9$p~oKB '4}nQ<O6CQ/F<@ 5l:jM4/?/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+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?sxɞ|"u=H$J!L{Ob~[|hAde>hXTDxY2_xHdO>Y{ܞ|wY8$Z"='A^͓:I|L%aXZV$=:YyM뢭:C`S*yE[K&uhkA@}=JUsO`y:[>* WJ乧j_x4۞{"o:)fy7xsO<9d>ښ&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<@@\Ȩ,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/kwcp-Cl"?p nzȨ2ڑ\OTj&D\̈́J(WDXG, _mW6ӫx[%c]j3 ;h$i^6m b h쎴xX(vF9im-@OЭ/%^-@Uͮqy`PR*ie#vRW9,>"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-[SlG僖^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 kjxaxldeh^}t74lxVeժ<(< ByY+cY/k-^ZLx^Zi=tt%uط7Z ?e5+tVV*rB kY< kY<>%xl}eZ+r˲ܔ7G񟱝ԤQZoOϿv>68H\韣(4z~nQO·z{Y?G=1/;$IٟS푸fezbPԳ"P,fH\ |J/ 1DoS?G=Qg6<·|;ZݱΧz=c;ZiDOz8ꉨ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ǥOzKqOzKrOz[YrQOJ>妞ׇv>ك·oS4)]yԮ7ZglS;iTC;B]'v>)uޣoS^G= |jS'OmbWhS |*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ż縊@oYHWM>6j&qO>Gk1Y?G>Nc>*ss\E7WM>+|Nӆ:ٔ99ˎyڔ99Ϧϑi|6 =6F'*gSxjA/ٔ:ZЛ|ve|.=+tc޳+?*'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ޑϦבOgE먘Dѐh3בOABޓbDO{֪בO '+! mK{RȄ'u41 M{R센'e=1i*TxE#:+ཁzJ1IY<ż'幎zR9& &7@%CyQ8.6/7}|^\׃J?"{B`P\ EȆ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,-e:0=/OUt12#8)Q#wIC"-|5?ݴ=\%mM.V4z240F#A4َ F" ]U4li4Z歊cAsuu0L2R1 vŽ4.U)C-@AN6'6|TR,+Bcj1mJ_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@cRS1kUh0J_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&ˡvBlqh| ` `!^>lZDZQG;o},+SW-q wx-))ύ.]i ]3es›r=G©f9f57r¬X61cx3eT0eU%P6Y\ |dY~>2"+>91:ƨa1b5-C3a"1x01 ƲqhX(]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ލ V3uIFe:#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:%-Z5ji=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 <>stream x+2T0B˥kJx endstream endobj 181 0 obj <>/XObject<>/ProcSet[/PDF/Text]>> endobj 126 0 obj <>/XObject<>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 121 0 R>> endobj 183 0 obj <>stream xڍZKWJ~$'[&rr]Y@@W>uj3z7/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'AY`L# p0-jT%"@@Ai\" rE1b8lԦ_)Fc)Ee -MbO% XӜ;a|Ũ*;@vZ0iVΌ:[~AoNFG6Oi[W[p^|(~?JB'uοr7:RROod0%]?80yY6%S/Ӣ__H $ⴙc 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 9b~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?EZ 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

> endobj 180 0 obj <>/ProcSet[/PDF]>> endobj 176 0 obj <> endobj 177 0 obj <> endobj 178 0 obj <> endobj 186 0 obj <> endobj 187 0 obj <>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,Zdr6K|?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.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$Oj=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$*R4hr  ")lTCٳ('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 03pv>&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 Ȍ&#^D66adt/ٰ{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ߋ{(|pQWB 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 <>/ProcSet[/PDF/Text]>> endobj 189 0 obj <>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 191 0 R>> endobj 190 0 obj <>stream xZvȒ}[,$k6kzN2@vHwm͋;"q&gr_>}9D;}w4 I(ۑa9agOY=iL{Gch<8pq$&3>ɏ8ֵ=k4kOQlگI]T;/صTVEռLoM=QgU}iVc=?a:V6u}wI<)U%}1±m(u~RuOgRV&~ n|Dˤͫ{થ^fmzEI_N=gLk"BW实zUY ;Ħ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~\Kc 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,&YXeV(: sڱO2RyD #:.uU7}9nhWב'=rŴe6ü3pmiUHދLb".c#3IWY'yAAjdT,r[.Ju'[/ͪ%W-W(^m\[gј$DY7nTEHhg`̘3eo/DOiHj7i{{h3g7ym4Ʈ e5ePwwS71W{ `dN <.f̄2遑P!#ފC > endobj 193 0 obj <>/ProcSet[/PDF/Text]>> endobj 192 0 obj <>/ProcSet[/PDF/Text]>>/MediaBox[0 0 612 792]/Parent 191 0 R>> endobj 194 0 obj <>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}_;UfzFQAjCkj5{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@\ϟQha'@I͙cI"?ϙ \<1!{Z`Z5y 7! /lU׋5o $z#9nEQ(Zy,D4X4i@O@hbU#$fwOВiO4dI$ӢBL M EV~;{ظ tW ]D;N}]՜CruKp''#6lG]j>=d+)뀡^W%lGD].zxʃ ְl\{]^8reR"@]{c?YػSKU+5vIr]eKs w?냳TzL$bP~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-1CIST()ˁ//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>qHw@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 ǏQkrpĭ(_նVӛ׏ZQ Zu.nuul!!lLݫ-Mtjk\,e(0mu.k1FݩV)ٗ3/d43|rS7=޾3 RA<]ggR :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#C0XJn(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 ]jA K!y endstream endobj 44 0 obj <> 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 <>stream xڍT6tJ#Ƞ4Ctw7 0Ht#%-)"J7{_֬5s_ =& ,u@q@rakAvɱu08 $ B<Ȥ@Ce@'Jpe @ 2;@cKB`+kCLׯ_ 2a hhB!``F 88\\\Apv(JAX4p0l2@d5vlz5Bjppy`4`6`3';;/g9qXBU%v+rmCA Aqu 8;bGa,`! ; ؿ듂swrm.B mXuv8Kc _qN5Zn࿔=xy8Bm l8 @ނnk`w*w$p_^Lxu<2B~̳uHUb{:C>*Ita;Y  B_^h 7soվ>r @۱HTPg݊/YdHS8pVnߣ%i5Z ֨rܦ{=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%ۊΛ"Uz=J.>Ek'VnRڽ7@qŴ0h 7͈ (ysҽwI!i6Xm;kd1G{2qtm>ڳ(v3|NYXnU.^~rrW f7nPׯu6e?]s4[P j6e)QN͋?&=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} @Adf7Q>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&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_ocZqE 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.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.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@Ï 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)v8l!}<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 0x)9273~e~4>h#:K>}o,OOݟh}LWIaZ=.?D endstream endobj 94 0 obj <> endobj 196 0 obj <>stream xڍtTT6)HcNE`b$[PB@N )iDBQ$D@Rq}k:s~b30PGQHD, jJ` ,L  ЮnR BJ/'Th,N]HH0,/ SP ]A@ {TPG'40"%%;Pr{"`P$ E;ݰ' 1 Q[ vy <y_ 0{=}=d or@B=ኀ^ o=kp?n f*@N`(7w(tp@_]GH_@ "\vXΡ!wG8KJH psU;NrCpi=;O04`wD IS;DwXA߿aeB{B5T,L2b`ؗV1"vO&@^ӿ:KYLe-ɭb`URvu?P7_htQX 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۶rGgB6ix"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ѽ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"ːhU[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-vқ}UHw$!(jצ?Q~|0m)MC~ۺpɘ@%'oUVt V/8Sҍ}Z/@L▸!D猢R'h-xdvʤdLMQ}=VH,Ͼ}e}>M:n"9Ln짓˷YJyS 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إ {#?;> endobj 197 0 obj <>stream xڍtT." )H %!% CP҂tҝtHJ )*19߹w{׬ͷ~~~| p;*Jtt4$@(LiEBI8M $_%A( NhzDBĥ@0(/ !PyA:M8 $T"N(1zpyB| n t@('D0`C!(vBܥ@nH8Q E9Bk`. g2Now@y C`HL'`jh!?`?>߻ _ 0Ba+-A@0_@+y ; w xH0B FUs*0{%BO1+g.07;]h(`\$9BP1(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',vB @$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.\tQE4(;~:Zj -z.˫hHOxGvŶ26@fy' Mӕ?pJл/}@5~:A_6߼0+۬ p":Iބhw=-#2.pNU6Qpt=,I4eCLU~}+bTlv=3)'J䤁/(-ئ~)ex>q;{>-Y{φEhs+\@/0uSigf=!Eٜ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{.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֌ȸ;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 .yLK$Zb:rjb4k85~&h>, T5M_7QT.ݓ hcξQv-P,|]U@E[$.sZp|hO ,6R26T=_Q.a E[%(|nխiKV4/ ϙ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@BjFW;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Ǜ~[D0ZUUФ"{K2W:*t̘M=,L#ف8@,9G\ k7G(_Q㍩fSN)aE숡^T&q.k (/wvJoz2ľ>+a,Je-IrG+5ɀ*?t/-'բ> endobj 198 0 obj <>stream xڍtT6" %"0tJw7"9 CtH#"!(H#1twt|yy}kz׎{{_{#l!*8W$ T6A !>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 qCa`kߙ*@ufܠ_% sp{E3rO ]?7>#< Pï"]PWw_&ߘ#@bB@+e+ R|] p]= @J=8BG!#^@s5_OGa6_~-uEc?SP@x}yA@^APĮ @?^?ӿ2\ 7t׬Mr #oEH `\ yepMZwh#ߦ?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'yZͨoATۘWt/xo B -e KY/w( +)T2'ƵC&/&$}9mV5Nwv9Zl7͗mepd55)&sNy 0i  #- ;"Y~+tFa!W!:W_^9 )ۤse)7=˞ԗ AS)z3k=_Ζ'ڲ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>s1A[d; 0wGBOĠq5{)1\O/:rUմrIZ9Պ@.&y"X%͔;߁;8遌vt6 -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*k85 :r:M5<Ҫ- w wkȜHõfO `jpn,%nutH.zqz aFKHe㈷ݨT^S)f'/K&`)һҫ?CT|l*+e.aqH_ݬ{ cSe3X,Z,uy(3Xrni_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@1x֌ ^<Բ_? y{Qd|=-yy4ȗRwl#آ>_aQz4yFM {JѪ!j)ͧoʻcjzbM4LfKU:CO&5Kv*e YѤfhtW`')FF|! 4vA=IS.UV> ֹ,l%Tk(_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$ulJ㙤^t9B|zPC!P%hĚs/4!xxaJU~|Ek7~YvK1ֶ<:dEKtTRQ9kɉY`6:K`Š'bN7Jv|㈦vHOP8;Y5bT0}1<9G· Jb/g,ӡ>뻔Đ1Xc\xWjҷH!wSFn?5omsk0*]}W!ut$ {|87 Qě [E[<ˊ;> endobj 199 0 obj <>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. TU0Bq՝' 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/94AۏE ,EϽ0lgfdb'8}ff&㧀1+hvO0^F[Wv\RueM,22819d9S*nD(8 (T)u^8Yx9o J;rZ!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#m>OgSlWfL11+x ip6zWuQ3E[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]ZbTY4y?<솷zMck]s刜96oLzw%Jɂ)p<;7zsJ\Z%0sẓ6kϲ$a9$c5:DlsϏ3;dڅKw*}" q0_FYH qˌ ;@s kKYZt뺃&|Eޣ-d_JXZ3|~RӐkP ?N =&&LVe7WHS :z$ngOk&;6*⯗NX\|ߣ2 3?],Nh`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ͶHkQr1{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@gFx4Nt{ 4X্/,A Pf?=UQӠX1P87|p!-T2]f0q8ϙoP J,[dBoWL"-,Zg;;c%(!xR٤Ǔ\ [*S5hdl˜en-7W*)jg\~|UF-F#  8NQʽy;^HŸd uC&͍k& ΞWDQFY}N2^Qk˼/iNJ`wJ/, BaI&Wu"dRS!s U}V±f̺о;(oC8d-Em~xcya*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 Y^mbI[+M9ύvW @H*+ 7ˍf $+:D9 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 <> endobj 200 0 obj <>stream xڍtT/]%Ȁ9H#) 00--J#tJK(ߨ9{Ͻkݻf<~mp/( P >"bc3?Z"6c0 $]:E ݡ#PT(&)(#! Py@Zu8 $bSz# (t\p Z #]m!`?Rp>vD\%<==A.H~8 /r胑`k\6{0~"6#GmGy`Z؂aHt;k 4:`g?;ѿA`ApWsC`&? ~9H8:@A6hߍrzzC" ($?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/)ʗcIC6 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,Ũ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- ^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#0Gf}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:yx4tJlRB7Z+&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>ج]™kXp8Hx>:4"9 P6!K@Hf./+w52:' 8G'0c@|#bySb?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~@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&"ViJ|Yςc9(C"U)7ݣ6%{5!9i!E͘0o"ؒ]3{Vp_} 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ڑ-^l7f@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Ûxectk->g)6pzE ~F 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 <> endobj 201 0 obj <>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":`Cu6W?ѿ!G } xc[J3P;%(+ f*H77("՟* 컯uE a_c8x "`H\Dtc,+#uO8/z1*Ebo9/B`nB1jPB8a-(r[t/; :X0z`@aDqP,"S C; H s,FjG{Y '߹R37ҡ6ryƪof~}[lV/<*|oʨo>X0,Qu,[̈́_ڢé_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 $w~m\>TLނ'2Qߙy=;[ʁ ۮ K+F{<36 l˫nXcd0 ?Ԅ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\yY`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"ϼ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~\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?oNמ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 <> endobj 202 0 obj <>stream xڭTgXT[%J|sF2Hnt7DP@ɒQT@rA$E@$HMko53?]U{ժZKJ\ v@pR*tšQ&h t000Fq0ta@FVVVt~+pp wEo큄p ` t A>Ї`00rF! Caa€  <4 UV|!0_.1A±X?0 G QM@|0s4`8\Ɵfi~@S zchܱ'aY} RU*!X$7ʯ-6\N]fr Wr*iEŌ#=^!44}."^;\PD4Wi ~嚧W,)5˟hO\UVu&y}$uͻ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̡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./<Q*yј/ On'25cvl*3CeV-,|o45yr\Say0ye%yW#@j|zי 8&JQw|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??%^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<[tVy8HBDz*JSjZ7.!Fi0pM"QDDXQrRvu?RvOq1mc/ZFy~>=P|MPf& 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 <> endobj 203 0 obj <>stream xڭweTݒ.!Kh4kpw =9ΝgkSOSj*2u&Q 3K)++'>XI nDw4uqKZ--66+///@ TӦc``zoX G{K;%h YĕUteJiK)f1(ؘ[],V?s_0cL.6,=-r1-m\\޿6.kgS{\6`s_Vrtvx8;8޳HH+.6n{_%{yڀ]2Xظ8Ls9:MlOgKkSg ;;_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]9qts,UqD 7ʖ>>NK]FCX\Skivi)omM=$bqXt\~3G9ƪBxb Q۸N4G Ƅ=:MWd $=*S/!A-Xɼ~B*Y,b`)bf(446f +0J?ŵin!/l1 q&`LҶ*Xоs=O;U0@n+a8[uoQQ@\>Z>dy'B(:EDȽXтrW#>m)UJ"f]X )1cc4n[FmذR#q|X M-O^Sn<usy޼nkgC@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!wrIyhN_ 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{+aT34"&d?_zjZXZ)cӻ6䭩 C"sB\7т3P=v|v7`m",ڠC(5@B5uRQv蛘 "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渹 Ωzkog|&pw<_1ܢ , BP< ƩUqe$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یdUYH WRnK|yn|oS֘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ƀ/7a8X.#Waҥ9 G'_u0x8/V܆.VZ&D1 QstdWSY~WTpt F\-}}]aPoIJc{Lv*Щ)2碑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,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!?|Pޣَ\4m'vP Nj$1#Rn1hYe]š=$7`jZz Eɗ6K 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!#g8JH'[>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;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,Vlo+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 <> endobj 204 0 obj <>stream xڭUeXFQ: AA;$F@:.8wu1sϺu׻^M"<|u3B Ujl5, `= "2GaPys$H`ȃ,~111\ E 8e}`X\@ESu@ C@9 ͗Jv%u= r24K-AP` s@:,aP+<\29Y@n o99gq2"{PKְ? `7{2MtÑ剴5GM+`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"K8GUtXXe+T&1-~[FF~j@ cm%H+ThڠYie1,>ɑShK-]=#(OGh}~׶D/?H^7Hr%%=+nM2v 9ؿڅ`ZWNa bO/Q,r] s6J=R*ef5'=+QAΏ_¾uwODL`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%|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}Βrr 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(nKw,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[[ۉ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[%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:B>o EDjC?} -|+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-;drZ 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⺐&ػޜ烘[-%76~GW&Ib(  J> endobj 205 0 obj <>stream xڬct&NE+m۶b۶*YmIUlVSاϟ>ߏ5sO^s^3"'VR6s0J8ػ330L\TfVrv8rrQg+ 4M,,fnnn8r+J]E$L毧=;h7*p̭lQE%miI:@ht6(ZL.@j3?0%08M=M@g;++:MmWn/@-Srpqu1urtͪ$&oƮv84s0uu5w=]eY8{7տ`X[:37to;:z_V ֜oNS׿-i{s3ӿfnStW mf@s8F׿)Tw,3@ -#?9.%lmw 139?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. ZlyEK)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 ~ΉBf &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}'ywtFsL&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@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`1exs>mM3U}R+ Dd%,4Je]!)kcGƊ?x-"U|ϐ*U0 c/&fGP®`Aއ{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&ԣ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&]㑫 :z" 7lABq (.edvݠiΐw %xpg4B4 :r*USߴT{J$h%wLP lwPADnD{(@.n >oo&//` Z"zeo˿tzw)KXe,@6 c[Ή[}RO3x4hB(ndiϲ쿩w+[WR"}fE?d L ~C6]M|s1唉C25Y)"Eb&vVsO%.ͮWPa"2FDxI|髽Y*ALmaUFvˮZXSL'` p)w Kی|~K-(mǸvOME&Ɔ/kꑼt<h|lj l![qt\1͒8 ?a*GI1 HLdlsp8|pN2zQ Pr󹇘C^gyJJxGɡs̄L&V!H1;Hush?[b<(Xn_ė?jƩ7V cMRf[TSlk@hvqg6yo}giU{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 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ӧvAg%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 ,p6K{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*/9phUrazV+\~Ɯ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ç/AR 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*H@`{"l\Qo,˔y]حDԋ?,Z`N*vL=qL:P z}z(0߆.dԀj8XqSuB /4ٗ$yvM:;s@fPL@CY N*ۨ9*\_GFT؂bq*{I듽=+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р򭶪 dGsvrd[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@ 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'*)nzmY)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[]4E4NF G۲+4G|de7FeTw\6j+Uԇ(*) DYRx<Ś|[Z.sd`LTRDt[=Q[JAG97noϫ5ZJقct31r-z(eSfrp\.v1L^]eؖ 6loaǖ<@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_ H5F_!m`o@ǛXL\sW;}L*cOĤϧTYfmڼp'Oe v"PL9gxi GrlzK_ЩMӰD} 2p츳Z A OE]¬դȋ*&.4{kv¼dLa$kb/c4b_s;>"r7Hcbnpǎ aȭQ=ήg*!V9&*ٳ8t3`&wS{lAb~TΫ\H)>q4Eg8eύ Cѐ IVm9D5|)a@R?>f$O endstream endobj 42 0 obj <> endobj 206 0 obj <>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 `hj838h)a=Bap#x̪*%Wp+S0# Z `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~ZBK|5LA I2Յcu*Z5UzO AaW#kkX9AADbX?(wV'6h/ղJx11T 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']҇)o2^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:]/\,!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,{/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 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*YQJ =,4᠞FH Ԋ 7t@9VXƓ=ۃqN]]w[gy1aQ 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|KX"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:DqNi#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 ƖKv>H"cr%*rg;6LF:+߁(5#c'v%tJ)! XN(XK43P1 _ׅ+ddP^I.şf&,Q->c\U4o> endobj 207 0 obj <>stream xڬct&;[+m۶b۩ضm۬$fb|ݧOu>?5q{ENJ/l`pwgf`(Xٙ8)8pѫ-p@cW+{1cW @hXXpQG/g+ KW&5--jqxo=;h:PZV@$JRA :LlLrV@{ 5`o``ofOk. ]G0)lg`p6w;W?՛; Ggvm\\]L]*INWKcrX5z9ҿlaZ]]@OrfV.^sstWn.VYhalf tq g{cGG[E;`5gcfon +{8vEo܁?;Cc3{[/QoJ }$7PB oWG+=2w ;blOQ[r`Ү"lo&+\$;?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řvK 8S'c ,2FZQBȶxgVvynڇ !ފ}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+DQYbHKS$;|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(^< %.S"tcpIC]Mg PXMIDtS=?FrT>yִuŴ m)iaz<1"<5>2Ûlf ʦiv;OTe31, /lb_lȹ\4+"bRM=mWy۷1>4Ӫ@@)GaHXً/oqJ01AL;.6r-6UU({R奠ًYzΌ~ěA.oflKŞkOK[V^YQf4 $*\9ũׁ5cٖZz_8)V,4]axO0VgRPGV- x6> !P33 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[/yIh)Ѓ)"|vNAH_:;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*-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*!s{Xܩ{rZLr1wHN}ږa|=@DU;$ySRdç=|:lVeZ.hFK1Ҏ O4\<߷3{,YEygVN l u.q4n(%8V(ղDt^OC(;c‰m-} 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(: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|E3NaNYjB^ݺS Oɛ3yRus֓~,G7uۍ@x>Doԗ/k%@^>1CHb|9 -˟_ͮ=ɓfW야.pEBt<"<_.4Swa ~TW㺾HR9J=r]UƃwNX: 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.ɠ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|bjN@ n6}pWfBMJ+8 9lPZ76q*ow>;C8zIfqfpY6Ğ4%Q@ :O=fl>||WKǀ֑@CTp8JO$KɮX84BEqm[UВ5ЉV#hbi`Z_kŨȪDvMQ3 CCrͬW!AL6\ =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 :J"?R>twc ]IܚOK6{|kE"3, B̌""|*CJ]Qxv?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' {z0p@ƂK=Z X%W8f)Ib/pBz% +MaET2uRn2Eo^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%/-.oymKXvItq9X@iFc.J1#7O*[+\PxzlF ~tdI"@uN2OvLD%-4Lqx$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ɜ?:o |z>-|@^m΋PZOiF0h^ݛ !bW$OƬSqZ"^WGO6@&]/~A7B*늧ٲܫV-eiKFDٌRG$D+!ӿ 'ۯ_-NY 7@:Pigѥe"]Z#y *Kr臨{#~`Ufw&y;s1U#R)rP2w62Ro˸fKbCN zscM: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 _mӚqf[˞C@p2U-?qvVy;!=!X֝tcBr[I7+Js\ƫ!KZ !8i/J~- HؔM'yI:P|=yIp,7Vm5oMw6}֤$P/)@aeeMsWtXb@prfGH>Xt3:IQ5(5ңN߲ CͩWȣC:.(_ٛlɀ/;DGhfBo+ץObJUA5Z03G/aRQ+Хyq}h3  Zq^_`@&v27G?f.+!y%8^H26hd)k-5xjf9&|0p}ymTך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 <> endobj 208 0 obj <>stream xڭvcx$\el۩mc۶*vI:vұ펭mvlx7y3sU^k]EEQ^L΅ 5vuV疥W6p|Ě\vF.f< 3S  GwtP)kP@>=v/nf6fv.ώ*ffK39 %%/WHٙ9]m&Y5 `4gO.!gfab89? '#;v&6 OL (*<],\v~{ϛ&KD]v3@go? NfFN6fΟ4_ulmϭlfc3Hٙe7uuQ$LlL]>CTfA[5_{wjqWy#׎|.#;.#h5L-Bvs3q1q :=L.&s#ϾcW35sڙ}OkLLZM /߫Eh%Kb9lrQ ofOC? =^\z.7 C,g01011d? 37;H*.Fvӧ+0303[^7 JHsf vYZWe]nZ0۞4p7 eWEwR|u6NFiQ^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 3lX;n_GC<"fjФG@ -l4amڑ8:u"lb}e_'֠E^}r9DR;zeTbvͿv7z%bk:ԧ9MAe&Z[Tj$CNt(5JzyZoz*!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ɛ^>JJFIY'ݗЕ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+ ! @)}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>]2x"㢾= {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݇ ΋OuqaapsAD3\\+L Cf&(8Z `+D2s{ݨ>Z]t|E%Lԙܽk}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+RO 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~RcDwqL#[Z!u33hPɶ]U6A?s;T08m'oʤq"cEσ]sat"E #{kjб( È+4F.Vx%=XST5 Ipc3Gyk0VR.9PNQ/fdbiy+mLkbq@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,ȵu>ZV[T6%R!J:W@ZjnDv#ŚH(980|\|"/:lH__$]gX ^C섽h_Oh]v]&3!=}w@zQ5pdz4xdV6,/ي5ykڍ gg-Sq|2bl |# յ$Z!†+qLiqOJ*6}j F $1H4BRm)(7lisEHh=a'.c6hon ;ℋƥ:ʔ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)TBxAwiUԴLj VS[*plP]15usC]UDP}$OկfE։D$+k8ֹw쟋۬M3Ң{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'|01z%_-a@Vl{ `Nq#ȉhzdKBk`WVׅOt VƌDmFo :k">^_zR*5dY/L=7+<'DNI,NԒcoSӨ]o (DmN1yfL]hfąkNrGm'YSՂJ&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|b~LrY(t=c jɌ<SÄ*øhr׏FENoS -pv+K0~Rk"egQKTc,jcӎCX\zsSJks>)`]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 0nrlLJdQ \E \_Yc= =`dY{5T94Ncpc/#"T`,} Og׹oUCQWbia 1 [ܶ+%J)HY!7Av.V).Yg-6` ꋹYWCh> endobj 209 0 obj <>stream xڬctem&Tl[;VŶmٱmJ*mFvŶt=ۧO}{{⚸=XĊ*tB@q{;:&zFn=,2UWKN.4r5rr4Q Kwt4pP)kP~O?&ct4P}p;\B_; QPԒPIȫ$v@E(Xd-Mv@j?9r&݀&@T@'[KgKg,Ll\MI_ 98 hldUQTyX`o Wbdi pz0tv1/pu3 FN6@g0 ߪ7rpW.@3zX&1M\6eg^L: Q3302`]P߱LG-FBEcdkciq5?9Wk ࿳UBvcbgYhhbb03ۼLN6v$N6ڙ TԔdi_ƊE4M(aa{7;e{Y} ?rF.NFzFF&Iٙ؛3G*.FvG Q:9e_oq%=&K&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$LfFUYgK]Ąvkl@UJyD^ D3u`0]pjBgOˆokIctD8sA'0':'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ً[΄}V̔ xZj'E@LniL&tYWf'3 W%Nw-}bTG~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ɸpI.ٴ 4U2\ƿ>:9WqSu uّbͥ^{4*2LO-r wT{$K3<7\jݫu./J F-!33m5t:gy6Tn (XF<Iefٹ/qTod<:Hf3(Q 3e`\g3F ym[,.XN䁯gU؅QkaaΝșjrV2]LFu%Ə8TMkIƮ)࿀Tk$-['}%#. \-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}c|T vJVojmjU̮?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{ 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=O0Rh$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+ĸ>״}.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γi1uM??'wƧs072.Fy7s ͡9f뼷nFdu]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: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^]½~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!>*~%sTK>Ӻ/*=tb녿uPƂzO Åew dS~0WPȤye%7fZ0w9C!aKFui7E[xn2ԑ\;u7Pbݑf g5ZKSȢ@;J TODlm7p* ֣1Qt>'PyhK20lL]b|/ErjNvr%׃SoI'!Ô6+ b;$b"6mrCl< !&Rhm@01aHoxTl| 7b:_m_l~+O;TJ/{4U x| ۦƌ t.Ҷ731UdQm> >~~6O0N?oIFHQT\ix| *}l5V`nQO\/gO:pЛ[3+!EnU>9`n:fc2cc~XqkO:uƟZ(gs|Ô+I^I"%2r';" W" #:-]vpо!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}~gQ5DBj Ͻu)hv o6Zٰs^SnnHXލU<ưe_{D<͎}H%bhAXK+f};ۍS%ֻk cjp(ki}{~81~3I;w3B(XDĽV8M4>$&hRJ2B$&f&kbgpF]&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?ŗ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$@{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 dIIh=u0.~FuP O1^FhQ:^)U RT(;~d^ b2A9㎅xʤn\Z4t$4M׳FUh.~^XqMiðǥnʭɥ_ ‡;=\{rlM5G|$ki֫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_cGE2rp`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:^GwFz?ؖaOnrchHUaڰ{` qoiZ-?Y BvqG{ 6"wɔ{6P+uzT:)obI'TLj)d&1]n8Baֲ endstream endobj 49 0 obj <> endobj 53 0 obj <> endobj 210 0 obj <> 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 <]>> %iText-5.5.9 startxref 453291 %%EOF 4 0 obj <> endobj 41 0 obj <> endobj 53 0 obj <> endobj 127 0 obj <> endobj 134 0 obj <> endobj 164 0 obj <> endobj 169 0 obj <> endobj 178 0 obj <> endobj 210 0 obj <> endobj 211 0 obj <> endobj 212 0 obj <> endobj 213 0 obj <> endobj 214 0 obj <> endobj 215 0 obj <> endobj 216 0 obj <> endobj 217 0 obj <>stream 2017-01-31T18:35:32-05:00 TeX 2017-02-17T15:01:25-08:00 2017-02-17T15:01:25-08:00 This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016) kpathsea version 6.2.2 pdfTeX-1.40.17; modified using iText® 5.5.9 ©2000-2015 iText Group NV (AGPL-version) False application/pdf uuid:c1b0e4b5-d14d-9342-9145-22c99ffba43d uuid:ecdfa650-f2c5-7b42-bb3d-654b62e4dfd4 endstream endobj 218 0 obj <>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?,emh 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 uP21WHZԾ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ƸJ877'+4&XtiivrCpntO+(MQ\Zv8;qVZQ2bX/.DAx}23,#&^DO(xXRHa۲k@TGNtMHTƄ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)!)v-²]d$8VJv%>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'43yQGk֌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ɾyDo4T8^Zi7QT˚=+A'CA ?%pwPvjԞ:at79$A %䅙SW.L(S,Z 3 |ZuRmnࡂx6 ] .hj,}|!>AMu~tX}:o2}z}*۳ps*凴e#ITꓰ6)ٶLpͱO'߃O endstream endobj 219 0 obj <>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~yr0<0+aVط6f\ cHQ\;89͟C$,7図}ُ'l̫DcP{mh9"RDjWm瑲Gvf3J޴BwY嬣Rm|`:1?Bb?jlZnIcBE-N뉺';g*U!Zc\n6+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} d8F΋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 br!! @ȍ $[GX" -/U٩cw;a??vƿy{}-)3}^8i ac6/ꔀj/iZI5E9 It( 0Z80@"ֳLE TwmGzzOg+EH^q4-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])rCц!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Έ,4Xn m6Zfd^m)`0[Vrws/O28u6 >b_14 b.,[DDl[Z>ۃqZ}Qe_jLl%z[Sߟ:|@(fbcsaLoSZ2%2NBґ>DJ,a[(@FτAE ^ "n$ղ$۪(fNQg<n 0rt۩ X%1ޜh ѾZ ;U endstream endobj 220 0 obj <>/ExtGState<>/Font<>/ProcSet[/PDF/Text]/XObject<>>>/Rotate 0/TrimBox[0.0 0.0 612.0 792.0]/Type/Page>> endobj 221 0 obj <>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 ·HRΊB`^G 9q~nz C5S˂pz%,hzR舻1딀oj%j$QBap|ZO5js'œ#|.(IaelI`5!xjw:͏VQZN`y_ endstream endobj 222 0 obj <>/ExtGState<>/ProcSet[/PDF/ImageC]/XObject<>>>/Subtype/Form>>stream H @ yynn^ ⩊g3$31GzG6A1Ό.xܟzs0ZF׌V >/ExtGState<>/Font<>/ProcSet[/PDF/Text]/Properties<>/Shading<>>>/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Ś!#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܃ѧ6FSGcBkHy$)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,0C"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 jdeIϔ]g?|b_ `/,O n4iKىHQԦcZk?~ #,w_X>~KQS9IBf@OyJ0lK>U& ĠI-Փ{sU}Umbm0t{)7 YmJP[VC*C sߖ?;үlUhLIyϿ|jzrcqwqڋzhyp<=Zjzd9E+9ds4؋1ԍpp=瘓HXgȻK3EW܅JlۋL)l+I3EF%Jud7CJ^kBFb7{9fgBAQ-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(NNzi0pg BN:/EL/>?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+¬\ RPhFhi/,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~2a2{ڞ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"$yNsi ⤆}tu^A|]?OQ3 Zd4KV;cxu$ؚ8ۨ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=N\Gy v䲏lt=/M,FTp9# 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 ]W3gI@Ƒt}Zʊ({tC7z^[e˥yϔ*nG0ҸD{'K8^N'xb<1o<1(_Oy:O;O'GX8گKyʳ_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 uJwHODn8)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>~\{sj_5G_jƪ\cj|yg5jVgU'hnwtV9A4:x#Mʝ d= s ml]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ВsQ>G&*(X7ND9Ie e҈ySI'Q$ m>(|Ssxb8 b]J򮲓ASU0K8c S\~]"f@ɌEuJT @ =63/kX^eԈGp6Xqa7_kuUcQψ#H*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頾ami14gƱA۸.hذ`( ,L`q8ȡS,n4H51Ksė/$˅O endstream endobj 224 0 obj <> endobj 225 0 obj <> endobj 226 0 obj [/ICCBased 230 0 R] endobj 227 0 obj <> endobj 228 0 obj [229 0 R] endobj 229 0 obj <>stream H?\\[ZYYXWVVUTTSRRQPPOONMMLKKJI~I|HzHxGvGsFqFoEmDjDhCeBcB`A^A[@Y@V?T?Q>N>K=Hj endstream endobj 230 0 obj <>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>'Kt;\ ӥ$պ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=[==<=GTB(/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 <> endobj 233 0 obj [234 0 R] endobj 234 0 obj <>stream H?h j k m oprtuwyz|~  !""##$%%&&'(()**++,--.//011233445667 6@ endstream endobj 235 0 obj <> endobj 236 0 obj <>stream application/pdf usenix_logo_full_color_tm 2016-07-28T13:48:14-07:00 2016-07-28T13:48:14-07:00 2016-07-28T13:48:14-07:00 Adobe Illustrator CC 2015.3 (Macintosh) uuid:6be068a3-6f66-e14c-a412-1d9510750738 xmp.did:047e04d2-fa69-4b26-9428-764c0547b43d uuid:5D20892493BFDB11914A8590D31508C8 proof:pdf uuid:2e734826-cf82-ce45-903b-dfd35b207876 xmp.did:5f37efaf-fdf2-4ef4-b6c7-a42aed802ebd uuid:5D20892493BFDB11914A8590D31508C8 proof:pdf saved xmp.iid:01801174072068118083C67C01A9FAB7 2011-10-17T00:16:06-04:00 Adobe Illustrator CS5.1 / saved xmp.iid:047e04d2-fa69-4b26-9428-764c0547b43d 2016-07-28T13:48:12-07:00 Adobe Illustrator CC 2015.3 (Macintosh) / Document Print False False 1 170.000000 60.000000 Pixels Cyan Magenta Yellow Black Default Swatch Group 0 White CMYK PROCESS 0.000000 0.000000 0.000000 0.000000 Black CMYK PROCESS 0.000000 0.000000 0.000000 100.000000 CMYK Red CMYK PROCESS 0.000000 100.000000 100.000000 0.000000 CMYK Yellow CMYK PROCESS 0.000000 0.000000 100.000000 0.000000 CMYK Green CMYK PROCESS 100.000000 0.000000 100.000000 0.000000 CMYK Cyan CMYK PROCESS 100.000000 0.000000 0.000000 0.000000 CMYK Blue CMYK PROCESS 100.000000 100.000000 0.000000 0.000000 CMYK Magenta CMYK PROCESS 0.000000 100.000000 0.000000 0.000000 C=15 M=100 Y=90 K=10 CMYK PROCESS 15.000000 100.000000 90.000000 10.000000 C=0 M=90 Y=85 K=0 CMYK PROCESS 0.000000 90.000000 85.000000 0.000000 C=0 M=80 Y=95 K=0 CMYK PROCESS 0.000000 80.000000 95.000000 0.000000 C=0 M=50 Y=100 K=0 CMYK PROCESS 0.000000 50.000000 100.000000 0.000000 C=0 M=35 Y=85 K=0 CMYK PROCESS 0.000000 35.000000 85.000000 0.000000 C=5 M=0 Y=90 K=0 CMYK PROCESS 5.000000 0.000000 90.000000 0.000000 C=20 M=0 Y=100 K=0 CMYK PROCESS 20.000000 0.000000 100.000000 0.000000 C=50 M=0 Y=100 K=0 CMYK PROCESS 50.000000 0.000000 100.000000 0.000000 C=75 M=0 Y=100 K=0 CMYK PROCESS 75.000000 0.000000 100.000000 0.000000 C=85 M=10 Y=100 K=10 CMYK PROCESS 85.000000 10.000000 100.000000 10.000000 C=90 M=30 Y=95 K=30 CMYK PROCESS 90.000000 30.000000 95.000000 30.000000 C=75 M=0 Y=75 K=0 CMYK PROCESS 75.000000 0.000000 75.000000 0.000000 C=80 M=10 Y=45 K=0 CMYK PROCESS 80.000000 10.000000 45.000000 0.000000 C=70 M=15 Y=0 K=0 CMYK PROCESS 70.000000 15.000000 0.000000 0.000000 C=85 M=50 Y=0 K=0 CMYK PROCESS 85.000000 50.000000 0.000000 0.000000 C=100 M=95 Y=5 K=0 CMYK PROCESS 100.000000 95.000000 5.000000 0.000000 C=100 M=100 Y=25 K=25 CMYK PROCESS 100.000000 100.000000 25.000000 25.000000 C=75 M=100 Y=0 K=0 CMYK PROCESS 75.000000 100.000000 0.000000 0.000000 C=50 M=100 Y=0 K=0 CMYK PROCESS 50.000000 100.000000 0.000000 0.000000 C=35 M=100 Y=35 K=10 CMYK PROCESS 35.000000 100.000000 35.000000 10.000000 C=10 M=100 Y=50 K=0 CMYK PROCESS 10.000000 100.000000 50.000000 0.000000 C=0 M=95 Y=20 K=0 CMYK PROCESS 0.000000 95.000000 20.000000 0.000000 C=25 M=25 Y=40 K=0 CMYK PROCESS 25.000000 25.000000 40.000000 0.000000 C=40 M=45 Y=50 K=5 CMYK PROCESS 40.000000 45.000000 50.000000 5.000000 C=50 M=50 Y=60 K=25 CMYK PROCESS 50.000000 50.000000 60.000000 25.000000 C=55 M=60 Y=65 K=40 CMYK PROCESS 55.000000 60.000000 65.000000 40.000000 C=25 M=40 Y=65 K=0 CMYK PROCESS 25.000000 40.000000 65.000000 0.000000 C=30 M=50 Y=75 K=10 CMYK PROCESS 30.000000 50.000000 75.000000 10.000000 C=35 M=60 Y=80 K=25 CMYK PROCESS 35.000000 60.000000 80.000000 25.000000 C=40 M=65 Y=90 K=35 CMYK PROCESS 40.000000 65.000000 90.000000 35.000000 C=40 M=70 Y=100 K=50 CMYK PROCESS 40.000000 70.000000 100.000000 50.000000 C=50 M=70 Y=80 K=70 CMYK PROCESS 50.000000 70.000000 80.000000 70.000000 PANTONE 186 C 1 PROCESS 100.000000 CMYK 0.000000 100.000000 81.000000 4.000000 PANTONE 186 C SPOT 100.000000 CMYK 0.000000 100.000000 81.000000 4.000000 Grays 1 C=0 M=0 Y=0 K=100 CMYK PROCESS 0.000000 0.000000 0.000000 100.000000 C=0 M=0 Y=0 K=90 CMYK PROCESS 0.000000 0.000000 0.000000 89.999400 C=0 M=0 Y=0 K=80 CMYK PROCESS 0.000000 0.000000 0.000000 79.998800 C=0 M=0 Y=0 K=70 CMYK PROCESS 0.000000 0.000000 0.000000 69.999700 C=0 M=0 Y=0 K=60 CMYK PROCESS 0.000000 0.000000 0.000000 59.999100 C=0 M=0 Y=0 K=50 CMYK PROCESS 0.000000 0.000000 0.000000 50.000000 C=0 M=0 Y=0 K=40 CMYK PROCESS 0.000000 0.000000 0.000000 39.999400 C=0 M=0 Y=0 K=30 CMYK PROCESS 0.000000 0.000000 0.000000 29.998800 C=0 M=0 Y=0 K=20 CMYK PROCESS 0.000000 0.000000 0.000000 19.999700 C=0 M=0 Y=0 K=10 CMYK PROCESS 0.000000 0.000000 0.000000 9.999100 C=0 M=0 Y=0 K=5 CMYK PROCESS 0.000000 0.000000 0.000000 4.998800 Brights 1 C=0 M=100 Y=100 K=0 CMYK PROCESS 0.000000 100.000000 100.000000 0.000000 C=0 M=75 Y=100 K=0 CMYK PROCESS 0.000000 75.000000 100.000000 0.000000 C=0 M=10 Y=95 K=0 CMYK PROCESS 0.000000 10.000000 95.000000 0.000000 C=85 M=10 Y=100 K=0 CMYK PROCESS 85.000000 10.000000 100.000000 0.000000 C=100 M=90 Y=0 K=0 CMYK PROCESS 100.000000 90.000000 0.000000 0.000000 C=60 M=90 Y=0 K=0 CMYK PROCESS 60.000000 90.000000 0.003100 0.003100 Adobe PDF library 15.00 endstream endobj 237 0 obj <> endobj 238 0 obj <>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 0Q endstream endobj 239 0 obj <>stream H|TiTAMOb.#@@7dqAZF%n@v)A3*K *(( . qcIbsR?sN;w{޽&ik"tDp$Rq"&q]-pH{BjDt odttyޏ嗴AARNǗ)F@Cx0J4'oJn:qօЌeYn@!Hij/ wi|_>)^{'{aXzwe#\T kU R/J֨^lF5ɥI /lCvhAd27?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߀ޡih13BL`.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>h9L;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+WLȥ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>:##w- "iβ(> endobj 241 0 obj <>stream H\j0~ CjJBiFqyj4lvG3ff۹Yw/sjJC7>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̊ N3+!Tv72DMHKq9>^&~a`.~]Bӵ1f?T㥺n`}Tx΢ %24X xhV0%O)Zck /IX>Nrzr &C|R!M(gl gDb(?3xW{< ] @ 6=e )_7?[ͯrB,>)rW֓y5QѼiqoyU]Aꍞ闠%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#LAYmWEsy+qxr݆{X&JƸ1; p[#?Uu;;8Hν;~$GC%]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!'{1'LyD΄"Оjd* XxCqz ³vS 3`.Ҩ5沆 =s^,z^(.KY2#NQCn&!J9c,\,6'jkb[4IՇX}P0vZ?A d _b Lh@#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 <> endobj 244 0 obj <> endobj 245 0 obj [/ICCBased 246 0 R] endobj 246 0 obj <>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>'Kt;\ ӥ$պ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=[==<=GTB(/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 <>stream JFIFIkAdobed!!##!,##!.333.!>BBBB>DDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD5kI"   )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-tX8u>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)LPs7IWe!7E'&sM Oh1(9E!F޺d8˓$5uLYWAʩ!HW8q̢l!bb8!(9m n4HpapTvI$ ֧?'.I+: dg6ӎ8VN異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̚ˣBJgiRlmA"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'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ՓTA74dm 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\ce9լ88JT#TlrR 7rM`Q8 ΰ3ȞA%kL 缆=F #CmGX v|6jz*E`'$$&\b5EG!k75D5ާ s\gN 883'@Б=si+4Z˫ZM)5כN0]Zڵ.h5\7NN^멶Տ:xm[mv2۱7N~rV|1'kwUo<خ6`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̟[сsmwdm\IR.k￑*J3Ϟ(:qHW=#!9m0#e3bgp#$]r"}܅2T;'(19)#.5~֒96"XP#rQ 6Df.vArQ)-9!]Z9 !gq8ɐ ux3:yn3vw=B0@ֻЋfs1EuhܿapNӚ )`xo}pmy8ڠ)iJ+1ܜiUp q]Qρ9̀\xWSDr~2s@Er%q4uUaOq եͨ;AR'b耒+;ĂƋPDV`@'0P^pnǹ7n܉KK%V$ة*lуyM0K4Ad( )4nk4Bzf./]vmω]!)Z.4`8kV RR-n[2mf%h~ ֆϝ"c뱗Lv.Y5XM*# uQҹ7hJ$$8'МpPSx# ̀` ͦ: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.OdtOIe?BYg%!k8_Y(;#"Z(.AOLqa a:>B dvĞ]Oй-ZKNt!:zFB!B!H P8,85`,sP:Lia#*U.N&v&w2PTt.&ŗ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ѳ&zѕ2ޥ"G9hȍj-g` ]ĸliH_tHgH2oQ@9pϺ"C.Λ]* sD8)¦9Nff\mg]0lT j)b i rycIĜPDBt(˨7]08zV -΃0:K$oy 3cv5pt790PR$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'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& %vS JzI\:o̚ie9CRgtB;/@iTdHޞd\bzA^4N4pO2r9m1zS^ .ok ,; :V59vX\Hc\ *̟*i+ps'=׮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 翾MPoM6S[57&\v#ҏkO4ЋNg1r0yK4dtBqz䍾VӪ\ K8޴CbOB[Ssghkm6;WTpj9 )lr;@zRZy'0!gl C2~=J)1 `Aq)6zg@.JKoeg% 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ӰacKH֝4 䝊/6X`Sb2ӻ[=kN唯u7Q*5de4KfCBsa":KsrLj9HJD $B!+Z`.:'"E2b.h`SU7ת 9%EO \Λ[HJjE|ȕ\冫)cX, Řlĩ; O?ԃ8Rov'"ɠ>stream JFIFIgAdobed!!##!,##!.333.!>BBBB>DDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD  &.###&3')))'3>344443>;DDDDD;DDDDDDDDDDDDDDDDDDDDD5gI"   w!1AQ"2Raq3r#bBCDS $%&'()*456789:EFGHIJTUVWXYZcdefghijstuvwxyz8W!1AQ"aqB #$%&'()*23456789:CDEFGHIJRSTUVWXYZbcdefghijrstuvwxyz ?m &D5ȳ(G)wRK@` N ) nuS^D\g[UsQNeȯ=k{n~!P]BPˑ_ c=+/ td~es<}k = TJ<5jC" o5H΍vO 3&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εz9Q9Sί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Әkid>(pj9.,ωSQƔT4)wCÐvYD;է^xQ}D|%:rϧ' -qveZxhʴiVmXFk\Zq*yá𗜮ɼ1VSUἢ㓭ܑcVm.UۉԙD3<{˧| Q{[9k6AɷU T}HVߘ(LG9.AP:8 t[ FXI'ΎRi [٢ZѾ\NVi٫2(FjI$ISn܏#ʜ%c5cGӜMN$\dRR]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/&9O̳PK Y1ڞKt.\nu!^>)SS}m6rSg.G'n ⼖'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' ^\NOj۾_`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<֞ Qi\rZW::8z)ӓxR+Oaރ^gy29^Hz786Iv kJ,,ЙRF~ث&=ۭ7aU%€XDƲK7V3C⼔'V*\Fw-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$*ɧuY8Տ9ƭ xKa`'m=~50NTw{ס|TOJVjb3M<ʗV-H4׮,TXGl/Ic~)NP\Fn<}s*Z&UeY(+ع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ϾtMmcrHlX09p$,.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&1cLԩ,^)pzGo6sUV-Nss;-gQDTw896sQf:*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)$yT"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-eXEJcUZI#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']G3\QΜ2Q1jwX 8tbO %^qpIk #!SѮ,2Q:8?3ko ;~*nmxˡB9Z֧kƲ -(F7QyԲw:6~f g7VA 9U)y'sܪ2E򥯙argT7]YWVvU_->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ҒbjDI"lyRyٲ`(ߝhcnzZ i٭a-Ϊͪ?YъI[pԫQd6kGHVS< $:8X0%:Q N.w\Y&Q¤X9jRH(f>E8=O4(;HZ!yM:wp2)-}8Mg/SXJEIyM(<ĂΗ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ˋ'Uu-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(QwtZW8f8ue 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(^@^{%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,j8J868friX̎#$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:1J9-Xh|ٜb"|"йvSW8LjM\^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^v8~|vqN Hry҄Zxj8KhPIHҥSAP>%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< ֩iFs;ak#\$9&g;!=!9 DsCC楚;?,HW0M4fҵeASW8|)90A#0~3q9gkdvCۡp&$gӥ)s")3)2"q G1Q2"D&rcC".dNPȋ91͈fɍvj5LhPLhPLh[!ld-RpRPm)%iAdPd*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 N7iN9gz'Eɒr~v*ƺz>㬒أΦc'HZ0˔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}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,ZrwB9 v@ZthzDRk@t认,@Ӣ1C9s:Iզl3XRgd[3d̑S fs̐(LfH\Lfl͐N ǀ.k(sXų(1lbYf(rbYYf(pK1lS`8K10B8 10:pQ@P8( tXF[{&&gQF'!*8gR6bD9x:\å!I֓yWt Ƽ %(^.A%?vJR178pMK[S>up\".pԱ[|%8W-+' ]\VY$q\'#VLEb&#Wy&8+|jw$0:CY!>q"4/}VqIٜDA#Ѻrs>c=/gULux -jΧYlbW=i$-$Z9s-TZNgv5Ԏ˴z{N̜)Йu>wԆwԉk9$v+'CžK K$6W%ΎJDN&et''c9/uQ>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 <> endobj 252 0 obj <>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 <>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ר׉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~>Ƀ:ؔgxMRI5Li6w:L?O9yDvtܴi*@I@]O[4?S=<|ߛD~D!n endstream endobj 254 0 obj <>stream H\͊0yCښ Bk[`+1D{w)]؀/dfGTҵCo*e:`­ubHۚq052}0I.RDoBhM.j)tF<LR׺a{;N+> endobj 256 0 obj <>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 <>stream H|TkPWffz B+4MpZ{} qQAF@\PE"*A- 1٨f)cYf=MXMj֭u=w=$!!Ht YS MA7A,%6*$ &P9iClJl$cRFF6l|l2l&Ⱦ'rP+)j+Fi ^0,KSJ jM0I!]aaZWZ2DXvZBM#5 Yc4Ph{ ?>|**{HjWƏ_%`< `a[E]6p+fJ*W IިA?EV3%RS&szrwc/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 <<1E176FDB5E6C4E6F93264F099BE4DA4D>]/Prev 453291>> startxref 597803 %%EOF fuse-3.17.2/doc/html/files.html0000644000175000017500000005133515002273413015221 0ustar berndbernd libfuse: File List

libfuse
File List
Here is a list of all documented files with brief descriptions:
[detail level 123]
  build-ubuntu
  meson-private
 sanitycheckc.c
 fuse_config.h
 libfuse_config.h
  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
  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
 iconv.c
 subdir.c
 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
  test
 hello.c
 readdir_inode.c
 release_unlink_race.c
 stracedecode.c
 test_setattr.c
 test_syscalls.c
 test_want_conversion.c
 test_write_cache.c
 wrong_command.c
  util
 fusermount.c
 mount.fuse.c
fuse-3.17.2/doc/html/folderclosed.png0000644000175000017500000000115014240475424016403 0ustar berndberndPNG  IHDR}\/IDATx]MO@~uؐlp]#]PYEC\9y`xC &=qvZv3m؃vLN}}ޝZA@n ONp xKxj8s _[D'yye+ 7#rNlk* 0Ь_d_(Öz=xvhzP-䍒̪u$\DJcB4.:Ϗ-}LE #gN;B6䬜@p&h>p9EEάʑ"un$R"?{<%PNt$߶+^<"2Dqq\ҙaA"ԵP}#Ez{.8i p(ADwDE߂z;Kק8t q:uvvݛvEn{MFXgfZ֝*ߩ:jYq#3SWr'  IENDB`fuse-3.17.2/doc/html/folderopen.png0000644000175000017500000000112514240475424016075 0ustar berndberndPNG  IHDR}\IDATx]?oP9i4i;iiZ7`b٬,HU'$*T]TDP6w};C; aӝߟjAInS}9Hӎ|? =_Ɗue*;YEsYBėsٌ ɫYq !Gǿv̇خ F}qb]70)d-}PfY{4@}2ԗNIǃc%UImcƝ>xt9$ OVE*Û#׈r@l$PrHaa dZrqIoT\,tj2FAxv-Lp׌p TI/ \sf; jViTo^cpb]€<a՜y9:+,E f6NEKU}^;nZuUS4 ѬbN.kjT% iV )GJ@TxIENDB`fuse-3.17.2/doc/html/functions.html0000644000175000017500000005751715002273413016137 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.17.2/doc/html/functions_vars.html0000644000175000017500000005751515002273413017170 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.17.2/doc/html/fuse_8c_source.html0000644000175000017500000324554515002273413017046 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 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 container_of(ptr, type, member) ({ \
96 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
97 (type *)( (char *)__mptr - offsetof(type,member) );})
98
99#define list_entry(ptr, type, member) \
100 container_of(ptr, type, member)
101
102struct list_head {
103 struct list_head *next;
104 struct list_head *prev;
105};
106
107struct node_slab {
108 struct list_head list; /* must be the first member */
109 struct list_head freelist;
110 int used;
111};
112
113struct fuse {
114 struct fuse_session *se;
115 struct node_table name_table;
116 struct node_table id_table;
117 struct list_head lru_table;
118 fuse_ino_t ctr;
119 unsigned int generation;
120 unsigned int hidectr;
121 pthread_mutex_t lock;
122 struct fuse_config conf;
123 int intr_installed;
124 struct fuse_fs *fs;
125 struct lock_queue_element *lockq;
126 int pagesize;
127 struct list_head partial_slabs;
128 struct list_head full_slabs;
129 pthread_t prune_thread;
130};
131
132struct lock {
133 int type;
134 off_t start;
135 off_t end;
136 pid_t pid;
137 uint64_t owner;
138 struct lock *next;
139};
140
141struct node {
142 struct node *name_next;
143 struct node *id_next;
144 fuse_ino_t nodeid;
145 unsigned int generation;
146 int refctr;
147 struct node *parent;
148 char *name;
149 uint64_t nlookup;
150 int open_count;
151 struct timespec stat_updated;
152 struct timespec mtime;
153 off_t size;
154 struct lock *locks;
155 unsigned int is_hidden : 1;
156 unsigned int cache_valid : 1;
157 int treelock;
158 char inline_name[32];
159};
160
161#define TREELOCK_WRITE -1
162#define TREELOCK_WAIT_OFFSET INT_MIN
163
164struct node_lru {
165 struct node node;
166 struct list_head lru;
167 struct timespec forget_time;
168};
169
170struct fuse_direntry {
171 struct stat stat;
172 enum fuse_fill_dir_flags flags;
173 char *name;
174 struct fuse_direntry *next;
175};
176
177struct fuse_dh {
178 pthread_mutex_t lock;
179 struct fuse *fuse;
180 fuse_req_t req;
181 char *contents;
182 struct fuse_direntry *first;
183 struct fuse_direntry **last;
184 unsigned len;
185 unsigned size;
186 unsigned needlen;
187 int filled;
188 uint64_t fh;
189 int error;
190 fuse_ino_t nodeid;
191};
192
193struct fuse_context_i {
194 struct fuse_context ctx;
195 fuse_req_t req;
196};
197
198/* Defined by FUSE_REGISTER_MODULE() in lib/modules/subdir.c and iconv.c. */
199extern fuse_module_factory_t fuse_module_subdir_factory;
200#ifdef HAVE_ICONV
201extern fuse_module_factory_t fuse_module_iconv_factory;
202#endif
203
204static pthread_key_t fuse_context_key;
205static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
206static int fuse_context_ref;
207static struct fuse_module *fuse_modules = NULL;
208
209static int fuse_register_module(const char *name,
210 fuse_module_factory_t factory,
211 struct fusemod_so *so)
212{
213 struct fuse_module *mod;
214
215 mod = calloc(1, sizeof(struct fuse_module));
216 if (!mod) {
217 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module\n");
218 return -1;
219 }
220 mod->name = strdup(name);
221 if (!mod->name) {
222 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module name\n");
223 free(mod);
224 return -1;
225 }
226 mod->factory = factory;
227 mod->ctr = 0;
228 mod->so = so;
229 if (mod->so)
230 mod->so->ctr++;
231 mod->next = fuse_modules;
232 fuse_modules = mod;
233
234 return 0;
235}
236
237static void fuse_unregister_module(struct fuse_module *m)
238{
239 struct fuse_module **mp;
240 for (mp = &fuse_modules; *mp; mp = &(*mp)->next) {
241 if (*mp == m) {
242 *mp = (*mp)->next;
243 break;
244 }
245 }
246 free(m->name);
247 free(m);
248}
249
250static int fuse_load_so_module(const char *module)
251{
252 int ret = -1;
253 char *tmp;
254 struct fusemod_so *so;
255 fuse_module_factory_t *factory;
256
257 tmp = malloc(strlen(module) + 64);
258 if (!tmp) {
259 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
260 return -1;
261 }
262 sprintf(tmp, "libfusemod_%s.so", module);
263 so = calloc(1, sizeof(struct fusemod_so));
264 if (!so) {
265 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module so\n");
266 goto out;
267 }
268
269 so->handle = dlopen(tmp, RTLD_NOW);
270 if (so->handle == NULL) {
271 fuse_log(FUSE_LOG_ERR, "fuse: dlopen(%s) failed: %s\n",
272 tmp, dlerror());
273 goto out_free_so;
274 }
275
276 sprintf(tmp, "fuse_module_%s_factory", module);
277 factory = (fuse_module_factory_t*)dlsym(so->handle, tmp);
278 if (factory == NULL) {
279 fuse_log(FUSE_LOG_ERR, "fuse: symbol <%s> not found in module: %s\n",
280 tmp, dlerror());
281 goto out_dlclose;
282 }
283 ret = fuse_register_module(module, *factory, so);
284 if (ret)
285 goto out_dlclose;
286
287out:
288 free(tmp);
289 return ret;
290
291out_dlclose:
292 dlclose(so->handle);
293out_free_so:
294 free(so);
295 goto out;
296}
297
298static struct fuse_module *fuse_find_module(const char *module)
299{
300 struct fuse_module *m;
301 for (m = fuse_modules; m; m = m->next) {
302 if (strcmp(module, m->name) == 0) {
303 m->ctr++;
304 break;
305 }
306 }
307 return m;
308}
309
310static struct fuse_module *fuse_get_module(const char *module)
311{
312 struct fuse_module *m;
313
314 pthread_mutex_lock(&fuse_context_lock);
315 m = fuse_find_module(module);
316 if (!m) {
317 int err = fuse_load_so_module(module);
318 if (!err)
319 m = fuse_find_module(module);
320 }
321 pthread_mutex_unlock(&fuse_context_lock);
322 return m;
323}
324
325static void fuse_put_module(struct fuse_module *m)
326{
327 pthread_mutex_lock(&fuse_context_lock);
328 if (m->so)
329 assert(m->ctr > 0);
330 /* Builtin modules may already have m->ctr == 0 */
331 if (m->ctr > 0)
332 m->ctr--;
333 if (!m->ctr && m->so) {
334 struct fusemod_so *so = m->so;
335 assert(so->ctr > 0);
336 so->ctr--;
337 if (!so->ctr) {
338 struct fuse_module **mp;
339 for (mp = &fuse_modules; *mp;) {
340 if ((*mp)->so == so)
341 fuse_unregister_module(*mp);
342 else
343 mp = &(*mp)->next;
344 }
345 dlclose(so->handle);
346 free(so);
347 }
348 } else if (!m->ctr) {
349 fuse_unregister_module(m);
350 }
351 pthread_mutex_unlock(&fuse_context_lock);
352}
353
354static void init_list_head(struct list_head *list)
355{
356 list->next = list;
357 list->prev = list;
358}
359
360static int list_empty(const struct list_head *head)
361{
362 return head->next == head;
363}
364
365static void list_add(struct list_head *new, struct list_head *prev,
366 struct list_head *next)
367{
368 next->prev = new;
369 new->next = next;
370 new->prev = prev;
371 prev->next = new;
372}
373
374static inline void list_add_head(struct list_head *new, struct list_head *head)
375{
376 list_add(new, head, head->next);
377}
378
379static inline void list_add_tail(struct list_head *new, struct list_head *head)
380{
381 list_add(new, head->prev, head);
382}
383
384static inline void list_del(struct list_head *entry)
385{
386 struct list_head *prev = entry->prev;
387 struct list_head *next = entry->next;
388
389 next->prev = prev;
390 prev->next = next;
391}
392
393static inline int lru_enabled(struct fuse *f)
394{
395 return f->conf.remember > 0;
396}
397
398static struct node_lru *node_lru(struct node *node)
399{
400 return (struct node_lru *) node;
401}
402
403static size_t get_node_size(struct fuse *f)
404{
405 if (lru_enabled(f))
406 return sizeof(struct node_lru);
407 else
408 return sizeof(struct node);
409}
410
411#ifdef FUSE_NODE_SLAB
412static struct node_slab *list_to_slab(struct list_head *head)
413{
414 return (struct node_slab *) head;
415}
416
417static struct node_slab *node_to_slab(struct fuse *f, struct node *node)
418{
419 return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1));
420}
421
422static int alloc_slab(struct fuse *f)
423{
424 void *mem;
425 struct node_slab *slab;
426 char *start;
427 size_t num;
428 size_t i;
429 size_t node_size = get_node_size(f);
430
431 mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE,
432 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
433
434 if (mem == MAP_FAILED)
435 return -1;
436
437 slab = mem;
438 init_list_head(&slab->freelist);
439 slab->used = 0;
440 num = (f->pagesize - sizeof(struct node_slab)) / node_size;
441
442 start = (char *) mem + f->pagesize - num * node_size;
443 for (i = 0; i < num; i++) {
444 struct list_head *n;
445
446 n = (struct list_head *) (start + i * node_size);
447 list_add_tail(n, &slab->freelist);
448 }
449 list_add_tail(&slab->list, &f->partial_slabs);
450
451 return 0;
452}
453
454static struct node *alloc_node(struct fuse *f)
455{
456 struct node_slab *slab;
457 struct list_head *node;
458
459 if (list_empty(&f->partial_slabs)) {
460 int res = alloc_slab(f);
461 if (res != 0)
462 return NULL;
463 }
464 slab = list_to_slab(f->partial_slabs.next);
465 slab->used++;
466 node = slab->freelist.next;
467 list_del(node);
468 if (list_empty(&slab->freelist)) {
469 list_del(&slab->list);
470 list_add_tail(&slab->list, &f->full_slabs);
471 }
472 memset(node, 0, sizeof(struct node));
473
474 return (struct node *) node;
475}
476
477static void free_slab(struct fuse *f, struct node_slab *slab)
478{
479 int res;
480
481 list_del(&slab->list);
482 res = munmap(slab, f->pagesize);
483 if (res == -1)
484 fuse_log(FUSE_LOG_WARNING, "fuse warning: munmap(%p) failed\n",
485 slab);
486}
487
488static void free_node_mem(struct fuse *f, struct node *node)
489{
490 struct node_slab *slab = node_to_slab(f, node);
491 struct list_head *n = (struct list_head *) node;
492
493 slab->used--;
494 if (slab->used) {
495 if (list_empty(&slab->freelist)) {
496 list_del(&slab->list);
497 list_add_tail(&slab->list, &f->partial_slabs);
498 }
499 list_add_head(n, &slab->freelist);
500 } else {
501 free_slab(f, slab);
502 }
503}
504#else
505static struct node *alloc_node(struct fuse *f)
506{
507 return (struct node *) calloc(1, get_node_size(f));
508}
509
510static void free_node_mem(struct fuse *f, struct node *node)
511{
512 (void) f;
513 free(node);
514}
515#endif
516
517static size_t id_hash(struct fuse *f, fuse_ino_t ino)
518{
519 uint64_t hash = ((uint32_t) ino * 2654435761U) % f->id_table.size;
520 uint64_t oldhash = hash % (f->id_table.size / 2);
521
522 if (oldhash >= f->id_table.split)
523 return oldhash;
524 else
525 return hash;
526}
527
528static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
529{
530 size_t hash = id_hash(f, nodeid);
531 struct node *node;
532
533 for (node = f->id_table.array[hash]; node != NULL; node = node->id_next)
534 if (node->nodeid == nodeid)
535 return node;
536
537 return NULL;
538}
539
540static struct node *get_node(struct fuse *f, fuse_ino_t nodeid)
541{
542 struct node *node = get_node_nocheck(f, nodeid);
543 if (!node) {
544 fuse_log(FUSE_LOG_ERR, "fuse internal error: node %llu not found\n",
545 (unsigned long long) nodeid);
546 abort();
547 }
548 return node;
549}
550
551static void curr_time(struct timespec *now);
552static double diff_timespec(const struct timespec *t1,
553 const struct timespec *t2);
554
555static void remove_node_lru(struct node *node)
556{
557 struct node_lru *lnode = node_lru(node);
558 list_del(&lnode->lru);
559 init_list_head(&lnode->lru);
560}
561
562static void set_forget_time(struct fuse *f, struct node *node)
563{
564 struct node_lru *lnode = node_lru(node);
565
566 list_del(&lnode->lru);
567 list_add_tail(&lnode->lru, &f->lru_table);
568 curr_time(&lnode->forget_time);
569}
570
571static void free_node(struct fuse *f, struct node *node)
572{
573 if (node->name != node->inline_name)
574 free(node->name);
575 free_node_mem(f, node);
576}
577
578static void node_table_reduce(struct node_table *t)
579{
580 size_t newsize = t->size / 2;
581 void *newarray;
582
583 if (newsize < NODE_TABLE_MIN_SIZE)
584 return;
585
586 newarray = realloc(t->array, sizeof(struct node *) * newsize);
587 if (newarray != NULL)
588 t->array = newarray;
589
590 t->size = newsize;
591 t->split = t->size / 2;
592}
593
594static void remerge_id(struct fuse *f)
595{
596 struct node_table *t = &f->id_table;
597 int iter;
598
599 if (t->split == 0)
600 node_table_reduce(t);
601
602 for (iter = 8; t->split > 0 && iter; iter--) {
603 struct node **upper;
604
605 t->split--;
606 upper = &t->array[t->split + t->size / 2];
607 if (*upper) {
608 struct node **nodep;
609
610 for (nodep = &t->array[t->split]; *nodep;
611 nodep = &(*nodep)->id_next);
612
613 *nodep = *upper;
614 *upper = NULL;
615 break;
616 }
617 }
618}
619
620static void unhash_id(struct fuse *f, struct node *node)
621{
622 struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)];
623
624 for (; *nodep != NULL; nodep = &(*nodep)->id_next)
625 if (*nodep == node) {
626 *nodep = node->id_next;
627 f->id_table.use--;
628
629 if(f->id_table.use < f->id_table.size / 4)
630 remerge_id(f);
631 return;
632 }
633}
634
635static int node_table_resize(struct node_table *t)
636{
637 size_t newsize = t->size * 2;
638 void *newarray;
639
640 newarray = realloc(t->array, sizeof(struct node *) * newsize);
641 if (newarray == NULL)
642 return -1;
643
644 t->array = newarray;
645 memset(t->array + t->size, 0, t->size * sizeof(struct node *));
646 t->size = newsize;
647 t->split = 0;
648
649 return 0;
650}
651
652static void rehash_id(struct fuse *f)
653{
654 struct node_table *t = &f->id_table;
655 struct node **nodep;
656 struct node **next;
657 size_t hash;
658
659 if (t->split == t->size / 2)
660 return;
661
662 hash = t->split;
663 t->split++;
664 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
665 struct node *node = *nodep;
666 size_t newhash = id_hash(f, node->nodeid);
667
668 if (newhash != hash) {
669 next = nodep;
670 *nodep = node->id_next;
671 node->id_next = t->array[newhash];
672 t->array[newhash] = node;
673 } else {
674 next = &node->id_next;
675 }
676 }
677 if (t->split == t->size / 2)
678 node_table_resize(t);
679}
680
681static void hash_id(struct fuse *f, struct node *node)
682{
683 size_t hash = id_hash(f, node->nodeid);
684 node->id_next = f->id_table.array[hash];
685 f->id_table.array[hash] = node;
686 f->id_table.use++;
687
688 if (f->id_table.use >= f->id_table.size / 2)
689 rehash_id(f);
690}
691
692static size_t name_hash(struct fuse *f, fuse_ino_t parent,
693 const char *name)
694{
695 uint64_t hash = parent;
696 uint64_t oldhash;
697
698 for (; *name; name++)
699 hash = hash * 31 + (unsigned char) *name;
700
701 hash %= f->name_table.size;
702 oldhash = hash % (f->name_table.size / 2);
703 if (oldhash >= f->name_table.split)
704 return oldhash;
705 else
706 return hash;
707}
708
709static void unref_node(struct fuse *f, struct node *node);
710
711static void remerge_name(struct fuse *f)
712{
713 struct node_table *t = &f->name_table;
714 int iter;
715
716 if (t->split == 0)
717 node_table_reduce(t);
718
719 for (iter = 8; t->split > 0 && iter; iter--) {
720 struct node **upper;
721
722 t->split--;
723 upper = &t->array[t->split + t->size / 2];
724 if (*upper) {
725 struct node **nodep;
726
727 for (nodep = &t->array[t->split]; *nodep;
728 nodep = &(*nodep)->name_next);
729
730 *nodep = *upper;
731 *upper = NULL;
732 break;
733 }
734 }
735}
736
737static void unhash_name(struct fuse *f, struct node *node)
738{
739 if (node->name) {
740 size_t hash = name_hash(f, node->parent->nodeid, node->name);
741 struct node **nodep = &f->name_table.array[hash];
742
743 for (; *nodep != NULL; nodep = &(*nodep)->name_next)
744 if (*nodep == node) {
745 *nodep = node->name_next;
746 node->name_next = NULL;
747 unref_node(f, node->parent);
748 if (node->name != node->inline_name)
749 free(node->name);
750 node->name = NULL;
751 node->parent = NULL;
752 f->name_table.use--;
753
754 if (f->name_table.use < f->name_table.size / 4)
755 remerge_name(f);
756 return;
757 }
758 fuse_log(FUSE_LOG_ERR,
759 "fuse internal error: unable to unhash node: %llu\n",
760 (unsigned long long) node->nodeid);
761 abort();
762 }
763}
764
765static void rehash_name(struct fuse *f)
766{
767 struct node_table *t = &f->name_table;
768 struct node **nodep;
769 struct node **next;
770 size_t hash;
771
772 if (t->split == t->size / 2)
773 return;
774
775 hash = t->split;
776 t->split++;
777 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
778 struct node *node = *nodep;
779 size_t newhash = name_hash(f, node->parent->nodeid, node->name);
780
781 if (newhash != hash) {
782 next = nodep;
783 *nodep = node->name_next;
784 node->name_next = t->array[newhash];
785 t->array[newhash] = node;
786 } else {
787 next = &node->name_next;
788 }
789 }
790 if (t->split == t->size / 2)
791 node_table_resize(t);
792}
793
794static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
795 const char *name)
796{
797 size_t hash = name_hash(f, parentid, name);
798 struct node *parent = get_node(f, parentid);
799 if (strlen(name) < sizeof(node->inline_name)) {
800 strcpy(node->inline_name, name);
801 node->name = node->inline_name;
802 } else {
803 node->name = strdup(name);
804 if (node->name == NULL)
805 return -1;
806 }
807
808 parent->refctr ++;
809 node->parent = parent;
810 node->name_next = f->name_table.array[hash];
811 f->name_table.array[hash] = node;
812 f->name_table.use++;
813
814 if (f->name_table.use >= f->name_table.size / 2)
815 rehash_name(f);
816
817 return 0;
818}
819
820static void delete_node(struct fuse *f, struct node *node)
821{
822 if (f->conf.debug)
823 fuse_log(FUSE_LOG_DEBUG, "DELETE: %llu\n",
824 (unsigned long long) node->nodeid);
825
826 assert(node->treelock == 0);
827 unhash_name(f, node);
828 if (lru_enabled(f))
829 remove_node_lru(node);
830 unhash_id(f, node);
831 free_node(f, node);
832}
833
834static void unref_node(struct fuse *f, struct node *node)
835{
836 assert(node->refctr > 0);
837 node->refctr --;
838 if (!node->refctr)
839 delete_node(f, node);
840}
841
842static fuse_ino_t next_id(struct fuse *f)
843{
844 do {
845 f->ctr = (f->ctr + 1) & 0xffffffff;
846 if (!f->ctr)
847 f->generation ++;
848 } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO ||
849 get_node_nocheck(f, f->ctr) != NULL);
850 return f->ctr;
851}
852
853static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
854 const char *name)
855{
856 size_t hash = name_hash(f, parent, name);
857 struct node *node;
858
859 for (node = f->name_table.array[hash]; node != NULL; node = node->name_next)
860 if (node->parent->nodeid == parent &&
861 strcmp(node->name, name) == 0)
862 return node;
863
864 return NULL;
865}
866
867static void inc_nlookup(struct node *node)
868{
869 if (!node->nlookup)
870 node->refctr++;
871 node->nlookup++;
872}
873
874static struct node *find_node(struct fuse *f, fuse_ino_t parent,
875 const char *name)
876{
877 struct node *node;
878
879 pthread_mutex_lock(&f->lock);
880 if (!name)
881 node = get_node(f, parent);
882 else
883 node = lookup_node(f, parent, name);
884 if (node == NULL) {
885 node = alloc_node(f);
886 if (node == NULL)
887 goto out_err;
888
889 node->nodeid = next_id(f);
890 node->generation = f->generation;
891 if (f->conf.remember)
892 inc_nlookup(node);
893
894 if (hash_name(f, node, parent, name) == -1) {
895 free_node(f, node);
896 node = NULL;
897 goto out_err;
898 }
899 hash_id(f, node);
900 if (lru_enabled(f)) {
901 struct node_lru *lnode = node_lru(node);
902 init_list_head(&lnode->lru);
903 }
904 } else if (lru_enabled(f) && node->nlookup == 1) {
905 remove_node_lru(node);
906 }
907 inc_nlookup(node);
908out_err:
909 pthread_mutex_unlock(&f->lock);
910 return node;
911}
912
913static int lookup_path_in_cache(struct fuse *f,
914 const char *path, fuse_ino_t *inop)
915{
916 char *tmp = strdup(path);
917 if (!tmp)
918 return -ENOMEM;
919
920 pthread_mutex_lock(&f->lock);
921 fuse_ino_t ino = FUSE_ROOT_ID;
922
923 int err = 0;
924 char *save_ptr;
925 char *path_element = strtok_r(tmp, "/", &save_ptr);
926 while (path_element != NULL) {
927 struct node *node = lookup_node(f, ino, path_element);
928 if (node == NULL) {
929 err = -ENOENT;
930 break;
931 }
932 ino = node->nodeid;
933 path_element = strtok_r(NULL, "/", &save_ptr);
934 }
935 pthread_mutex_unlock(&f->lock);
936 free(tmp);
937
938 if (!err)
939 *inop = ino;
940 return err;
941}
942
943static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name)
944{
945 size_t len = strlen(name);
946
947 if (s - len <= *buf) {
948 unsigned pathlen = *bufsize - (s - *buf);
949 unsigned newbufsize = *bufsize;
950 char *newbuf;
951
952 while (newbufsize < pathlen + len + 1) {
953 if (newbufsize >= 0x80000000)
954 newbufsize = 0xffffffff;
955 else
956 newbufsize *= 2;
957 }
958
959 newbuf = realloc(*buf, newbufsize);
960 if (newbuf == NULL)
961 return NULL;
962
963 *buf = newbuf;
964 s = newbuf + newbufsize - pathlen;
965 memmove(s, newbuf + *bufsize - pathlen, pathlen);
966 *bufsize = newbufsize;
967 }
968 s -= len;
969 memcpy(s, name, len);
970 s--;
971 *s = '/';
972
973 return s;
974}
975
976static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode,
977 struct node *end)
978{
979 struct node *node;
980
981 if (wnode) {
982 assert(wnode->treelock == TREELOCK_WRITE);
983 wnode->treelock = 0;
984 }
985
986 for (node = get_node(f, nodeid);
987 node != end && node->nodeid != FUSE_ROOT_ID; node = node->parent) {
988 assert(node->treelock != 0);
989 assert(node->treelock != TREELOCK_WAIT_OFFSET);
990 assert(node->treelock != TREELOCK_WRITE);
991 node->treelock--;
992 if (node->treelock == TREELOCK_WAIT_OFFSET)
993 node->treelock = 0;
994 }
995}
996
997static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
998 char **path, struct node **wnodep, bool need_lock)
999{
1000 unsigned bufsize = 256;
1001 char *buf;
1002 char *s;
1003 struct node *node;
1004 struct node *wnode = NULL;
1005 int err;
1006
1007 *path = NULL;
1008
1009 err = -ENOMEM;
1010 buf = malloc(bufsize);
1011 if (buf == NULL)
1012 goto out_err;
1013
1014 s = buf + bufsize - 1;
1015 *s = '\0';
1016
1017 if (name != NULL) {
1018 s = add_name(&buf, &bufsize, s, name);
1019 err = -ENOMEM;
1020 if (s == NULL)
1021 goto out_free;
1022 }
1023
1024 if (wnodep) {
1025 assert(need_lock);
1026 wnode = lookup_node(f, nodeid, name);
1027 if (wnode) {
1028 if (wnode->treelock != 0) {
1029 if (wnode->treelock > 0)
1030 wnode->treelock += TREELOCK_WAIT_OFFSET;
1031 err = -EAGAIN;
1032 goto out_free;
1033 }
1034 wnode->treelock = TREELOCK_WRITE;
1035 }
1036 }
1037
1038 for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
1039 node = node->parent) {
1040 err = -ESTALE;
1041 if (node->name == NULL || node->parent == NULL)
1042 goto out_unlock;
1043
1044 err = -ENOMEM;
1045 s = add_name(&buf, &bufsize, s, node->name);
1046 if (s == NULL)
1047 goto out_unlock;
1048
1049 if (need_lock) {
1050 err = -EAGAIN;
1051 if (node->treelock < 0)
1052 goto out_unlock;
1053
1054 node->treelock++;
1055 }
1056 }
1057
1058 if (s[0])
1059 memmove(buf, s, bufsize - (s - buf));
1060 else
1061 strcpy(buf, "/");
1062
1063 *path = buf;
1064 if (wnodep)
1065 *wnodep = wnode;
1066
1067 return 0;
1068
1069 out_unlock:
1070 if (need_lock)
1071 unlock_path(f, nodeid, wnode, node);
1072 out_free:
1073 free(buf);
1074
1075 out_err:
1076 return err;
1077}
1078
1079static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
1080 fuse_ino_t nodeid2, const char *name2,
1081 char **path1, char **path2,
1082 struct node **wnode1, struct node **wnode2)
1083{
1084 int err;
1085
1086 /* FIXME: locking two paths needs deadlock checking */
1087 err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
1088 if (!err) {
1089 err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
1090 if (err) {
1091 struct node *wn1 = wnode1 ? *wnode1 : NULL;
1092
1093 unlock_path(f, nodeid1, wn1, NULL);
1094 free(*path1);
1095 }
1096 }
1097 return err;
1098}
1099
1100static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
1101{
1102 int err;
1103
1104 if (!qe->path1) {
1105 /* Just waiting for it to be unlocked */
1106 if (get_node(f, qe->nodeid1)->treelock == 0)
1107 pthread_cond_signal(&qe->cond);
1108
1109 return;
1110 }
1111
1112 if (qe->done)
1113 return; // Don't try to double-lock the element
1114
1115 if (!qe->path2) {
1116 err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
1117 qe->wnode1, true);
1118 } else {
1119 err = try_get_path2(f, qe->nodeid1, qe->name1, qe->nodeid2,
1120 qe->name2, qe->path1, qe->path2, qe->wnode1,
1121 qe->wnode2);
1122 }
1123
1124 if (err == -EAGAIN)
1125 return; /* keep trying */
1126
1127 qe->err = err;
1128 qe->done = true;
1129 pthread_cond_signal(&qe->cond);
1130}
1131
1132static void wake_up_queued(struct fuse *f)
1133{
1134 struct lock_queue_element *qe;
1135
1136 for (qe = f->lockq; qe != NULL; qe = qe->next)
1137 queue_element_wakeup(f, qe);
1138}
1139
1140static void debug_path(struct fuse *f, const char *msg, fuse_ino_t nodeid,
1141 const char *name, bool wr)
1142{
1143 if (f->conf.debug) {
1144 struct node *wnode = NULL;
1145
1146 if (wr)
1147 wnode = lookup_node(f, nodeid, name);
1148
1149 if (wnode) {
1150 fuse_log(FUSE_LOG_DEBUG, "%s %llu (w)\n",
1151 msg, (unsigned long long) wnode->nodeid);
1152 } else {
1153 fuse_log(FUSE_LOG_DEBUG, "%s %llu\n",
1154 msg, (unsigned long long) nodeid);
1155 }
1156 }
1157}
1158
1159static void queue_path(struct fuse *f, struct lock_queue_element *qe)
1160{
1161 struct lock_queue_element **qp;
1162
1163 qe->done = false;
1164 pthread_cond_init(&qe->cond, NULL);
1165 qe->next = NULL;
1166 for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
1167 *qp = qe;
1168}
1169
1170static void dequeue_path(struct fuse *f, struct lock_queue_element *qe)
1171{
1172 struct lock_queue_element **qp;
1173
1174 pthread_cond_destroy(&qe->cond);
1175 for (qp = &f->lockq; *qp != qe; qp = &(*qp)->next);
1176 *qp = qe->next;
1177}
1178
1179static int wait_path(struct fuse *f, struct lock_queue_element *qe)
1180{
1181 queue_path(f, qe);
1182
1183 do {
1184 pthread_cond_wait(&qe->cond, &f->lock);
1185 } while (!qe->done);
1186
1187 dequeue_path(f, qe);
1188
1189 return qe->err;
1190}
1191
1192static int get_path_common(struct fuse *f, fuse_ino_t nodeid, const char *name,
1193 char **path, struct node **wnode)
1194{
1195 int err;
1196
1197 pthread_mutex_lock(&f->lock);
1198 err = try_get_path(f, nodeid, name, path, wnode, true);
1199 if (err == -EAGAIN) {
1200 struct lock_queue_element qe = {
1201 .nodeid1 = nodeid,
1202 .name1 = name,
1203 .path1 = path,
1204 .wnode1 = wnode,
1205 };
1206 debug_path(f, "QUEUE PATH", nodeid, name, !!wnode);
1207 err = wait_path(f, &qe);
1208 debug_path(f, "DEQUEUE PATH", nodeid, name, !!wnode);
1209 }
1210 pthread_mutex_unlock(&f->lock);
1211
1212 return err;
1213}
1214
1215static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path)
1216{
1217 return get_path_common(f, nodeid, NULL, path, NULL);
1218}
1219
1220static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
1221{
1222 int err = 0;
1223
1224 if (f->conf.nullpath_ok) {
1225 *path = NULL;
1226 } else {
1227 err = get_path_common(f, nodeid, NULL, path, NULL);
1228 if (err == -ESTALE)
1229 err = 0;
1230 }
1231
1232 return err;
1233}
1234
1235static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name,
1236 char **path)
1237{
1238 return get_path_common(f, nodeid, name, path, NULL);
1239}
1240
1241static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name,
1242 char **path, struct node **wnode)
1243{
1244 return get_path_common(f, nodeid, name, path, wnode);
1245}
1246
1247#if defined(__FreeBSD__)
1248#define CHECK_DIR_LOOP
1249#endif
1250
1251#if defined(CHECK_DIR_LOOP)
1252static int check_dir_loop(struct fuse *f,
1253 fuse_ino_t nodeid1, const char *name1,
1254 fuse_ino_t nodeid2, const char *name2)
1255{
1256 struct node *node, *node1, *node2;
1257 fuse_ino_t id1, id2;
1258
1259 node1 = lookup_node(f, nodeid1, name1);
1260 id1 = node1 ? node1->nodeid : nodeid1;
1261
1262 node2 = lookup_node(f, nodeid2, name2);
1263 id2 = node2 ? node2->nodeid : nodeid2;
1264
1265 for (node = get_node(f, id2); node->nodeid != FUSE_ROOT_ID;
1266 node = node->parent) {
1267 if (node->name == NULL || node->parent == NULL)
1268 break;
1269
1270 if (node->nodeid != id2 && node->nodeid == id1)
1271 return -EINVAL;
1272 }
1273
1274 if (node2)
1275 {
1276 for (node = get_node(f, id1); node->nodeid != FUSE_ROOT_ID;
1277 node = node->parent) {
1278 if (node->name == NULL || node->parent == NULL)
1279 break;
1280
1281 if (node->nodeid != id1 && node->nodeid == id2)
1282 return -ENOTEMPTY;
1283 }
1284 }
1285
1286 return 0;
1287}
1288#endif
1289
1290static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
1291 fuse_ino_t nodeid2, const char *name2,
1292 char **path1, char **path2,
1293 struct node **wnode1, struct node **wnode2)
1294{
1295 int err;
1296
1297 pthread_mutex_lock(&f->lock);
1298
1299#if defined(CHECK_DIR_LOOP)
1300 if (name1)
1301 {
1302 // called during rename; perform dir loop check
1303 err = check_dir_loop(f, nodeid1, name1, nodeid2, name2);
1304 if (err)
1305 goto out_unlock;
1306 }
1307#endif
1308
1309 err = try_get_path2(f, nodeid1, name1, nodeid2, name2,
1310 path1, path2, wnode1, wnode2);
1311 if (err == -EAGAIN) {
1312 struct lock_queue_element qe = {
1313 .nodeid1 = nodeid1,
1314 .name1 = name1,
1315 .path1 = path1,
1316 .wnode1 = wnode1,
1317 .nodeid2 = nodeid2,
1318 .name2 = name2,
1319 .path2 = path2,
1320 .wnode2 = wnode2,
1321 };
1322
1323 debug_path(f, "QUEUE PATH1", nodeid1, name1, !!wnode1);
1324 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
1325 err = wait_path(f, &qe);
1326 debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1);
1327 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
1328 }
1329
1330#if defined(CHECK_DIR_LOOP)
1331out_unlock:
1332#endif
1333 pthread_mutex_unlock(&f->lock);
1334
1335 return err;
1336}
1337
1338static void free_path_wrlock(struct fuse *f, fuse_ino_t nodeid,
1339 struct node *wnode, char *path)
1340{
1341 pthread_mutex_lock(&f->lock);
1342 unlock_path(f, nodeid, wnode, NULL);
1343 if (f->lockq)
1344 wake_up_queued(f);
1345 pthread_mutex_unlock(&f->lock);
1346 free(path);
1347}
1348
1349static void free_path(struct fuse *f, fuse_ino_t nodeid, char *path)
1350{
1351 if (path)
1352 free_path_wrlock(f, nodeid, NULL, path);
1353}
1354
1355static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2,
1356 struct node *wnode1, struct node *wnode2,
1357 char *path1, char *path2)
1358{
1359 pthread_mutex_lock(&f->lock);
1360 unlock_path(f, nodeid1, wnode1, NULL);
1361 unlock_path(f, nodeid2, wnode2, NULL);
1362 wake_up_queued(f);
1363 pthread_mutex_unlock(&f->lock);
1364 free(path1);
1365 free(path2);
1366}
1367
1368static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
1369{
1370 struct node *node;
1371 if (nodeid == FUSE_ROOT_ID)
1372 return;
1373 pthread_mutex_lock(&f->lock);
1374 node = get_node(f, nodeid);
1375
1376 /*
1377 * Node may still be locked due to interrupt idiocy in open,
1378 * create and opendir
1379 */
1380 while (node->nlookup == nlookup && node->treelock) {
1381 struct lock_queue_element qe = {
1382 .nodeid1 = nodeid,
1383 };
1384
1385 debug_path(f, "QUEUE PATH (forget)", nodeid, NULL, false);
1386 queue_path(f, &qe);
1387
1388 do {
1389 pthread_cond_wait(&qe.cond, &f->lock);
1390 } while (node->nlookup == nlookup && node->treelock);
1391
1392 dequeue_path(f, &qe);
1393 debug_path(f, "DEQUEUE_PATH (forget)", nodeid, NULL, false);
1394 }
1395
1396 assert(node->nlookup >= nlookup);
1397 node->nlookup -= nlookup;
1398 if (!node->nlookup) {
1399 unref_node(f, node);
1400 } else if (lru_enabled(f) && node->nlookup == 1) {
1401 set_forget_time(f, node);
1402 }
1403 pthread_mutex_unlock(&f->lock);
1404}
1405
1406static void unlink_node(struct fuse *f, struct node *node)
1407{
1408 if (f->conf.remember) {
1409 assert(node->nlookup > 1);
1410 node->nlookup--;
1411 }
1412 unhash_name(f, node);
1413}
1414
1415static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name)
1416{
1417 struct node *node;
1418
1419 pthread_mutex_lock(&f->lock);
1420 node = lookup_node(f, dir, name);
1421 if (node != NULL)
1422 unlink_node(f, node);
1423 pthread_mutex_unlock(&f->lock);
1424}
1425
1426static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
1427 fuse_ino_t newdir, const char *newname, int hide)
1428{
1429 struct node *node;
1430 struct node *newnode;
1431 int err = 0;
1432
1433 pthread_mutex_lock(&f->lock);
1434 node = lookup_node(f, olddir, oldname);
1435 newnode = lookup_node(f, newdir, newname);
1436 if (node == NULL)
1437 goto out;
1438
1439 if (newnode != NULL) {
1440 if (hide) {
1441 fuse_log(FUSE_LOG_ERR, "fuse: hidden file got created during hiding\n");
1442 err = -EBUSY;
1443 goto out;
1444 }
1445 unlink_node(f, newnode);
1446 }
1447
1448 unhash_name(f, node);
1449 if (hash_name(f, node, newdir, newname) == -1) {
1450 err = -ENOMEM;
1451 goto out;
1452 }
1453
1454 if (hide)
1455 node->is_hidden = 1;
1456
1457out:
1458 pthread_mutex_unlock(&f->lock);
1459 return err;
1460}
1461
1462static int exchange_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
1463 fuse_ino_t newdir, const char *newname)
1464{
1465 struct node *oldnode;
1466 struct node *newnode;
1467 int err;
1468
1469 pthread_mutex_lock(&f->lock);
1470 oldnode = lookup_node(f, olddir, oldname);
1471 newnode = lookup_node(f, newdir, newname);
1472
1473 if (oldnode)
1474 unhash_name(f, oldnode);
1475 if (newnode)
1476 unhash_name(f, newnode);
1477
1478 err = -ENOMEM;
1479 if (oldnode) {
1480 if (hash_name(f, oldnode, newdir, newname) == -1)
1481 goto out;
1482 }
1483 if (newnode) {
1484 if (hash_name(f, newnode, olddir, oldname) == -1)
1485 goto out;
1486 }
1487 err = 0;
1488out:
1489 pthread_mutex_unlock(&f->lock);
1490 return err;
1491}
1492
1493static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
1494{
1495 if (!f->conf.use_ino)
1496 stbuf->st_ino = nodeid;
1497 if (f->conf.set_mode) {
1498 if (f->conf.dmask && S_ISDIR(stbuf->st_mode))
1499 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1500 (0777 & ~f->conf.dmask);
1501 else if (f->conf.fmask)
1502 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1503 (0777 & ~f->conf.fmask);
1504 else
1505 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
1506 (0777 & ~f->conf.umask);
1507 }
1508 if (f->conf.set_uid)
1509 stbuf->st_uid = f->conf.uid;
1510 if (f->conf.set_gid)
1511 stbuf->st_gid = f->conf.gid;
1512}
1513
1514static struct fuse *req_fuse(fuse_req_t req)
1515{
1516 return (struct fuse *) fuse_req_userdata(req);
1517}
1518
1519static void fuse_intr_sighandler(int sig)
1520{
1521 (void) sig;
1522 /* Nothing to do */
1523}
1524
1525struct fuse_intr_data {
1526 pthread_t id;
1527 pthread_cond_t cond;
1528 int finished;
1529};
1530
1531static void fuse_interrupt(fuse_req_t req, void *d_)
1532{
1533 struct fuse_intr_data *d = d_;
1534 struct fuse *f = req_fuse(req);
1535
1536 if (d->id == pthread_self())
1537 return;
1538
1539 pthread_mutex_lock(&f->lock);
1540 while (!d->finished) {
1541 struct timeval now;
1542 struct timespec timeout;
1543
1544 pthread_kill(d->id, f->conf.intr_signal);
1545 gettimeofday(&now, NULL);
1546 timeout.tv_sec = now.tv_sec + 1;
1547 timeout.tv_nsec = now.tv_usec * 1000;
1548 pthread_cond_timedwait(&d->cond, &f->lock, &timeout);
1549 }
1550 pthread_mutex_unlock(&f->lock);
1551}
1552
1553static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req,
1554 struct fuse_intr_data *d)
1555{
1556 pthread_mutex_lock(&f->lock);
1557 d->finished = 1;
1558 pthread_cond_broadcast(&d->cond);
1559 pthread_mutex_unlock(&f->lock);
1560 fuse_req_interrupt_func(req, NULL, NULL);
1561 pthread_cond_destroy(&d->cond);
1562}
1563
1564static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d)
1565{
1566 d->id = pthread_self();
1567 pthread_cond_init(&d->cond, NULL);
1568 d->finished = 0;
1569 fuse_req_interrupt_func(req, fuse_interrupt, d);
1570}
1571
1572static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req,
1573 struct fuse_intr_data *d)
1574{
1575 if (f->conf.intr)
1576 fuse_do_finish_interrupt(f, req, d);
1577}
1578
1579static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req,
1580 struct fuse_intr_data *d)
1581{
1582 if (f->conf.intr)
1583 fuse_do_prepare_interrupt(req, d);
1584}
1585
1586static const char* file_info_string(struct fuse_file_info *fi,
1587 char* buf, size_t len)
1588{
1589 if(fi == NULL)
1590 return "NULL";
1591 snprintf(buf, len, "%llu", (unsigned long long) fi->fh);
1592 return buf;
1593}
1594
1595int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
1596 struct fuse_file_info *fi)
1597{
1598 fuse_get_context()->private_data = fs->user_data;
1599 if (fs->op.getattr) {
1600 if (fs->debug) {
1601 char buf[10];
1602 fuse_log(FUSE_LOG_DEBUG, "getattr[%s] %s\n",
1603 file_info_string(fi, buf, sizeof(buf)),
1604 path);
1605 }
1606 return fs->op.getattr(path, buf, fi);
1607 } else {
1608 return -ENOSYS;
1609 }
1610}
1611
1612int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
1613 const char *newpath, unsigned int flags)
1614{
1615 fuse_get_context()->private_data = fs->user_data;
1616 if (fs->op.rename) {
1617 if (fs->debug)
1618 fuse_log(FUSE_LOG_DEBUG, "rename %s %s 0x%x\n", oldpath, newpath,
1619 flags);
1620
1621 return fs->op.rename(oldpath, newpath, flags);
1622 } else {
1623 return -ENOSYS;
1624 }
1625}
1626
1627int fuse_fs_unlink(struct fuse_fs *fs, const char *path)
1628{
1629 fuse_get_context()->private_data = fs->user_data;
1630 if (fs->op.unlink) {
1631 if (fs->debug)
1632 fuse_log(FUSE_LOG_DEBUG, "unlink %s\n", path);
1633
1634 return fs->op.unlink(path);
1635 } else {
1636 return -ENOSYS;
1637 }
1638}
1639
1640int fuse_fs_rmdir(struct fuse_fs *fs, const char *path)
1641{
1642 fuse_get_context()->private_data = fs->user_data;
1643 if (fs->op.rmdir) {
1644 if (fs->debug)
1645 fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path);
1646
1647 return fs->op.rmdir(path);
1648 } else {
1649 return -ENOSYS;
1650 }
1651}
1652
1653int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path)
1654{
1655 fuse_get_context()->private_data = fs->user_data;
1656 if (fs->op.symlink) {
1657 if (fs->debug)
1658 fuse_log(FUSE_LOG_DEBUG, "symlink %s %s\n", linkname, path);
1659
1660 return fs->op.symlink(linkname, path);
1661 } else {
1662 return -ENOSYS;
1663 }
1664}
1665
1666int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath)
1667{
1668 fuse_get_context()->private_data = fs->user_data;
1669 if (fs->op.link) {
1670 if (fs->debug)
1671 fuse_log(FUSE_LOG_DEBUG, "link %s %s\n", oldpath, newpath);
1672
1673 return fs->op.link(oldpath, newpath);
1674 } else {
1675 return -ENOSYS;
1676 }
1677}
1678
1679int fuse_fs_release(struct fuse_fs *fs, const char *path,
1680 struct fuse_file_info *fi)
1681{
1682 fuse_get_context()->private_data = fs->user_data;
1683 if (fs->op.release) {
1684 if (fs->debug)
1685 fuse_log(FUSE_LOG_DEBUG, "release%s[%llu] flags: 0x%x\n",
1686 fi->flush ? "+flush" : "",
1687 (unsigned long long) fi->fh, fi->flags);
1688
1689 return fs->op.release(path, fi);
1690 } else {
1691 return 0;
1692 }
1693}
1694
1695int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
1696 struct fuse_file_info *fi)
1697{
1698 fuse_get_context()->private_data = fs->user_data;
1699 if (fs->op.opendir) {
1700 int err;
1701
1702 if (fs->debug)
1703 fuse_log(FUSE_LOG_DEBUG, "opendir flags: 0x%x %s\n", fi->flags,
1704 path);
1705
1706 err = fs->op.opendir(path, fi);
1707
1708 if (fs->debug && !err)
1709 fuse_log(FUSE_LOG_DEBUG, " opendir[%llu] flags: 0x%x %s\n",
1710 (unsigned long long) fi->fh, fi->flags, path);
1711
1712 return err;
1713 } else {
1714 return 0;
1715 }
1716}
1717
1718int fuse_fs_open(struct fuse_fs *fs, const char *path,
1719 struct fuse_file_info *fi)
1720{
1721 fuse_get_context()->private_data = fs->user_data;
1722 if (fs->op.open) {
1723 int err;
1724
1725 if (fs->debug)
1726 fuse_log(FUSE_LOG_DEBUG, "open flags: 0x%x %s\n", fi->flags,
1727 path);
1728
1729 err = fs->op.open(path, fi);
1730
1731 if (fs->debug && !err)
1732 fuse_log(FUSE_LOG_DEBUG, " open[%llu] flags: 0x%x %s\n",
1733 (unsigned long long) fi->fh, fi->flags, path);
1734
1735 return err;
1736 } else {
1737 return 0;
1738 }
1739}
1740
1741static void fuse_free_buf(struct fuse_bufvec *buf)
1742{
1743 if (buf != NULL) {
1744 size_t i;
1745
1746 for (i = 0; i < buf->count; i++)
1747 if (!(buf->buf[i].flags & FUSE_BUF_IS_FD))
1748 free(buf->buf[i].mem);
1749 free(buf);
1750 }
1751}
1752
1753int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
1754 struct fuse_bufvec **bufp, size_t size, off_t off,
1755 struct fuse_file_info *fi)
1756{
1757 fuse_get_context()->private_data = fs->user_data;
1758 if (fs->op.read || fs->op.read_buf) {
1759 int res;
1760
1761 if (fs->debug)
1762 fuse_log(FUSE_LOG_DEBUG,
1763 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
1764 (unsigned long long) fi->fh,
1765 size, (unsigned long long) off, fi->flags);
1766
1767 if (fs->op.read_buf) {
1768 res = fs->op.read_buf(path, bufp, size, off, fi);
1769 } else {
1770 struct fuse_bufvec *buf;
1771 void *mem;
1772
1773 buf = malloc(sizeof(struct fuse_bufvec));
1774 if (buf == NULL)
1775 return -ENOMEM;
1776
1777 mem = malloc(size);
1778 if (mem == NULL) {
1779 free(buf);
1780 return -ENOMEM;
1781 }
1782 *buf = FUSE_BUFVEC_INIT(size);
1783 buf->buf[0].mem = mem;
1784 *bufp = buf;
1785
1786 res = fs->op.read(path, mem, size, off, fi);
1787 if (res >= 0)
1788 buf->buf[0].size = res;
1789 }
1790
1791 if (fs->debug && res >= 0)
1792 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %zu bytes from %llu\n",
1793 (unsigned long long) fi->fh,
1794 fuse_buf_size(*bufp),
1795 (unsigned long long) off);
1796 if (res >= 0 && fuse_buf_size(*bufp) > size)
1797 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
1798
1799 if (res < 0)
1800 return res;
1801
1802 return 0;
1803 } else {
1804 return -ENOSYS;
1805 }
1806}
1807
1808int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size,
1809 off_t off, struct fuse_file_info *fi)
1810{
1811 fuse_get_context()->private_data = fs->user_data;
1812 if (fs->op.read || fs->op.read_buf) {
1813 int res;
1814
1815 if (fs->debug)
1816 fuse_log(FUSE_LOG_DEBUG,
1817 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
1818 (unsigned long long) fi->fh,
1819 size, (unsigned long long) off, fi->flags);
1820
1821 if (fs->op.read_buf) {
1822 struct fuse_bufvec *buf = NULL;
1823
1824 res = fs->op.read_buf(path, &buf, size, off, fi);
1825 if (res == 0) {
1826 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size);
1827
1828 dst.buf[0].mem = mem;
1829 res = fuse_buf_copy(&dst, buf, 0);
1830 }
1831 fuse_free_buf(buf);
1832 } else {
1833 res = fs->op.read(path, mem, size, off, fi);
1834 }
1835
1836 if (fs->debug && res >= 0)
1837 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %u bytes from %llu\n",
1838 (unsigned long long) fi->fh,
1839 res,
1840 (unsigned long long) off);
1841 if (res >= 0 && res > (int) size)
1842 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
1843
1844 return res;
1845 } else {
1846 return -ENOSYS;
1847 }
1848}
1849
1850int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
1851 struct fuse_bufvec *buf, off_t off,
1852 struct fuse_file_info *fi)
1853{
1854 fuse_get_context()->private_data = fs->user_data;
1855 if (fs->op.write_buf || fs->op.write) {
1856 int res;
1857 size_t size = fuse_buf_size(buf);
1858
1859 assert(buf->idx == 0 && buf->off == 0);
1860 if (fs->debug)
1861 fuse_log(FUSE_LOG_DEBUG,
1862 "write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
1863 fi->writepage ? "page" : "",
1864 (unsigned long long) fi->fh,
1865 size,
1866 (unsigned long long) off,
1867 fi->flags);
1868
1869 if (fs->op.write_buf) {
1870 res = fs->op.write_buf(path, buf, off, fi);
1871 } else {
1872 void *mem = NULL;
1873 struct fuse_buf *flatbuf;
1874 struct fuse_bufvec tmp = FUSE_BUFVEC_INIT(size);
1875
1876 if (buf->count == 1 &&
1877 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
1878 flatbuf = &buf->buf[0];
1879 } else {
1880 res = -ENOMEM;
1881 mem = malloc(size);
1882 if (mem == NULL)
1883 goto out;
1884
1885 tmp.buf[0].mem = mem;
1886 res = fuse_buf_copy(&tmp, buf, 0);
1887 if (res <= 0)
1888 goto out_free;
1889
1890 tmp.buf[0].size = res;
1891 flatbuf = &tmp.buf[0];
1892 }
1893
1894 res = fs->op.write(path, flatbuf->mem, flatbuf->size,
1895 off, fi);
1896out_free:
1897 free(mem);
1898 }
1899out:
1900 if (fs->debug && res >= 0)
1901 fuse_log(FUSE_LOG_DEBUG, " write%s[%llu] %u bytes to %llu\n",
1902 fi->writepage ? "page" : "",
1903 (unsigned long long) fi->fh, res,
1904 (unsigned long long) off);
1905 if (res > (int) size)
1906 fuse_log(FUSE_LOG_ERR, "fuse: wrote too many bytes\n");
1907
1908 return res;
1909 } else {
1910 return -ENOSYS;
1911 }
1912}
1913
1914int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *mem,
1915 size_t size, off_t off, struct fuse_file_info *fi)
1916{
1917 struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(size);
1918
1919 bufv.buf[0].mem = (void *) mem;
1920
1921 return fuse_fs_write_buf(fs, path, &bufv, off, fi);
1922}
1923
1924int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
1925 struct fuse_file_info *fi)
1926{
1927 fuse_get_context()->private_data = fs->user_data;
1928 if (fs->op.fsync) {
1929 if (fs->debug)
1930 fuse_log(FUSE_LOG_DEBUG, "fsync[%llu] datasync: %i\n",
1931 (unsigned long long) fi->fh, datasync);
1932
1933 return fs->op.fsync(path, datasync, fi);
1934 } else {
1935 return -ENOSYS;
1936 }
1937}
1938
1939int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
1940 struct fuse_file_info *fi)
1941{
1942 fuse_get_context()->private_data = fs->user_data;
1943 if (fs->op.fsyncdir) {
1944 if (fs->debug)
1945 fuse_log(FUSE_LOG_DEBUG, "fsyncdir[%llu] datasync: %i\n",
1946 (unsigned long long) fi->fh, datasync);
1947
1948 return fs->op.fsyncdir(path, datasync, fi);
1949 } else {
1950 return -ENOSYS;
1951 }
1952}
1953
1954int fuse_fs_flush(struct fuse_fs *fs, const char *path,
1955 struct fuse_file_info *fi)
1956{
1957 fuse_get_context()->private_data = fs->user_data;
1958 if (fs->op.flush) {
1959 if (fs->debug)
1960 fuse_log(FUSE_LOG_DEBUG, "flush[%llu]\n",
1961 (unsigned long long) fi->fh);
1962
1963 return fs->op.flush(path, fi);
1964 } else {
1965 return -ENOSYS;
1966 }
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 if (fs->debug)
1990 fuse_log(FUSE_LOG_DEBUG, "releasedir[%llu] flags: 0x%x\n",
1991 (unsigned long long) fi->fh, fi->flags);
1992
1993 return fs->op.releasedir(path, fi);
1994 } else {
1995 return 0;
1996 }
1997}
1998
1999int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
2000 fuse_fill_dir_t filler, off_t off,
2001 struct fuse_file_info *fi,
2002 enum fuse_readdir_flags flags)
2003{
2004 fuse_get_context()->private_data = fs->user_data;
2005 if (fs->op.readdir) {
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 } else {
2015 return -ENOSYS;
2016 }
2017}
2018
2019int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
2020 struct fuse_file_info *fi)
2021{
2022 fuse_get_context()->private_data = fs->user_data;
2023 if (fs->op.create) {
2024 int err;
2025
2026 if (fs->debug)
2027 fuse_log(FUSE_LOG_DEBUG,
2028 "create flags: 0x%x %s 0%o umask=0%03o\n",
2029 fi->flags, path, mode,
2030 fuse_get_context()->umask);
2031
2032 err = fs->op.create(path, mode, fi);
2033
2034 if (fs->debug && !err)
2035 fuse_log(FUSE_LOG_DEBUG, " create[%llu] flags: 0x%x %s\n",
2036 (unsigned long long) fi->fh, fi->flags, path);
2037
2038 return err;
2039 } else {
2040 return -ENOSYS;
2041 }
2042}
2043
2044int fuse_fs_lock(struct fuse_fs *fs, const char *path,
2045 struct fuse_file_info *fi, int cmd, struct flock *lock)
2046{
2047 fuse_get_context()->private_data = fs->user_data;
2048 if (fs->op.lock) {
2049 if (fs->debug)
2050 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
2051 (unsigned long long) fi->fh,
2052 (cmd == F_GETLK ? "F_GETLK" :
2053 (cmd == F_SETLK ? "F_SETLK" :
2054 (cmd == F_SETLKW ? "F_SETLKW" : "???"))),
2055 (lock->l_type == F_RDLCK ? "F_RDLCK" :
2056 (lock->l_type == F_WRLCK ? "F_WRLCK" :
2057 (lock->l_type == F_UNLCK ? "F_UNLCK" :
2058 "???"))),
2059 (unsigned long long) lock->l_start,
2060 (unsigned long long) lock->l_len,
2061 (unsigned long long) lock->l_pid);
2062
2063 return fs->op.lock(path, fi, cmd, lock);
2064 } else {
2065 return -ENOSYS;
2066 }
2067}
2068
2069int fuse_fs_flock(struct fuse_fs *fs, const char *path,
2070 struct fuse_file_info *fi, int op)
2071{
2072 fuse_get_context()->private_data = fs->user_data;
2073 if (fs->op.flock) {
2074 if (fs->debug) {
2075 int xop = op & ~LOCK_NB;
2076
2077 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s%s\n",
2078 (unsigned long long) fi->fh,
2079 xop == LOCK_SH ? "LOCK_SH" :
2080 (xop == LOCK_EX ? "LOCK_EX" :
2081 (xop == LOCK_UN ? "LOCK_UN" : "???")),
2082 (op & LOCK_NB) ? "|LOCK_NB" : "");
2083 }
2084 return fs->op.flock(path, fi, op);
2085 } else {
2086 return -ENOSYS;
2087 }
2088}
2089
2090int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid,
2091 gid_t gid, struct fuse_file_info *fi)
2092{
2093 fuse_get_context()->private_data = fs->user_data;
2094 if (fs->op.chown) {
2095 if (fs->debug) {
2096 char buf[10];
2097 fuse_log(FUSE_LOG_DEBUG, "chown[%s] %s %lu %lu\n",
2098 file_info_string(fi, buf, sizeof(buf)),
2099 path, (unsigned long) uid, (unsigned long) gid);
2100 }
2101 return fs->op.chown(path, uid, gid, fi);
2102 } else {
2103 return -ENOSYS;
2104 }
2105}
2106
2107int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
2108 struct fuse_file_info *fi)
2109{
2110 fuse_get_context()->private_data = fs->user_data;
2111 if (fs->op.truncate) {
2112 if (fs->debug) {
2113 char buf[10];
2114 fuse_log(FUSE_LOG_DEBUG, "truncate[%s] %llu\n",
2115 file_info_string(fi, buf, sizeof(buf)),
2116 (unsigned long long) size);
2117 }
2118 return fs->op.truncate(path, size, fi);
2119 } else {
2120 return -ENOSYS;
2121 }
2122}
2123
2124int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
2125 const struct timespec tv[2], struct fuse_file_info *fi)
2126{
2127 fuse_get_context()->private_data = fs->user_data;
2128 if (fs->op.utimens) {
2129 if (fs->debug) {
2130 char buf[10];
2131 fuse_log(FUSE_LOG_DEBUG, "utimens[%s] %s %li.%09lu %li.%09lu\n",
2132 file_info_string(fi, buf, sizeof(buf)),
2133 path, tv[0].tv_sec, tv[0].tv_nsec,
2134 tv[1].tv_sec, tv[1].tv_nsec);
2135 }
2136 return fs->op.utimens(path, tv, fi);
2137 } else {
2138 return -ENOSYS;
2139 }
2140}
2141
2142int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask)
2143{
2144 fuse_get_context()->private_data = fs->user_data;
2145 if (fs->op.access) {
2146 if (fs->debug)
2147 fuse_log(FUSE_LOG_DEBUG, "access %s 0%o\n", path, mask);
2148
2149 return fs->op.access(path, mask);
2150 } else {
2151 return -ENOSYS;
2152 }
2153}
2154
2155int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
2156 size_t len)
2157{
2158 fuse_get_context()->private_data = fs->user_data;
2159 if (fs->op.readlink) {
2160 if (fs->debug)
2161 fuse_log(FUSE_LOG_DEBUG, "readlink %s %lu\n", path,
2162 (unsigned long) len);
2163
2164 return fs->op.readlink(path, buf, len);
2165 } else {
2166 return -ENOSYS;
2167 }
2168}
2169
2170int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
2171 dev_t rdev)
2172{
2173 fuse_get_context()->private_data = fs->user_data;
2174 if (fs->op.mknod) {
2175 if (fs->debug)
2176 fuse_log(FUSE_LOG_DEBUG, "mknod %s 0%o 0x%llx umask=0%03o\n",
2177 path, mode, (unsigned long long) rdev,
2178 fuse_get_context()->umask);
2179
2180 return fs->op.mknod(path, mode, rdev);
2181 } else {
2182 return -ENOSYS;
2183 }
2184}
2185
2186int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode)
2187{
2188 fuse_get_context()->private_data = fs->user_data;
2189 if (fs->op.mkdir) {
2190 if (fs->debug)
2191 fuse_log(FUSE_LOG_DEBUG, "mkdir %s 0%o umask=0%03o\n",
2192 path, mode, fuse_get_context()->umask);
2193
2194 return fs->op.mkdir(path, mode);
2195 } else {
2196 return -ENOSYS;
2197 }
2198}
2199
2200int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
2201 const char *value, size_t size, int flags)
2202{
2203 fuse_get_context()->private_data = fs->user_data;
2204 if (fs->op.setxattr) {
2205 if (fs->debug)
2206 fuse_log(FUSE_LOG_DEBUG, "setxattr %s %s %lu 0x%x\n",
2207 path, name, (unsigned long) size, flags);
2208
2209 return fs->op.setxattr(path, name, value, size, flags);
2210 } else {
2211 return -ENOSYS;
2212 }
2213}
2214
2215int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
2216 char *value, size_t size)
2217{
2218 fuse_get_context()->private_data = fs->user_data;
2219 if (fs->op.getxattr) {
2220 if (fs->debug)
2221 fuse_log(FUSE_LOG_DEBUG, "getxattr %s %s %lu\n",
2222 path, name, (unsigned long) size);
2223
2224 return fs->op.getxattr(path, name, value, size);
2225 } else {
2226 return -ENOSYS;
2227 }
2228}
2229
2230int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
2231 size_t size)
2232{
2233 fuse_get_context()->private_data = fs->user_data;
2234 if (fs->op.listxattr) {
2235 if (fs->debug)
2236 fuse_log(FUSE_LOG_DEBUG, "listxattr %s %lu\n",
2237 path, (unsigned long) size);
2238
2239 return fs->op.listxattr(path, list, size);
2240 } else {
2241 return -ENOSYS;
2242 }
2243}
2244
2245int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
2246 uint64_t *idx)
2247{
2248 fuse_get_context()->private_data = fs->user_data;
2249 if (fs->op.bmap) {
2250 if (fs->debug)
2251 fuse_log(FUSE_LOG_DEBUG, "bmap %s blocksize: %lu index: %llu\n",
2252 path, (unsigned long) blocksize,
2253 (unsigned long long) *idx);
2254
2255 return fs->op.bmap(path, blocksize, idx);
2256 } else {
2257 return -ENOSYS;
2258 }
2259}
2260
2261int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
2262{
2263 fuse_get_context()->private_data = fs->user_data;
2264 if (fs->op.removexattr) {
2265 if (fs->debug)
2266 fuse_log(FUSE_LOG_DEBUG, "removexattr %s %s\n", path, name);
2267
2268 return fs->op.removexattr(path, name);
2269 } else {
2270 return -ENOSYS;
2271 }
2272}
2273
2274int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
2275 void *arg, struct fuse_file_info *fi, unsigned int flags,
2276 void *data)
2277{
2278 fuse_get_context()->private_data = fs->user_data;
2279 if (fs->op.ioctl) {
2280 if (fs->debug)
2281 fuse_log(FUSE_LOG_DEBUG, "ioctl[%llu] 0x%x flags: 0x%x\n",
2282 (unsigned long long) fi->fh, cmd, flags);
2283
2284 return fs->op.ioctl(path, cmd, arg, fi, flags, data);
2285 } else
2286 return -ENOSYS;
2287}
2288
2289int fuse_fs_poll(struct fuse_fs *fs, const char *path,
2290 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
2291 unsigned *reventsp)
2292{
2293 fuse_get_context()->private_data = fs->user_data;
2294 if (fs->op.poll) {
2295 int res;
2296
2297 if (fs->debug)
2298 fuse_log(FUSE_LOG_DEBUG, "poll[%llu] ph: %p, events 0x%x\n",
2299 (unsigned long long) fi->fh, ph,
2300 fi->poll_events);
2301
2302 res = fs->op.poll(path, fi, ph, reventsp);
2303
2304 if (fs->debug && !res)
2305 fuse_log(FUSE_LOG_DEBUG, " poll[%llu] revents: 0x%x\n",
2306 (unsigned long long) fi->fh, *reventsp);
2307
2308 return res;
2309 } else
2310 return -ENOSYS;
2311}
2312
2313int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
2314 off_t offset, off_t length, struct fuse_file_info *fi)
2315{
2316 fuse_get_context()->private_data = fs->user_data;
2317 if (fs->op.fallocate) {
2318 if (fs->debug)
2319 fuse_log(FUSE_LOG_DEBUG, "fallocate %s mode %x, offset: %llu, length: %llu\n",
2320 path,
2321 mode,
2322 (unsigned long long) offset,
2323 (unsigned long long) length);
2324
2325 return fs->op.fallocate(path, mode, offset, length, fi);
2326 } else
2327 return -ENOSYS;
2328}
2329
2330ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
2331 struct fuse_file_info *fi_in, off_t off_in,
2332 const char *path_out,
2333 struct fuse_file_info *fi_out, off_t off_out,
2334 size_t len, int flags)
2335{
2336 fuse_get_context()->private_data = fs->user_data;
2337 if (fs->op.copy_file_range) {
2338 if (fs->debug)
2339 fuse_log(FUSE_LOG_DEBUG, "copy_file_range from %s:%llu to "
2340 "%s:%llu, length: %llu\n",
2341 path_in,
2342 (unsigned long long) off_in,
2343 path_out,
2344 (unsigned long long) off_out,
2345 (unsigned long long) len);
2346
2347 return fs->op.copy_file_range(path_in, fi_in, off_in, path_out,
2348 fi_out, off_out, len, flags);
2349 } else
2350 return -ENOSYS;
2351}
2352
2353off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
2354 struct fuse_file_info *fi)
2355{
2356 fuse_get_context()->private_data = fs->user_data;
2357 if (fs->op.lseek) {
2358 if (fs->debug) {
2359 char buf[10];
2360 fuse_log(FUSE_LOG_DEBUG, "lseek[%s] %llu %d\n",
2361 file_info_string(fi, buf, sizeof(buf)),
2362 (unsigned long long) off, whence);
2363 }
2364 return fs->op.lseek(path, off, whence, fi);
2365 } else {
2366 return -ENOSYS;
2367 }
2368}
2369
2370static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
2371{
2372 struct node *node;
2373 int isopen = 0;
2374 pthread_mutex_lock(&f->lock);
2375 node = lookup_node(f, dir, name);
2376 if (node && node->open_count > 0)
2377 isopen = 1;
2378 pthread_mutex_unlock(&f->lock);
2379 return isopen;
2380}
2381
2382static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname,
2383 char *newname, size_t bufsize)
2384{
2385 struct stat buf;
2386 struct node *node;
2387 struct node *newnode;
2388 char *newpath;
2389 int res;
2390 int failctr = 10;
2391
2392 do {
2393 pthread_mutex_lock(&f->lock);
2394 node = lookup_node(f, dir, oldname);
2395 if (node == NULL) {
2396 pthread_mutex_unlock(&f->lock);
2397 return NULL;
2398 }
2399 do {
2400 f->hidectr ++;
2401 snprintf(newname, bufsize, ".fuse_hidden%08x%08x",
2402 (unsigned int) node->nodeid, f->hidectr);
2403 newnode = lookup_node(f, dir, newname);
2404 } while(newnode);
2405
2406 res = try_get_path(f, dir, newname, &newpath, NULL, false);
2407 pthread_mutex_unlock(&f->lock);
2408 if (res)
2409 break;
2410
2411 memset(&buf, 0, sizeof(buf));
2412 res = fuse_fs_getattr(f->fs, newpath, &buf, NULL);
2413 if (res == -ENOENT)
2414 break;
2415 free(newpath);
2416 newpath = NULL;
2417 } while(res == 0 && --failctr);
2418
2419 return newpath;
2420}
2421
2422static int hide_node(struct fuse *f, const char *oldpath,
2423 fuse_ino_t dir, const char *oldname)
2424{
2425 char newname[64];
2426 char *newpath;
2427 int err = -EBUSY;
2428
2429 newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
2430 if (newpath) {
2431 err = fuse_fs_rename(f->fs, oldpath, newpath, 0);
2432 if (!err)
2433 err = rename_node(f, dir, oldname, dir, newname, 1);
2434 free(newpath);
2435 }
2436 return err;
2437}
2438
2439static int mtime_eq(const struct stat *stbuf, const struct timespec *ts)
2440{
2441 return stbuf->st_mtime == ts->tv_sec &&
2442 ST_MTIM_NSEC(stbuf) == ts->tv_nsec;
2443}
2444
2445#ifndef CLOCK_MONOTONIC
2446#define CLOCK_MONOTONIC CLOCK_REALTIME
2447#endif
2448
2449static void curr_time(struct timespec *now)
2450{
2451 static clockid_t clockid = CLOCK_MONOTONIC;
2452 int res = clock_gettime(clockid, now);
2453 if (res == -1 && errno == EINVAL) {
2454 clockid = CLOCK_REALTIME;
2455 res = clock_gettime(clockid, now);
2456 }
2457 if (res == -1) {
2458 perror("fuse: clock_gettime");
2459 abort();
2460 }
2461}
2462
2463static void update_stat(struct node *node, const struct stat *stbuf)
2464{
2465 if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) ||
2466 stbuf->st_size != node->size))
2467 node->cache_valid = 0;
2468 node->mtime.tv_sec = stbuf->st_mtime;
2469 node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf);
2470 node->size = stbuf->st_size;
2471 curr_time(&node->stat_updated);
2472}
2473
2474static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name,
2475 struct fuse_entry_param *e)
2476{
2477 struct node *node;
2478
2479 node = find_node(f, nodeid, name);
2480 if (node == NULL)
2481 return -ENOMEM;
2482
2483 e->ino = node->nodeid;
2484 e->generation = node->generation;
2485 e->entry_timeout = f->conf.entry_timeout;
2486 e->attr_timeout = f->conf.attr_timeout;
2487 if (f->conf.auto_cache) {
2488 pthread_mutex_lock(&f->lock);
2489 update_stat(node, &e->attr);
2490 pthread_mutex_unlock(&f->lock);
2491 }
2492 set_stat(f, e->ino, &e->attr);
2493 return 0;
2494}
2495
2496static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
2497 const char *name, const char *path,
2498 struct fuse_entry_param *e, struct fuse_file_info *fi)
2499{
2500 int res;
2501
2502 memset(e, 0, sizeof(struct fuse_entry_param));
2503 res = fuse_fs_getattr(f->fs, path, &e->attr, fi);
2504 if (res == 0) {
2505 res = do_lookup(f, nodeid, name, e);
2506 if (res == 0 && f->conf.debug) {
2507 fuse_log(FUSE_LOG_DEBUG, " NODEID: %llu\n",
2508 (unsigned long long) e->ino);
2509 }
2510 }
2511 return res;
2512}
2513
2514static struct fuse_context_i *fuse_get_context_internal(void)
2515{
2516 return (struct fuse_context_i *) pthread_getspecific(fuse_context_key);
2517}
2518
2519static struct fuse_context_i *fuse_create_context(struct fuse *f)
2520{
2521 struct fuse_context_i *c = fuse_get_context_internal();
2522 if (c == NULL) {
2523 c = (struct fuse_context_i *)
2524 calloc(1, sizeof(struct fuse_context_i));
2525 if (c == NULL) {
2526 /* This is hard to deal with properly, so just
2527 abort. If memory is so low that the
2528 context cannot be allocated, there's not
2529 much hope for the filesystem anyway */
2530 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate thread specific data\n");
2531 abort();
2532 }
2533 pthread_setspecific(fuse_context_key, c);
2534 } else {
2535 memset(c, 0, sizeof(*c));
2536 }
2537 c->ctx.fuse = f;
2538
2539 return c;
2540}
2541
2542static void fuse_freecontext(void *data)
2543{
2544 free(data);
2545}
2546
2547static int fuse_create_context_key(void)
2548{
2549 int err = 0;
2550 pthread_mutex_lock(&fuse_context_lock);
2551 if (!fuse_context_ref) {
2552 err = pthread_key_create(&fuse_context_key, fuse_freecontext);
2553 if (err) {
2554 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
2555 strerror(err));
2556 pthread_mutex_unlock(&fuse_context_lock);
2557 return -1;
2558 }
2559 }
2560 fuse_context_ref++;
2561 pthread_mutex_unlock(&fuse_context_lock);
2562 return 0;
2563}
2564
2565static void fuse_delete_context_key(void)
2566{
2567 pthread_mutex_lock(&fuse_context_lock);
2568 fuse_context_ref--;
2569 if (!fuse_context_ref) {
2570 free(pthread_getspecific(fuse_context_key));
2571 pthread_key_delete(fuse_context_key);
2572 }
2573 pthread_mutex_unlock(&fuse_context_lock);
2574}
2575
2576static struct fuse *req_fuse_prepare(fuse_req_t req)
2577{
2578 struct fuse_context_i *c = fuse_create_context(req_fuse(req));
2579 const struct fuse_ctx *ctx = fuse_req_ctx(req);
2580 c->req = req;
2581 c->ctx.uid = ctx->uid;
2582 c->ctx.gid = ctx->gid;
2583 c->ctx.pid = ctx->pid;
2584 c->ctx.umask = ctx->umask;
2585 return c->ctx.fuse;
2586}
2587
2588static inline void reply_err(fuse_req_t req, int err)
2589{
2590 /* fuse_reply_err() uses non-negated errno values */
2591 fuse_reply_err(req, -err);
2592}
2593
2594static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
2595 int err)
2596{
2597 if (!err) {
2598 struct fuse *f = req_fuse(req);
2599 if (fuse_reply_entry(req, e) == -ENOENT) {
2600 /* Skip forget for negative result */
2601 if (e->ino != 0)
2602 forget_node(f, e->ino, 1);
2603 }
2604 } else
2605 reply_err(req, err);
2606}
2607
2608void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
2609 struct fuse_config *cfg)
2610{
2611 fuse_get_context()->private_data = fs->user_data;
2612 if (!fs->op.write_buf)
2613 fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ);
2614 if (!fs->op.lock)
2615 fuse_unset_feature_flag(conn, FUSE_CAP_POSIX_LOCKS);
2616 if (!fs->op.flock)
2617 fuse_unset_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS);
2618 if (fs->op.init) {
2619 uint64_t want_ext_default = conn->want_ext;
2620 uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
2621 int rc;
2622
2623 conn->want = want_default;
2624 fs->user_data = fs->op.init(conn, cfg);
2625
2626 rc = convert_to_conn_want_ext(conn, want_ext_default,
2627 want_default);
2628
2629 if (rc != 0) {
2630 /*
2631 * This is a grave developer error, but
2632 * we cannot return an error here, as the function
2633 * signature does not allow it.
2634 */
2635 fuse_log(
2636 FUSE_LOG_ERR,
2637 "fuse: Aborting due to invalid conn want flags.\n");
2638 _exit(EXIT_FAILURE);
2639 }
2640 }
2641}
2642
2643static int fuse_init_intr_signal(int signum, int *installed);
2644
2645static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
2646{
2647 struct fuse *f = (struct fuse *) data;
2648
2649 fuse_create_context(f);
2650 fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
2651 fuse_fs_init(f->fs, conn, &f->conf);
2652
2653 if (f->conf.intr) {
2654 if (fuse_init_intr_signal(f->conf.intr_signal,
2655 &f->intr_installed) == -1)
2656 fuse_log(FUSE_LOG_ERR, "fuse: failed to init interrupt signal\n");
2657 } else {
2658 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
2659 conn->no_interrupt = 1;
2660 }
2661}
2662
2663void fuse_fs_destroy(struct fuse_fs *fs)
2664{
2665 fuse_get_context()->private_data = fs->user_data;
2666 if (fs->op.destroy)
2667 fs->op.destroy(fs->user_data);
2668}
2669
2670static void fuse_lib_destroy(void *data)
2671{
2672 struct fuse *f = (struct fuse *) data;
2673
2674 fuse_create_context(f);
2675 fuse_fs_destroy(f->fs);
2676}
2677
2678static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
2679 const char *name)
2680{
2681 struct fuse *f = req_fuse_prepare(req);
2682 struct fuse_entry_param e;
2683 char *path;
2684 int err;
2685 struct node *dot = NULL;
2686
2687 if (name[0] == '.') {
2688 int len = strlen(name);
2689
2690 if (len == 1 || (name[1] == '.' && len == 2)) {
2691 pthread_mutex_lock(&f->lock);
2692 if (len == 1) {
2693 if (f->conf.debug)
2694 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOT\n");
2695 dot = get_node_nocheck(f, parent);
2696 if (dot == NULL) {
2697 pthread_mutex_unlock(&f->lock);
2698 reply_entry(req, &e, -ESTALE);
2699 return;
2700 }
2701 dot->refctr++;
2702 } else {
2703 if (f->conf.debug)
2704 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOTDOT\n");
2705 parent = get_node(f, parent)->parent->nodeid;
2706 }
2707 pthread_mutex_unlock(&f->lock);
2708 name = NULL;
2709 }
2710 }
2711
2712 err = get_path_name(f, parent, name, &path);
2713 if (!err) {
2714 struct fuse_intr_data d;
2715 if (f->conf.debug)
2716 fuse_log(FUSE_LOG_DEBUG, "LOOKUP %s\n", path);
2717 fuse_prepare_interrupt(f, req, &d);
2718 err = lookup_path(f, parent, name, path, &e, NULL);
2719 if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
2720 e.ino = 0;
2721 e.entry_timeout = f->conf.negative_timeout;
2722 err = 0;
2723 }
2724 fuse_finish_interrupt(f, req, &d);
2725 free_path(f, parent, path);
2726 }
2727 if (dot) {
2728 pthread_mutex_lock(&f->lock);
2729 unref_node(f, dot);
2730 pthread_mutex_unlock(&f->lock);
2731 }
2732 reply_entry(req, &e, err);
2733}
2734
2735static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup)
2736{
2737 if (f->conf.debug)
2738 fuse_log(FUSE_LOG_DEBUG, "FORGET %llu/%llu\n", (unsigned long long)ino,
2739 (unsigned long long) nlookup);
2740 forget_node(f, ino, nlookup);
2741}
2742
2743static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
2744{
2745 do_forget(req_fuse(req), ino, nlookup);
2746 fuse_reply_none(req);
2747}
2748
2749static void fuse_lib_forget_multi(fuse_req_t req, size_t count,
2750 struct fuse_forget_data *forgets)
2751{
2752 struct fuse *f = req_fuse(req);
2753 size_t i;
2754
2755 for (i = 0; i < count; i++)
2756 do_forget(f, forgets[i].ino, forgets[i].nlookup);
2757
2758 fuse_reply_none(req);
2759}
2760
2761
2762static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino,
2763 struct fuse_file_info *fi)
2764{
2765 struct fuse *f = req_fuse_prepare(req);
2766 struct stat buf;
2767 char *path;
2768 int err;
2769
2770 memset(&buf, 0, sizeof(buf));
2771
2772 if (fi != NULL)
2773 err = get_path_nullok(f, ino, &path);
2774 else
2775 err = get_path(f, ino, &path);
2776 if (!err) {
2777 struct fuse_intr_data d;
2778 fuse_prepare_interrupt(f, req, &d);
2779 err = fuse_fs_getattr(f->fs, path, &buf, fi);
2780 fuse_finish_interrupt(f, req, &d);
2781 free_path(f, ino, path);
2782 }
2783 if (!err) {
2784 struct node *node;
2785
2786 pthread_mutex_lock(&f->lock);
2787 node = get_node(f, ino);
2788 if (node->is_hidden && buf.st_nlink > 0)
2789 buf.st_nlink--;
2790 if (f->conf.auto_cache)
2791 update_stat(node, &buf);
2792 pthread_mutex_unlock(&f->lock);
2793 set_stat(f, ino, &buf);
2794 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
2795 } else
2796 reply_err(req, err);
2797}
2798
2799int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
2800 struct fuse_file_info *fi)
2801{
2802 fuse_get_context()->private_data = fs->user_data;
2803 if (fs->op.chmod) {
2804 if (fs->debug) {
2805 char buf[10];
2806 fuse_log(FUSE_LOG_DEBUG, "chmod[%s] %s %llo\n",
2807 file_info_string(fi, buf, sizeof(buf)),
2808 path, (unsigned long long) mode);
2809 }
2810 return fs->op.chmod(path, mode, fi);
2811 }
2812 else
2813 return -ENOSYS;
2814}
2815
2816static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
2817 int valid, struct fuse_file_info *fi)
2818{
2819 struct fuse *f = req_fuse_prepare(req);
2820 struct stat buf;
2821 char *path;
2822 int err;
2823
2824 memset(&buf, 0, sizeof(buf));
2825 if (fi != NULL)
2826 err = get_path_nullok(f, ino, &path);
2827 else
2828 err = get_path(f, ino, &path);
2829 if (!err) {
2830 struct fuse_intr_data d;
2831 fuse_prepare_interrupt(f, req, &d);
2832 err = 0;
2833 if (!err && (valid & FUSE_SET_ATTR_MODE))
2834 err = fuse_fs_chmod(f->fs, path, attr->st_mode, fi);
2835 if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) {
2836 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
2837 attr->st_uid : (uid_t) -1;
2838 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
2839 attr->st_gid : (gid_t) -1;
2840 err = fuse_fs_chown(f->fs, path, uid, gid, fi);
2841 }
2842 if (!err && (valid & FUSE_SET_ATTR_SIZE)) {
2843 err = fuse_fs_truncate(f->fs, path,
2844 attr->st_size, fi);
2845 }
2846#ifdef HAVE_UTIMENSAT
2847 if (!err &&
2848 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
2849 struct timespec tv[2];
2850
2851 tv[0].tv_sec = 0;
2852 tv[1].tv_sec = 0;
2853 tv[0].tv_nsec = UTIME_OMIT;
2854 tv[1].tv_nsec = UTIME_OMIT;
2855
2856 if (valid & FUSE_SET_ATTR_ATIME_NOW)
2857 tv[0].tv_nsec = UTIME_NOW;
2858 else if (valid & FUSE_SET_ATTR_ATIME)
2859 tv[0] = attr->st_atim;
2860
2861 if (valid & FUSE_SET_ATTR_MTIME_NOW)
2862 tv[1].tv_nsec = UTIME_NOW;
2863 else if (valid & FUSE_SET_ATTR_MTIME)
2864 tv[1] = attr->st_mtim;
2865
2866 err = fuse_fs_utimens(f->fs, path, tv, fi);
2867 } else
2868#endif
2869 if (!err &&
2870 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ==
2871 (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
2872 struct timespec tv[2];
2873 tv[0].tv_sec = attr->st_atime;
2874 tv[0].tv_nsec = ST_ATIM_NSEC(attr);
2875 tv[1].tv_sec = attr->st_mtime;
2876 tv[1].tv_nsec = ST_MTIM_NSEC(attr);
2877 err = fuse_fs_utimens(f->fs, path, tv, fi);
2878 }
2879 if (!err) {
2880 err = fuse_fs_getattr(f->fs, path, &buf, fi);
2881 }
2882 fuse_finish_interrupt(f, req, &d);
2883 free_path(f, ino, path);
2884 }
2885 if (!err) {
2886 if (f->conf.auto_cache) {
2887 pthread_mutex_lock(&f->lock);
2888 update_stat(get_node(f, ino), &buf);
2889 pthread_mutex_unlock(&f->lock);
2890 }
2891 set_stat(f, ino, &buf);
2892 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
2893 } else
2894 reply_err(req, err);
2895}
2896
2897static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask)
2898{
2899 struct fuse *f = req_fuse_prepare(req);
2900 char *path;
2901 int err;
2902
2903 err = get_path(f, ino, &path);
2904 if (!err) {
2905 struct fuse_intr_data d;
2906
2907 fuse_prepare_interrupt(f, req, &d);
2908 err = fuse_fs_access(f->fs, path, mask);
2909 fuse_finish_interrupt(f, req, &d);
2910 free_path(f, ino, path);
2911 }
2912 reply_err(req, err);
2913}
2914
2915static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino)
2916{
2917 struct fuse *f = req_fuse_prepare(req);
2918 char linkname[PATH_MAX + 1];
2919 char *path;
2920 int err;
2921
2922 err = get_path(f, ino, &path);
2923 if (!err) {
2924 struct fuse_intr_data d;
2925 fuse_prepare_interrupt(f, req, &d);
2926 err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname));
2927 fuse_finish_interrupt(f, req, &d);
2928 free_path(f, ino, path);
2929 }
2930 if (!err) {
2931 linkname[PATH_MAX] = '\0';
2932 fuse_reply_readlink(req, linkname);
2933 } else
2934 reply_err(req, err);
2935}
2936
2937static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
2938 mode_t mode, dev_t rdev)
2939{
2940 struct fuse *f = req_fuse_prepare(req);
2941 struct fuse_entry_param e;
2942 char *path;
2943 int err;
2944
2945 err = get_path_name(f, parent, name, &path);
2946 if (!err) {
2947 struct fuse_intr_data d;
2948
2949 fuse_prepare_interrupt(f, req, &d);
2950 err = -ENOSYS;
2951 if (S_ISREG(mode)) {
2952 struct fuse_file_info fi;
2953
2954 memset(&fi, 0, sizeof(fi));
2955 fi.flags = O_CREAT | O_EXCL | O_WRONLY;
2956 err = fuse_fs_create(f->fs, path, mode, &fi);
2957 if (!err) {
2958 err = lookup_path(f, parent, name, path, &e,
2959 &fi);
2960 fuse_fs_release(f->fs, path, &fi);
2961 }
2962 }
2963 if (err == -ENOSYS) {
2964 err = fuse_fs_mknod(f->fs, path, mode, rdev);
2965 if (!err)
2966 err = lookup_path(f, parent, name, path, &e,
2967 NULL);
2968 }
2969 fuse_finish_interrupt(f, req, &d);
2970 free_path(f, parent, path);
2971 }
2972 reply_entry(req, &e, err);
2973}
2974
2975static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
2976 mode_t mode)
2977{
2978 struct fuse *f = req_fuse_prepare(req);
2979 struct fuse_entry_param e;
2980 char *path;
2981 int err;
2982
2983 err = get_path_name(f, parent, name, &path);
2984 if (!err) {
2985 struct fuse_intr_data d;
2986
2987 fuse_prepare_interrupt(f, req, &d);
2988 err = fuse_fs_mkdir(f->fs, path, mode);
2989 if (!err)
2990 err = lookup_path(f, parent, name, path, &e, NULL);
2991 fuse_finish_interrupt(f, req, &d);
2992 free_path(f, parent, path);
2993 }
2994 reply_entry(req, &e, err);
2995}
2996
2997static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent,
2998 const char *name)
2999{
3000 struct fuse *f = req_fuse_prepare(req);
3001 struct node *wnode;
3002 char *path;
3003 int err;
3004
3005 err = get_path_wrlock(f, parent, name, &path, &wnode);
3006 if (!err) {
3007 struct fuse_intr_data d;
3008
3009 fuse_prepare_interrupt(f, req, &d);
3010 if (!f->conf.hard_remove && is_open(f, parent, name)) {
3011 err = hide_node(f, path, parent, name);
3012 if (!err) {
3013 /* we have hidden the node so now check again under a lock in case it is not used any more */
3014 if (!is_open(f, parent, wnode->name)) {
3015 char *unlinkpath;
3016
3017 /* get the hidden file path, to unlink it */
3018 if (try_get_path(f, wnode->nodeid, NULL, &unlinkpath, NULL, false) == 0) {
3019 err = fuse_fs_unlink(f->fs, unlinkpath);
3020 if (!err)
3021 remove_node(f, parent, wnode->name);
3022 free(unlinkpath);
3023 }
3024 }
3025 }
3026 } else {
3027 err = fuse_fs_unlink(f->fs, path);
3028 if (!err)
3029 remove_node(f, parent, name);
3030 }
3031 fuse_finish_interrupt(f, req, &d);
3032 free_path_wrlock(f, parent, wnode, path);
3033 }
3034 reply_err(req, err);
3035}
3036
3037static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
3038{
3039 struct fuse *f = req_fuse_prepare(req);
3040 struct node *wnode;
3041 char *path;
3042 int err;
3043
3044 err = get_path_wrlock(f, parent, name, &path, &wnode);
3045 if (!err) {
3046 struct fuse_intr_data d;
3047
3048 fuse_prepare_interrupt(f, req, &d);
3049 err = fuse_fs_rmdir(f->fs, path);
3050 fuse_finish_interrupt(f, req, &d);
3051 if (!err)
3052 remove_node(f, parent, name);
3053 free_path_wrlock(f, parent, wnode, path);
3054 }
3055 reply_err(req, err);
3056}
3057
3058static void fuse_lib_symlink(fuse_req_t req, const char *linkname,
3059 fuse_ino_t parent, const char *name)
3060{
3061 struct fuse *f = req_fuse_prepare(req);
3062 struct fuse_entry_param e;
3063 char *path;
3064 int err;
3065
3066 err = get_path_name(f, parent, name, &path);
3067 if (!err) {
3068 struct fuse_intr_data d;
3069
3070 fuse_prepare_interrupt(f, req, &d);
3071 err = fuse_fs_symlink(f->fs, linkname, path);
3072 if (!err)
3073 err = lookup_path(f, parent, name, path, &e, NULL);
3074 fuse_finish_interrupt(f, req, &d);
3075 free_path(f, parent, path);
3076 }
3077 reply_entry(req, &e, err);
3078}
3079
3080static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
3081 const char *oldname, fuse_ino_t newdir,
3082 const char *newname, unsigned int flags)
3083{
3084 struct fuse *f = req_fuse_prepare(req);
3085 char *oldpath;
3086 char *newpath;
3087 struct node *wnode1;
3088 struct node *wnode2;
3089 int err;
3090
3091 err = get_path2(f, olddir, oldname, newdir, newname,
3092 &oldpath, &newpath, &wnode1, &wnode2);
3093 if (!err) {
3094 struct fuse_intr_data d;
3095 err = 0;
3096 fuse_prepare_interrupt(f, req, &d);
3097 if (!f->conf.hard_remove && !(flags & RENAME_EXCHANGE) &&
3098 is_open(f, newdir, newname))
3099 err = hide_node(f, newpath, newdir, newname);
3100 if (!err) {
3101 err = fuse_fs_rename(f->fs, oldpath, newpath, flags);
3102 if (!err) {
3103 if (flags & RENAME_EXCHANGE) {
3104 err = exchange_node(f, olddir, oldname,
3105 newdir, newname);
3106 } else {
3107 err = rename_node(f, olddir, oldname,
3108 newdir, newname, 0);
3109 }
3110 }
3111 }
3112 fuse_finish_interrupt(f, req, &d);
3113 free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath);
3114 }
3115 reply_err(req, err);
3116}
3117
3118static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
3119 const char *newname)
3120{
3121 struct fuse *f = req_fuse_prepare(req);
3122 struct fuse_entry_param e;
3123 char *oldpath;
3124 char *newpath;
3125 int err;
3126
3127 err = get_path2(f, ino, NULL, newparent, newname,
3128 &oldpath, &newpath, NULL, NULL);
3129 if (!err) {
3130 struct fuse_intr_data d;
3131
3132 fuse_prepare_interrupt(f, req, &d);
3133 err = fuse_fs_link(f->fs, oldpath, newpath);
3134 if (!err)
3135 err = lookup_path(f, newparent, newname, newpath,
3136 &e, NULL);
3137 fuse_finish_interrupt(f, req, &d);
3138 free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath);
3139 }
3140 reply_entry(req, &e, err);
3141}
3142
3143static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path,
3144 struct fuse_file_info *fi)
3145{
3146 struct node *node;
3147 int unlink_hidden = 0;
3148
3149 fuse_fs_release(f->fs, path, fi);
3150
3151 pthread_mutex_lock(&f->lock);
3152 node = get_node(f, ino);
3153 assert(node->open_count > 0);
3154 --node->open_count;
3155 if (node->is_hidden && !node->open_count) {
3156 unlink_hidden = 1;
3157 node->is_hidden = 0;
3158 }
3159 pthread_mutex_unlock(&f->lock);
3160
3161 if(unlink_hidden) {
3162 if (path) {
3163 fuse_fs_unlink(f->fs, path);
3164 } else if (f->conf.nullpath_ok) {
3165 char *unlinkpath;
3166
3167 if (get_path(f, ino, &unlinkpath) == 0)
3168 fuse_fs_unlink(f->fs, unlinkpath);
3169
3170 free_path(f, ino, unlinkpath);
3171 }
3172 }
3173}
3174
3175static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent,
3176 const char *name, mode_t mode,
3177 struct fuse_file_info *fi)
3178{
3179 struct fuse *f = req_fuse_prepare(req);
3180 struct fuse_intr_data d;
3181 struct fuse_entry_param e;
3182 char *path;
3183 int err;
3184
3185 err = get_path_name(f, parent, name, &path);
3186 if (!err) {
3187 fuse_prepare_interrupt(f, req, &d);
3188 err = fuse_fs_create(f->fs, path, mode, fi);
3189 if (!err) {
3190 err = lookup_path(f, parent, name, path, &e, fi);
3191 if (err)
3192 fuse_fs_release(f->fs, path, fi);
3193 else if (!S_ISREG(e.attr.st_mode)) {
3194 err = -EIO;
3195 fuse_fs_release(f->fs, path, fi);
3196 forget_node(f, e.ino, 1);
3197 } else {
3198 if (f->conf.direct_io)
3199 fi->direct_io = 1;
3200 if (f->conf.kernel_cache)
3201 fi->keep_cache = 1;
3202 if (fi->direct_io &&
3203 f->conf.parallel_direct_writes)
3204 fi->parallel_direct_writes = 1;
3205 }
3206 }
3207 fuse_finish_interrupt(f, req, &d);
3208 }
3209 if (!err) {
3210 pthread_mutex_lock(&f->lock);
3211 get_node(f, e.ino)->open_count++;
3212 pthread_mutex_unlock(&f->lock);
3213 if (fuse_reply_create(req, &e, fi) == -ENOENT) {
3214 /* The open syscall was interrupted, so it
3215 must be cancelled */
3216 fuse_do_release(f, e.ino, path, fi);
3217 forget_node(f, e.ino, 1);
3218 }
3219 } else {
3220 reply_err(req, err);
3221 }
3222
3223 free_path(f, parent, path);
3224}
3225
3226static double diff_timespec(const struct timespec *t1,
3227 const struct timespec *t2)
3228{
3229 return (t1->tv_sec - t2->tv_sec) +
3230 ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0;
3231}
3232
3233static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path,
3234 struct fuse_file_info *fi)
3235{
3236 struct node *node;
3237
3238 pthread_mutex_lock(&f->lock);
3239 node = get_node(f, ino);
3240 if (node->cache_valid) {
3241 struct timespec now;
3242
3243 curr_time(&now);
3244 if (diff_timespec(&now, &node->stat_updated) >
3245 f->conf.ac_attr_timeout) {
3246 struct stat stbuf;
3247 int err;
3248 pthread_mutex_unlock(&f->lock);
3249 err = fuse_fs_getattr(f->fs, path, &stbuf, fi);
3250 pthread_mutex_lock(&f->lock);
3251 if (!err)
3252 update_stat(node, &stbuf);
3253 else
3254 node->cache_valid = 0;
3255 }
3256 }
3257 if (node->cache_valid)
3258 fi->keep_cache = 1;
3259
3260 node->cache_valid = 1;
3261 pthread_mutex_unlock(&f->lock);
3262}
3263
3264static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino,
3265 struct fuse_file_info *fi)
3266{
3267 struct fuse *f = req_fuse_prepare(req);
3268 struct fuse_intr_data d;
3269 char *path;
3270 int err;
3271
3272 err = get_path(f, ino, &path);
3273 if (!err) {
3274 fuse_prepare_interrupt(f, req, &d);
3275 err = fuse_fs_open(f->fs, path, fi);
3276 if (!err) {
3277 if (f->conf.direct_io)
3278 fi->direct_io = 1;
3279 if (f->conf.kernel_cache)
3280 fi->keep_cache = 1;
3281
3282 if (f->conf.auto_cache)
3283 open_auto_cache(f, ino, path, fi);
3284
3285 if (f->conf.no_rofd_flush &&
3286 (fi->flags & O_ACCMODE) == O_RDONLY)
3287 fi->noflush = 1;
3288
3289 if (fi->direct_io && f->conf.parallel_direct_writes)
3290 fi->parallel_direct_writes = 1;
3291
3292 }
3293 fuse_finish_interrupt(f, req, &d);
3294 }
3295 if (!err) {
3296 pthread_mutex_lock(&f->lock);
3297 get_node(f, ino)->open_count++;
3298 pthread_mutex_unlock(&f->lock);
3299 if (fuse_reply_open(req, fi) == -ENOENT) {
3300 /* The open syscall was interrupted, so it
3301 must be cancelled */
3302 fuse_do_release(f, ino, path, fi);
3303 }
3304 } else
3305 reply_err(req, err);
3306
3307 free_path(f, ino, path);
3308}
3309
3310static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
3311 off_t off, struct fuse_file_info *fi)
3312{
3313 struct fuse *f = req_fuse_prepare(req);
3314 struct fuse_bufvec *buf = NULL;
3315 char *path;
3316 int res;
3317
3318 res = get_path_nullok(f, ino, &path);
3319 if (res == 0) {
3320 struct fuse_intr_data d;
3321
3322 fuse_prepare_interrupt(f, req, &d);
3323 res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi);
3324 fuse_finish_interrupt(f, req, &d);
3325 free_path(f, ino, path);
3326 }
3327
3328 if (res == 0)
3330 else
3331 reply_err(req, res);
3332
3333 fuse_free_buf(buf);
3334}
3335
3336static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino,
3337 struct fuse_bufvec *buf, off_t off,
3338 struct fuse_file_info *fi)
3339{
3340 struct fuse *f = req_fuse_prepare(req);
3341 char *path;
3342 int res;
3343
3344 res = get_path_nullok(f, ino, &path);
3345 if (res == 0) {
3346 struct fuse_intr_data d;
3347
3348 fuse_prepare_interrupt(f, req, &d);
3349 res = fuse_fs_write_buf(f->fs, path, buf, off, fi);
3350 fuse_finish_interrupt(f, req, &d);
3351 free_path(f, ino, path);
3352 }
3353
3354 if (res >= 0)
3355 fuse_reply_write(req, res);
3356 else
3357 reply_err(req, res);
3358}
3359
3360static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
3361 struct fuse_file_info *fi)
3362{
3363 struct fuse *f = req_fuse_prepare(req);
3364 char *path;
3365 int err;
3366
3367 err = get_path_nullok(f, ino, &path);
3368 if (!err) {
3369 struct fuse_intr_data d;
3370
3371 fuse_prepare_interrupt(f, req, &d);
3372 err = fuse_fs_fsync(f->fs, path, datasync, fi);
3373 fuse_finish_interrupt(f, req, &d);
3374 free_path(f, ino, path);
3375 }
3376 reply_err(req, err);
3377}
3378
3379static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi,
3380 struct fuse_file_info *fi)
3381{
3382 struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh;
3383 memset(fi, 0, sizeof(struct fuse_file_info));
3384 fi->fh = dh->fh;
3385 return dh;
3386}
3387
3388static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
3389 struct fuse_file_info *llfi)
3390{
3391 struct fuse *f = req_fuse_prepare(req);
3392 struct fuse_intr_data d;
3393 struct fuse_dh *dh;
3394 struct fuse_file_info fi;
3395 char *path;
3396 int err;
3397
3398 dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
3399 if (dh == NULL) {
3400 reply_err(req, -ENOMEM);
3401 return;
3402 }
3403 memset(dh, 0, sizeof(struct fuse_dh));
3404 dh->fuse = f;
3405 dh->contents = NULL;
3406 dh->first = NULL;
3407 dh->len = 0;
3408 dh->filled = 0;
3409 dh->nodeid = ino;
3410 pthread_mutex_init(&dh->lock, NULL);
3411
3412 llfi->fh = (uintptr_t) dh;
3413
3414 memset(&fi, 0, sizeof(fi));
3415 fi.flags = llfi->flags;
3416
3417 err = get_path(f, ino, &path);
3418 if (!err) {
3419 fuse_prepare_interrupt(f, req, &d);
3420 err = fuse_fs_opendir(f->fs, path, &fi);
3421 fuse_finish_interrupt(f, req, &d);
3422 dh->fh = fi.fh;
3423 llfi->cache_readdir = fi.cache_readdir;
3424 llfi->keep_cache = fi.keep_cache;
3425 }
3426 if (!err) {
3427 if (fuse_reply_open(req, llfi) == -ENOENT) {
3428 /* The opendir syscall was interrupted, so it
3429 must be cancelled */
3430 fuse_fs_releasedir(f->fs, path, &fi);
3431 pthread_mutex_destroy(&dh->lock);
3432 free(dh);
3433 }
3434 } else {
3435 reply_err(req, err);
3436 pthread_mutex_destroy(&dh->lock);
3437 free(dh);
3438 }
3439 free_path(f, ino, path);
3440}
3441
3442static int extend_contents(struct fuse_dh *dh, unsigned minsize)
3443{
3444 if (minsize > dh->size) {
3445 char *newptr;
3446 unsigned newsize = dh->size;
3447 if (!newsize)
3448 newsize = 1024;
3449 while (newsize < minsize) {
3450 if (newsize >= 0x80000000)
3451 newsize = 0xffffffff;
3452 else
3453 newsize *= 2;
3454 }
3455
3456 newptr = (char *) realloc(dh->contents, newsize);
3457 if (!newptr) {
3458 dh->error = -ENOMEM;
3459 return -1;
3460 }
3461 dh->contents = newptr;
3462 dh->size = newsize;
3463 }
3464 return 0;
3465}
3466
3467static int fuse_add_direntry_to_dh(struct fuse_dh *dh, const char *name,
3468 struct stat *st, enum fuse_fill_dir_flags flags)
3469{
3470 struct fuse_direntry *de;
3471
3472 de = malloc(sizeof(struct fuse_direntry));
3473 if (!de) {
3474 dh->error = -ENOMEM;
3475 return -1;
3476 }
3477 de->name = strdup(name);
3478 if (!de->name) {
3479 dh->error = -ENOMEM;
3480 free(de);
3481 return -1;
3482 }
3483 de->flags = flags;
3484 de->stat = *st;
3485 de->next = NULL;
3486
3487 *dh->last = de;
3488 dh->last = &de->next;
3489
3490 return 0;
3491}
3492
3493static fuse_ino_t lookup_nodeid(struct fuse *f, fuse_ino_t parent,
3494 const char *name)
3495{
3496 struct node *node;
3497 fuse_ino_t res = FUSE_UNKNOWN_INO;
3498
3499 pthread_mutex_lock(&f->lock);
3500 node = lookup_node(f, parent, name);
3501 if (node)
3502 res = node->nodeid;
3503 pthread_mutex_unlock(&f->lock);
3504
3505 return res;
3506}
3507
3508static int fill_dir(void *dh_, const char *name, const struct stat *statp,
3509 off_t off, enum fuse_fill_dir_flags flags)
3510{
3511 struct fuse_dh *dh = (struct fuse_dh *) dh_;
3512 struct stat stbuf;
3513
3514 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
3515 dh->error = -EIO;
3516 return 1;
3517 }
3518
3519 if (statp)
3520 stbuf = *statp;
3521 else {
3522 memset(&stbuf, 0, sizeof(stbuf));
3523 stbuf.st_ino = FUSE_UNKNOWN_INO;
3524 }
3525
3526 if (!dh->fuse->conf.use_ino) {
3527 stbuf.st_ino = FUSE_UNKNOWN_INO;
3528 if (dh->fuse->conf.readdir_ino) {
3529 stbuf.st_ino = (ino_t)
3530 lookup_nodeid(dh->fuse, dh->nodeid, name);
3531 }
3532 }
3533
3534 if (off) {
3535 size_t newlen;
3536
3537 if (dh->filled) {
3538 dh->error = -EIO;
3539 return 1;
3540 }
3541
3542 if (dh->first) {
3543 dh->error = -EIO;
3544 return 1;
3545 }
3546
3547 if (extend_contents(dh, dh->needlen) == -1)
3548 return 1;
3549
3550 newlen = dh->len +
3551 fuse_add_direntry(dh->req, dh->contents + dh->len,
3552 dh->needlen - dh->len, name,
3553 &stbuf, off);
3554 if (newlen > dh->needlen)
3555 return 1;
3556
3557 dh->len = newlen;
3558 } else {
3559 dh->filled = 1;
3560
3561 if (fuse_add_direntry_to_dh(dh, name, &stbuf, flags) == -1)
3562 return 1;
3563 }
3564 return 0;
3565}
3566
3567static int is_dot_or_dotdot(const char *name)
3568{
3569 return name[0] == '.' && (name[1] == '\0' ||
3570 (name[1] == '.' && name[2] == '\0'));
3571}
3572
3573static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp,
3574 off_t off, enum fuse_fill_dir_flags flags)
3575{
3576 struct fuse_dh *dh = (struct fuse_dh *) dh_;
3577 struct fuse_entry_param e = {
3578 /* ino=0 tells the kernel to ignore readdirplus stat info */
3579 .ino = 0,
3580 };
3581 struct fuse *f = dh->fuse;
3582 int res;
3583
3584 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
3585 dh->error = -EIO;
3586 return 1;
3587 }
3588
3589 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
3590 e.attr = *statp;
3591 } else {
3592 e.attr.st_ino = FUSE_UNKNOWN_INO;
3593 if (statp) {
3594 e.attr.st_mode = statp->st_mode;
3595 if (f->conf.use_ino)
3596 e.attr.st_ino = statp->st_ino;
3597 }
3598 if (!f->conf.use_ino && f->conf.readdir_ino) {
3599 e.attr.st_ino = (ino_t)
3600 lookup_nodeid(f, dh->nodeid, name);
3601 }
3602 }
3603
3604 if (off) {
3605 size_t newlen;
3606
3607 if (dh->filled) {
3608 dh->error = -EIO;
3609 return 1;
3610 }
3611
3612 if (dh->first) {
3613 dh->error = -EIO;
3614 return 1;
3615 }
3616 if (extend_contents(dh, dh->needlen) == -1)
3617 return 1;
3618
3619 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
3620 if (!is_dot_or_dotdot(name)) {
3621 res = do_lookup(f, dh->nodeid, name, &e);
3622 if (res) {
3623 dh->error = res;
3624 return 1;
3625 }
3626 }
3627 }
3628
3629 newlen = dh->len +
3630 fuse_add_direntry_plus(dh->req, dh->contents + dh->len,
3631 dh->needlen - dh->len, name,
3632 &e, off);
3633 if (newlen > dh->needlen)
3634 return 1;
3635 dh->len = newlen;
3636 } else {
3637 dh->filled = 1;
3638
3639 if (fuse_add_direntry_to_dh(dh, name, &e.attr, flags) == -1)
3640 return 1;
3641 }
3642
3643 return 0;
3644}
3645
3646static void free_direntries(struct fuse_direntry *de)
3647{
3648 while (de) {
3649 struct fuse_direntry *next = de->next;
3650 free(de->name);
3651 free(de);
3652 de = next;
3653 }
3654}
3655
3656static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3657 size_t size, off_t off, struct fuse_dh *dh,
3658 struct fuse_file_info *fi,
3659 enum fuse_readdir_flags flags)
3660{
3661 char *path;
3662 int err;
3663
3664 if (f->fs->op.readdir)
3665 err = get_path_nullok(f, ino, &path);
3666 else
3667 err = get_path(f, ino, &path);
3668 if (!err) {
3669 struct fuse_intr_data d;
3670 fuse_fill_dir_t filler = fill_dir;
3671
3672 if (flags & FUSE_READDIR_PLUS)
3673 filler = fill_dir_plus;
3674
3675 free_direntries(dh->first);
3676 dh->first = NULL;
3677 dh->last = &dh->first;
3678 dh->len = 0;
3679 dh->error = 0;
3680 dh->needlen = size;
3681 dh->filled = 0;
3682 dh->req = req;
3683 fuse_prepare_interrupt(f, req, &d);
3684 err = fuse_fs_readdir(f->fs, path, dh, filler, off, fi, flags);
3685 fuse_finish_interrupt(f, req, &d);
3686 dh->req = NULL;
3687 if (!err)
3688 err = dh->error;
3689 if (err)
3690 dh->filled = 0;
3691 free_path(f, ino, path);
3692 }
3693 return err;
3694}
3695
3696static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh,
3697 off_t off, enum fuse_readdir_flags flags)
3698{
3699 off_t pos;
3700 struct fuse_direntry *de = dh->first;
3701 int res;
3702
3703 dh->len = 0;
3704
3705 if (extend_contents(dh, dh->needlen) == -1)
3706 return dh->error;
3707
3708 for (pos = 0; pos < off; pos++) {
3709 if (!de)
3710 break;
3711
3712 de = de->next;
3713 }
3714 while (de) {
3715 char *p = dh->contents + dh->len;
3716 unsigned rem = dh->needlen - dh->len;
3717 unsigned thislen;
3718 unsigned newlen;
3719 pos++;
3720
3721 if (flags & FUSE_READDIR_PLUS) {
3722 struct fuse_entry_param e = {
3723 .ino = 0,
3724 .attr = de->stat,
3725 };
3726
3727 if (de->flags & FUSE_FILL_DIR_PLUS &&
3728 !is_dot_or_dotdot(de->name)) {
3729 res = do_lookup(dh->fuse, dh->nodeid,
3730 de->name, &e);
3731 if (res) {
3732 dh->error = res;
3733 return 1;
3734 }
3735 }
3736
3737 thislen = fuse_add_direntry_plus(req, p, rem,
3738 de->name, &e, pos);
3739 } else {
3740 thislen = fuse_add_direntry(req, p, rem,
3741 de->name, &de->stat, pos);
3742 }
3743 newlen = dh->len + thislen;
3744 if (newlen > dh->needlen)
3745 break;
3746 dh->len = newlen;
3747 de = de->next;
3748 }
3749 return 0;
3750}
3751
3752static void fuse_readdir_common(fuse_req_t req, fuse_ino_t ino, size_t size,
3753 off_t off, struct fuse_file_info *llfi,
3754 enum fuse_readdir_flags flags)
3755{
3756 struct fuse *f = req_fuse_prepare(req);
3757 struct fuse_file_info fi;
3758 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
3759 int err;
3760
3761 pthread_mutex_lock(&dh->lock);
3762 /* According to SUS, directory contents need to be refreshed on
3763 rewinddir() */
3764 if (!off)
3765 dh->filled = 0;
3766
3767 if (!dh->filled) {
3768 err = readdir_fill(f, req, ino, size, off, dh, &fi, flags);
3769 if (err) {
3770 reply_err(req, err);
3771 goto out;
3772 }
3773 }
3774 if (dh->filled) {
3775 dh->needlen = size;
3776 err = readdir_fill_from_list(req, dh, off, flags);
3777 if (err) {
3778 reply_err(req, err);
3779 goto out;
3780 }
3781 }
3782 fuse_reply_buf(req, dh->contents, dh->len);
3783out:
3784 pthread_mutex_unlock(&dh->lock);
3785}
3786
3787static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
3788 off_t off, struct fuse_file_info *llfi)
3789{
3790 fuse_readdir_common(req, ino, size, off, llfi, 0);
3791}
3792
3793static void fuse_lib_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
3794 off_t off, struct fuse_file_info *llfi)
3795{
3796 fuse_readdir_common(req, ino, size, off, llfi, FUSE_READDIR_PLUS);
3797}
3798
3799static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
3800 struct fuse_file_info *llfi)
3801{
3802 struct fuse *f = req_fuse_prepare(req);
3803 struct fuse_intr_data d;
3804 struct fuse_file_info fi;
3805 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
3806 char *path;
3807
3808 get_path_nullok(f, ino, &path);
3809
3810 fuse_prepare_interrupt(f, req, &d);
3811 fuse_fs_releasedir(f->fs, path, &fi);
3812 fuse_finish_interrupt(f, req, &d);
3813 free_path(f, ino, path);
3814
3815 pthread_mutex_lock(&dh->lock);
3816 pthread_mutex_unlock(&dh->lock);
3817 pthread_mutex_destroy(&dh->lock);
3818 free_direntries(dh->first);
3819 free(dh->contents);
3820 free(dh);
3821 reply_err(req, 0);
3822}
3823
3824static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
3825 struct fuse_file_info *llfi)
3826{
3827 struct fuse *f = req_fuse_prepare(req);
3828 struct fuse_file_info fi;
3829 char *path;
3830 int err;
3831
3832 get_dirhandle(llfi, &fi);
3833
3834 err = get_path_nullok(f, ino, &path);
3835 if (!err) {
3836 struct fuse_intr_data d;
3837 fuse_prepare_interrupt(f, req, &d);
3838 err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi);
3839 fuse_finish_interrupt(f, req, &d);
3840 free_path(f, ino, path);
3841 }
3842 reply_err(req, err);
3843}
3844
3845static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino)
3846{
3847 struct fuse *f = req_fuse_prepare(req);
3848 struct statvfs buf;
3849 char *path = NULL;
3850 int err = 0;
3851
3852 memset(&buf, 0, sizeof(buf));
3853 if (ino)
3854 err = get_path(f, ino, &path);
3855
3856 if (!err) {
3857 struct fuse_intr_data d;
3858 fuse_prepare_interrupt(f, req, &d);
3859 err = fuse_fs_statfs(f->fs, path ? path : "/", &buf);
3860 fuse_finish_interrupt(f, req, &d);
3861 free_path(f, ino, path);
3862 }
3863
3864 if (!err)
3865 fuse_reply_statfs(req, &buf);
3866 else
3867 reply_err(req, err);
3868}
3869
3870static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
3871 const char *value, size_t size, int flags)
3872{
3873 struct fuse *f = req_fuse_prepare(req);
3874 char *path;
3875 int err;
3876
3877 err = get_path(f, ino, &path);
3878 if (!err) {
3879 struct fuse_intr_data d;
3880 fuse_prepare_interrupt(f, req, &d);
3881 err = fuse_fs_setxattr(f->fs, path, name, value, size, flags);
3882 fuse_finish_interrupt(f, req, &d);
3883 free_path(f, ino, path);
3884 }
3885 reply_err(req, err);
3886}
3887
3888static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3889 const char *name, char *value, size_t size)
3890{
3891 int err;
3892 char *path;
3893
3894 err = get_path(f, ino, &path);
3895 if (!err) {
3896 struct fuse_intr_data d;
3897 fuse_prepare_interrupt(f, req, &d);
3898 err = fuse_fs_getxattr(f->fs, path, name, value, size);
3899 fuse_finish_interrupt(f, req, &d);
3900 free_path(f, ino, path);
3901 }
3902 return err;
3903}
3904
3905static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
3906 size_t size)
3907{
3908 struct fuse *f = req_fuse_prepare(req);
3909 int res;
3910
3911 if (size) {
3912 char *value = (char *) malloc(size);
3913 if (value == NULL) {
3914 reply_err(req, -ENOMEM);
3915 return;
3916 }
3917 res = common_getxattr(f, req, ino, name, value, size);
3918 if (res > 0)
3919 fuse_reply_buf(req, value, res);
3920 else
3921 reply_err(req, res);
3922 free(value);
3923 } else {
3924 res = common_getxattr(f, req, ino, name, NULL, 0);
3925 if (res >= 0)
3926 fuse_reply_xattr(req, res);
3927 else
3928 reply_err(req, res);
3929 }
3930}
3931
3932static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
3933 char *list, size_t size)
3934{
3935 char *path;
3936 int err;
3937
3938 err = get_path(f, ino, &path);
3939 if (!err) {
3940 struct fuse_intr_data d;
3941 fuse_prepare_interrupt(f, req, &d);
3942 err = fuse_fs_listxattr(f->fs, path, list, size);
3943 fuse_finish_interrupt(f, req, &d);
3944 free_path(f, ino, path);
3945 }
3946 return err;
3947}
3948
3949static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
3950{
3951 struct fuse *f = req_fuse_prepare(req);
3952 int res;
3953
3954 if (size) {
3955 char *list = (char *) malloc(size);
3956 if (list == NULL) {
3957 reply_err(req, -ENOMEM);
3958 return;
3959 }
3960 res = common_listxattr(f, req, ino, list, size);
3961 if (res > 0)
3962 fuse_reply_buf(req, list, res);
3963 else
3964 reply_err(req, res);
3965 free(list);
3966 } else {
3967 res = common_listxattr(f, req, ino, NULL, 0);
3968 if (res >= 0)
3969 fuse_reply_xattr(req, res);
3970 else
3971 reply_err(req, res);
3972 }
3973}
3974
3975static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino,
3976 const char *name)
3977{
3978 struct fuse *f = req_fuse_prepare(req);
3979 char *path;
3980 int err;
3981
3982 err = get_path(f, ino, &path);
3983 if (!err) {
3984 struct fuse_intr_data d;
3985 fuse_prepare_interrupt(f, req, &d);
3986 err = fuse_fs_removexattr(f->fs, path, name);
3987 fuse_finish_interrupt(f, req, &d);
3988 free_path(f, ino, path);
3989 }
3990 reply_err(req, err);
3991}
3992
3993static struct lock *locks_conflict(struct node *node, const struct lock *lock)
3994{
3995 struct lock *l;
3996
3997 for (l = node->locks; l; l = l->next)
3998 if (l->owner != lock->owner &&
3999 lock->start <= l->end && l->start <= lock->end &&
4000 (l->type == F_WRLCK || lock->type == F_WRLCK))
4001 break;
4002
4003 return l;
4004}
4005
4006static void delete_lock(struct lock **lockp)
4007{
4008 struct lock *l = *lockp;
4009 *lockp = l->next;
4010 free(l);
4011}
4012
4013static void insert_lock(struct lock **pos, struct lock *lock)
4014{
4015 lock->next = *pos;
4016 *pos = lock;
4017}
4018
4019static int locks_insert(struct node *node, struct lock *lock)
4020{
4021 struct lock **lp;
4022 struct lock *newl1 = NULL;
4023 struct lock *newl2 = NULL;
4024
4025 if (lock->type != F_UNLCK || lock->start != 0 ||
4026 lock->end != OFFSET_MAX) {
4027 newl1 = malloc(sizeof(struct lock));
4028 newl2 = malloc(sizeof(struct lock));
4029
4030 if (!newl1 || !newl2) {
4031 free(newl1);
4032 free(newl2);
4033 return -ENOLCK;
4034 }
4035 }
4036
4037 for (lp = &node->locks; *lp;) {
4038 struct lock *l = *lp;
4039 if (l->owner != lock->owner)
4040 goto skip;
4041
4042 if (lock->type == l->type) {
4043 if (l->end < lock->start - 1)
4044 goto skip;
4045 if (lock->end < l->start - 1)
4046 break;
4047 if (l->start <= lock->start && lock->end <= l->end)
4048 goto out;
4049 if (l->start < lock->start)
4050 lock->start = l->start;
4051 if (lock->end < l->end)
4052 lock->end = l->end;
4053 goto delete;
4054 } else {
4055 if (l->end < lock->start)
4056 goto skip;
4057 if (lock->end < l->start)
4058 break;
4059 if (lock->start <= l->start && l->end <= lock->end)
4060 goto delete;
4061 if (l->end <= lock->end) {
4062 l->end = lock->start - 1;
4063 goto skip;
4064 }
4065 if (lock->start <= l->start) {
4066 l->start = lock->end + 1;
4067 break;
4068 }
4069 *newl2 = *l;
4070 newl2->start = lock->end + 1;
4071 l->end = lock->start - 1;
4072 insert_lock(&l->next, newl2);
4073 newl2 = NULL;
4074 }
4075 skip:
4076 lp = &l->next;
4077 continue;
4078
4079 delete:
4080 delete_lock(lp);
4081 }
4082 if (lock->type != F_UNLCK) {
4083 *newl1 = *lock;
4084 insert_lock(lp, newl1);
4085 newl1 = NULL;
4086 }
4087out:
4088 free(newl1);
4089 free(newl2);
4090 return 0;
4091}
4092
4093static void flock_to_lock(struct flock *flock, struct lock *lock)
4094{
4095 memset(lock, 0, sizeof(struct lock));
4096 lock->type = flock->l_type;
4097 lock->start = flock->l_start;
4098 lock->end =
4099 flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX;
4100 lock->pid = flock->l_pid;
4101}
4102
4103static void lock_to_flock(struct lock *lock, struct flock *flock)
4104{
4105 flock->l_type = lock->type;
4106 flock->l_start = lock->start;
4107 flock->l_len =
4108 (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1;
4109 flock->l_pid = lock->pid;
4110}
4111
4112static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
4113 const char *path, struct fuse_file_info *fi)
4114{
4115 struct fuse_intr_data d;
4116 struct flock lock;
4117 struct lock l;
4118 int err;
4119 int errlock;
4120
4121 fuse_prepare_interrupt(f, req, &d);
4122 memset(&lock, 0, sizeof(lock));
4123 lock.l_type = F_UNLCK;
4124 lock.l_whence = SEEK_SET;
4125 err = fuse_fs_flush(f->fs, path, fi);
4126 errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock);
4127 fuse_finish_interrupt(f, req, &d);
4128
4129 if (errlock != -ENOSYS) {
4130 flock_to_lock(&lock, &l);
4131 l.owner = fi->lock_owner;
4132 pthread_mutex_lock(&f->lock);
4133 locks_insert(get_node(f, ino), &l);
4134 pthread_mutex_unlock(&f->lock);
4135
4136 /* if op.lock() is defined FLUSH is needed regardless
4137 of op.flush() */
4138 if (err == -ENOSYS)
4139 err = 0;
4140 }
4141 return err;
4142}
4143
4144static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino,
4145 struct fuse_file_info *fi)
4146{
4147 struct fuse *f = req_fuse_prepare(req);
4148 struct fuse_intr_data d;
4149 char *path;
4150 int err = 0;
4151
4152 get_path_nullok(f, ino, &path);
4153 if (fi->flush) {
4154 err = fuse_flush_common(f, req, ino, path, fi);
4155 if (err == -ENOSYS)
4156 err = 0;
4157 }
4158
4159 fuse_prepare_interrupt(f, req, &d);
4160 fuse_do_release(f, ino, path, fi);
4161 fuse_finish_interrupt(f, req, &d);
4162 free_path(f, ino, path);
4163
4164 reply_err(req, err);
4165}
4166
4167static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino,
4168 struct fuse_file_info *fi)
4169{
4170 struct fuse *f = req_fuse_prepare(req);
4171 char *path;
4172 int err;
4173
4174 get_path_nullok(f, ino, &path);
4175 err = fuse_flush_common(f, req, ino, path, fi);
4176 free_path(f, ino, path);
4177
4178 reply_err(req, err);
4179}
4180
4181static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino,
4182 struct fuse_file_info *fi, struct flock *lock,
4183 int cmd)
4184{
4185 struct fuse *f = req_fuse_prepare(req);
4186 char *path;
4187 int err;
4188
4189 err = get_path_nullok(f, ino, &path);
4190 if (!err) {
4191 struct fuse_intr_data d;
4192 fuse_prepare_interrupt(f, req, &d);
4193 err = fuse_fs_lock(f->fs, path, fi, cmd, lock);
4194 fuse_finish_interrupt(f, req, &d);
4195 free_path(f, ino, path);
4196 }
4197 return err;
4198}
4199
4200static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino,
4201 struct fuse_file_info *fi, struct flock *lock)
4202{
4203 int err;
4204 struct lock l;
4205 struct lock *conflict;
4206 struct fuse *f = req_fuse(req);
4207
4208 flock_to_lock(lock, &l);
4209 l.owner = fi->lock_owner;
4210 pthread_mutex_lock(&f->lock);
4211 conflict = locks_conflict(get_node(f, ino), &l);
4212 if (conflict)
4213 lock_to_flock(conflict, lock);
4214 pthread_mutex_unlock(&f->lock);
4215 if (!conflict)
4216 err = fuse_lock_common(req, ino, fi, lock, F_GETLK);
4217 else
4218 err = 0;
4219
4220 if (!err)
4221 fuse_reply_lock(req, lock);
4222 else
4223 reply_err(req, err);
4224}
4225
4226static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino,
4227 struct fuse_file_info *fi, struct flock *lock,
4228 int sleep)
4229{
4230 int err = fuse_lock_common(req, ino, fi, lock,
4231 sleep ? F_SETLKW : F_SETLK);
4232 if (!err) {
4233 struct fuse *f = req_fuse(req);
4234 struct lock l;
4235 flock_to_lock(lock, &l);
4236 l.owner = fi->lock_owner;
4237 pthread_mutex_lock(&f->lock);
4238 locks_insert(get_node(f, ino), &l);
4239 pthread_mutex_unlock(&f->lock);
4240 }
4241 reply_err(req, err);
4242}
4243
4244static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino,
4245 struct fuse_file_info *fi, int op)
4246{
4247 struct fuse *f = req_fuse_prepare(req);
4248 char *path;
4249 int err;
4250
4251 err = get_path_nullok(f, ino, &path);
4252 if (err == 0) {
4253 struct fuse_intr_data d;
4254 fuse_prepare_interrupt(f, req, &d);
4255 err = fuse_fs_flock(f->fs, path, fi, op);
4256 fuse_finish_interrupt(f, req, &d);
4257 free_path(f, ino, path);
4258 }
4259 reply_err(req, err);
4260}
4261
4262static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
4263 uint64_t idx)
4264{
4265 struct fuse *f = req_fuse_prepare(req);
4266 struct fuse_intr_data d;
4267 char *path;
4268 int err;
4269
4270 err = get_path(f, ino, &path);
4271 if (!err) {
4272 fuse_prepare_interrupt(f, req, &d);
4273 err = fuse_fs_bmap(f->fs, path, blocksize, &idx);
4274 fuse_finish_interrupt(f, req, &d);
4275 free_path(f, ino, path);
4276 }
4277 if (!err)
4278 fuse_reply_bmap(req, idx);
4279 else
4280 reply_err(req, err);
4281}
4282
4283static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
4284 void *arg, struct fuse_file_info *llfi,
4285 unsigned int flags, const void *in_buf,
4286 size_t in_bufsz, size_t out_bufsz)
4287{
4288 struct fuse *f = req_fuse_prepare(req);
4289 struct fuse_intr_data d;
4290 struct fuse_file_info fi;
4291 char *path, *out_buf = NULL;
4292 int err;
4293
4294 err = -EPERM;
4295 if (flags & FUSE_IOCTL_UNRESTRICTED)
4296 goto err;
4297
4298 if (flags & FUSE_IOCTL_DIR)
4299 get_dirhandle(llfi, &fi);
4300 else
4301 fi = *llfi;
4302
4303 if (out_bufsz) {
4304 err = -ENOMEM;
4305 out_buf = malloc(out_bufsz);
4306 if (!out_buf)
4307 goto err;
4308 }
4309
4310 assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
4311 if (out_buf && in_bufsz)
4312 memcpy(out_buf, in_buf, in_bufsz);
4313
4314 err = get_path_nullok(f, ino, &path);
4315 if (err)
4316 goto err;
4317
4318 fuse_prepare_interrupt(f, req, &d);
4319
4320 err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags,
4321 out_buf ? out_buf : (void *)in_buf);
4322
4323 fuse_finish_interrupt(f, req, &d);
4324 free_path(f, ino, path);
4325
4326 if (err < 0)
4327 goto err;
4328 fuse_reply_ioctl(req, err, out_buf, out_bufsz);
4329 goto out;
4330err:
4331 reply_err(req, err);
4332out:
4333 free(out_buf);
4334}
4335
4336static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
4337 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
4338{
4339 struct fuse *f = req_fuse_prepare(req);
4340 struct fuse_intr_data d;
4341 char *path;
4342 int err;
4343 unsigned revents = 0;
4344
4345 err = get_path_nullok(f, ino, &path);
4346 if (!err) {
4347 fuse_prepare_interrupt(f, req, &d);
4348 err = fuse_fs_poll(f->fs, path, fi, ph, &revents);
4349 fuse_finish_interrupt(f, req, &d);
4350 free_path(f, ino, path);
4351 }
4352 if (!err)
4353 fuse_reply_poll(req, revents);
4354 else
4355 reply_err(req, err);
4356}
4357
4358static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
4359 off_t offset, off_t length, struct fuse_file_info *fi)
4360{
4361 struct fuse *f = req_fuse_prepare(req);
4362 struct fuse_intr_data d;
4363 char *path;
4364 int err;
4365
4366 err = get_path_nullok(f, ino, &path);
4367 if (!err) {
4368 fuse_prepare_interrupt(f, req, &d);
4369 err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi);
4370 fuse_finish_interrupt(f, req, &d);
4371 free_path(f, ino, path);
4372 }
4373 reply_err(req, err);
4374}
4375
4376static void fuse_lib_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
4377 off_t off_in, struct fuse_file_info *fi_in,
4378 fuse_ino_t nodeid_out, off_t off_out,
4379 struct fuse_file_info *fi_out, size_t len,
4380 int flags)
4381{
4382 struct fuse *f = req_fuse_prepare(req);
4383 struct fuse_intr_data d;
4384 char *path_in, *path_out;
4385 int err;
4386 ssize_t res;
4387
4388 err = get_path_nullok(f, nodeid_in, &path_in);
4389 if (err) {
4390 reply_err(req, err);
4391 return;
4392 }
4393
4394 err = get_path_nullok(f, nodeid_out, &path_out);
4395 if (err) {
4396 free_path(f, nodeid_in, path_in);
4397 reply_err(req, err);
4398 return;
4399 }
4400
4401 fuse_prepare_interrupt(f, req, &d);
4402 res = fuse_fs_copy_file_range(f->fs, path_in, fi_in, off_in, path_out,
4403 fi_out, off_out, len, flags);
4404 fuse_finish_interrupt(f, req, &d);
4405
4406 if (res >= 0)
4407 fuse_reply_write(req, res);
4408 else
4409 reply_err(req, res);
4410
4411 free_path(f, nodeid_in, path_in);
4412 free_path(f, nodeid_out, path_out);
4413}
4414
4415static void fuse_lib_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
4416 struct fuse_file_info *fi)
4417{
4418 struct fuse *f = req_fuse_prepare(req);
4419 struct fuse_intr_data d;
4420 char *path;
4421 int err;
4422 off_t res;
4423
4424 err = get_path(f, ino, &path);
4425 if (err) {
4426 reply_err(req, err);
4427 return;
4428 }
4429
4430 fuse_prepare_interrupt(f, req, &d);
4431 res = fuse_fs_lseek(f->fs, path, off, whence, fi);
4432 fuse_finish_interrupt(f, req, &d);
4433 free_path(f, ino, path);
4434 if (res >= 0)
4435 fuse_reply_lseek(req, res);
4436 else
4437 reply_err(req, res);
4438}
4439
4440static int clean_delay(struct fuse *f)
4441{
4442 /*
4443 * This is calculating the delay between clean runs. To
4444 * reduce the number of cleans we are doing them 10 times
4445 * within the remember window.
4446 */
4447 int min_sleep = 60;
4448 int max_sleep = 3600;
4449 int sleep_time = f->conf.remember / 10;
4450
4451 if (sleep_time > max_sleep)
4452 return max_sleep;
4453 if (sleep_time < min_sleep)
4454 return min_sleep;
4455 return sleep_time;
4456}
4457
4458int fuse_clean_cache(struct fuse *f)
4459{
4460 struct node_lru *lnode;
4461 struct list_head *curr, *next;
4462 struct node *node;
4463 struct timespec now;
4464
4465 pthread_mutex_lock(&f->lock);
4466
4467 curr_time(&now);
4468
4469 for (curr = f->lru_table.next; curr != &f->lru_table; curr = next) {
4470 double age;
4471
4472 next = curr->next;
4473 lnode = list_entry(curr, struct node_lru, lru);
4474 node = &lnode->node;
4475
4476 age = diff_timespec(&now, &lnode->forget_time);
4477 if (age <= f->conf.remember)
4478 break;
4479
4480 assert(node->nlookup == 1);
4481
4482 /* Don't forget active directories */
4483 if (node->refctr > 1)
4484 continue;
4485
4486 node->nlookup = 0;
4487 unhash_name(f, node);
4488 unref_node(f, node);
4489 }
4490 pthread_mutex_unlock(&f->lock);
4491
4492 return clean_delay(f);
4493}
4494
4495static struct fuse_lowlevel_ops fuse_path_ops = {
4496 .init = fuse_lib_init,
4497 .destroy = fuse_lib_destroy,
4498 .lookup = fuse_lib_lookup,
4499 .forget = fuse_lib_forget,
4500 .forget_multi = fuse_lib_forget_multi,
4501 .getattr = fuse_lib_getattr,
4502 .setattr = fuse_lib_setattr,
4503 .access = fuse_lib_access,
4504 .readlink = fuse_lib_readlink,
4505 .mknod = fuse_lib_mknod,
4506 .mkdir = fuse_lib_mkdir,
4507 .unlink = fuse_lib_unlink,
4508 .rmdir = fuse_lib_rmdir,
4509 .symlink = fuse_lib_symlink,
4510 .rename = fuse_lib_rename,
4511 .link = fuse_lib_link,
4512 .create = fuse_lib_create,
4513 .open = fuse_lib_open,
4514 .read = fuse_lib_read,
4515 .write_buf = fuse_lib_write_buf,
4516 .flush = fuse_lib_flush,
4517 .release = fuse_lib_release,
4518 .fsync = fuse_lib_fsync,
4519 .opendir = fuse_lib_opendir,
4520 .readdir = fuse_lib_readdir,
4521 .readdirplus = fuse_lib_readdirplus,
4522 .releasedir = fuse_lib_releasedir,
4523 .fsyncdir = fuse_lib_fsyncdir,
4524 .statfs = fuse_lib_statfs,
4525 .setxattr = fuse_lib_setxattr,
4526 .getxattr = fuse_lib_getxattr,
4527 .listxattr = fuse_lib_listxattr,
4528 .removexattr = fuse_lib_removexattr,
4529 .getlk = fuse_lib_getlk,
4530 .setlk = fuse_lib_setlk,
4531 .flock = fuse_lib_flock,
4532 .bmap = fuse_lib_bmap,
4533 .ioctl = fuse_lib_ioctl,
4534 .poll = fuse_lib_poll,
4535 .fallocate = fuse_lib_fallocate,
4536 .copy_file_range = fuse_lib_copy_file_range,
4537 .lseek = fuse_lib_lseek,
4538};
4539
4540int fuse_notify_poll(struct fuse_pollhandle *ph)
4541{
4542 return fuse_lowlevel_notify_poll(ph);
4543}
4544
4545struct fuse_session *fuse_get_session(struct fuse *f)
4546{
4547 return f->se;
4548}
4549
4550static int fuse_session_loop_remember(struct fuse *f)
4551{
4552 struct fuse_session *se = f->se;
4553 int res = 0;
4554 struct timespec now;
4555 time_t next_clean;
4556 struct pollfd fds = {
4557 .fd = se->fd,
4558 .events = POLLIN
4559 };
4560 struct fuse_buf fbuf = {
4561 .mem = NULL,
4562 };
4563
4564 curr_time(&now);
4565 next_clean = now.tv_sec;
4566 while (!fuse_session_exited(se)) {
4567 unsigned timeout;
4568
4569 curr_time(&now);
4570 if (now.tv_sec < next_clean)
4571 timeout = next_clean - now.tv_sec;
4572 else
4573 timeout = 0;
4574
4575 res = poll(&fds, 1, timeout * 1000);
4576 if (res == -1) {
4577 if (errno == EINTR)
4578 continue;
4579 else
4580 break;
4581 } else if (res > 0) {
4582 res = fuse_session_receive_buf_internal(se, &fbuf,
4583 NULL);
4584 if (res == -EINTR)
4585 continue;
4586 if (res <= 0)
4587 break;
4588
4589 fuse_session_process_buf_internal(se, &fbuf, NULL);
4590 } else {
4591 timeout = fuse_clean_cache(f);
4592 curr_time(&now);
4593 next_clean = now.tv_sec + timeout;
4594 }
4595 }
4596
4597 free(fbuf.mem);
4599 return res < 0 ? -1 : 0;
4600}
4601
4602int fuse_loop(struct fuse *f)
4603{
4604 if (!f)
4605 return -1;
4606
4607 if (lru_enabled(f))
4608 return fuse_session_loop_remember(f);
4609
4610 return fuse_session_loop(f->se);
4611}
4612
4613FUSE_SYMVER("fuse_loop_mt_312", "fuse_loop_mt@@FUSE_3.12")
4614int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config)
4615{
4616 if (f == NULL)
4617 return -1;
4618
4619 int res = fuse_start_cleanup_thread(f);
4620 if (res)
4621 return -1;
4622
4623 res = fuse_session_loop_mt_312(fuse_get_session(f), config);
4625 return res;
4626}
4627
4628int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1);
4629FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@FUSE_3.2")
4630int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1)
4631{
4632 struct fuse_loop_config *config = fuse_loop_cfg_create();
4633 if (config == NULL)
4634 return ENOMEM;
4635
4636 fuse_loop_cfg_convert(config, config_v1);
4637
4638 int res = fuse_loop_mt_312(f, config);
4639
4640 fuse_loop_cfg_destroy(config);
4641
4642 return res;
4643}
4644
4645int fuse_loop_mt_31(struct fuse *f, int clone_fd);
4646FUSE_SYMVER("fuse_loop_mt_31", "fuse_loop_mt@FUSE_3.0")
4647int fuse_loop_mt_31(struct fuse *f, int clone_fd)
4648{
4649 int err;
4650 struct fuse_loop_config *config = fuse_loop_cfg_create();
4651
4652 if (config == NULL)
4653 return ENOMEM;
4654
4655 fuse_loop_cfg_set_clone_fd(config, clone_fd);
4656
4657 err = fuse_loop_mt_312(f, config);
4658
4659 fuse_loop_cfg_destroy(config);
4660
4661 return err;
4662}
4663
4664void fuse_exit(struct fuse *f)
4665{
4666 fuse_session_exit(f->se);
4667}
4668
4670{
4671 struct fuse_context_i *c = fuse_get_context_internal();
4672
4673 if (c)
4674 return &c->ctx;
4675 else
4676 return NULL;
4677}
4678
4679int fuse_getgroups(int size, gid_t list[])
4680{
4681 struct fuse_context_i *c = fuse_get_context_internal();
4682 if (!c)
4683 return -EINVAL;
4684
4685 return fuse_req_getgroups(c->req, size, list);
4686}
4687
4689{
4690 struct fuse_context_i *c = fuse_get_context_internal();
4691
4692 if (c)
4693 return fuse_req_interrupted(c->req);
4694 else
4695 return 0;
4696}
4697
4698int fuse_invalidate_path(struct fuse *f, const char *path) {
4699 fuse_ino_t ino;
4700 int err = lookup_path_in_cache(f, path, &ino);
4701 if (err) {
4702 return err;
4703 }
4704
4705 return fuse_lowlevel_notify_inval_inode(f->se, ino, 0, 0);
4706}
4707
4708#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
4709
4710static const struct fuse_opt fuse_lib_opts[] = {
4713 FUSE_LIB_OPT("debug", debug, 1),
4714 FUSE_LIB_OPT("-d", debug, 1),
4715 FUSE_LIB_OPT("kernel_cache", kernel_cache, 1),
4716 FUSE_LIB_OPT("auto_cache", auto_cache, 1),
4717 FUSE_LIB_OPT("noauto_cache", auto_cache, 0),
4718 FUSE_LIB_OPT("no_rofd_flush", no_rofd_flush, 1),
4719 FUSE_LIB_OPT("umask=", set_mode, 1),
4720 FUSE_LIB_OPT("umask=%o", umask, 0),
4721 FUSE_LIB_OPT("fmask=", set_mode, 1),
4722 FUSE_LIB_OPT("fmask=%o", fmask, 0),
4723 FUSE_LIB_OPT("dmask=", set_mode, 1),
4724 FUSE_LIB_OPT("dmask=%o", dmask, 0),
4725 FUSE_LIB_OPT("uid=", set_uid, 1),
4726 FUSE_LIB_OPT("uid=%d", uid, 0),
4727 FUSE_LIB_OPT("gid=", set_gid, 1),
4728 FUSE_LIB_OPT("gid=%d", gid, 0),
4729 FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0),
4730 FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0),
4731 FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0),
4732 FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1),
4733 FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0),
4734 FUSE_LIB_OPT("noforget", remember, -1),
4735 FUSE_LIB_OPT("remember=%u", remember, 0),
4736 FUSE_LIB_OPT("modules=%s", modules, 0),
4737 FUSE_LIB_OPT("parallel_direct_write=%d", parallel_direct_writes, 0),
4739};
4740
4741static int fuse_lib_opt_proc(void *data, const char *arg, int key,
4742 struct fuse_args *outargs)
4743{
4744 (void) arg; (void) outargs; (void) data; (void) key;
4745
4746 /* Pass through unknown options */
4747 return 1;
4748}
4749
4750
4751static const struct fuse_opt fuse_help_opts[] = {
4752 FUSE_LIB_OPT("modules=%s", modules, 1),
4753 FUSE_OPT_KEY("modules=%s", FUSE_OPT_KEY_KEEP),
4755};
4756
4757static void print_module_help(const char *name,
4759{
4760 struct fuse_args a = FUSE_ARGS_INIT(0, NULL);
4761 if (fuse_opt_add_arg(&a, "") == -1 ||
4762 fuse_opt_add_arg(&a, "-h") == -1)
4763 return;
4764 printf("\nOptions for %s module:\n", name);
4765 (*fac)(&a, NULL);
4767}
4768
4769void fuse_lib_help(struct fuse_args *args)
4770{
4771 /* These are not all options, but only the ones that
4772 may be of interest to an end-user */
4773 printf(
4774" -o kernel_cache cache files in kernel\n"
4775" -o [no]auto_cache enable caching based on modification times (off)\n"
4776" -o no_rofd_flush disable flushing of read-only fd on close (off)\n"
4777" -o umask=M set file permissions (octal)\n"
4778" -o fmask=M set file permissions (octal)\n"
4779" -o dmask=M set dir permissions (octal)\n"
4780" -o uid=N set file owner\n"
4781" -o gid=N set file group\n"
4782" -o entry_timeout=T cache timeout for names (1.0s)\n"
4783" -o negative_timeout=T cache timeout for deleted names (0.0s)\n"
4784" -o attr_timeout=T cache timeout for attributes (1.0s)\n"
4785" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n"
4786" -o noforget never forget cached inodes\n"
4787" -o remember=T remember cached inodes for T seconds (0s)\n"
4788" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n");
4789
4790
4791 /* Print low-level help */
4793
4794 /* Print help for builtin modules */
4795 print_module_help("subdir", &fuse_module_subdir_factory);
4796#ifdef HAVE_ICONV
4797 print_module_help("iconv", &fuse_module_iconv_factory);
4798#endif
4799
4800 /* Parse command line options in case we need to
4801 activate more modules */
4802 struct fuse_config conf = { .modules = NULL };
4803 if (fuse_opt_parse(args, &conf, fuse_help_opts,
4804 fuse_lib_opt_proc) == -1
4805 || !conf.modules)
4806 return;
4807
4808 char *module;
4809 char *next;
4810 struct fuse_module *m;
4811
4812 // Iterate over all modules
4813 for (module = conf.modules; module; module = next) {
4814 char *p;
4815 for (p = module; *p && *p != ':'; p++);
4816 next = *p ? p + 1 : NULL;
4817 *p = '\0';
4818
4819 m = fuse_get_module(module);
4820 if (m)
4821 print_module_help(module, &m->factory);
4822 }
4823}
4824
4825static int fuse_init_intr_signal(int signum, int *installed)
4826{
4827 struct sigaction old_sa;
4828
4829 if (sigaction(signum, NULL, &old_sa) == -1) {
4830 perror("fuse: cannot get old signal handler");
4831 return -1;
4832 }
4833
4834 if (old_sa.sa_handler == SIG_DFL) {
4835 struct sigaction sa;
4836
4837 memset(&sa, 0, sizeof(struct sigaction));
4838 sa.sa_handler = fuse_intr_sighandler;
4839 sigemptyset(&sa.sa_mask);
4840
4841 if (sigaction(signum, &sa, NULL) == -1) {
4842 perror("fuse: cannot set interrupt signal handler");
4843 return -1;
4844 }
4845 *installed = 1;
4846 }
4847 return 0;
4848}
4849
4850static void fuse_restore_intr_signal(int signum)
4851{
4852 struct sigaction sa;
4853
4854 memset(&sa, 0, sizeof(struct sigaction));
4855 sa.sa_handler = SIG_DFL;
4856 sigaction(signum, &sa, NULL);
4857}
4858
4859
4860static int fuse_push_module(struct fuse *f, const char *module,
4861 struct fuse_args *args)
4862{
4863 struct fuse_fs *fs[2] = { f->fs, NULL };
4864 struct fuse_fs *newfs;
4865 struct fuse_module *m = fuse_get_module(module);
4866
4867 if (!m)
4868 return -1;
4869
4870 newfs = m->factory(args, fs);
4871 if (!newfs) {
4872 fuse_put_module(m);
4873 return -1;
4874 }
4875 f->fs = newfs;
4876 return 0;
4877}
4878
4879struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
4880 void *user_data)
4881{
4882 struct fuse_fs *fs;
4883
4884 if (sizeof(struct fuse_operations) < op_size) {
4885 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not not work\n");
4886 op_size = sizeof(struct fuse_operations);
4887 }
4888
4889 fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
4890 if (!fs) {
4891 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse_fs object\n");
4892 return NULL;
4893 }
4894
4895 fs->user_data = user_data;
4896 if (op)
4897 memcpy(&fs->op, op, op_size);
4898 return fs;
4899}
4900
4901static int node_table_init(struct node_table *t)
4902{
4903 t->size = NODE_TABLE_MIN_SIZE;
4904 t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
4905 if (t->array == NULL) {
4906 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
4907 return -1;
4908 }
4909 t->use = 0;
4910 t->split = 0;
4911
4912 return 0;
4913}
4914
4915static void *fuse_prune_nodes(void *fuse)
4916{
4917 struct fuse *f = fuse;
4918 int sleep_time;
4919
4920#ifdef HAVE_PTHREAD_SETNAME_NP
4921 pthread_setname_np(pthread_self(), "fuse_prune_nodes");
4922#endif
4923
4924 while(1) {
4925 sleep_time = fuse_clean_cache(f);
4926 sleep(sleep_time);
4927 }
4928 return NULL;
4929}
4930
4931int fuse_start_cleanup_thread(struct fuse *f)
4932{
4933 if (lru_enabled(f))
4934 return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
4935
4936 return 0;
4937}
4938
4939void fuse_stop_cleanup_thread(struct fuse *f)
4940{
4941 if (lru_enabled(f)) {
4942 pthread_mutex_lock(&f->lock);
4943 pthread_cancel(f->prune_thread);
4944 pthread_mutex_unlock(&f->lock);
4945 pthread_join(f->prune_thread, NULL);
4946 }
4947}
4948
4949/*
4950 * Not supposed to be called directly, but supposed to be called
4951 * through the fuse_new macro
4952 */
4953struct fuse *_fuse_new_31(struct fuse_args *args,
4954 const struct fuse_operations *op, size_t op_size,
4955 struct libfuse_version *version, void *user_data)
4956{
4957 struct fuse *f;
4958 struct node *root;
4959 struct fuse_fs *fs;
4960 struct fuse_lowlevel_ops llop = fuse_path_ops;
4961
4962 f = (struct fuse *) calloc(1, sizeof(struct fuse));
4963 if (f == NULL) {
4964 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
4965 goto out;
4966 }
4967
4968 f->conf.entry_timeout = 1.0;
4969 f->conf.attr_timeout = 1.0;
4970 f->conf.negative_timeout = 0.0;
4971 f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
4972
4973 /* Parse options */
4974 if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
4975 fuse_lib_opt_proc) == -1)
4976 goto out_free;
4977
4978 pthread_mutex_lock(&fuse_context_lock);
4979 static int builtin_modules_registered = 0;
4980 /* Have the builtin modules already been registered? */
4981 if (builtin_modules_registered == 0) {
4982 /* If not, register them. */
4983 fuse_register_module("subdir", fuse_module_subdir_factory, NULL);
4984#ifdef HAVE_ICONV
4985 fuse_register_module("iconv", fuse_module_iconv_factory, NULL);
4986#endif
4987 builtin_modules_registered= 1;
4988 }
4989 pthread_mutex_unlock(&fuse_context_lock);
4990
4991 if (fuse_create_context_key() == -1)
4992 goto out_free;
4993
4994 fs = fuse_fs_new(op, op_size, user_data);
4995 if (!fs)
4996 goto out_delete_context_key;
4997
4998 f->fs = fs;
4999
5000 /* Oh f**k, this is ugly! */
5001 if (!fs->op.lock) {
5002 llop.getlk = NULL;
5003 llop.setlk = NULL;
5004 }
5005
5006 f->pagesize = getpagesize();
5007 init_list_head(&f->partial_slabs);
5008 init_list_head(&f->full_slabs);
5009 init_list_head(&f->lru_table);
5010
5011 if (f->conf.modules) {
5012 char *module;
5013 char *next;
5014
5015 for (module = f->conf.modules; module; module = next) {
5016 char *p;
5017 for (p = module; *p && *p != ':'; p++);
5018 next = *p ? p + 1 : NULL;
5019 *p = '\0';
5020 if (module[0] &&
5021 fuse_push_module(f, module, args) == -1)
5022 goto out_free_fs;
5023 }
5024 }
5025
5026 if (!f->conf.ac_attr_timeout_set)
5027 f->conf.ac_attr_timeout = f->conf.attr_timeout;
5028
5029#if defined(__FreeBSD__) || defined(__NetBSD__)
5030 /*
5031 * In FreeBSD, we always use these settings as inode numbers
5032 * are needed to make getcwd(3) work.
5033 */
5034 f->conf.readdir_ino = 1;
5035#endif
5036
5037 /* not declared globally, to restrict usage of this function */
5038 struct fuse_session *fuse_session_new_versioned(
5039 struct fuse_args *args, const struct fuse_lowlevel_ops *op,
5040 size_t op_size, struct libfuse_version *version,
5041 void *userdata);
5042 f->se = fuse_session_new_versioned(args, &llop, sizeof(llop), version,
5043 f);
5044 if (f->se == NULL)
5045 goto out_free_fs;
5046
5047 if (f->conf.debug) {
5048 fuse_log(FUSE_LOG_DEBUG, "nullpath_ok: %i\n", f->conf.nullpath_ok);
5049 }
5050
5051 /* Trace topmost layer by default */
5052 f->fs->debug = f->conf.debug;
5053 f->ctr = 0;
5054 f->generation = 0;
5055 if (node_table_init(&f->name_table) == -1)
5056 goto out_free_session;
5057
5058 if (node_table_init(&f->id_table) == -1)
5059 goto out_free_name_table;
5060
5061 pthread_mutex_init(&f->lock, NULL);
5062
5063 root = alloc_node(f);
5064 if (root == NULL) {
5065 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
5066 goto out_free_id_table;
5067 }
5068 if (lru_enabled(f)) {
5069 struct node_lru *lnode = node_lru(root);
5070 init_list_head(&lnode->lru);
5071 }
5072
5073 strcpy(root->inline_name, "/");
5074 root->name = root->inline_name;
5075 root->parent = NULL;
5076 root->nodeid = FUSE_ROOT_ID;
5077 inc_nlookup(root);
5078 hash_id(f, root);
5079
5080 return f;
5081
5082out_free_id_table:
5083 free(f->id_table.array);
5084out_free_name_table:
5085 free(f->name_table.array);
5086out_free_session:
5087 fuse_session_destroy(f->se);
5088out_free_fs:
5089 free(f->fs);
5090 free(f->conf.modules);
5091out_delete_context_key:
5092 fuse_delete_context_key();
5093out_free:
5094 free(f);
5095out:
5096 return NULL;
5097}
5098
5099/* Emulates 3.0-style fuse_new(), which processes --help */
5100FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0")
5101struct fuse *_fuse_new_30(struct fuse_args *args,
5102 const struct fuse_operations *op,
5103 size_t op_size,
5104 struct libfuse_version *version,
5105 void *user_data)
5106{
5107 struct fuse_config conf = {0};
5108
5109 const struct fuse_opt opts[] = {
5110 FUSE_LIB_OPT("-h", show_help, 1),
5111 FUSE_LIB_OPT("--help", show_help, 1),
5113 };
5114
5115 if (fuse_opt_parse(args, &conf, opts,
5116 fuse_lib_opt_proc) == -1)
5117 return NULL;
5118
5119 if (conf.show_help) {
5120 fuse_lib_help(args);
5121 return NULL;
5122 } else
5123 return _fuse_new_31(args, op, op_size, version, user_data);
5124}
5125
5126/* ABI compat version */
5127struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
5128 size_t op_size, void *user_data);
5129FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1")
5130struct fuse *fuse_new_31(struct fuse_args *args,
5131 const struct fuse_operations *op,
5132 size_t op_size, void *user_data)
5133{
5134 /* unknown version */
5135 struct libfuse_version version = { 0 };
5136
5137 return _fuse_new_31(args, op, op_size, &version, user_data);
5138}
5139
5140/*
5141 * ABI compat version
5142 * Emulates 3.0-style fuse_new(), which processes --help
5143 */
5144struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
5145 size_t op_size, void *user_data);
5146FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0")
5147struct fuse *fuse_new_30(struct fuse_args *args,
5148 const struct fuse_operations *op,
5149 size_t op_size, void *user_data)
5150{
5151 struct fuse_config conf = {0};
5152
5153 const struct fuse_opt opts[] = {
5154 FUSE_LIB_OPT("-h", show_help, 1),
5155 FUSE_LIB_OPT("--help", show_help, 1),
5157 };
5158
5159 if (fuse_opt_parse(args, &conf, opts,
5160 fuse_lib_opt_proc) == -1)
5161 return NULL;
5162
5163 if (conf.show_help) {
5164 fuse_lib_help(args);
5165 return NULL;
5166 } else
5167 return fuse_new_31(args, op, op_size, user_data);
5168}
5169
5170
5171void fuse_destroy(struct fuse *f)
5172{
5173 size_t i;
5174
5175 if (f->conf.intr && f->intr_installed)
5176 fuse_restore_intr_signal(f->conf.intr_signal);
5177
5178 if (f->fs) {
5179 fuse_create_context(f);
5180
5181 for (i = 0; i < f->id_table.size; i++) {
5182 struct node *node;
5183
5184 for (node = f->id_table.array[i]; node != NULL;
5185 node = node->id_next) {
5186 if (node->is_hidden) {
5187 char *path;
5188 if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
5189 fuse_fs_unlink(f->fs, path);
5190 free(path);
5191 }
5192 }
5193 }
5194 }
5195 }
5196 for (i = 0; i < f->id_table.size; i++) {
5197 struct node *node;
5198 struct node *next;
5199
5200 for (node = f->id_table.array[i]; node != NULL; node = next) {
5201 next = node->id_next;
5202 free_node(f, node);
5203 f->id_table.use--;
5204 }
5205 }
5206 assert(list_empty(&f->partial_slabs));
5207 assert(list_empty(&f->full_slabs));
5208
5209 while (fuse_modules) {
5210 fuse_put_module(fuse_modules);
5211 }
5212 free(f->id_table.array);
5213 free(f->name_table.array);
5214 pthread_mutex_destroy(&f->lock);
5215 fuse_session_destroy(f->se);
5216 free(f->fs);
5217 free(f->conf.modules);
5218 free(f);
5219 fuse_delete_context_key();
5220}
5221
5222int fuse_mount(struct fuse *f, const char *mountpoint) {
5223 return fuse_session_mount(fuse_get_session(f), mountpoint);
5224}
5225
5226
5227void fuse_unmount(struct fuse *f) {
5229}
5230
5232{
5233 return FUSE_VERSION;
5234}
5235
5236const char *fuse_pkgversion(void)
5237{
5238 return PACKAGE_VERSION;
5239}
int fuse_getgroups(int size, gid_t list[])
Definition fuse.c:4679
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5222
int fuse_interrupted(void)
Definition fuse.c:4688
void fuse_destroy(struct fuse *f)
Definition fuse.c:5171
int fuse_start_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4931
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4698
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:4669
int fuse_loop(struct fuse *f)
Definition fuse.c:4602
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:4664
int fuse_clean_cache(struct fuse *fuse)
Definition fuse.c:4458
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4769
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4545
void fuse_unmount(struct fuse *f)
Definition fuse.c:5227
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4879
void fuse_stop_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4939
fuse_fill_dir_flags
Definition fuse.h:58
fuse_readdir_flags
Definition fuse.h:42
#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:5236
int fuse_version(void)
Definition fuse.c:5231
@ 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
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
uint64_t want_ext
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:66
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:95
uint32_t parallel_direct_writes
uint32_t noflush
Definition fuse_common.h:99
uint32_t flush
Definition fuse_common.h:80
uint32_t direct_io
Definition fuse_common.h:69
uint32_t keep_cache
Definition fuse_common.h:75
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)
void(* getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
fuse-3.17.2/doc/html/fuse_8h.html0000644000175000017500000013156615002273413015465 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 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 4458 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 5171 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 4664 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 4879 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 4669 of file fuse.c.

◆ fuse_get_session()

struct fuse_session * fuse_get_session ( struct fuse *  f)

Get session from fuse object

Definition at line 4545 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 4679 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 4688 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 4698 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 4769 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 4602 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 5222 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 475 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 4931 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 4939 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 5227 of file fuse.c.

fuse-3.17.2/doc/html/fuse_8h_source.html0000644000175000017500000042526415002273413017046 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 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:4679
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5222
int fuse_interrupted(void)
Definition fuse.c:4688
void fuse_destroy(struct fuse *f)
Definition fuse.c:5171
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:4931
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4698
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4669
int fuse_loop(struct fuse *f)
Definition fuse.c:4602
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:4664
int fuse_clean_cache(struct fuse *fuse)
Definition fuse.c:4458
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4769
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:475
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4545
void fuse_unmount(struct fuse *f)
Definition fuse.c:5227
struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
Definition fuse.c:4879
void fuse_stop_cleanup_thread(struct fuse *fuse)
Definition fuse.c:4939
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
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(* mknod)(const char *, mode_t, dev_t)
Definition fuse.h:379
int(* open)(const char *, struct fuse_file_info *)
Definition fuse.h:486
int(* readlink)(const char *, char *, size_t)
Definition fuse.h:371
int(* write)(const char *, const char *, size_t, off_t, struct fuse_file_info *)
Definition fuse.h:509
int(* fallocate)(const char *, int, off_t, off_t, struct fuse_file_info *)
Definition fuse.h:828
int(* read)(const char *, char *, size_t, off_t, struct fuse_file_info *)
Definition fuse.h:497
int(* symlink)(const char *, const char *)
Definition fuse.h:396
int(* write_buf)(const char *, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *)
Definition fuse.h:781
int(* release)(const char *, struct fuse_file_info *)
Definition fuse.h:560
int(* access)(const char *, int)
Definition fuse.h:660
int(* lock)(const char *, struct fuse_file_info *, int cmd, struct flock *)
Definition fuse.h:704
int(* fsyncdir)(const char *, int, struct fuse_file_info *)
Definition fuse.h:631
int(* mkdir)(const char *, mode_t)
Definition fuse.h:387
int(* chown)(const char *, uid_t, gid_t, struct fuse_file_info *fi)
Definition fuse.h:427
int(* unlink)(const char *)
Definition fuse.h:390
int(* flush)(const char *, struct fuse_file_info *)
Definition fuse.h:546
int(* listxattr)(const char *, char *, size_t)
Definition fuse.h:576
int(* truncate)(const char *, off_t, struct fuse_file_info *fi)
Definition fuse.h:437
int(* statfs)(const char *, struct statvfs *)
Definition fuse.h:516
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(* create)(const char *, mode_t, struct fuse_file_info *)
Definition fuse.h:672
int(* utimens)(const char *, const struct timespec tv[2], struct fuse_file_info *fi)
Definition fuse.h:719
off_t(* lseek)(const char *, off_t off, int whence, struct fuse_file_info *)
Definition fuse.h:852
int(* getxattr)(const char *, const char *, char *, size_t)
Definition fuse.h:573
int(* setxattr)(const char *, const char *, const char *, size_t, int)
Definition fuse.h:570
int(* poll)(const char *, struct fuse_file_info *, struct fuse_pollhandle *ph, unsigned *reventsp)
Definition fuse.h:769
int(* fsync)(const char *, int, struct fuse_file_info *)
Definition fuse.h:567
int(* chmod)(const char *, mode_t, struct fuse_file_info *fi)
Definition fuse.h:417
int(* opendir)(const char *, struct fuse_file_info *)
Definition fuse.h:589
int(* rmdir)(const char *)
Definition fuse.h:393
int(* releasedir)(const char *, struct fuse_file_info *)
Definition fuse.h:621
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition fuse.h:361
int(* read_buf)(const char *, struct fuse_bufvec **bufp, size_t size, off_t off, struct fuse_file_info *)
Definition fuse.h:798
int(* link)(const char *, const char *)
Definition fuse.h:410
int(* flock)(const char *, struct fuse_file_info *, int op)
Definition fuse.h:818
int(* removexattr)(const char *, const char *)
Definition fuse.h:579
int(* ioctl)(const char *, unsigned int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data)
Definition fuse.h:750
int(* rename)(const char *, const char *, unsigned int flags)
Definition fuse.h:407
int(* readdir)(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags)
Definition fuse.h:613
void(* destroy)(void *private_data)
Definition fuse.h:649
int(* bmap)(const char *, size_t blocksize, uint64_t *idx)
Definition fuse.h:728
fuse-3.17.2/doc/html/fuse__common_8h.html0000644000175000017500000023602715002273413017172 0ustar berndbernd libfuse: 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)
 

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 673 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 336 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 185 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 202 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 289 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 426 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 496 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 222 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 480 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 464 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 214 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 260 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 396 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 413 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 267 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 516 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 360 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 441 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 368 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 508 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 387 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 193 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 297 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 325 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 487 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 238 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 247 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 230 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 345 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 528 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 839 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 808 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_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_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 459 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 5236 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 1907 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 185 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 163 of file fuse_signals.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 138 of file fuse_signals.c.

◆ fuse_version()

int fuse_version ( void  )

Get the version of the library

Returns
the version

Definition at line 5231 of file fuse.c.

fuse-3.17.2/doc/html/fuse__common_8h_source.html0000644000175000017500000026722315002273413020554 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 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 HAVE_STATIC_ASSERT
34#define fuse_static_assert(condition, message) static_assert(condition, message)
35#else
36#define fuse_static_assert(condition, message)
37#endif
38
39#ifdef __cplusplus
40extern "C" {
41#endif
42
58 int32_t flags;
59
66 uint32_t writepage : 1;
67
69 uint32_t direct_io : 1;
70
75 uint32_t keep_cache : 1;
76
80 uint32_t flush : 1;
81
84 uint32_t nonseekable : 1;
85
86 /* Indicates that flock locks for this file should be
87 released. If set, lock_owner shall contain a valid value.
88 May only be set in ->release(). */
89 uint32_t flock_release : 1;
90
95 uint32_t cache_readdir : 1;
96
99 uint32_t noflush : 1;
100
104
106 uint32_t padding : 23;
107 uint32_t padding2 : 32;
108 uint32_t padding3 : 32;
109
113 uint64_t fh;
114
116 uint64_t lock_owner;
117
120 uint32_t poll_events;
121
125 int32_t backing_id;
126
128 uint64_t compat_flags;
129
130 uint64_t reserved[2];
131};
132fuse_static_assert(sizeof(struct fuse_file_info) == 64,
133 "fuse_file_info size mismatch");
134
145#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
146struct fuse_loop_config_v1; /* forward declaration */
148#else
149struct fuse_loop_config_v1 {
150#endif
156
167 unsigned int max_idle_threads;
168};
169
170
171/**************************************************************************
172 * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
173 **************************************************************************/
174
185#define FUSE_CAP_ASYNC_READ (1 << 0)
186
193#define FUSE_CAP_POSIX_LOCKS (1 << 1)
194
202#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
203
214#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
215
222#define FUSE_CAP_DONT_MASK (1 << 6)
223
230#define FUSE_CAP_SPLICE_WRITE (1 << 7)
231
238#define FUSE_CAP_SPLICE_MOVE (1 << 8)
239
247#define FUSE_CAP_SPLICE_READ (1 << 9)
248
260#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
261
267#define FUSE_CAP_IOCTL_DIR (1 << 11)
268
289#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12)
290
297#define FUSE_CAP_READDIRPLUS (1 << 13)
298
325#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14)
326
336#define FUSE_CAP_ASYNC_DIO (1 << 15)
337
345#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
346
360#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17)
361
368#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
369
387#define FUSE_CAP_POSIX_ACL (1 << 19)
388
396#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20)
397
413#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1 << 21)
414
426#define FUSE_CAP_CACHE_SYMLINKS (1 << 23)
427
441#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24)
442
464#define FUSE_CAP_EXPLICIT_INVAL_DATA (1 << 25)
465
480#define FUSE_CAP_EXPIRE_ONLY (1 << 26)
481
487#define FUSE_CAP_SETXATTR_EXT (1 << 27)
488
496#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1 << 28)
497
508#define FUSE_CAP_PASSTHROUGH (1 << 29)
509
516#define FUSE_CAP_NO_EXPORT_SUPPORT (1 << 30)
517
528#define FUSE_IOCTL_COMPAT (1 << 0)
529#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
530#define FUSE_IOCTL_RETRY (1 << 2)
531#define FUSE_IOCTL_DIR (1 << 4)
532
533#define FUSE_IOCTL_MAX_IOV 256
534
550 uint32_t proto_major;
551
555 uint32_t proto_minor;
556
560 uint32_t max_write;
561
574 uint32_t max_read;
575
580
586 uint32_t capable;
587
598 uint32_t want;
599
629
639
655 uint32_t time_gran;
656
673#define FUSE_BACKING_STACKED_UNDER (0)
674#define FUSE_BACKING_STACKED_OVER (1)
675 uint32_t max_backing_stack_depth;
676
685 uint32_t no_interrupt : 1;
686
687 /* reserved bits for future use */
688 uint32_t padding : 31;
689
694 uint64_t capable_ext;
695
704 uint64_t want_ext;
705
709 uint32_t reserved[16];
710};
711fuse_static_assert(sizeof(struct fuse_conn_info) == 128,
712 "Size of struct fuse_conn_info must be 128 bytes");
713
714struct fuse_session;
715struct fuse_pollhandle;
716struct fuse_conn_info_opts;
717
760struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
761
769void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
770 struct fuse_conn_info *conn);
771
778int fuse_daemonize(int foreground);
779
785int fuse_version(void);
786
792const char *fuse_pkgversion(void);
793
799void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
800
801/* ----------------------------------------------------------- *
802 * Data buffer *
803 * ----------------------------------------------------------- */
804
815 FUSE_BUF_IS_FD = (1 << 1),
816
825
833 FUSE_BUF_FD_RETRY = (1 << 3)
835
877
884struct fuse_buf {
888 size_t size;
889
894
900 void *mem;
901
907 int fd;
908
914 off_t pos;
915
922 size_t mem_size;
923};
924
937 size_t count;
938
942 size_t idx;
943
947 size_t off;
948
952 struct fuse_buf buf[1];
953};
954
960{
961 uint32_t major;
962 uint32_t minor;
963 uint32_t hotfix;
964 uint32_t padding;
965};
966
967/* Initialize bufvec with a single buffer of given size */
968#define FUSE_BUFVEC_INIT(size__) \
969 ((struct fuse_bufvec) { \
970 /* .count= */ 1, \
971 /* .idx = */ 0, \
972 /* .off = */ 0, \
973 /* .buf = */ { /* [0] = */ { \
974 /* .size = */ (size__), \
975 /* .flags = */ (enum fuse_buf_flags) 0, \
976 /* .mem = */ NULL, \
977 /* .fd = */ -1, \
978 /* .pos = */ 0, \
979 /* .mem_size = */ 0, \
980 } } \
981 } )
982
989size_t fuse_buf_size(const struct fuse_bufvec *bufv);
990
999ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
1000 enum fuse_buf_copy_flags flags);
1001
1002/* ----------------------------------------------------------- *
1003 * Signal handling *
1004 * ----------------------------------------------------------- */
1005
1021int fuse_set_signal_handlers(struct fuse_session *se);
1022
1038int fuse_set_fail_signal_handlers(struct fuse_session *se);
1039
1051void fuse_remove_signal_handlers(struct fuse_session *se);
1052
1058#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
1064struct fuse_loop_config *fuse_loop_cfg_create(void);
1065
1069void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
1070
1074void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
1075 unsigned int value);
1076
1080void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
1081 unsigned int value);
1082
1086void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
1087 unsigned int value);
1088
1095void fuse_loop_cfg_convert(struct fuse_loop_config *config,
1096 struct fuse_loop_config_v1 *v1_conf);
1097#endif
1098
1099
1100static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn,
1101 uint64_t flag)
1102{
1103 if (conn->capable_ext & flag) {
1104 conn->want_ext |= flag;
1105 return true;
1106 }
1107 return false;
1108}
1109
1110static inline void fuse_unset_feature_flag(struct fuse_conn_info *conn,
1111 uint64_t flag)
1112{
1113 conn->want_ext &= ~flag;
1114}
1115
1116static inline bool fuse_get_feature_flag(struct fuse_conn_info *conn,
1117 uint64_t flag)
1118{
1119 return conn->capable_ext & flag ? true : false;
1120}
1121
1122/* ----------------------------------------------------------- *
1123 * Compatibility stuff *
1124 * ----------------------------------------------------------- */
1125
1126#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30
1127# error only API version 30 or greater is supported
1128#endif
1129
1130#ifdef __cplusplus
1131}
1132#endif
1133
1134
1135/*
1136 * This interface uses 64 bit off_t.
1137 *
1138 * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags!
1139 */
1140
1141#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
1142_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit");
1143#else
1144struct _fuse_off_t_must_be_64bit_dummy_struct \
1145 { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); };
1146#endif
1147
1148#endif /* FUSE_COMMON_H_ */
int fuse_set_fail_signal_handlers(struct fuse_session *se)
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:459
const char * fuse_pkgversion(void)
Definition fuse.c:5236
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
int fuse_version(void)
Definition fuse.c:5231
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
enum fuse_buf_flags flags
size_t mem_size
void * mem
off_t pos
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:66
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:95
uint32_t nonseekable
Definition fuse_common.h:84
int32_t backing_id
uint32_t parallel_direct_writes
uint32_t padding
uint32_t noflush
Definition fuse_common.h:99
uint64_t compat_flags
uint32_t flush
Definition fuse_common.h:80
uint32_t direct_io
Definition fuse_common.h:69
uint32_t keep_cache
Definition fuse_common.h:75
unsigned int max_idle_threads
fuse-3.17.2/doc/html/fuse__i_8h_source.html0000644000175000017500000012535715002273413017515 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 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
17#define MIN(a, b) \
18({ \
19 typeof(a) _a = (a); \
20 typeof(b) _b = (b); \
21 _a < _b ? _a : _b; \
22})
23
24struct mount_opts;
25
26struct fuse_req {
27 struct fuse_session *se;
28 uint64_t unique;
29 _Atomic int ref_cnt;
30 pthread_mutex_t lock;
31 struct fuse_ctx ctx;
32 struct fuse_chan *ch;
33 int interrupted;
34 unsigned int ioctl_64bit : 1;
35 union {
36 struct {
37 uint64_t unique;
38 } i;
39 struct {
41 void *data;
42 } ni;
43 } u;
44 struct fuse_req *next;
45 struct fuse_req *prev;
46};
47
48struct fuse_notify_req {
49 uint64_t unique;
50 void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
51 const void *, const struct fuse_buf *);
52 struct fuse_notify_req *next;
53 struct fuse_notify_req *prev;
54};
55
56struct fuse_session {
57 char *mountpoint;
58 volatile int exited;
59 int fd;
60 struct fuse_custom_io *io;
61 struct mount_opts *mo;
62 int debug;
63 int deny_others;
64 struct fuse_lowlevel_ops op;
65 int got_init;
66 struct cuse_data *cuse_data;
67 void *userdata;
68 uid_t owner;
69 struct fuse_conn_info conn;
70 struct fuse_req list;
71 struct fuse_req interrupts;
72 pthread_mutex_t lock;
73 int got_destroy;
74 pthread_key_t pipe_key;
75 int broken_splice_nonblock;
76 uint64_t notify_ctr;
77 struct fuse_notify_req notify_list;
78 _Atomic size_t bufsize;
79 int error;
80
81 /* This is useful if any kind of ABI incompatibility is found at
82 * a later version, to 'fix' it at run time.
83 */
84 struct libfuse_version version;
85
86 /* true if reading requests from /dev/fuse are handled internally */
87 bool buf_reallocable;
88};
89
90struct fuse_chan {
91 pthread_mutex_t lock;
92 int ctr;
93 int fd;
94};
95
104 char *name;
105 fuse_module_factory_t factory;
106 struct fuse_module *next;
107 struct fusemod_so *so;
108 int ctr;
109};
110
119#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
120struct fuse_loop_config
121{
122 /* verififier that a correct struct was was passed. This is especially
123 * needed, as versions below (3, 12) were using a public struct
124 * (now called fuse_loop_config_v1), which was hard to extend with
125 * additional parameters, without risking that file system implementations
126 * would not have noticed and might either pass uninitialized members
127 * or even too small structs.
128 * fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
129 * or 1. v2 or even higher version just need to set a value here
130 * which not conflicting and very unlikely as having been set by
131 * file system implementation.
132 */
133 int version_id;
134
139 int clone_fd;
152
158 unsigned int max_threads;
159};
160#endif
161
162/* ----------------------------------------------------------- *
163 * Channel interface (when using -o clone_fd) *
164 * ----------------------------------------------------------- */
165
172struct fuse_chan *fuse_chan_get(struct fuse_chan *ch);
173
179void fuse_chan_put(struct fuse_chan *ch);
180
181struct mount_opts *parse_mount_opts(struct fuse_args *args);
182void destroy_mount_opts(struct mount_opts *mo);
183void fuse_mount_version(void);
184unsigned get_max_read(struct mount_opts *o);
185void fuse_kern_unmount(const char *mountpoint, int fd);
186int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo);
187
188int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
189 int count);
190void fuse_free_req(fuse_req_t req);
191
192void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
193
194int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
195
196void fuse_buf_free(struct fuse_buf *buf);
197
198int fuse_session_receive_buf_internal(struct fuse_session *se,
199 struct fuse_buf *buf,
200 struct fuse_chan *ch);
201void fuse_session_process_buf_internal(struct fuse_session *se,
202 const struct fuse_buf *buf,
203 struct fuse_chan *ch);
204
205struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
206 size_t op_size, void *private_data);
207int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
208int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
209
215int fuse_loop_cfg_verify(struct fuse_loop_config *config);
216
217
218/*
219 * This can be changed dynamically on recent kernels through the
220 * /proc/sys/fs/fuse/max_pages_limit interface.
221 *
222 * Older kernels will always use the default value.
223 */
224#define FUSE_DEFAULT_MAX_PAGES_LIMIT 256
225#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
226
227/* room needed in buffer to accommodate header */
228#define FUSE_BUFFER_HEADER_SIZE 0x1000
229
233static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn,
234 uint64_t want_ext_default,
235 uint32_t want_default)
236{
237 /*
238 * Convert want to want_ext if necessary.
239 * For the high level interface this function might be called
240 * twice, once from the high level interface and once from the
241 * low level interface. Both, with different want_ext_default and
242 * want_default values. In order to suppress a failure for the
243 * second call, we check if the lower 32 bits of want_ext are
244 * already set to the value of want.
245 */
246 if (conn->want != want_default &&
247 fuse_lower_32_bits(conn->want_ext) != conn->want) {
248 if (conn->want_ext != want_ext_default) {
249 fuse_log(FUSE_LOG_ERR,
250 "fuse: both 'want' and 'want_ext' are set\n");
251 return -EINVAL;
252 }
253
254 /* high bits from want_ext, low bits from want */
255 conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
256 conn->want;
257 }
258
259 /* ensure there won't be a second conversion */
260 conn->want = fuse_lower_32_bits(conn->want_ext);
261
262 return 0;
263}
struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
Definition fuse.h:1383
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition fuse_log.c:77
void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
struct fuse_req * fuse_req_t
uint64_t fuse_ino_t
uint64_t want_ext
unsigned int max_threads
Definition fuse_i.h:158
int max_idle_threads
Definition fuse_i.h:151
fuse-3.17.2/doc/html/fuse__kernel_8h_source.html0000644000175000017500000042320015002273413020531 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
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.17.2/doc/html/fuse__log_8c_source.html0000644000175000017500000004552615002273413020040 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 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}
76
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.17.2/doc/html/fuse__log_8h.html0000644000175000017500000003270015002273413016453 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,...)
 
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.17.2/doc/html/fuse__log_8h_source.html0000644000175000017500000002657415002273413020047 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 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.17.2/doc/html/fuse__loop_8c_source.html0000644000175000017500000002614415002273413020223 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 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.17.2/doc/html/fuse__loop__mt_8c_source.html0000644000175000017500000024220415002273413021057 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 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#ifndef O_CLOEXEC
279 fcntl(clonefd, F_SETFD, FD_CLOEXEC);
280#endif
281
282 masterfd = se->fd;
283 res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
284 if (res == -1) {
285 fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
286 strerror(errno));
287 close(clonefd);
288 return -1;
289 }
290 return clonefd;
291}
292
293static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt)
294{
295 int clonefd;
296 struct fuse_session *se = mt->se;
297 struct fuse_chan *newch;
298
299 if (se->io != NULL) {
300 if (se->io->clone_fd != NULL)
301 clonefd = se->io->clone_fd(se->fd);
302 else
303 return NULL;
304 } else {
305 clonefd = fuse_clone_chan_fd_default(se);
306 }
307 if (clonefd < 0)
308 return NULL;
309
310 newch = fuse_chan_new(clonefd);
311 if (newch == NULL)
312 close(clonefd);
313
314 return newch;
315}
316
317static int fuse_loop_start_thread(struct fuse_mt *mt)
318{
319 int res;
320
321 struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
322 if (!w) {
323 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
324 return -1;
325 }
326 memset(w, 0, sizeof(struct fuse_worker));
327 w->fbuf.mem = NULL;
328 w->mt = mt;
329
330 w->ch = NULL;
331 if (mt->clone_fd) {
332 w->ch = fuse_clone_chan(mt);
333 if(!w->ch) {
334 /* Don't attempt this again */
335 fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
336 "without -o clone_fd.\n");
337 mt->clone_fd = 0;
338 }
339 }
340
341 res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
342 if (res == -1) {
343 fuse_chan_put(w->ch);
344 free(w);
345 return -1;
346 }
347 list_add_worker(w, &mt->main);
348 mt->numavail ++;
349 mt->numworker ++;
350
351 return 0;
352}
353
354static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
355{
356 pthread_join(w->thread_id, NULL);
357 pthread_mutex_lock(&mt->lock);
358 list_del_worker(w);
359 pthread_mutex_unlock(&mt->lock);
360 fuse_buf_free(&w->fbuf);
361 fuse_chan_put(w->ch);
362 free(w);
363}
364
365int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
366FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
367int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
368{
369int err;
370 struct fuse_mt mt;
371 struct fuse_worker *w;
372 int created_config = 0;
373
374 if (config) {
375 err = fuse_loop_cfg_verify(config);
376 if (err)
377 return err;
378 } else {
379 /* The caller does not care about parameters - use the default */
380 config = fuse_loop_cfg_create();
381 created_config = 1;
382 }
383
384
385 memset(&mt, 0, sizeof(struct fuse_mt));
386 mt.se = se;
387 mt.clone_fd = config->clone_fd;
388 mt.error = 0;
389 mt.numworker = 0;
390 mt.numavail = 0;
391 mt.max_idle = config->max_idle_threads;
392 mt.max_threads = config->max_threads;
393 mt.main.thread_id = pthread_self();
394 mt.main.prev = mt.main.next = &mt.main;
395 sem_init(&mt.finish, 0, 0);
396 pthread_mutex_init(&mt.lock, NULL);
397
398 pthread_mutex_lock(&mt.lock);
399 err = fuse_loop_start_thread(&mt);
400 pthread_mutex_unlock(&mt.lock);
401 if (!err) {
402 /* sem_wait() is interruptible */
403 while (!fuse_session_exited(se))
404 sem_wait(&mt.finish);
405
406 pthread_mutex_lock(&mt.lock);
407 for (w = mt.main.next; w != &mt.main; w = w->next)
408 pthread_cancel(w->thread_id);
409 mt.exit = 1;
410 pthread_mutex_unlock(&mt.lock);
411
412 while (mt.main.next != &mt.main)
413 fuse_join_worker(&mt, mt.main.next);
414
415 err = mt.error;
416 }
417
418 pthread_mutex_destroy(&mt.lock);
419 sem_destroy(&mt.finish);
420 if(se->error != 0)
421 err = se->error;
423
424 if (created_config) {
425 fuse_loop_cfg_destroy(config);
426 config = NULL;
427 }
428
429 return err;
430}
431
432int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
433FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
434int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
435{
436 int err;
437 struct fuse_loop_config *config = NULL;
438
439 if (config_v1 != NULL) {
440 /* convert the given v1 config */
441 config = fuse_loop_cfg_create();
442 if (config == NULL)
443 return ENOMEM;
444
445 fuse_loop_cfg_convert(config, config_v1);
446 }
447
448 err = fuse_session_loop_mt_312(se, config);
449
450 fuse_loop_cfg_destroy(config);
451
452 return err;
453}
454
455
456int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
457FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
458int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
459{
460 int err;
461 struct fuse_loop_config *config = fuse_loop_cfg_create();
462 if (clone_fd > 0)
463 fuse_loop_cfg_set_clone_fd(config, clone_fd);
464 err = fuse_session_loop_mt_312(se, config);
465
466 fuse_loop_cfg_destroy(config);
467
468 return err;
469}
470
471struct fuse_loop_config *fuse_loop_cfg_create(void)
472{
473 struct fuse_loop_config *config = calloc(1, sizeof(*config));
474 if (config == NULL)
475 return NULL;
476
477 config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
478 config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
479 config->max_threads = FUSE_LOOP_MT_DEF_MAX_THREADS;
480 config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
481
482 return config;
483}
484
485void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
486{
487 free(config);
488}
489
490int fuse_loop_cfg_verify(struct fuse_loop_config *config)
491{
492 if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
493 return -EINVAL;
494
495 return 0;
496}
497
498void fuse_loop_cfg_convert(struct fuse_loop_config *config,
499 struct fuse_loop_config_v1 *v1_conf)
500{
501 fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
502
503 fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
504}
505
506void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
507 unsigned int value)
508{
509 if (value > FUSE_LOOP_MT_MAX_THREADS) {
510 if (value != UINT_MAX)
511 fuse_log(FUSE_LOG_ERR,
512 "Ignoring invalid max threads value "
513 "%u > max (%u).\n", value,
514 FUSE_LOOP_MT_MAX_THREADS);
515 return;
516 }
517 config->max_idle_threads = value;
518}
519
520void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
521 unsigned int value)
522{
523 config->max_threads = value;
524}
525
526void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
527 unsigned int value)
528{
529 config->clone_fd = value;
530}
531
@ 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:158
unsigned int max_idle_threads
fuse-3.17.2/doc/html/fuse__lowlevel_8c_source.html0000644000175000017500000233447115002273413021112 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 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
47#define container_of(ptr, type, member) ({ \
48 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
49 (type *)( (char *)__mptr - offsetof(type,member) );})
50
51struct fuse_pollhandle {
52 uint64_t kh;
53 struct fuse_session *se;
54};
55
56static size_t pagesize;
57
58static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
59{
60 pagesize = getpagesize();
61}
62
63static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
64{
65 attr->ino = stbuf->st_ino;
66 attr->mode = stbuf->st_mode;
67 attr->nlink = stbuf->st_nlink;
68 attr->uid = stbuf->st_uid;
69 attr->gid = stbuf->st_gid;
70 attr->rdev = stbuf->st_rdev;
71 attr->size = stbuf->st_size;
72 attr->blksize = stbuf->st_blksize;
73 attr->blocks = stbuf->st_blocks;
74 attr->atime = stbuf->st_atime;
75 attr->mtime = stbuf->st_mtime;
76 attr->ctime = stbuf->st_ctime;
77 attr->atimensec = ST_ATIM_NSEC(stbuf);
78 attr->mtimensec = ST_MTIM_NSEC(stbuf);
79 attr->ctimensec = ST_CTIM_NSEC(stbuf);
80}
81
82static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
83{
84 stbuf->st_mode = attr->mode;
85 stbuf->st_uid = attr->uid;
86 stbuf->st_gid = attr->gid;
87 stbuf->st_size = attr->size;
88 stbuf->st_atime = attr->atime;
89 stbuf->st_mtime = attr->mtime;
90 stbuf->st_ctime = attr->ctime;
91 ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
92 ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
93 ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
94}
95
96static size_t iov_length(const struct iovec *iov, size_t count)
97{
98 size_t seg;
99 size_t ret = 0;
100
101 for (seg = 0; seg < count; seg++)
102 ret += iov[seg].iov_len;
103 return ret;
104}
105
106static void list_init_req(struct fuse_req *req)
107{
108 req->next = req;
109 req->prev = req;
110}
111
112static void list_del_req(struct fuse_req *req)
113{
114 struct fuse_req *prev = req->prev;
115 struct fuse_req *next = req->next;
116 prev->next = next;
117 next->prev = prev;
118}
119
120static void list_add_req(struct fuse_req *req, struct fuse_req *next)
121{
122 struct fuse_req *prev = next->prev;
123 req->next = next;
124 req->prev = prev;
125 prev->next = req;
126 next->prev = req;
127}
128
129static void destroy_req(fuse_req_t req)
130{
131 assert(req->ch == NULL);
132 pthread_mutex_destroy(&req->lock);
133 free(req);
134}
135
136void fuse_free_req(fuse_req_t req)
137{
138 int ctr;
139 struct fuse_session *se = req->se;
140
141 if (se->conn.no_interrupt) {
142 ctr = --req->ref_cnt;
143 fuse_chan_put(req->ch);
144 req->ch = NULL;
145 } else {
146 pthread_mutex_lock(&se->lock);
147 req->u.ni.func = NULL;
148 req->u.ni.data = NULL;
149 list_del_req(req);
150 ctr = --req->ref_cnt;
151 fuse_chan_put(req->ch);
152 req->ch = NULL;
153 pthread_mutex_unlock(&se->lock);
154 }
155 if (!ctr)
156 destroy_req(req);
157}
158
159static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
160{
161 struct fuse_req *req;
162
163 req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
164 if (req == NULL) {
165 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
166 } else {
167 req->se = se;
168 req->ref_cnt = 1;
169 list_init_req(req);
170 pthread_mutex_init(&req->lock, NULL);
171 }
172
173 return req;
174}
175
176/* Send data. If *ch* is NULL, send via session master fd */
177static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
178 struct iovec *iov, int count)
179{
180 struct fuse_out_header *out = iov[0].iov_base;
181
182 assert(se != NULL);
183 out->len = iov_length(iov, count);
184 if (se->debug) {
185 if (out->unique == 0) {
186 fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
187 out->error, out->len);
188 } else if (out->error) {
189 fuse_log(FUSE_LOG_DEBUG,
190 " unique: %llu, error: %i (%s), outsize: %i\n",
191 (unsigned long long) out->unique, out->error,
192 strerror(-out->error), out->len);
193 } else {
194 fuse_log(FUSE_LOG_DEBUG,
195 " unique: %llu, success, outsize: %i\n",
196 (unsigned long long) out->unique, out->len);
197 }
198 }
199
200 ssize_t res;
201 if (se->io != NULL)
202 /* se->io->writev is never NULL if se->io is not NULL as
203 specified by fuse_session_custom_io()*/
204 res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
205 se->userdata);
206 else
207 res = writev(ch ? ch->fd : se->fd, iov, count);
208
209 int err = errno;
210
211 if (res == -1) {
212 /* ENOENT means the operation was interrupted */
213 if (!fuse_session_exited(se) && err != ENOENT)
214 perror("fuse: writing device");
215 return -err;
216 }
217
218 return 0;
219}
220
221
222int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
223 int count)
224{
225 struct fuse_out_header out;
226
227#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
228 const char *str = strerrordesc_np(error * -1);
229 if ((str == NULL && error != 0) || error > 0) {
230#else
231 if (error <= -1000 || error > 0) {
232#endif
233 fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
234 error = -ERANGE;
235 }
236
237 out.unique = req->unique;
238 out.error = error;
239
240 iov[0].iov_base = &out;
241 iov[0].iov_len = sizeof(struct fuse_out_header);
242
243 return fuse_send_msg(req->se, req->ch, iov, count);
244}
245
246static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
247 int count)
248{
249 int res;
250
251 res = fuse_send_reply_iov_nofree(req, error, iov, count);
252 fuse_free_req(req);
253 return res;
254}
255
256static int send_reply(fuse_req_t req, int error, const void *arg,
257 size_t argsize)
258{
259 struct iovec iov[2];
260 int count = 1;
261 if (argsize) {
262 iov[1].iov_base = (void *) arg;
263 iov[1].iov_len = argsize;
264 count++;
265 }
266 return send_reply_iov(req, error, iov, count);
267}
268
269int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
270{
271 int res;
272 struct iovec *padded_iov;
273
274 padded_iov = malloc((count + 1) * sizeof(struct iovec));
275 if (padded_iov == NULL)
276 return fuse_reply_err(req, ENOMEM);
277
278 memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
279 count++;
280
281 res = send_reply_iov(req, 0, padded_iov, count);
282 free(padded_iov);
283
284 return res;
285}
286
287
288/* `buf` is allowed to be empty so that the proper size may be
289 allocated by the caller */
290size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
291 const char *name, const struct stat *stbuf, off_t off)
292{
293 (void)req;
294 size_t namelen;
295 size_t entlen;
296 size_t entlen_padded;
297 struct fuse_dirent *dirent;
298
299 namelen = strlen(name);
300 entlen = FUSE_NAME_OFFSET + namelen;
301 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
302
303 if ((buf == NULL) || (entlen_padded > bufsize))
304 return entlen_padded;
305
306 dirent = (struct fuse_dirent*) buf;
307 dirent->ino = stbuf->st_ino;
308 dirent->off = off;
309 dirent->namelen = namelen;
310 dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
311 memcpy(dirent->name, name, namelen);
312 memset(dirent->name + namelen, 0, entlen_padded - entlen);
313
314 return entlen_padded;
315}
316
317static void convert_statfs(const struct statvfs *stbuf,
318 struct fuse_kstatfs *kstatfs)
319{
320 kstatfs->bsize = stbuf->f_bsize;
321 kstatfs->frsize = stbuf->f_frsize;
322 kstatfs->blocks = stbuf->f_blocks;
323 kstatfs->bfree = stbuf->f_bfree;
324 kstatfs->bavail = stbuf->f_bavail;
325 kstatfs->files = stbuf->f_files;
326 kstatfs->ffree = stbuf->f_ffree;
327 kstatfs->namelen = stbuf->f_namemax;
328}
329
330static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
331{
332 return send_reply(req, 0, arg, argsize);
333}
334
335int fuse_reply_err(fuse_req_t req, int err)
336{
337 return send_reply(req, -err, NULL, 0);
338}
339
341{
342 fuse_free_req(req);
343}
344
345static unsigned long calc_timeout_sec(double t)
346{
347 if (t > (double) ULONG_MAX)
348 return ULONG_MAX;
349 else if (t < 0.0)
350 return 0;
351 else
352 return (unsigned long) t;
353}
354
355static unsigned int calc_timeout_nsec(double t)
356{
357 double f = t - (double) calc_timeout_sec(t);
358 if (f < 0.0)
359 return 0;
360 else if (f >= 0.999999999)
361 return 999999999;
362 else
363 return (unsigned int) (f * 1.0e9);
364}
365
366static void fill_entry(struct fuse_entry_out *arg,
367 const struct fuse_entry_param *e)
368{
369 arg->nodeid = e->ino;
370 arg->generation = e->generation;
371 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
372 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
373 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
374 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
375 convert_stat(&e->attr, &arg->attr);
376}
377
378/* `buf` is allowed to be empty so that the proper size may be
379 allocated by the caller */
380size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
381 const char *name,
382 const struct fuse_entry_param *e, off_t off)
383{
384 (void)req;
385 size_t namelen;
386 size_t entlen;
387 size_t entlen_padded;
388
389 namelen = strlen(name);
390 entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
391 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
392 if ((buf == NULL) || (entlen_padded > bufsize))
393 return entlen_padded;
394
395 struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
396 memset(&dp->entry_out, 0, sizeof(dp->entry_out));
397 fill_entry(&dp->entry_out, e);
398
399 struct fuse_dirent *dirent = &dp->dirent;
400 dirent->ino = e->attr.st_ino;
401 dirent->off = off;
402 dirent->namelen = namelen;
403 dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
404 memcpy(dirent->name, name, namelen);
405 memset(dirent->name + namelen, 0, entlen_padded - entlen);
406
407 return entlen_padded;
408}
409
410static void fill_open(struct fuse_open_out *arg,
411 const struct fuse_file_info *f)
412{
413 arg->fh = f->fh;
414 if (f->backing_id > 0) {
415 arg->backing_id = f->backing_id;
416 arg->open_flags |= FOPEN_PASSTHROUGH;
417 }
418 if (f->direct_io)
419 arg->open_flags |= FOPEN_DIRECT_IO;
420 if (f->keep_cache)
421 arg->open_flags |= FOPEN_KEEP_CACHE;
422 if (f->cache_readdir)
423 arg->open_flags |= FOPEN_CACHE_DIR;
424 if (f->nonseekable)
425 arg->open_flags |= FOPEN_NONSEEKABLE;
426 if (f->noflush)
427 arg->open_flags |= FOPEN_NOFLUSH;
429 arg->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
430}
431
433{
434 struct fuse_entry_out arg;
435 size_t size = req->se->conn.proto_minor < 9 ?
436 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
437
438 /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
439 negative entry */
440 if (!e->ino && req->se->conn.proto_minor < 4)
441 return fuse_reply_err(req, ENOENT);
442
443 memset(&arg, 0, sizeof(arg));
444 fill_entry(&arg, e);
445 return send_reply_ok(req, &arg, size);
446}
447
449 const struct fuse_file_info *f)
450{
451 alignas(uint64_t) char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
452 size_t entrysize = req->se->conn.proto_minor < 9 ?
453 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
454 struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
455 struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
456
457 memset(buf, 0, sizeof(buf));
458 fill_entry(earg, e);
459 fill_open(oarg, f);
460 return send_reply_ok(req, buf,
461 entrysize + sizeof(struct fuse_open_out));
462}
463
464int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
465 double attr_timeout)
466{
467 struct fuse_attr_out arg;
468 size_t size = req->se->conn.proto_minor < 9 ?
469 FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
470
471 memset(&arg, 0, sizeof(arg));
472 arg.attr_valid = calc_timeout_sec(attr_timeout);
473 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
474 convert_stat(attr, &arg.attr);
475
476 return send_reply_ok(req, &arg, size);
477}
478
479int fuse_reply_readlink(fuse_req_t req, const char *linkname)
480{
481 return send_reply_ok(req, linkname, strlen(linkname));
482}
483
485{
486 struct fuse_backing_map map = { .fd = fd };
487 int ret;
488
489 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map);
490 if (ret <= 0) {
491 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno));
492 return 0;
493 }
494
495 return ret;
496}
497
498int fuse_passthrough_close(fuse_req_t req, int backing_id)
499{
500 int ret;
501
502 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id);
503 if (ret < 0)
504 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno));
505
506 return ret;
507}
508
510{
511 struct fuse_open_out arg;
512
513 memset(&arg, 0, sizeof(arg));
514 fill_open(&arg, f);
515 return send_reply_ok(req, &arg, sizeof(arg));
516}
517
518int fuse_reply_write(fuse_req_t req, size_t count)
519{
520 struct fuse_write_out arg;
521
522 memset(&arg, 0, sizeof(arg));
523 arg.size = count;
524
525 return send_reply_ok(req, &arg, sizeof(arg));
526}
527
528int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
529{
530 return send_reply_ok(req, buf, size);
531}
532
533static int fuse_send_data_iov_fallback(struct fuse_session *se,
534 struct fuse_chan *ch,
535 struct iovec *iov, int iov_count,
536 struct fuse_bufvec *buf,
537 size_t len)
538{
539 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
540 void *mbuf;
541 int res;
542
543 /* Optimize common case */
544 if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
545 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
546 /* FIXME: also avoid memory copy if there are multiple buffers
547 but none of them contain an fd */
548
549 iov[iov_count].iov_base = buf->buf[0].mem;
550 iov[iov_count].iov_len = len;
551 iov_count++;
552 return fuse_send_msg(se, ch, iov, iov_count);
553 }
554
555 res = posix_memalign(&mbuf, pagesize, len);
556 if (res != 0)
557 return res;
558
559 mem_buf.buf[0].mem = mbuf;
560 res = fuse_buf_copy(&mem_buf, buf, 0);
561 if (res < 0) {
562 free(mbuf);
563 return -res;
564 }
565 len = res;
566
567 iov[iov_count].iov_base = mbuf;
568 iov[iov_count].iov_len = len;
569 iov_count++;
570 res = fuse_send_msg(se, ch, iov, iov_count);
571 free(mbuf);
572
573 return res;
574}
575
576struct fuse_ll_pipe {
577 size_t size;
578 int can_grow;
579 int pipe[2];
580};
581
582static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
583{
584 close(llp->pipe[0]);
585 close(llp->pipe[1]);
586 free(llp);
587}
588
589#ifdef HAVE_SPLICE
590#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC)
591static int fuse_pipe(int fds[2])
592{
593 int rv = pipe(fds);
594
595 if (rv == -1)
596 return rv;
597
598 if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
599 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 ||
600 fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
601 fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
602 close(fds[0]);
603 close(fds[1]);
604 rv = -1;
605 }
606 return rv;
607}
608#else
609static int fuse_pipe(int fds[2])
610{
611 return pipe2(fds, O_CLOEXEC | O_NONBLOCK);
612}
613#endif
614
615static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
616{
617 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
618 if (llp == NULL) {
619 int res;
620
621 llp = malloc(sizeof(struct fuse_ll_pipe));
622 if (llp == NULL)
623 return NULL;
624
625 res = fuse_pipe(llp->pipe);
626 if (res == -1) {
627 free(llp);
628 return NULL;
629 }
630
631 /*
632 *the default size is 16 pages on linux
633 */
634 llp->size = pagesize * 16;
635 llp->can_grow = 1;
636
637 pthread_setspecific(se->pipe_key, llp);
638 }
639
640 return llp;
641}
642#endif
643
644static void fuse_ll_clear_pipe(struct fuse_session *se)
645{
646 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
647 if (llp) {
648 pthread_setspecific(se->pipe_key, NULL);
649 fuse_ll_pipe_free(llp);
650 }
651}
652
653#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
654static int read_back(int fd, char *buf, size_t len)
655{
656 int res;
657
658 res = read(fd, buf, len);
659 if (res == -1) {
660 fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
661 return -EIO;
662 }
663 if (res != len) {
664 fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
665 return -EIO;
666 }
667 return 0;
668}
669
670static int grow_pipe_to_max(int pipefd)
671{
672 int res;
673 long max;
674 long maxfd;
675 char buf[32];
676
677 maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY);
678 if (maxfd < 0)
679 return -errno;
680
681 res = read(maxfd, buf, sizeof(buf) - 1);
682 if (res < 0) {
683 int saved_errno;
684
685 saved_errno = errno;
686 close(maxfd);
687 return -saved_errno;
688 }
689 close(maxfd);
690 buf[res] = '\0';
691
692 res = libfuse_strtol(buf, &max);
693 if (res)
694 return res;
695 res = fcntl(pipefd, F_SETPIPE_SZ, max);
696 if (res < 0)
697 return -errno;
698 return max;
699}
700
701static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
702 struct iovec *iov, int iov_count,
703 struct fuse_bufvec *buf, unsigned int flags)
704{
705 int res;
706 size_t len = fuse_buf_size(buf);
707 struct fuse_out_header *out = iov[0].iov_base;
708 struct fuse_ll_pipe *llp;
709 int splice_flags;
710 size_t pipesize;
711 size_t total_buf_size;
712 size_t idx;
713 size_t headerlen;
714 struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
715
716 if (se->broken_splice_nonblock)
717 goto fallback;
718
719 if (flags & FUSE_BUF_NO_SPLICE)
720 goto fallback;
721
722 total_buf_size = 0;
723 for (idx = buf->idx; idx < buf->count; idx++) {
724 total_buf_size += buf->buf[idx].size;
725 if (idx == buf->idx)
726 total_buf_size -= buf->off;
727 }
728 if (total_buf_size < 2 * pagesize)
729 goto fallback;
730
731 if (se->conn.proto_minor < 14 ||
732 !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE))
733 goto fallback;
734
735 llp = fuse_ll_get_pipe(se);
736 if (llp == NULL)
737 goto fallback;
738
739
740 headerlen = iov_length(iov, iov_count);
741
742 out->len = headerlen + len;
743
744 /*
745 * Heuristic for the required pipe size, does not work if the
746 * source contains less than page size fragments
747 */
748 pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
749
750 if (llp->size < pipesize) {
751 if (llp->can_grow) {
752 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
753 if (res == -1) {
754 res = grow_pipe_to_max(llp->pipe[0]);
755 if (res > 0)
756 llp->size = res;
757 llp->can_grow = 0;
758 goto fallback;
759 }
760 llp->size = res;
761 }
762 if (llp->size < pipesize)
763 goto fallback;
764 }
765
766
767 res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
768 if (res == -1)
769 goto fallback;
770
771 if (res != headerlen) {
772 res = -EIO;
773 fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
774 headerlen);
775 goto clear_pipe;
776 }
777
778 pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
779 pipe_buf.buf[0].fd = llp->pipe[1];
780
781 res = fuse_buf_copy(&pipe_buf, buf,
783 if (res < 0) {
784 if (res == -EAGAIN || res == -EINVAL) {
785 /*
786 * Should only get EAGAIN on kernels with
787 * broken SPLICE_F_NONBLOCK support (<=
788 * 2.6.35) where this error or a short read is
789 * returned even if the pipe itself is not
790 * full
791 *
792 * EINVAL might mean that splice can't handle
793 * this combination of input and output.
794 */
795 if (res == -EAGAIN)
796 se->broken_splice_nonblock = 1;
797
798 pthread_setspecific(se->pipe_key, NULL);
799 fuse_ll_pipe_free(llp);
800 goto fallback;
801 }
802 res = -res;
803 goto clear_pipe;
804 }
805
806 if (res != 0 && res < len) {
807 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
808 void *mbuf;
809 size_t now_len = res;
810 /*
811 * For regular files a short count is either
812 * 1) due to EOF, or
813 * 2) because of broken SPLICE_F_NONBLOCK (see above)
814 *
815 * For other inputs it's possible that we overflowed
816 * the pipe because of small buffer fragments.
817 */
818
819 res = posix_memalign(&mbuf, pagesize, len);
820 if (res != 0)
821 goto clear_pipe;
822
823 mem_buf.buf[0].mem = mbuf;
824 mem_buf.off = now_len;
825 res = fuse_buf_copy(&mem_buf, buf, 0);
826 if (res > 0) {
827 char *tmpbuf;
828 size_t extra_len = res;
829 /*
830 * Trickiest case: got more data. Need to get
831 * back the data from the pipe and then fall
832 * back to regular write.
833 */
834 tmpbuf = malloc(headerlen);
835 if (tmpbuf == NULL) {
836 free(mbuf);
837 res = ENOMEM;
838 goto clear_pipe;
839 }
840 res = read_back(llp->pipe[0], tmpbuf, headerlen);
841 free(tmpbuf);
842 if (res != 0) {
843 free(mbuf);
844 goto clear_pipe;
845 }
846 res = read_back(llp->pipe[0], mbuf, now_len);
847 if (res != 0) {
848 free(mbuf);
849 goto clear_pipe;
850 }
851 len = now_len + extra_len;
852 iov[iov_count].iov_base = mbuf;
853 iov[iov_count].iov_len = len;
854 iov_count++;
855 res = fuse_send_msg(se, ch, iov, iov_count);
856 free(mbuf);
857 return res;
858 }
859 free(mbuf);
860 res = now_len;
861 }
862 len = res;
863 out->len = headerlen + len;
864
865 if (se->debug) {
866 fuse_log(FUSE_LOG_DEBUG,
867 " unique: %llu, success, outsize: %i (splice)\n",
868 (unsigned long long) out->unique, out->len);
869 }
870
871 splice_flags = 0;
872 if ((flags & FUSE_BUF_SPLICE_MOVE) &&
873 (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE))
874 splice_flags |= SPLICE_F_MOVE;
875
876 if (se->io != NULL && se->io->splice_send != NULL) {
877 res = se->io->splice_send(llp->pipe[0], NULL,
878 ch ? ch->fd : se->fd, NULL, out->len,
879 splice_flags, se->userdata);
880 } else {
881 res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
882 out->len, splice_flags);
883 }
884 if (res == -1) {
885 res = -errno;
886 perror("fuse: splice from pipe");
887 goto clear_pipe;
888 }
889 if (res != out->len) {
890 res = -EIO;
891 fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
892 res, out->len);
893 goto clear_pipe;
894 }
895 return 0;
896
897clear_pipe:
898 fuse_ll_clear_pipe(se);
899 return res;
900
901fallback:
902 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
903}
904#else
905static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
906 struct iovec *iov, int iov_count,
907 struct fuse_bufvec *buf, unsigned int flags)
908{
909 size_t len = fuse_buf_size(buf);
910 (void) flags;
911
912 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
913}
914#endif
915
917 enum fuse_buf_copy_flags flags)
918{
919 struct iovec iov[2];
920 struct fuse_out_header out;
921 int res;
922
923 iov[0].iov_base = &out;
924 iov[0].iov_len = sizeof(struct fuse_out_header);
925
926 out.unique = req->unique;
927 out.error = 0;
928
929 res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags);
930 if (res <= 0) {
931 fuse_free_req(req);
932 return res;
933 } else {
934 return fuse_reply_err(req, res);
935 }
936}
937
938int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
939{
940 struct fuse_statfs_out arg;
941 size_t size = req->se->conn.proto_minor < 4 ?
942 FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
943
944 memset(&arg, 0, sizeof(arg));
945 convert_statfs(stbuf, &arg.st);
946
947 return send_reply_ok(req, &arg, size);
948}
949
950int fuse_reply_xattr(fuse_req_t req, size_t count)
951{
952 struct fuse_getxattr_out arg;
953
954 memset(&arg, 0, sizeof(arg));
955 arg.size = count;
956
957 return send_reply_ok(req, &arg, sizeof(arg));
958}
959
960int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
961{
962 struct fuse_lk_out arg;
963
964 memset(&arg, 0, sizeof(arg));
965 arg.lk.type = lock->l_type;
966 if (lock->l_type != F_UNLCK) {
967 arg.lk.start = lock->l_start;
968 if (lock->l_len == 0)
969 arg.lk.end = OFFSET_MAX;
970 else
971 arg.lk.end = lock->l_start + lock->l_len - 1;
972 }
973 arg.lk.pid = lock->l_pid;
974 return send_reply_ok(req, &arg, sizeof(arg));
975}
976
977int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
978{
979 struct fuse_bmap_out arg;
980
981 memset(&arg, 0, sizeof(arg));
982 arg.block = idx;
983
984 return send_reply_ok(req, &arg, sizeof(arg));
985}
986
987static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
988 size_t count)
989{
990 struct fuse_ioctl_iovec *fiov;
991 size_t i;
992
993 fiov = malloc(sizeof(fiov[0]) * count);
994 if (!fiov)
995 return NULL;
996
997 for (i = 0; i < count; i++) {
998 fiov[i].base = (uintptr_t) iov[i].iov_base;
999 fiov[i].len = iov[i].iov_len;
1000 }
1001
1002 return fiov;
1003}
1004
1006 const struct iovec *in_iov, size_t in_count,
1007 const struct iovec *out_iov, size_t out_count)
1008{
1009 struct fuse_ioctl_out arg;
1010 struct fuse_ioctl_iovec *in_fiov = NULL;
1011 struct fuse_ioctl_iovec *out_fiov = NULL;
1012 struct iovec iov[4];
1013 size_t count = 1;
1014 int res;
1015
1016 memset(&arg, 0, sizeof(arg));
1017 arg.flags |= FUSE_IOCTL_RETRY;
1018 arg.in_iovs = in_count;
1019 arg.out_iovs = out_count;
1020 iov[count].iov_base = &arg;
1021 iov[count].iov_len = sizeof(arg);
1022 count++;
1023
1024 if (req->se->conn.proto_minor < 16) {
1025 if (in_count) {
1026 iov[count].iov_base = (void *)in_iov;
1027 iov[count].iov_len = sizeof(in_iov[0]) * in_count;
1028 count++;
1029 }
1030
1031 if (out_count) {
1032 iov[count].iov_base = (void *)out_iov;
1033 iov[count].iov_len = sizeof(out_iov[0]) * out_count;
1034 count++;
1035 }
1036 } else {
1037 /* Can't handle non-compat 64bit ioctls on 32bit */
1038 if (sizeof(void *) == 4 && req->ioctl_64bit) {
1039 res = fuse_reply_err(req, EINVAL);
1040 goto out;
1041 }
1042
1043 if (in_count) {
1044 in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
1045 if (!in_fiov)
1046 goto enomem;
1047
1048 iov[count].iov_base = (void *)in_fiov;
1049 iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
1050 count++;
1051 }
1052 if (out_count) {
1053 out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
1054 if (!out_fiov)
1055 goto enomem;
1056
1057 iov[count].iov_base = (void *)out_fiov;
1058 iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
1059 count++;
1060 }
1061 }
1062
1063 res = send_reply_iov(req, 0, iov, count);
1064out:
1065 free(in_fiov);
1066 free(out_fiov);
1067
1068 return res;
1069
1070enomem:
1071 res = fuse_reply_err(req, ENOMEM);
1072 goto out;
1073}
1074
1075int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
1076{
1077 struct fuse_ioctl_out arg;
1078 struct iovec iov[3];
1079 size_t count = 1;
1080
1081 memset(&arg, 0, sizeof(arg));
1082 arg.result = result;
1083 iov[count].iov_base = &arg;
1084 iov[count].iov_len = sizeof(arg);
1085 count++;
1086
1087 if (size) {
1088 iov[count].iov_base = (char *) buf;
1089 iov[count].iov_len = size;
1090 count++;
1091 }
1092
1093 return send_reply_iov(req, 0, iov, count);
1094}
1095
1096int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
1097 int count)
1098{
1099 struct iovec *padded_iov;
1100 struct fuse_ioctl_out arg;
1101 int res;
1102
1103 padded_iov = malloc((count + 2) * sizeof(struct iovec));
1104 if (padded_iov == NULL)
1105 return fuse_reply_err(req, ENOMEM);
1106
1107 memset(&arg, 0, sizeof(arg));
1108 arg.result = result;
1109 padded_iov[1].iov_base = &arg;
1110 padded_iov[1].iov_len = sizeof(arg);
1111
1112 memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
1113
1114 res = send_reply_iov(req, 0, padded_iov, count + 2);
1115 free(padded_iov);
1116
1117 return res;
1118}
1119
1120int fuse_reply_poll(fuse_req_t req, unsigned revents)
1121{
1122 struct fuse_poll_out arg;
1123
1124 memset(&arg, 0, sizeof(arg));
1125 arg.revents = revents;
1126
1127 return send_reply_ok(req, &arg, sizeof(arg));
1128}
1129
1130int fuse_reply_lseek(fuse_req_t req, off_t off)
1131{
1132 struct fuse_lseek_out arg;
1133
1134 memset(&arg, 0, sizeof(arg));
1135 arg.offset = off;
1136
1137 return send_reply_ok(req, &arg, sizeof(arg));
1138}
1139
1140static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1141{
1142 char *name = (char *) inarg;
1143
1144 if (req->se->op.lookup)
1145 req->se->op.lookup(req, nodeid, name);
1146 else
1147 fuse_reply_err(req, ENOSYS);
1148}
1149
1150static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1151{
1152 struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
1153
1154 if (req->se->op.forget)
1155 req->se->op.forget(req, nodeid, arg->nlookup);
1156 else
1157 fuse_reply_none(req);
1158}
1159
1160static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
1161 const void *inarg)
1162{
1163 struct fuse_batch_forget_in *arg = (void *) inarg;
1164 struct fuse_forget_one *param = (void *) PARAM(arg);
1165 unsigned int i;
1166
1167 (void) nodeid;
1168
1169 if (req->se->op.forget_multi) {
1170 req->se->op.forget_multi(req, arg->count,
1171 (struct fuse_forget_data *) param);
1172 } else if (req->se->op.forget) {
1173 for (i = 0; i < arg->count; i++) {
1174 struct fuse_forget_one *forget = &param[i];
1175 struct fuse_req *dummy_req;
1176
1177 dummy_req = fuse_ll_alloc_req(req->se);
1178 if (dummy_req == NULL)
1179 break;
1180
1181 dummy_req->unique = req->unique;
1182 dummy_req->ctx = req->ctx;
1183 dummy_req->ch = NULL;
1184
1185 req->se->op.forget(dummy_req, forget->nodeid,
1186 forget->nlookup);
1187 }
1188 fuse_reply_none(req);
1189 } else {
1190 fuse_reply_none(req);
1191 }
1192}
1193
1194static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1195{
1196 struct fuse_file_info *fip = NULL;
1197 struct fuse_file_info fi;
1198
1199 if (req->se->conn.proto_minor >= 9) {
1200 struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
1201
1202 if (arg->getattr_flags & FUSE_GETATTR_FH) {
1203 memset(&fi, 0, sizeof(fi));
1204 fi.fh = arg->fh;
1205 fip = &fi;
1206 }
1207 }
1208
1209 if (req->se->op.getattr)
1210 req->se->op.getattr(req, nodeid, fip);
1211 else
1212 fuse_reply_err(req, ENOSYS);
1213}
1214
1215static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1216{
1217 struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
1218
1219 if (req->se->op.setattr) {
1220 struct fuse_file_info *fi = NULL;
1221 struct fuse_file_info fi_store;
1222 struct stat stbuf;
1223 memset(&stbuf, 0, sizeof(stbuf));
1224 convert_attr(arg, &stbuf);
1225 if (arg->valid & FATTR_FH) {
1226 arg->valid &= ~FATTR_FH;
1227 memset(&fi_store, 0, sizeof(fi_store));
1228 fi = &fi_store;
1229 fi->fh = arg->fh;
1230 }
1231 arg->valid &=
1232 FUSE_SET_ATTR_MODE |
1233 FUSE_SET_ATTR_UID |
1234 FUSE_SET_ATTR_GID |
1235 FUSE_SET_ATTR_SIZE |
1236 FUSE_SET_ATTR_ATIME |
1237 FUSE_SET_ATTR_MTIME |
1238 FUSE_SET_ATTR_KILL_SUID |
1239 FUSE_SET_ATTR_KILL_SGID |
1240 FUSE_SET_ATTR_ATIME_NOW |
1241 FUSE_SET_ATTR_MTIME_NOW |
1242 FUSE_SET_ATTR_CTIME;
1243
1244 req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
1245 } else
1246 fuse_reply_err(req, ENOSYS);
1247}
1248
1249static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1250{
1251 struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
1252
1253 if (req->se->op.access)
1254 req->se->op.access(req, nodeid, arg->mask);
1255 else
1256 fuse_reply_err(req, ENOSYS);
1257}
1258
1259static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1260{
1261 (void) inarg;
1262
1263 if (req->se->op.readlink)
1264 req->se->op.readlink(req, nodeid);
1265 else
1266 fuse_reply_err(req, ENOSYS);
1267}
1268
1269static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1270{
1271 struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
1272 char *name = PARAM(arg);
1273
1274 if (req->se->conn.proto_minor >= 12)
1275 req->ctx.umask = arg->umask;
1276 else
1277 name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
1278
1279 if (req->se->op.mknod)
1280 req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
1281 else
1282 fuse_reply_err(req, ENOSYS);
1283}
1284
1285static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1286{
1287 struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
1288
1289 if (req->se->conn.proto_minor >= 12)
1290 req->ctx.umask = arg->umask;
1291
1292 if (req->se->op.mkdir)
1293 req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
1294 else
1295 fuse_reply_err(req, ENOSYS);
1296}
1297
1298static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1299{
1300 char *name = (char *) inarg;
1301
1302 if (req->se->op.unlink)
1303 req->se->op.unlink(req, nodeid, name);
1304 else
1305 fuse_reply_err(req, ENOSYS);
1306}
1307
1308static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1309{
1310 char *name = (char *) inarg;
1311
1312 if (req->se->op.rmdir)
1313 req->se->op.rmdir(req, nodeid, name);
1314 else
1315 fuse_reply_err(req, ENOSYS);
1316}
1317
1318static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1319{
1320 char *name = (char *) inarg;
1321 char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
1322
1323 if (req->se->op.symlink)
1324 req->se->op.symlink(req, linkname, nodeid, name);
1325 else
1326 fuse_reply_err(req, ENOSYS);
1327}
1328
1329static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1330{
1331 struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
1332 char *oldname = PARAM(arg);
1333 char *newname = oldname + strlen(oldname) + 1;
1334
1335 if (req->se->op.rename)
1336 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
1337 0);
1338 else
1339 fuse_reply_err(req, ENOSYS);
1340}
1341
1342static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1343{
1344 struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg;
1345 char *oldname = PARAM(arg);
1346 char *newname = oldname + strlen(oldname) + 1;
1347
1348 if (req->se->op.rename)
1349 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
1350 arg->flags);
1351 else
1352 fuse_reply_err(req, ENOSYS);
1353}
1354
1355static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1356{
1357 struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
1358
1359 if (req->se->op.link)
1360 req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
1361 else
1362 fuse_reply_err(req, ENOSYS);
1363}
1364
1365static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1366{
1367 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
1368
1369 if (req->se->op.tmpfile) {
1370 struct fuse_file_info fi;
1371
1372 memset(&fi, 0, sizeof(fi));
1373 fi.flags = arg->flags;
1374
1375 if (req->se->conn.proto_minor >= 12)
1376 req->ctx.umask = arg->umask;
1377
1378 req->se->op.tmpfile(req, nodeid, arg->mode, &fi);
1379 } else
1380 fuse_reply_err(req, ENOSYS);
1381}
1382
1383static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1384{
1385 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
1386
1387 if (req->se->op.create) {
1388 struct fuse_file_info fi;
1389 char *name = PARAM(arg);
1390
1391 memset(&fi, 0, sizeof(fi));
1392 fi.flags = arg->flags;
1393
1394 if (req->se->conn.proto_minor >= 12)
1395 req->ctx.umask = arg->umask;
1396 else
1397 name = (char *) inarg + sizeof(struct fuse_open_in);
1398
1399 req->se->op.create(req, nodeid, name, arg->mode, &fi);
1400 } else
1401 fuse_reply_err(req, ENOSYS);
1402}
1403
1404static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1405{
1406 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
1407 struct fuse_file_info fi;
1408
1409 memset(&fi, 0, sizeof(fi));
1410 fi.flags = arg->flags;
1411
1412 if (req->se->op.open)
1413 req->se->op.open(req, nodeid, &fi);
1414 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT)
1415 fuse_reply_err(req, ENOSYS);
1416 else
1417 fuse_reply_open(req, &fi);
1418}
1419
1420static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1421{
1422 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
1423
1424 if (req->se->op.read) {
1425 struct fuse_file_info fi;
1426
1427 memset(&fi, 0, sizeof(fi));
1428 fi.fh = arg->fh;
1429 if (req->se->conn.proto_minor >= 9) {
1430 fi.lock_owner = arg->lock_owner;
1431 fi.flags = arg->flags;
1432 }
1433 req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
1434 } else
1435 fuse_reply_err(req, ENOSYS);
1436}
1437
1438static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1439{
1440 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
1441 struct fuse_file_info fi;
1442 char *param;
1443
1444 memset(&fi, 0, sizeof(fi));
1445 fi.fh = arg->fh;
1446 fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
1447
1448 if (req->se->conn.proto_minor < 9) {
1449 param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
1450 } else {
1451 fi.lock_owner = arg->lock_owner;
1452 fi.flags = arg->flags;
1453 param = PARAM(arg);
1454 }
1455
1456 if (req->se->op.write)
1457 req->se->op.write(req, nodeid, param, arg->size,
1458 arg->offset, &fi);
1459 else
1460 fuse_reply_err(req, ENOSYS);
1461}
1462
1463static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
1464 const struct fuse_buf *ibuf)
1465{
1466 struct fuse_session *se = req->se;
1467 struct fuse_bufvec bufv = {
1468 .buf[0] = *ibuf,
1469 .count = 1,
1470 };
1471 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
1472 struct fuse_file_info fi;
1473
1474 memset(&fi, 0, sizeof(fi));
1475 fi.fh = arg->fh;
1476 fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
1477
1478 if (se->conn.proto_minor < 9) {
1479 bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
1480 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
1481 FUSE_COMPAT_WRITE_IN_SIZE;
1482 assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
1483 } else {
1484 fi.lock_owner = arg->lock_owner;
1485 fi.flags = arg->flags;
1486 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
1487 bufv.buf[0].mem = PARAM(arg);
1488
1489 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
1490 sizeof(struct fuse_write_in);
1491 }
1492 if (bufv.buf[0].size < arg->size) {
1493 fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n");
1494 fuse_reply_err(req, EIO);
1495 goto out;
1496 }
1497 bufv.buf[0].size = arg->size;
1498
1499 se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
1500
1501out:
1502 /* Need to reset the pipe if ->write_buf() didn't consume all data */
1503 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
1504 fuse_ll_clear_pipe(se);
1505}
1506
1507static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1508{
1509 struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
1510 struct fuse_file_info fi;
1511
1512 memset(&fi, 0, sizeof(fi));
1513 fi.fh = arg->fh;
1514 fi.flush = 1;
1515 if (req->se->conn.proto_minor >= 7)
1516 fi.lock_owner = arg->lock_owner;
1517
1518 if (req->se->op.flush)
1519 req->se->op.flush(req, nodeid, &fi);
1520 else
1521 fuse_reply_err(req, ENOSYS);
1522}
1523
1524static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1525{
1526 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
1527 struct fuse_file_info fi;
1528
1529 memset(&fi, 0, sizeof(fi));
1530 fi.flags = arg->flags;
1531 fi.fh = arg->fh;
1532 if (req->se->conn.proto_minor >= 8) {
1533 fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
1534 fi.lock_owner = arg->lock_owner;
1535 }
1536 if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
1537 fi.flock_release = 1;
1538 fi.lock_owner = arg->lock_owner;
1539 }
1540
1541 if (req->se->op.release)
1542 req->se->op.release(req, nodeid, &fi);
1543 else
1544 fuse_reply_err(req, 0);
1545}
1546
1547static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1548{
1549 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
1550 struct fuse_file_info fi;
1551 int datasync = arg->fsync_flags & 1;
1552
1553 memset(&fi, 0, sizeof(fi));
1554 fi.fh = arg->fh;
1555
1556 if (req->se->op.fsync)
1557 req->se->op.fsync(req, nodeid, datasync, &fi);
1558 else
1559 fuse_reply_err(req, ENOSYS);
1560}
1561
1562static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1563{
1564 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
1565 struct fuse_file_info fi;
1566
1567 memset(&fi, 0, sizeof(fi));
1568 fi.flags = arg->flags;
1569
1570 if (req->se->op.opendir)
1571 req->se->op.opendir(req, nodeid, &fi);
1572 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT)
1573 fuse_reply_err(req, ENOSYS);
1574 else
1575 fuse_reply_open(req, &fi);
1576}
1577
1578static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1579{
1580 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
1581 struct fuse_file_info fi;
1582
1583 memset(&fi, 0, sizeof(fi));
1584 fi.fh = arg->fh;
1585
1586 if (req->se->op.readdir)
1587 req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
1588 else
1589 fuse_reply_err(req, ENOSYS);
1590}
1591
1592static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1593{
1594 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
1595 struct fuse_file_info fi;
1596
1597 memset(&fi, 0, sizeof(fi));
1598 fi.fh = arg->fh;
1599
1600 if (req->se->op.readdirplus)
1601 req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
1602 else
1603 fuse_reply_err(req, ENOSYS);
1604}
1605
1606static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1607{
1608 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
1609 struct fuse_file_info fi;
1610
1611 memset(&fi, 0, sizeof(fi));
1612 fi.flags = arg->flags;
1613 fi.fh = arg->fh;
1614
1615 if (req->se->op.releasedir)
1616 req->se->op.releasedir(req, nodeid, &fi);
1617 else
1618 fuse_reply_err(req, 0);
1619}
1620
1621static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1622{
1623 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
1624 struct fuse_file_info fi;
1625 int datasync = arg->fsync_flags & 1;
1626
1627 memset(&fi, 0, sizeof(fi));
1628 fi.fh = arg->fh;
1629
1630 if (req->se->op.fsyncdir)
1631 req->se->op.fsyncdir(req, nodeid, datasync, &fi);
1632 else
1633 fuse_reply_err(req, ENOSYS);
1634}
1635
1636static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1637{
1638 (void) nodeid;
1639 (void) inarg;
1640
1641 if (req->se->op.statfs)
1642 req->se->op.statfs(req, nodeid);
1643 else {
1644 struct statvfs buf = {
1645 .f_namemax = 255,
1646 .f_bsize = 512,
1647 };
1648 fuse_reply_statfs(req, &buf);
1649 }
1650}
1651
1652static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1653{
1654 struct fuse_session *se = req->se;
1655 unsigned int xattr_ext = !!(se->conn.want_ext & FUSE_CAP_SETXATTR_EXT);
1656 struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
1657 char *name = xattr_ext ? PARAM(arg) :
1658 (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
1659 char *value = name + strlen(name) + 1;
1660
1661 /* XXX:The API should be extended to support extra_flags/setxattr_flags */
1662 if (req->se->op.setxattr)
1663 req->se->op.setxattr(req, nodeid, name, value, arg->size,
1664 arg->flags);
1665 else
1666 fuse_reply_err(req, ENOSYS);
1667}
1668
1669static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1670{
1671 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
1672
1673 if (req->se->op.getxattr)
1674 req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size);
1675 else
1676 fuse_reply_err(req, ENOSYS);
1677}
1678
1679static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1680{
1681 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
1682
1683 if (req->se->op.listxattr)
1684 req->se->op.listxattr(req, nodeid, arg->size);
1685 else
1686 fuse_reply_err(req, ENOSYS);
1687}
1688
1689static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1690{
1691 char *name = (char *) inarg;
1692
1693 if (req->se->op.removexattr)
1694 req->se->op.removexattr(req, nodeid, name);
1695 else
1696 fuse_reply_err(req, ENOSYS);
1697}
1698
1699static void convert_fuse_file_lock(struct fuse_file_lock *fl,
1700 struct flock *flock)
1701{
1702 memset(flock, 0, sizeof(struct flock));
1703 flock->l_type = fl->type;
1704 flock->l_whence = SEEK_SET;
1705 flock->l_start = fl->start;
1706 if (fl->end == OFFSET_MAX)
1707 flock->l_len = 0;
1708 else
1709 flock->l_len = fl->end - fl->start + 1;
1710 flock->l_pid = fl->pid;
1711}
1712
1713static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1714{
1715 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
1716 struct fuse_file_info fi;
1717 struct flock flock;
1718
1719 memset(&fi, 0, sizeof(fi));
1720 fi.fh = arg->fh;
1721 fi.lock_owner = arg->owner;
1722
1723 convert_fuse_file_lock(&arg->lk, &flock);
1724 if (req->se->op.getlk)
1725 req->se->op.getlk(req, nodeid, &fi, &flock);
1726 else
1727 fuse_reply_err(req, ENOSYS);
1728}
1729
1730static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
1731 const void *inarg, int sleep)
1732{
1733 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
1734 struct fuse_file_info fi;
1735 struct flock flock;
1736
1737 memset(&fi, 0, sizeof(fi));
1738 fi.fh = arg->fh;
1739 fi.lock_owner = arg->owner;
1740
1741 if (arg->lk_flags & FUSE_LK_FLOCK) {
1742 int op = 0;
1743
1744 switch (arg->lk.type) {
1745 case F_RDLCK:
1746 op = LOCK_SH;
1747 break;
1748 case F_WRLCK:
1749 op = LOCK_EX;
1750 break;
1751 case F_UNLCK:
1752 op = LOCK_UN;
1753 break;
1754 }
1755 if (!sleep)
1756 op |= LOCK_NB;
1757
1758 if (req->se->op.flock)
1759 req->se->op.flock(req, nodeid, &fi, op);
1760 else
1761 fuse_reply_err(req, ENOSYS);
1762 } else {
1763 convert_fuse_file_lock(&arg->lk, &flock);
1764 if (req->se->op.setlk)
1765 req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
1766 else
1767 fuse_reply_err(req, ENOSYS);
1768 }
1769}
1770
1771static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1772{
1773 do_setlk_common(req, nodeid, inarg, 0);
1774}
1775
1776static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1777{
1778 do_setlk_common(req, nodeid, inarg, 1);
1779}
1780
1781static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
1782{
1783 struct fuse_req *curr;
1784
1785 for (curr = se->list.next; curr != &se->list; curr = curr->next) {
1786 if (curr->unique == req->u.i.unique) {
1788 void *data;
1789
1790 curr->ref_cnt++;
1791 pthread_mutex_unlock(&se->lock);
1792
1793 /* Ugh, ugly locking */
1794 pthread_mutex_lock(&curr->lock);
1795 pthread_mutex_lock(&se->lock);
1796 curr->interrupted = 1;
1797 func = curr->u.ni.func;
1798 data = curr->u.ni.data;
1799 pthread_mutex_unlock(&se->lock);
1800 if (func)
1801 func(curr, data);
1802 pthread_mutex_unlock(&curr->lock);
1803
1804 pthread_mutex_lock(&se->lock);
1805 curr->ref_cnt--;
1806 if (!curr->ref_cnt) {
1807 destroy_req(curr);
1808 }
1809
1810 return 1;
1811 }
1812 }
1813 for (curr = se->interrupts.next; curr != &se->interrupts;
1814 curr = curr->next) {
1815 if (curr->u.i.unique == req->u.i.unique)
1816 return 1;
1817 }
1818 return 0;
1819}
1820
1821static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1822{
1823 struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
1824 struct fuse_session *se = req->se;
1825
1826 (void) nodeid;
1827 if (se->debug)
1828 fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
1829 (unsigned long long) arg->unique);
1830
1831 req->u.i.unique = arg->unique;
1832
1833 pthread_mutex_lock(&se->lock);
1834 if (find_interrupted(se, req)) {
1835 fuse_chan_put(req->ch);
1836 req->ch = NULL;
1837 destroy_req(req);
1838 } else
1839 list_add_req(req, &se->interrupts);
1840 pthread_mutex_unlock(&se->lock);
1841}
1842
1843static struct fuse_req *check_interrupt(struct fuse_session *se,
1844 struct fuse_req *req)
1845{
1846 struct fuse_req *curr;
1847
1848 for (curr = se->interrupts.next; curr != &se->interrupts;
1849 curr = curr->next) {
1850 if (curr->u.i.unique == req->unique) {
1851 req->interrupted = 1;
1852 list_del_req(curr);
1853 fuse_chan_put(curr->ch);
1854 curr->ch = NULL;
1855 destroy_req(curr);
1856 return NULL;
1857 }
1858 }
1859 curr = se->interrupts.next;
1860 if (curr != &se->interrupts) {
1861 list_del_req(curr);
1862 list_init_req(curr);
1863 return curr;
1864 } else
1865 return NULL;
1866}
1867
1868static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1869{
1870 struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
1871
1872 if (req->se->op.bmap)
1873 req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
1874 else
1875 fuse_reply_err(req, ENOSYS);
1876}
1877
1878static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1879{
1880 struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
1881 unsigned int flags = arg->flags;
1882 void *in_buf = arg->in_size ? PARAM(arg) : NULL;
1883 struct fuse_file_info fi;
1884
1885 if (flags & FUSE_IOCTL_DIR &&
1886 !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) {
1887 fuse_reply_err(req, ENOTTY);
1888 return;
1889 }
1890
1891 memset(&fi, 0, sizeof(fi));
1892 fi.fh = arg->fh;
1893
1894 if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
1895 !(flags & FUSE_IOCTL_32BIT)) {
1896 req->ioctl_64bit = 1;
1897 }
1898
1899 if (req->se->op.ioctl)
1900 req->se->op.ioctl(req, nodeid, arg->cmd,
1901 (void *)(uintptr_t)arg->arg, &fi, flags,
1902 in_buf, arg->in_size, arg->out_size);
1903 else
1904 fuse_reply_err(req, ENOSYS);
1905}
1906
1907void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
1908{
1909 free(ph);
1910}
1911
1912static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1913{
1914 struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
1915 struct fuse_file_info fi;
1916
1917 memset(&fi, 0, sizeof(fi));
1918 fi.fh = arg->fh;
1919 fi.poll_events = arg->events;
1920
1921 if (req->se->op.poll) {
1922 struct fuse_pollhandle *ph = NULL;
1923
1924 if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
1925 ph = malloc(sizeof(struct fuse_pollhandle));
1926 if (ph == NULL) {
1927 fuse_reply_err(req, ENOMEM);
1928 return;
1929 }
1930 ph->kh = arg->kh;
1931 ph->se = req->se;
1932 }
1933
1934 req->se->op.poll(req, nodeid, &fi, ph);
1935 } else {
1936 fuse_reply_err(req, ENOSYS);
1937 }
1938}
1939
1940static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1941{
1942 struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
1943 struct fuse_file_info fi;
1944
1945 memset(&fi, 0, sizeof(fi));
1946 fi.fh = arg->fh;
1947
1948 if (req->se->op.fallocate)
1949 req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
1950 else
1951 fuse_reply_err(req, ENOSYS);
1952}
1953
1954static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg)
1955{
1956 struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg;
1957 struct fuse_file_info fi_in, fi_out;
1958
1959 memset(&fi_in, 0, sizeof(fi_in));
1960 fi_in.fh = arg->fh_in;
1961
1962 memset(&fi_out, 0, sizeof(fi_out));
1963 fi_out.fh = arg->fh_out;
1964
1965
1966 if (req->se->op.copy_file_range)
1967 req->se->op.copy_file_range(req, nodeid_in, arg->off_in,
1968 &fi_in, arg->nodeid_out,
1969 arg->off_out, &fi_out, arg->len,
1970 arg->flags);
1971 else
1972 fuse_reply_err(req, ENOSYS);
1973}
1974
1975static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
1976{
1977 struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg;
1978 struct fuse_file_info fi;
1979
1980 memset(&fi, 0, sizeof(fi));
1981 fi.fh = arg->fh;
1982
1983 if (req->se->op.lseek)
1984 req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
1985 else
1986 fuse_reply_err(req, ENOSYS);
1987}
1988
1989static bool want_flags_valid(uint64_t capable, uint64_t want)
1990{
1991 uint64_t unknown_flags = want & (~capable);
1992 if (unknown_flags != 0) {
1993 fuse_log(FUSE_LOG_ERR,
1994 "fuse: unknown connection 'want' flags: 0x%08lx\n",
1995 unknown_flags);
1996 return false;
1997 }
1998 return true;
1999}
2000
2001/* Prevent bogus data races (bogus since "init" is called before
2002 * multi-threading becomes relevant */
2003static __attribute__((no_sanitize("thread")))
2004void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2005{
2006 struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
2007 struct fuse_init_out outarg;
2008 struct fuse_session *se = req->se;
2009 size_t bufsize = se->bufsize;
2010 size_t outargsize = sizeof(outarg);
2011 uint64_t inargflags = 0;
2012 uint64_t outargflags = 0;
2013 bool buf_reallocable = se->buf_reallocable;
2014 (void) nodeid;
2015 if (se->debug) {
2016 fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
2017 if (arg->major == 7 && arg->minor >= 6) {
2018 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
2019 fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
2020 arg->max_readahead);
2021 }
2022 }
2023 se->conn.proto_major = arg->major;
2024 se->conn.proto_minor = arg->minor;
2025 se->conn.capable_ext = 0;
2026 se->conn.want_ext = 0;
2027
2028 memset(&outarg, 0, sizeof(outarg));
2029 outarg.major = FUSE_KERNEL_VERSION;
2030 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
2031
2032 if (arg->major < 7) {
2033 fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
2034 arg->major, arg->minor);
2035 fuse_reply_err(req, EPROTO);
2036 return;
2037 }
2038
2039 if (arg->major > 7) {
2040 /* Wait for a second INIT request with a 7.X version */
2041 send_reply_ok(req, &outarg, sizeof(outarg));
2042 return;
2043 }
2044
2045 if (arg->minor >= 6) {
2046 if (arg->max_readahead < se->conn.max_readahead)
2047 se->conn.max_readahead = arg->max_readahead;
2048 inargflags = arg->flags;
2049 if (inargflags & FUSE_INIT_EXT)
2050 inargflags = inargflags | (uint64_t) arg->flags2 << 32;
2051 if (inargflags & FUSE_ASYNC_READ)
2052 se->conn.capable_ext |= FUSE_CAP_ASYNC_READ;
2053 if (inargflags & FUSE_POSIX_LOCKS)
2054 se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS;
2055 if (inargflags & FUSE_ATOMIC_O_TRUNC)
2056 se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC;
2057 if (inargflags & FUSE_EXPORT_SUPPORT)
2058 se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT;
2059 if (inargflags & FUSE_DONT_MASK)
2060 se->conn.capable_ext |= FUSE_CAP_DONT_MASK;
2061 if (inargflags & FUSE_FLOCK_LOCKS)
2062 se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS;
2063 if (inargflags & FUSE_AUTO_INVAL_DATA)
2064 se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA;
2065 if (inargflags & FUSE_DO_READDIRPLUS)
2066 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS;
2067 if (inargflags & FUSE_READDIRPLUS_AUTO)
2068 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO;
2069 if (inargflags & FUSE_ASYNC_DIO)
2070 se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO;
2071 if (inargflags & FUSE_WRITEBACK_CACHE)
2072 se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE;
2073 if (inargflags & FUSE_NO_OPEN_SUPPORT)
2074 se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT;
2075 if (inargflags & FUSE_PARALLEL_DIROPS)
2076 se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS;
2077 if (inargflags & FUSE_POSIX_ACL)
2078 se->conn.capable_ext |= FUSE_CAP_POSIX_ACL;
2079 if (inargflags & FUSE_HANDLE_KILLPRIV)
2080 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV;
2081 if (inargflags & FUSE_HANDLE_KILLPRIV_V2)
2082 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2;
2083 if (inargflags & FUSE_CACHE_SYMLINKS)
2084 se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS;
2085 if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
2086 se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT;
2087 if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
2088 se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA;
2089 if (inargflags & FUSE_SETXATTR_EXT)
2090 se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT;
2091 if (!(inargflags & FUSE_MAX_PAGES)) {
2092 size_t max_bufsize =
2093 FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
2094 + FUSE_BUFFER_HEADER_SIZE;
2095 if (bufsize > max_bufsize) {
2096 bufsize = max_bufsize;
2097 }
2098 buf_reallocable = false;
2099 }
2100 if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP)
2101 se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
2102 if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY))
2103 se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY;
2104 if (inargflags & FUSE_PASSTHROUGH)
2105 se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH;
2106 if (inargflags & FUSE_NO_EXPORT_SUPPORT)
2107 se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
2108 } else {
2109 se->conn.max_readahead = 0;
2110 }
2111
2112 if (se->conn.proto_minor >= 14) {
2113#ifdef HAVE_SPLICE
2114#ifdef HAVE_VMSPLICE
2115 if ((se->io == NULL) || (se->io->splice_send != NULL)) {
2116 se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE |
2118 }
2119#endif
2120 if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
2121 se->conn.capable_ext |= FUSE_CAP_SPLICE_READ;
2122 }
2123#endif
2124 }
2125 if (se->conn.proto_minor >= 18)
2126 se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR;
2127
2128 /* Default settings for modern filesystems.
2129 *
2130 * Most of these capabilities were disabled by default in
2131 * libfuse2 for backwards compatibility reasons. In libfuse3,
2132 * we can finally enable them by default (as long as they're
2133 * supported by the kernel).
2134 */
2135#define LL_SET_DEFAULT(cond, cap) \
2136 if ((cond)) \
2137 fuse_set_feature_flag(&se->conn, cap)
2138
2139 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
2140 LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
2141 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
2142 LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
2143 LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
2144 LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
2145 LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
2147 LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
2148 LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
2149 LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
2151
2152 /* This could safely become default, but libfuse needs an API extension
2153 * to support it
2154 * LL_SET_DEFAULT(1, FUSE_CAP_SETXATTR_EXT);
2155 */
2156
2157 se->conn.time_gran = 1;
2158
2159 se->got_init = 1;
2160 if (se->op.init) {
2161 uint64_t want_ext_default = se->conn.want_ext;
2162 uint32_t want_default = fuse_lower_32_bits(se->conn.want_ext);
2163 int rc;
2164
2165 // Apply the first 32 bits of capable_ext to capable
2166 se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext);
2167 se->conn.want = want_default;
2168
2169 se->op.init(se->userdata, &se->conn);
2170
2171 /*
2172 * se->conn.want is 32-bit value and deprecated in favour of
2173 * se->conn.want_ext
2174 * Userspace might still use conn.want - we need to convert it
2175 */
2176 rc = convert_to_conn_want_ext(&se->conn, want_ext_default,
2177 want_default);
2178 if (rc != 0) {
2179 fuse_reply_err(req, EPROTO);
2180 se->error = -EPROTO;
2182 return;
2183 }
2184 }
2185
2186 if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
2187 fuse_reply_err(req, EPROTO);
2188 se->error = -EPROTO;
2190 return;
2191 }
2192
2193 unsigned max_read_mo = get_max_read(se->mo);
2194 if (se->conn.max_read != max_read_mo) {
2195 fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() "
2196 "requested different maximum read size (%u vs %u)\n",
2197 se->conn.max_read, max_read_mo);
2198 fuse_reply_err(req, EPROTO);
2199 se->error = -EPROTO;
2201 return;
2202 }
2203
2204 if (bufsize < FUSE_MIN_READ_BUFFER) {
2205 fuse_log(FUSE_LOG_ERR,
2206 "fuse: warning: buffer size too small: %zu\n",
2207 bufsize);
2208 bufsize = FUSE_MIN_READ_BUFFER;
2209 }
2210
2211 if (buf_reallocable)
2212 bufsize = UINT_MAX;
2213 se->conn.max_write = MIN(se->conn.max_write, bufsize - FUSE_BUFFER_HEADER_SIZE);
2214 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
2215
2216 if (arg->flags & FUSE_MAX_PAGES) {
2217 outarg.flags |= FUSE_MAX_PAGES;
2218 outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
2219 }
2220 outargflags = outarg.flags;
2221 /* Always enable big writes, this is superseded
2222 by the max_write option */
2223 outargflags |= FUSE_BIG_WRITES;
2224
2225 if (se->conn.want_ext & FUSE_CAP_ASYNC_READ)
2226 outargflags |= FUSE_ASYNC_READ;
2227 if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS)
2228 outargflags |= FUSE_POSIX_LOCKS;
2229 if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC)
2230 outargflags |= FUSE_ATOMIC_O_TRUNC;
2231 if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT)
2232 outargflags |= FUSE_EXPORT_SUPPORT;
2233 if (se->conn.want_ext & FUSE_CAP_DONT_MASK)
2234 outargflags |= FUSE_DONT_MASK;
2235 if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS)
2236 outargflags |= FUSE_FLOCK_LOCKS;
2237 if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA)
2238 outargflags |= FUSE_AUTO_INVAL_DATA;
2239 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS)
2240 outargflags |= FUSE_DO_READDIRPLUS;
2241 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO)
2242 outargflags |= FUSE_READDIRPLUS_AUTO;
2243 if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO)
2244 outargflags |= FUSE_ASYNC_DIO;
2245 if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE)
2246 outargflags |= FUSE_WRITEBACK_CACHE;
2247 if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS)
2248 outargflags |= FUSE_PARALLEL_DIROPS;
2249 if (se->conn.want_ext & FUSE_CAP_POSIX_ACL)
2250 outargflags |= FUSE_POSIX_ACL;
2251 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV)
2252 outargflags |= FUSE_HANDLE_KILLPRIV;
2253 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2)
2254 outargflags |= FUSE_HANDLE_KILLPRIV_V2;
2255 if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS)
2256 outargflags |= FUSE_CACHE_SYMLINKS;
2257 if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA)
2258 outargflags |= FUSE_EXPLICIT_INVAL_DATA;
2259 if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT)
2260 outargflags |= FUSE_SETXATTR_EXT;
2261 if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
2262 outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
2263 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) {
2264 outargflags |= FUSE_PASSTHROUGH;
2265 /*
2266 * outarg.max_stack_depth includes the fuse stack layer,
2267 * so it is one more than max_backing_stack_depth.
2268 */
2269 outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1;
2270 }
2271 if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT)
2272 outargflags |= FUSE_NO_EXPORT_SUPPORT;
2273
2274 if (inargflags & FUSE_INIT_EXT) {
2275 outargflags |= FUSE_INIT_EXT;
2276 outarg.flags2 = outargflags >> 32;
2277 }
2278
2279 outarg.flags = outargflags;
2280
2281 outarg.max_readahead = se->conn.max_readahead;
2282 outarg.max_write = se->conn.max_write;
2283 if (se->conn.proto_minor >= 13) {
2284 if (se->conn.max_background >= (1 << 16))
2285 se->conn.max_background = (1 << 16) - 1;
2286 if (se->conn.congestion_threshold > se->conn.max_background)
2287 se->conn.congestion_threshold = se->conn.max_background;
2288 if (!se->conn.congestion_threshold) {
2289 se->conn.congestion_threshold =
2290 se->conn.max_background * 3 / 4;
2291 }
2292
2293 outarg.max_background = se->conn.max_background;
2294 outarg.congestion_threshold = se->conn.congestion_threshold;
2295 }
2296 if (se->conn.proto_minor >= 23)
2297 outarg.time_gran = se->conn.time_gran;
2298
2299 if (se->debug) {
2300 fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
2301 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
2302 fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
2303 outarg.max_readahead);
2304 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
2305 fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
2306 outarg.max_background);
2307 fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
2308 outarg.congestion_threshold);
2309 fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
2310 outarg.time_gran);
2311 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH)
2312 fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n",
2313 outarg.max_stack_depth);
2314 }
2315 if (arg->minor < 5)
2316 outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
2317 else if (arg->minor < 23)
2318 outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
2319
2320 send_reply_ok(req, &outarg, outargsize);
2321}
2322
2323static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
2324{
2325 struct fuse_session *se = req->se;
2326
2327 (void) nodeid;
2328 (void) inarg;
2329
2330 se->got_destroy = 1;
2331 se->got_init = 0;
2332 if (se->op.destroy)
2333 se->op.destroy(se->userdata);
2334
2335 send_reply_ok(req, NULL, 0);
2336}
2337
2338static void list_del_nreq(struct fuse_notify_req *nreq)
2339{
2340 struct fuse_notify_req *prev = nreq->prev;
2341 struct fuse_notify_req *next = nreq->next;
2342 prev->next = next;
2343 next->prev = prev;
2344}
2345
2346static void list_add_nreq(struct fuse_notify_req *nreq,
2347 struct fuse_notify_req *next)
2348{
2349 struct fuse_notify_req *prev = next->prev;
2350 nreq->next = next;
2351 nreq->prev = prev;
2352 prev->next = nreq;
2353 next->prev = nreq;
2354}
2355
2356static void list_init_nreq(struct fuse_notify_req *nreq)
2357{
2358 nreq->next = nreq;
2359 nreq->prev = nreq;
2360}
2361
2362static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
2363 const void *inarg, const struct fuse_buf *buf)
2364{
2365 struct fuse_session *se = req->se;
2366 struct fuse_notify_req *nreq;
2367 struct fuse_notify_req *head;
2368
2369 pthread_mutex_lock(&se->lock);
2370 head = &se->notify_list;
2371 for (nreq = head->next; nreq != head; nreq = nreq->next) {
2372 if (nreq->unique == req->unique) {
2373 list_del_nreq(nreq);
2374 break;
2375 }
2376 }
2377 pthread_mutex_unlock(&se->lock);
2378
2379 if (nreq != head)
2380 nreq->reply(nreq, req, nodeid, inarg, buf);
2381}
2382
2383static int send_notify_iov(struct fuse_session *se, int notify_code,
2384 struct iovec *iov, int count)
2385{
2386 struct fuse_out_header out;
2387
2388 if (!se->got_init)
2389 return -ENOTCONN;
2390
2391 out.unique = 0;
2392 out.error = notify_code;
2393 iov[0].iov_base = &out;
2394 iov[0].iov_len = sizeof(struct fuse_out_header);
2395
2396 return fuse_send_msg(se, NULL, iov, count);
2397}
2398
2399int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
2400{
2401 if (ph != NULL) {
2402 struct fuse_notify_poll_wakeup_out outarg;
2403 struct iovec iov[2];
2404
2405 outarg.kh = ph->kh;
2406
2407 iov[1].iov_base = &outarg;
2408 iov[1].iov_len = sizeof(outarg);
2409
2410 return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
2411 } else {
2412 return 0;
2413 }
2414}
2415
2416int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
2417 off_t off, off_t len)
2418{
2419 struct fuse_notify_inval_inode_out outarg;
2420 struct iovec iov[2];
2421
2422 if (!se)
2423 return -EINVAL;
2424
2425 if (se->conn.proto_minor < 12)
2426 return -ENOSYS;
2427
2428 outarg.ino = ino;
2429 outarg.off = off;
2430 outarg.len = len;
2431
2432 iov[1].iov_base = &outarg;
2433 iov[1].iov_len = sizeof(outarg);
2434
2435 return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
2436}
2437
2457static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
2458 const char *name, size_t namelen,
2459 enum fuse_notify_entry_flags flags)
2460{
2461 struct fuse_notify_inval_entry_out outarg;
2462 struct iovec iov[3];
2463
2464 if (!se)
2465 return -EINVAL;
2466
2467 if (se->conn.proto_minor < 12)
2468 return -ENOSYS;
2469
2470 outarg.parent = parent;
2471 outarg.namelen = namelen;
2472 outarg.flags = 0;
2473 if (flags & FUSE_LL_EXPIRE_ONLY)
2474 outarg.flags |= FUSE_EXPIRE_ONLY;
2475
2476 iov[1].iov_base = &outarg;
2477 iov[1].iov_len = sizeof(outarg);
2478 iov[2].iov_base = (void *)name;
2479 iov[2].iov_len = namelen + 1;
2480
2481 return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
2482}
2483
2484int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
2485 const char *name, size_t namelen)
2486{
2487 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
2488}
2489
2490int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
2491 const char *name, size_t namelen)
2492{
2493 if (!se)
2494 return -EINVAL;
2495
2496 if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY))
2497 return -ENOSYS;
2498
2499 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
2500}
2501
2502
2503int fuse_lowlevel_notify_delete(struct fuse_session *se,
2504 fuse_ino_t parent, fuse_ino_t child,
2505 const char *name, size_t namelen)
2506{
2507 struct fuse_notify_delete_out outarg;
2508 struct iovec iov[3];
2509
2510 if (!se)
2511 return -EINVAL;
2512
2513 if (se->conn.proto_minor < 18)
2514 return -ENOSYS;
2515
2516 outarg.parent = parent;
2517 outarg.child = child;
2518 outarg.namelen = namelen;
2519 outarg.padding = 0;
2520
2521 iov[1].iov_base = &outarg;
2522 iov[1].iov_len = sizeof(outarg);
2523 iov[2].iov_base = (void *)name;
2524 iov[2].iov_len = namelen + 1;
2525
2526 return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
2527}
2528
2529int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
2530 off_t offset, struct fuse_bufvec *bufv,
2531 enum fuse_buf_copy_flags flags)
2532{
2533 struct fuse_out_header out;
2534 struct fuse_notify_store_out outarg;
2535 struct iovec iov[3];
2536 size_t size = fuse_buf_size(bufv);
2537 int res;
2538
2539 if (!se)
2540 return -EINVAL;
2541
2542 if (se->conn.proto_minor < 15)
2543 return -ENOSYS;
2544
2545 out.unique = 0;
2546 out.error = FUSE_NOTIFY_STORE;
2547
2548 outarg.nodeid = ino;
2549 outarg.offset = offset;
2550 outarg.size = size;
2551 outarg.padding = 0;
2552
2553 iov[0].iov_base = &out;
2554 iov[0].iov_len = sizeof(out);
2555 iov[1].iov_base = &outarg;
2556 iov[1].iov_len = sizeof(outarg);
2557
2558 res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags);
2559 if (res > 0)
2560 res = -res;
2561
2562 return res;
2563}
2564
2565struct fuse_retrieve_req {
2566 struct fuse_notify_req nreq;
2567 void *cookie;
2568};
2569
2570static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
2571 fuse_req_t req, fuse_ino_t ino,
2572 const void *inarg,
2573 const struct fuse_buf *ibuf)
2574{
2575 struct fuse_session *se = req->se;
2576 struct fuse_retrieve_req *rreq =
2577 container_of(nreq, struct fuse_retrieve_req, nreq);
2578 const struct fuse_notify_retrieve_in *arg = inarg;
2579 struct fuse_bufvec bufv = {
2580 .buf[0] = *ibuf,
2581 .count = 1,
2582 };
2583
2584 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
2585 bufv.buf[0].mem = PARAM(arg);
2586
2587 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
2588 sizeof(struct fuse_notify_retrieve_in);
2589
2590 if (bufv.buf[0].size < arg->size) {
2591 fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
2592 fuse_reply_none(req);
2593 goto out;
2594 }
2595 bufv.buf[0].size = arg->size;
2596
2597 if (se->op.retrieve_reply) {
2598 se->op.retrieve_reply(req, rreq->cookie, ino,
2599 arg->offset, &bufv);
2600 } else {
2601 fuse_reply_none(req);
2602 }
2603out:
2604 free(rreq);
2605 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
2606 fuse_ll_clear_pipe(se);
2607}
2608
2609int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
2610 size_t size, off_t offset, void *cookie)
2611{
2612 struct fuse_notify_retrieve_out outarg;
2613 struct iovec iov[2];
2614 struct fuse_retrieve_req *rreq;
2615 int err;
2616
2617 if (!se)
2618 return -EINVAL;
2619
2620 if (se->conn.proto_minor < 15)
2621 return -ENOSYS;
2622
2623 rreq = malloc(sizeof(*rreq));
2624 if (rreq == NULL)
2625 return -ENOMEM;
2626
2627 pthread_mutex_lock(&se->lock);
2628 rreq->cookie = cookie;
2629 rreq->nreq.unique = se->notify_ctr++;
2630 rreq->nreq.reply = fuse_ll_retrieve_reply;
2631 list_add_nreq(&rreq->nreq, &se->notify_list);
2632 pthread_mutex_unlock(&se->lock);
2633
2634 outarg.notify_unique = rreq->nreq.unique;
2635 outarg.nodeid = ino;
2636 outarg.offset = offset;
2637 outarg.size = size;
2638 outarg.padding = 0;
2639
2640 iov[1].iov_base = &outarg;
2641 iov[1].iov_len = sizeof(outarg);
2642
2643 err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
2644 if (err) {
2645 pthread_mutex_lock(&se->lock);
2646 list_del_nreq(&rreq->nreq);
2647 pthread_mutex_unlock(&se->lock);
2648 free(rreq);
2649 }
2650
2651 return err;
2652}
2653
2655{
2656 return req->se->userdata;
2657}
2658
2660{
2661 return &req->ctx;
2662}
2663
2665 void *data)
2666{
2667 pthread_mutex_lock(&req->lock);
2668 pthread_mutex_lock(&req->se->lock);
2669 req->u.ni.func = func;
2670 req->u.ni.data = data;
2671 pthread_mutex_unlock(&req->se->lock);
2672 if (req->interrupted && func)
2673 func(req, data);
2674 pthread_mutex_unlock(&req->lock);
2675}
2676
2678{
2679 int interrupted;
2680
2681 pthread_mutex_lock(&req->se->lock);
2682 interrupted = req->interrupted;
2683 pthread_mutex_unlock(&req->se->lock);
2684
2685 return interrupted;
2686}
2687
2688static struct {
2689 void (*func)(fuse_req_t, fuse_ino_t, const void *);
2690 const char *name;
2691} fuse_ll_ops[] = {
2692 [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
2693 [FUSE_FORGET] = { do_forget, "FORGET" },
2694 [FUSE_GETATTR] = { do_getattr, "GETATTR" },
2695 [FUSE_SETATTR] = { do_setattr, "SETATTR" },
2696 [FUSE_READLINK] = { do_readlink, "READLINK" },
2697 [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
2698 [FUSE_MKNOD] = { do_mknod, "MKNOD" },
2699 [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
2700 [FUSE_UNLINK] = { do_unlink, "UNLINK" },
2701 [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
2702 [FUSE_RENAME] = { do_rename, "RENAME" },
2703 [FUSE_LINK] = { do_link, "LINK" },
2704 [FUSE_OPEN] = { do_open, "OPEN" },
2705 [FUSE_READ] = { do_read, "READ" },
2706 [FUSE_WRITE] = { do_write, "WRITE" },
2707 [FUSE_STATFS] = { do_statfs, "STATFS" },
2708 [FUSE_RELEASE] = { do_release, "RELEASE" },
2709 [FUSE_FSYNC] = { do_fsync, "FSYNC" },
2710 [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
2711 [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
2712 [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
2713 [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
2714 [FUSE_FLUSH] = { do_flush, "FLUSH" },
2715 [FUSE_INIT] = { do_init, "INIT" },
2716 [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
2717 [FUSE_READDIR] = { do_readdir, "READDIR" },
2718 [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
2719 [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
2720 [FUSE_GETLK] = { do_getlk, "GETLK" },
2721 [FUSE_SETLK] = { do_setlk, "SETLK" },
2722 [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
2723 [FUSE_ACCESS] = { do_access, "ACCESS" },
2724 [FUSE_CREATE] = { do_create, "CREATE" },
2725 [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" },
2726 [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
2727 [FUSE_BMAP] = { do_bmap, "BMAP" },
2728 [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
2729 [FUSE_POLL] = { do_poll, "POLL" },
2730 [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
2731 [FUSE_DESTROY] = { do_destroy, "DESTROY" },
2732 [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
2733 [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
2734 [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
2735 [FUSE_RENAME2] = { do_rename2, "RENAME2" },
2736 [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
2737 [FUSE_LSEEK] = { do_lseek, "LSEEK" },
2738 [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
2739};
2740
2741/*
2742 * For ABI compatibility we cannot allow higher values than CUSE_INIT.
2743 * Without ABI compatibility we could use the size of the array.
2744 * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
2745 */
2746#define FUSE_MAXOP (CUSE_INIT + 1)
2747
2748static const char *opname(enum fuse_opcode opcode)
2749{
2750 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
2751 return "???";
2752 else
2753 return fuse_ll_ops[opcode].name;
2754}
2755
2756static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
2757 struct fuse_bufvec *src)
2758{
2759 ssize_t res = fuse_buf_copy(dst, src, 0);
2760 if (res < 0) {
2761 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res));
2762 return res;
2763 }
2764 if ((size_t)res < fuse_buf_size(dst)) {
2765 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
2766 return -1;
2767 }
2768 return 0;
2769}
2770
2771void fuse_session_process_buf(struct fuse_session *se,
2772 const struct fuse_buf *buf)
2773{
2774 fuse_session_process_buf_internal(se, buf, NULL);
2775}
2776
2777/* libfuse internal handler */
2778void fuse_session_process_buf_internal(struct fuse_session *se,
2779 const struct fuse_buf *buf, struct fuse_chan *ch)
2780{
2781 const size_t write_header_size = sizeof(struct fuse_in_header) +
2782 sizeof(struct fuse_write_in);
2783 struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
2784 struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
2785 struct fuse_in_header *in;
2786 const void *inarg;
2787 struct fuse_req *req;
2788 void *mbuf = NULL;
2789 int err;
2790 int res;
2791
2792 if (buf->flags & FUSE_BUF_IS_FD) {
2793 if (buf->size < tmpbuf.buf[0].size)
2794 tmpbuf.buf[0].size = buf->size;
2795
2796 mbuf = malloc(tmpbuf.buf[0].size);
2797 if (mbuf == NULL) {
2798 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n");
2799 goto clear_pipe;
2800 }
2801 tmpbuf.buf[0].mem = mbuf;
2802
2803 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
2804 if (res < 0)
2805 goto clear_pipe;
2806
2807 in = mbuf;
2808 } else {
2809 in = buf->mem;
2810 }
2811
2812 if (se->debug) {
2813 fuse_log(FUSE_LOG_DEBUG,
2814 "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
2815 (unsigned long long) in->unique,
2816 opname((enum fuse_opcode) in->opcode), in->opcode,
2817 (unsigned long long) in->nodeid, buf->size, in->pid);
2818 }
2819
2820 req = fuse_ll_alloc_req(se);
2821 if (req == NULL) {
2822 struct fuse_out_header out = {
2823 .unique = in->unique,
2824 .error = -ENOMEM,
2825 };
2826 struct iovec iov = {
2827 .iov_base = &out,
2828 .iov_len = sizeof(struct fuse_out_header),
2829 };
2830
2831 fuse_send_msg(se, ch, &iov, 1);
2832 goto clear_pipe;
2833 }
2834
2835 req->unique = in->unique;
2836 req->ctx.uid = in->uid;
2837 req->ctx.gid = in->gid;
2838 req->ctx.pid = in->pid;
2839 req->ch = ch ? fuse_chan_get(ch) : NULL;
2840
2841 err = EIO;
2842 if (!se->got_init) {
2843 enum fuse_opcode expected;
2844
2845 expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
2846 if (in->opcode != expected)
2847 goto reply_err;
2848 } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
2849 goto reply_err;
2850
2851 err = EACCES;
2852 /* Implement -o allow_root */
2853 if (se->deny_others && in->uid != se->owner && in->uid != 0 &&
2854 in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
2855 in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
2856 in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
2857 in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
2858 in->opcode != FUSE_NOTIFY_REPLY &&
2859 in->opcode != FUSE_READDIRPLUS)
2860 goto reply_err;
2861
2862 err = ENOSYS;
2863 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
2864 goto reply_err;
2865 /* Do not process interrupt request */
2866 if (se->conn.no_interrupt && in->opcode == FUSE_INTERRUPT) {
2867 if (se->debug)
2868 fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n");
2869 goto reply_err;
2870 }
2871 if (!se->conn.no_interrupt && in->opcode != FUSE_INTERRUPT) {
2872 struct fuse_req *intr;
2873 pthread_mutex_lock(&se->lock);
2874 intr = check_interrupt(se, req);
2875 list_add_req(req, &se->list);
2876 pthread_mutex_unlock(&se->lock);
2877 if (intr)
2878 fuse_reply_err(intr, EAGAIN);
2879 }
2880
2881 if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
2882 (in->opcode != FUSE_WRITE || !se->op.write_buf) &&
2883 in->opcode != FUSE_NOTIFY_REPLY) {
2884 void *newmbuf;
2885
2886 err = ENOMEM;
2887 newmbuf = realloc(mbuf, buf->size);
2888 if (newmbuf == NULL)
2889 goto reply_err;
2890 mbuf = newmbuf;
2891
2892 tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
2893 tmpbuf.buf[0].mem = (char *)mbuf + write_header_size;
2894
2895 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
2896 err = -res;
2897 if (res < 0)
2898 goto reply_err;
2899
2900 in = mbuf;
2901 }
2902
2903 inarg = (void *) &in[1];
2904 if (in->opcode == FUSE_WRITE && se->op.write_buf)
2905 do_write_buf(req, in->nodeid, inarg, buf);
2906 else if (in->opcode == FUSE_NOTIFY_REPLY)
2907 do_notify_reply(req, in->nodeid, inarg, buf);
2908 else
2909 fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
2910
2911out_free:
2912 free(mbuf);
2913 return;
2914
2915reply_err:
2916 fuse_reply_err(req, err);
2917clear_pipe:
2918 if (buf->flags & FUSE_BUF_IS_FD)
2919 fuse_ll_clear_pipe(se);
2920 goto out_free;
2921}
2922
2923#define LL_OPTION(n,o,v) \
2924 { n, offsetof(struct fuse_session, o), v }
2925
2926static const struct fuse_opt fuse_ll_opts[] = {
2927 LL_OPTION("debug", debug, 1),
2928 LL_OPTION("-d", debug, 1),
2929 LL_OPTION("--debug", debug, 1),
2930 LL_OPTION("allow_root", deny_others, 1),
2932};
2933
2935{
2936 printf("using FUSE kernel interface version %i.%i\n",
2937 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
2938 fuse_mount_version();
2939}
2940
2942{
2943 /* These are not all options, but the ones that are
2944 potentially of interest to an end-user */
2945 printf(
2946" -o allow_other allow access by all users\n"
2947" -o allow_root allow access by root\n"
2948" -o auto_unmount auto unmount on process termination\n");
2949}
2950
2951void fuse_session_destroy(struct fuse_session *se)
2952{
2953 struct fuse_ll_pipe *llp;
2954
2955 if (se->got_init && !se->got_destroy) {
2956 if (se->op.destroy)
2957 se->op.destroy(se->userdata);
2958 }
2959 llp = pthread_getspecific(se->pipe_key);
2960 if (llp != NULL)
2961 fuse_ll_pipe_free(llp);
2962 pthread_key_delete(se->pipe_key);
2963 pthread_mutex_destroy(&se->lock);
2964 free(se->cuse_data);
2965 if (se->fd != -1)
2966 close(se->fd);
2967 if (se->io != NULL)
2968 free(se->io);
2969 destroy_mount_opts(se->mo);
2970 free(se);
2971}
2972
2973
2974static void fuse_ll_pipe_destructor(void *data)
2975{
2976 struct fuse_ll_pipe *llp = data;
2977 fuse_ll_pipe_free(llp);
2978}
2979
2980void fuse_buf_free(struct fuse_buf *buf)
2981{
2982 if (buf->mem == NULL)
2983 return;
2984
2985 size_t write_header_sz =
2986 sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in);
2987
2988 char *ptr = (char *)buf->mem - pagesize + write_header_sz;
2989 free(ptr);
2990 buf->mem = NULL;
2991}
2992
2993/*
2994 * This is used to allocate buffers that hold fuse requests
2995 */
2996static void *buf_alloc(size_t size, bool internal)
2997{
2998 /*
2999 * For libfuse internal caller add in alignment. That cannot be done
3000 * for an external caller, as it is not guaranteed that the external
3001 * caller frees the raw pointer.
3002 */
3003 if (internal) {
3004 size_t write_header_sz = sizeof(struct fuse_in_header) +
3005 sizeof(struct fuse_write_in);
3006 size_t new_size = ROUND_UP(size + write_header_sz, pagesize);
3007
3008 char *buf = aligned_alloc(pagesize, new_size);
3009 if (buf == NULL)
3010 return NULL;
3011
3012 buf += pagesize - write_header_sz;
3013
3014 return buf;
3015 } else {
3016 return malloc(size);
3017 }
3018}
3019
3020/*
3021 *@param internal true if called from libfuse internal code
3022 */
3023static int _fuse_session_receive_buf(struct fuse_session *se,
3024 struct fuse_buf *buf, struct fuse_chan *ch,
3025 bool internal)
3026{
3027 int err;
3028 ssize_t res;
3029 size_t bufsize;
3030#ifdef HAVE_SPLICE
3031 struct fuse_ll_pipe *llp;
3032 struct fuse_buf tmpbuf;
3033
3034pipe_retry:
3035 bufsize = se->bufsize;
3036
3037 if (se->conn.proto_minor < 14 ||
3038 !(se->conn.want_ext & FUSE_CAP_SPLICE_READ))
3039 goto fallback;
3040
3041 llp = fuse_ll_get_pipe(se);
3042 if (llp == NULL)
3043 goto fallback;
3044
3045 if (llp->size < bufsize) {
3046 if (llp->can_grow) {
3047 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
3048 if (res == -1) {
3049 llp->can_grow = 0;
3050 res = grow_pipe_to_max(llp->pipe[0]);
3051 if (res > 0)
3052 llp->size = res;
3053 goto fallback;
3054 }
3055 llp->size = res;
3056 }
3057 if (llp->size < bufsize)
3058 goto fallback;
3059 }
3060
3061 if (se->io != NULL && se->io->splice_receive != NULL) {
3062 res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
3063 llp->pipe[1], NULL, bufsize, 0,
3064 se->userdata);
3065 } else {
3066 res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
3067 bufsize, 0);
3068 }
3069 err = errno;
3070
3071 if (fuse_session_exited(se))
3072 return 0;
3073
3074 if (res == -1) {
3075 if (err == ENODEV) {
3076 /* Filesystem was unmounted, or connection was aborted
3077 via /sys/fs/fuse/connections */
3079 return 0;
3080 }
3081
3082 /* FUSE_INIT might have increased the required bufsize */
3083 if (err == EINVAL && bufsize < se->bufsize) {
3084 fuse_ll_clear_pipe(se);
3085 goto pipe_retry;
3086 }
3087
3088 if (err != EINTR && err != EAGAIN)
3089 perror("fuse: splice from device");
3090 return -err;
3091 }
3092
3093 if (res < sizeof(struct fuse_in_header)) {
3094 fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n");
3095 return -EIO;
3096 }
3097
3098 tmpbuf = (struct fuse_buf){
3099 .size = res,
3100 .flags = FUSE_BUF_IS_FD,
3101 .fd = llp->pipe[0],
3102 };
3103
3104 /*
3105 * Don't bother with zero copy for small requests.
3106 * fuse_loop_mt() needs to check for FORGET so this more than
3107 * just an optimization.
3108 */
3109 if (res < sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) +
3110 pagesize) {
3111 struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
3112 struct fuse_bufvec dst = { .count = 1 };
3113
3114 if (!buf->mem) {
3115 buf->mem = buf_alloc(bufsize, internal);
3116 if (!buf->mem) {
3117 fuse_log(
3118 FUSE_LOG_ERR,
3119 "fuse: failed to allocate read buffer\n");
3120 return -ENOMEM;
3121 }
3122 buf->mem_size = bufsize;
3123 }
3124 buf->size = bufsize;
3125 buf->flags = 0;
3126 dst.buf[0] = *buf;
3127
3128 res = fuse_buf_copy(&dst, &src, 0);
3129 if (res < 0) {
3130 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n",
3131 strerror(-res));
3132 fuse_ll_clear_pipe(se);
3133 return res;
3134 }
3135 if (res < tmpbuf.size) {
3136 fuse_log(FUSE_LOG_ERR,
3137 "fuse: copy from pipe: short read\n");
3138 fuse_ll_clear_pipe(se);
3139 return -EIO;
3140 }
3141 assert(res == tmpbuf.size);
3142
3143 } else {
3144 /* Don't overwrite buf->mem, as that would cause a leak */
3145 buf->fd = tmpbuf.fd;
3146 buf->flags = tmpbuf.flags;
3147 }
3148 buf->size = tmpbuf.size;
3149
3150 return res;
3151
3152fallback:
3153#endif
3154 bufsize = internal ? buf->mem_size : se->bufsize;
3155 if (!buf->mem) {
3156 bufsize = se->bufsize; /* might have changed */
3157 buf->mem = buf_alloc(bufsize, internal);
3158 if (!buf->mem) {
3159 fuse_log(FUSE_LOG_ERR,
3160 "fuse: failed to allocate read buffer\n");
3161 return -ENOMEM;
3162 }
3163
3164 if (internal)
3165 buf->mem_size = bufsize;
3166 }
3167
3168restart:
3169 if (se->io != NULL) {
3170 /* se->io->read is never NULL if se->io is not NULL as
3171 specified by fuse_session_custom_io()*/
3172 res = se->io->read(ch ? ch->fd : se->fd, buf->mem, bufsize,
3173 se->userdata);
3174 } else {
3175 res = read(ch ? ch->fd : se->fd, buf->mem, bufsize);
3176 }
3177 err = errno;
3178
3179 if (fuse_session_exited(se))
3180 return 0;
3181 if (res == -1) {
3182 if (err == EINVAL && internal && se->bufsize > bufsize) {
3183 /* FUSE_INIT might have increased the required bufsize */
3184 bufsize = se->bufsize;
3185 void *newbuf = buf_alloc(bufsize, internal);
3186 if (!newbuf) {
3187 fuse_log(
3188 FUSE_LOG_ERR,
3189 "fuse: failed to (re)allocate read buffer\n");
3190 return -ENOMEM;
3191 }
3192 fuse_buf_free(buf);
3193 buf->mem = newbuf;
3194 buf->mem_size = bufsize;
3195 goto restart;
3196 }
3197
3198 /* ENOENT means the operation was interrupted, it's safe
3199 to restart */
3200 if (err == ENOENT)
3201 goto restart;
3202
3203 if (err == ENODEV) {
3204 /* Filesystem was unmounted, or connection was aborted
3205 via /sys/fs/fuse/connections */
3207 return 0;
3208 }
3209 /* Errors occurring during normal operation: EINTR (read
3210 interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
3211 umounted) */
3212 if (err != EINTR && err != EAGAIN)
3213 perror("fuse: reading device");
3214 return -err;
3215 }
3216 if ((size_t)res < sizeof(struct fuse_in_header)) {
3217 fuse_log(FUSE_LOG_ERR, "short read on fuse device\n");
3218 return -EIO;
3219 }
3220
3221 buf->size = res;
3222
3223 return res;
3224}
3225
3226int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
3227{
3228 return _fuse_session_receive_buf(se, buf, NULL, false);
3229}
3230
3231/* libfuse internal handler */
3232int fuse_session_receive_buf_internal(struct fuse_session *se,
3233 struct fuse_buf *buf,
3234 struct fuse_chan *ch)
3235{
3236 /*
3237 * if run internally thread buffers are from libfuse - we can
3238 * reallocate them
3239 */
3240 if (unlikely(!se->got_init) && !se->buf_reallocable)
3241 se->buf_reallocable = true;
3242
3243 return _fuse_session_receive_buf(se, buf, ch, true);
3244}
3245
3246struct fuse_session *
3247fuse_session_new_versioned(struct fuse_args *args,
3248 const struct fuse_lowlevel_ops *op, size_t op_size,
3249 struct libfuse_version *version, void *userdata)
3250{
3251 int err;
3252 struct fuse_session *se;
3253 struct mount_opts *mo;
3254
3255 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
3256 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
3257 op_size = sizeof(struct fuse_lowlevel_ops);
3258 }
3259
3260 if (args->argc == 0) {
3261 fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
3262 return NULL;
3263 }
3264
3265 se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
3266 if (se == NULL) {
3267 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
3268 goto out1;
3269 }
3270 se->fd = -1;
3271 se->conn.max_write = FUSE_DEFAULT_MAX_PAGES_LIMIT * getpagesize();
3272 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
3273 se->conn.max_readahead = UINT_MAX;
3274
3275 /* Parse options */
3276 if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
3277 goto out2;
3278 if(se->deny_others) {
3279 /* Allowing access only by root is done by instructing
3280 * kernel to allow access by everyone, and then restricting
3281 * access to root and mountpoint owner in libfuse.
3282 */
3283 // We may be adding the option a second time, but
3284 // that doesn't hurt.
3285 if(fuse_opt_add_arg(args, "-oallow_other") == -1)
3286 goto out2;
3287 }
3288 mo = parse_mount_opts(args);
3289 if (mo == NULL)
3290 goto out3;
3291
3292 if(args->argc == 1 &&
3293 args->argv[0][0] == '-') {
3294 fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
3295 "will be ignored\n");
3296 } else if (args->argc != 1) {
3297 int i;
3298 fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
3299 for(i = 1; i < args->argc-1; i++)
3300 fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
3301 fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
3302 goto out4;
3303 }
3304
3305 if (se->debug)
3306 fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION);
3307
3308 list_init_req(&se->list);
3309 list_init_req(&se->interrupts);
3310 list_init_nreq(&se->notify_list);
3311 se->notify_ctr = 1;
3312 pthread_mutex_init(&se->lock, NULL);
3313
3314 err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
3315 if (err) {
3316 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
3317 strerror(err));
3318 goto out5;
3319 }
3320
3321 memcpy(&se->op, op, op_size);
3322 se->owner = getuid();
3323 se->userdata = userdata;
3324
3325 se->mo = mo;
3326
3327 /* Fuse server application should pass the version it was compiled
3328 * against and pass it. If a libfuse version accidentally introduces an
3329 * ABI incompatibility, it might be possible to 'fix' that at run time,
3330 * by checking the version numbers.
3331 */
3332 se->version = *version;
3333
3334 return se;
3335
3336out5:
3337 pthread_mutex_destroy(&se->lock);
3338out4:
3339 fuse_opt_free_args(args);
3340out3:
3341 if (mo != NULL)
3342 destroy_mount_opts(mo);
3343out2:
3344 free(se);
3345out1:
3346 return NULL;
3347}
3348
3349struct fuse_session *fuse_session_new_30(struct fuse_args *args,
3350 const struct fuse_lowlevel_ops *op,
3351 size_t op_size, void *userdata);
3352struct fuse_session *fuse_session_new_30(struct fuse_args *args,
3353 const struct fuse_lowlevel_ops *op,
3354 size_t op_size,
3355 void *userdata)
3356{
3357 /* unknown version */
3358 struct libfuse_version version = { 0 };
3359
3360 return fuse_session_new_versioned(args, op, op_size, &version,
3361 userdata);
3362}
3363
3364FUSE_SYMVER("fuse_session_custom_io_317", "fuse_session_custom_io@@FUSE_3.17")
3365int fuse_session_custom_io_317(struct fuse_session *se,
3366 const struct fuse_custom_io *io, size_t op_size, int fd)
3367{
3368 if (sizeof(struct fuse_custom_io) < op_size) {
3369 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
3370 op_size = sizeof(struct fuse_custom_io);
3371 }
3372
3373 if (fd < 0) {
3374 fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
3375 "fuse_session_custom_io()\n", fd);
3376 return -EBADF;
3377 }
3378 if (io == NULL) {
3379 fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
3380 "fuse_session_custom_io()\n");
3381 return -EINVAL;
3382 } else if (io->read == NULL || io->writev == NULL) {
3383 /* If the user provides their own file descriptor, we can't
3384 guarantee that the default behavior of the io operations made
3385 in libfuse will function properly. Therefore, we enforce the
3386 user to implement these io operations when using custom io. */
3387 fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
3388 "implement both io->read() and io->writev\n");
3389 return -EINVAL;
3390 }
3391
3392 se->io = calloc(1, sizeof(struct fuse_custom_io));
3393 if (se->io == NULL) {
3394 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
3395 "Error: %s\n", strerror(errno));
3396 return -errno;
3397 }
3398
3399 se->fd = fd;
3400 memcpy(se->io, io, op_size);
3401 return 0;
3402}
3403
3404int fuse_session_custom_io_30(struct fuse_session *se,
3405 const struct fuse_custom_io *io, int fd);
3406FUSE_SYMVER("fuse_session_custom_io_30", "fuse_session_custom_io@FUSE_3.0")
3407int fuse_session_custom_io_30(struct fuse_session *se,
3408 const struct fuse_custom_io *io, int fd)
3409{
3410 return fuse_session_custom_io_317(se, io,
3411 offsetof(struct fuse_custom_io, clone_fd), fd);
3412}
3413
3414int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
3415{
3416 int fd;
3417
3418 if (mountpoint == NULL) {
3419 fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n");
3420 return -1;
3421 }
3422
3423 /*
3424 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
3425 * would ensue.
3426 */
3427 do {
3428 fd = open("/dev/null", O_RDWR);
3429 if (fd > 2)
3430 close(fd);
3431 } while (fd >= 0 && fd <= 2);
3432
3433 /*
3434 * To allow FUSE daemons to run without privileges, the caller may open
3435 * /dev/fuse before launching the file system and pass on the file
3436 * descriptor by specifying /dev/fd/N as the mount point. Note that the
3437 * parent process takes care of performing the mount in this case.
3438 */
3439 fd = fuse_mnt_parse_fuse_fd(mountpoint);
3440 if (fd != -1) {
3441 if (fcntl(fd, F_GETFD) == -1) {
3442 fuse_log(FUSE_LOG_ERR,
3443 "fuse: Invalid file descriptor /dev/fd/%u\n",
3444 fd);
3445 return -1;
3446 }
3447 se->fd = fd;
3448 return 0;
3449 }
3450
3451 /* Open channel */
3452 fd = fuse_kern_mount(mountpoint, se->mo);
3453 if (fd == -1)
3454 return -1;
3455 se->fd = fd;
3456
3457 /* Save mountpoint */
3458 se->mountpoint = strdup(mountpoint);
3459 if (se->mountpoint == NULL)
3460 goto error_out;
3461
3462 return 0;
3463
3464error_out:
3465 fuse_kern_unmount(mountpoint, fd);
3466 return -1;
3467}
3468
3469int fuse_session_fd(struct fuse_session *se)
3470{
3471 return se->fd;
3472}
3473
3474void fuse_session_unmount(struct fuse_session *se)
3475{
3476 if (se->mountpoint != NULL) {
3477 fuse_kern_unmount(se->mountpoint, se->fd);
3478 se->fd = -1;
3479 free(se->mountpoint);
3480 se->mountpoint = NULL;
3481 }
3482}
3483
3484#ifdef linux
3485int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
3486{
3487 char *buf;
3488 size_t bufsize = 1024;
3489 char path[128];
3490 int ret;
3491 int fd;
3492 unsigned long pid = req->ctx.pid;
3493 char *s;
3494
3495 sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
3496
3497retry:
3498 buf = malloc(bufsize);
3499 if (buf == NULL)
3500 return -ENOMEM;
3501
3502 ret = -EIO;
3503 fd = open(path, O_RDONLY);
3504 if (fd == -1)
3505 goto out_free;
3506
3507 ret = read(fd, buf, bufsize);
3508 close(fd);
3509 if (ret < 0) {
3510 ret = -EIO;
3511 goto out_free;
3512 }
3513
3514 if ((size_t)ret == bufsize) {
3515 free(buf);
3516 bufsize *= 4;
3517 goto retry;
3518 }
3519
3520 buf[ret] = '\0';
3521 ret = -EIO;
3522 s = strstr(buf, "\nGroups:");
3523 if (s == NULL)
3524 goto out_free;
3525
3526 s += 8;
3527 ret = 0;
3528 while (1) {
3529 char *end;
3530 unsigned long val = strtoul(s, &end, 0);
3531 if (end == s)
3532 break;
3533
3534 s = end;
3535 if (ret < size)
3536 list[ret] = val;
3537 ret++;
3538 }
3539
3540out_free:
3541 free(buf);
3542 return ret;
3543}
3544#else /* linux */
3545/*
3546 * This is currently not implemented on other than Linux...
3547 */
3548int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
3549{
3550 (void) req; (void) size; (void) list;
3551 return -ENOSYS;
3552}
3553#endif
3554
3555/* Prevent spurious data race warning - we don't care
3556 * about races for this flag */
3557__attribute__((no_sanitize_thread))
3558void fuse_session_exit(struct fuse_session *se)
3559{
3560 se->exited = 1;
3561}
3562
3563__attribute__((no_sanitize_thread))
3564void fuse_session_reset(struct fuse_session *se)
3565{
3566 se->exited = 0;
3567 se->error = 0;
3568}
3569
3570__attribute__((no_sanitize_thread))
3571int fuse_session_exited(struct fuse_session *se)
3572{
3573 return se->exited;
3574}
#define FUSE_CAP_IOCTL_DIR
#define FUSE_CAP_DONT_MASK
#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
#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)
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_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_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_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)
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
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]
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:66
uint32_t poll_events
uint32_t cache_readdir
Definition fuse_common.h:95
uint32_t nonseekable
Definition fuse_common.h:84
int32_t backing_id
uint32_t parallel_direct_writes
uint32_t noflush
Definition fuse_common.h:99
uint32_t flush
Definition fuse_common.h:80
uint32_t direct_io
Definition fuse_common.h:69
uint32_t keep_cache
Definition fuse_common.h:75
fuse-3.17.2/doc/html/fuse__lowlevel_8h.html0000644000175000017500000037710215002273413017533 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_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 290 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 380 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 2941 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 2503 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 2490 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 2484 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 2416 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 2399 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 2609 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 2529 of file fuse_lowlevel.c.

◆ fuse_lowlevel_version()

void fuse_lowlevel_version ( void  )

Print low-level version information to stdout.

Definition at line 2934 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 484 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 464 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 977 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 528 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 448 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 916 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 432 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 335 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 1075 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 1096 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 1005 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 269 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 960 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 1130 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 340 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 509 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 1120 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 479 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 938 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 518 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 950 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 2659 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 3548 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 2664 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 2677 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 2654 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 2951 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 3469 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 3414 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 2771 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 3226 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 3474 of file fuse_lowlevel.c.

fuse-3.17.2/doc/html/fuse__opt_8c_source.html0000644000175000017500000023505315002273413020055 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 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_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
#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
#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_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
const char * templ
Definition fuse_opt.h:79
unsigned long offset
Definition fuse_opt.h:85
int value
Definition fuse_opt.h:91
fuse-3.17.2/doc/html/fuse__opt_8h.html0000644000175000017500000007463515002273413016511 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.17.2/doc/html/fuse__opt_8h_source.html0000644000175000017500000005116115002273413020056 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 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
const char * templ
Definition fuse_opt.h:79
unsigned long offset
Definition fuse_opt.h:85
int value
Definition fuse_opt.h:91
fuse-3.17.2/doc/html/fuse__signals_8c_source.html0000644000175000017500000010411015002273413020700 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 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#define BT_STACK_SZ (1024 * 1024)
31static void *backtrace_buffer[BT_STACK_SZ];
32
33static void dump_stack(void)
34{
35#ifdef HAVE_BACKTRACE
36 char **strings;
37
38 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
39 strings = backtrace_symbols(backtrace_buffer, nptrs);
40
41 if (strings == NULL) {
42 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
43 strerror(errno));
44 return;
45 }
46
47 for (int idx = 0; idx < nptrs; idx++)
48 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
49
50 free(strings);
51#endif
52}
53
54static void exit_handler(int sig)
55{
56 if (fuse_instance == NULL)
57 return;
58
59 fuse_session_exit(fuse_instance);
60
61 if (sig < 0) {
62 fuse_log(FUSE_LOG_ERR,
63 "assertion error: signal value <= 0\n");
64 dump_stack();
65 abort();
66 fuse_instance->error = sig;
67 }
68
69 fuse_instance->error = sig;
70}
71
72static void exit_backtrace(int sig)
73{
74 if (fuse_instance == NULL)
75 return;
76
77 fuse_session_exit(fuse_instance);
78
79 fuse_remove_signal_handlers(fuse_instance);
80 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
81 dump_stack();
82 abort();
83}
84
85
86static void do_nothing(int sig)
87{
88 (void) sig;
89}
90
91static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
92{
93 struct sigaction sa;
94 struct sigaction old_sa;
95
96 memset(&sa, 0, sizeof(struct sigaction));
97 sa.sa_handler = remove ? SIG_DFL : handler;
98 sigemptyset(&(sa.sa_mask));
99 sa.sa_flags = 0;
100
101 if (sigaction(sig, NULL, &old_sa) == -1) {
102 perror("fuse: cannot get old signal handler");
103 return -1;
104 }
105
106 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
107 sigaction(sig, &sa, NULL) == -1) {
108 perror("fuse: cannot set signal handler");
109 return -1;
110 }
111 return 0;
112}
113
114static int _fuse_set_signal_handlers(int signals[], int nr_signals,
115 void (*handler)(int))
116{
117 for (int idx = 0; idx < nr_signals; idx++) {
118 int signal = signals[idx];
119
120 /*
121 * If we used SIG_IGN instead of the do_nothing function,
122 * then we would be unable to tell if we set SIG_IGN (and
123 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
124 * or if it was already set to SIG_IGN (and should be left
125 * untouched.
126 */
127 if (set_one_signal_handler(signal, handler, 0) == -1) {
128 fuse_log(FUSE_LOG_ERR,
129 "Failed to install signal handler for sig %d\n",
130 signal);
131 return -1;
132 }
133 }
134
135 return 0;
136}
137
138int fuse_set_signal_handlers(struct fuse_session *se)
139{
140 size_t nr_signals;
141 int rc;
142
143 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
144 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
145 if (rc < 0)
146 return rc;
147
148 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
149 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
150 if (rc < 0)
151 return rc;
152
153 /*
154 * needs to be set independently if already set, as some applications
155 * may have multiple sessions and might rely on traditional behavior
156 * that the last session is used.
157 */
158 fuse_instance = se;
159
160 return 0;
161}
162
163int fuse_set_fail_signal_handlers(struct fuse_session *se)
164{
165 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
166
167 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
168 exit_backtrace);
169 if (rc < 0)
170 return rc;
171
172 /* See fuse_set_signal_handlers, why set unconditionally */
173 fuse_instance = se;
174
175 return 0;
176}
177
178static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
179 void (*handler)(int))
180{
181 for (int idx = 0; idx < nr_signals; idx++)
182 set_one_signal_handler(signals[idx], handler, 1);
183}
184
185void fuse_remove_signal_handlers(struct fuse_session *se)
186{
187 size_t nr_signals;
188
189 if (fuse_instance != se)
190 fuse_log(FUSE_LOG_ERR,
191 "fuse: fuse_remove_signal_handlers: unknown session\n");
192 else
193 fuse_instance = NULL;
194
195 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
196 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
197
198 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
199 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
200
201 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
202 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
203}
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.17.2/doc/html/fusermount_8c_source.html0000644000175000017500000077610515002273413020311 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 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_LINUX_CLOSE_RANGE_H
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_LINUX_CLOSE_RANGE_H
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.17.2/doc/html/globals.html0000644000175000017500000005335415002273413015545 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.17.2/doc/html/globals_defs.html0000644000175000017500000001602315002273413016536 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented macros with links to the documentation:

- f -

fuse-3.17.2/doc/html/globals_enum.html0000644000175000017500000000510215002273413016555 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented enums with links to the documentation:
fuse-3.17.2/doc/html/globals_eval.html0000644000175000017500000000642115002273413016545 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented enum values with links to the documentation:
fuse-3.17.2/doc/html/globals_func.html0000644000175000017500000003273315002273413016556 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented functions with links to the documentation:

- f -

fuse-3.17.2/doc/html/globals_type.html0000644000175000017500000000527315002273413016603 0ustar berndbernd libfuse: Globals
libfuse
Here is a list of all documented typedefs with links to the documentation:
fuse-3.17.2/doc/html/hello_8c.html0000644000175000017500000006306514753351711015631 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 COPYING.
*/
#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;
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
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.17.2/doc/html/hello_8c_source.html0000644000175000017500000010717014753351710017204 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 COPYING.
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 return NULL;
61}
62
63static int hello_getattr(const char *path, struct stat *stbuf,
64 struct fuse_file_info *fi)
65{
66 (void) fi;
67 int res = 0;
68
69 memset(stbuf, 0, sizeof(struct stat));
70 if (strcmp(path, "/") == 0) {
71 stbuf->st_mode = S_IFDIR | 0755;
72 stbuf->st_nlink = 2;
73 } else if (strcmp(path+1, options.filename) == 0) {
74 stbuf->st_mode = S_IFREG | 0444;
75 stbuf->st_nlink = 1;
76 stbuf->st_size = strlen(options.contents);
77 } else
78 res = -ENOENT;
79
80 return res;
81}
82
83static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
84 off_t offset, struct fuse_file_info *fi,
85 enum fuse_readdir_flags flags)
86{
87 (void) offset;
88 (void) fi;
89 (void) flags;
90
91 if (strcmp(path, "/") != 0)
92 return -ENOENT;
93
94 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
95 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
96 filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
97
98 return 0;
99}
100
101static int hello_open(const char *path, struct fuse_file_info *fi)
102{
103 if (strcmp(path+1, options.filename) != 0)
104 return -ENOENT;
105
106 if ((fi->flags & O_ACCMODE) != O_RDONLY)
107 return -EACCES;
108
109 return 0;
110}
111
112static int hello_read(const char *path, char *buf, size_t size, off_t offset,
113 struct fuse_file_info *fi)
114{
115 size_t len;
116 (void) fi;
117 if(strcmp(path+1, options.filename) != 0)
118 return -ENOENT;
119
120 len = strlen(options.contents);
121 if (offset < len) {
122 if (offset + size > len)
123 size = len - offset;
124 memcpy(buf, options.contents + offset, size);
125 } else
126 size = 0;
127
128 return size;
129}
130
131static const struct fuse_operations hello_oper = {
132 .init = hello_init,
133 .getattr = hello_getattr,
134 .readdir = hello_readdir,
135 .open = hello_open,
136 .read = hello_read,
137};
138
139static void show_help(const char *progname)
140{
141 printf("usage: %s [options] <mountpoint>\n\n", progname);
142 printf("File-system specific options:\n"
143 " --name=<s> Name of the \"hello\" file\n"
144 " (default: \"hello\")\n"
145 " --contents=<s> Contents \"hello\" file\n"
146 " (default \"Hello, World!\\n\")\n"
147 "\n");
148}
149
150int main(int argc, char *argv[])
151{
152 int ret;
153 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
154
155 /* Set defaults -- we have to use strdup so that
156 fuse_opt_parse can free the defaults if other
157 values are specified */
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 if (options.show_help) {
171 show_help(argv[0]);
172 assert(fuse_opt_add_arg(&args, "--help") == 0);
173 args.argv[0][0] = '\0';
174 }
175
176 ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
177 fuse_opt_free_args(&args);
178 return ret;
179}
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
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.17.2/doc/html/hello__ll_8c.html0000644000175000017500000012310415002273413016434 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

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 COPYING.
*/
#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:5236
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.17.2/doc/html/hello__ll_8c_source.html0000644000175000017500000016574115002273413020031 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 COPYING.
7*/
8
21#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
22
23#include <fuse_lowlevel.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <assert.h>
31
32static const char *hello_str = "Hello World!\n";
33static const char *hello_name = "hello";
34
35static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
36{
37 stbuf->st_ino = ino;
38 switch (ino) {
39 case 1:
40 stbuf->st_mode = S_IFDIR | 0755;
41 stbuf->st_nlink = 2;
42 break;
43
44 case 2:
45 stbuf->st_mode = S_IFREG | 0444;
46 stbuf->st_nlink = 1;
47 stbuf->st_size = strlen(hello_str);
48 break;
49
50 default:
51 return -1;
52 }
53 return 0;
54}
55
56static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
57{
58 (void)userdata;
59
60 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
61 conn->no_interrupt = 1;
62
63 /* Test setting flags the old way */
65 conn->want &= ~FUSE_CAP_ASYNC_READ;
66}
67
68static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
69 struct fuse_file_info *fi)
70{
71 struct stat stbuf;
72
73 (void) fi;
74
75 memset(&stbuf, 0, sizeof(stbuf));
76 if (hello_stat(ino, &stbuf) == -1)
77 fuse_reply_err(req, ENOENT);
78 else
79 fuse_reply_attr(req, &stbuf, 1.0);
80}
81
82static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
83{
84 struct fuse_entry_param e;
85
86 if (parent != 1 || strcmp(name, hello_name) != 0)
87 fuse_reply_err(req, ENOENT);
88 else {
89 memset(&e, 0, sizeof(e));
90 e.ino = 2;
91 e.attr_timeout = 1.0;
92 e.entry_timeout = 1.0;
93 hello_stat(e.ino, &e.attr);
94
95 fuse_reply_entry(req, &e);
96 }
97}
98
99struct dirbuf {
100 char *p;
101 size_t size;
102};
103
104static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
105 fuse_ino_t ino)
106{
107 struct stat stbuf;
108 size_t oldsize = b->size;
109 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
110 b->p = (char *) realloc(b->p, b->size);
111 memset(&stbuf, 0, sizeof(stbuf));
112 stbuf.st_ino = ino;
113 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
114 b->size);
115}
116
117#define min(x, y) ((x) < (y) ? (x) : (y))
118
119static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
120 off_t off, size_t maxsize)
121{
122 if (off < bufsize)
123 return fuse_reply_buf(req, buf + off,
124 min(bufsize - off, maxsize));
125 else
126 return fuse_reply_buf(req, NULL, 0);
127}
128
129static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
130 off_t off, struct fuse_file_info *fi)
131{
132 (void) fi;
133
134 if (ino != 1)
135 fuse_reply_err(req, ENOTDIR);
136 else {
137 struct dirbuf b;
138
139 memset(&b, 0, sizeof(b));
140 dirbuf_add(req, &b, ".", 1);
141 dirbuf_add(req, &b, "..", 1);
142 dirbuf_add(req, &b, hello_name, 2);
143 reply_buf_limited(req, b.p, b.size, off, size);
144 free(b.p);
145 }
146}
147
148static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
149 struct fuse_file_info *fi)
150{
151 if (ino != 2)
152 fuse_reply_err(req, EISDIR);
153 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
154 fuse_reply_err(req, EACCES);
155 else
156 fuse_reply_open(req, fi);
157}
158
159static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
160 off_t off, struct fuse_file_info *fi)
161{
162 (void) fi;
163
164 assert(ino == 2);
165 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
166}
167
168static void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
169 size_t size)
170{
171 (void)size;
172 assert(ino == 1 || ino == 2);
173 if (strcmp(name, "hello_ll_getxattr_name") == 0)
174 {
175 const char *buf = "hello_ll_getxattr_value";
176 fuse_reply_buf(req, buf, strlen(buf));
177 }
178 else
179 {
180 fuse_reply_err(req, ENOTSUP);
181 }
182}
183
184static void hello_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
185 const char *value, size_t size, int flags)
186{
187 (void)flags;
188 (void)size;
189 assert(ino == 1 || ino == 2);
190 const char* exp_val = "hello_ll_setxattr_value";
191 if (strcmp(name, "hello_ll_setxattr_name") == 0 &&
192 strlen(exp_val) == size &&
193 strncmp(value, exp_val, size) == 0)
194 {
195 fuse_reply_err(req, 0);
196 }
197 else
198 {
199 fuse_reply_err(req, ENOTSUP);
200 }
201}
202
203static void hello_ll_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
204{
205 assert(ino == 1 || ino == 2);
206 if (strcmp(name, "hello_ll_removexattr_name") == 0)
207 {
208 fuse_reply_err(req, 0);
209 }
210 else
211 {
212 fuse_reply_err(req, ENOTSUP);
213 }
214}
215
216static const struct fuse_lowlevel_ops hello_ll_oper = {
217 .init = hello_ll_init,
218 .lookup = hello_ll_lookup,
219 .getattr = hello_ll_getattr,
220 .readdir = hello_ll_readdir,
221 .open = hello_ll_open,
222 .read = hello_ll_read,
223 .setxattr = hello_ll_setxattr,
224 .getxattr = hello_ll_getxattr,
225 .removexattr = hello_ll_removexattr,
226};
227
228int main(int argc, char *argv[])
229{
230 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
231 struct fuse_session *se;
232 struct fuse_cmdline_opts opts;
233 struct fuse_loop_config *config;
234 int ret = -1;
235
236 if (fuse_parse_cmdline(&args, &opts) != 0)
237 return 1;
238 if (opts.show_help) {
239 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
242 ret = 0;
243 goto err_out1;
244 } else if (opts.show_version) {
245 printf("FUSE library version %s\n", fuse_pkgversion());
247 ret = 0;
248 goto err_out1;
249 }
250
251 if(opts.mountpoint == NULL) {
252 printf("usage: %s [options] <mountpoint>\n", argv[0]);
253 printf(" %s --help\n", argv[0]);
254 ret = 1;
255 goto err_out1;
256 }
257
258 se = fuse_session_new(&args, &hello_ll_oper,
259 sizeof(hello_ll_oper), NULL);
260 if (se == NULL)
261 goto err_out1;
262
263 if (fuse_set_signal_handlers(se) != 0)
264 goto err_out2;
265
266 if (fuse_session_mount(se, opts.mountpoint) != 0)
267 goto err_out3;
268
269 fuse_daemonize(opts.foreground);
270
271 /* Block until ctrl+c or fusermount -u */
272 if (opts.singlethread)
273 ret = fuse_session_loop(se);
274 else {
275 config = fuse_loop_cfg_create();
276 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
277 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
278 ret = fuse_session_loop_mt(se, config);
279 fuse_loop_cfg_destroy(config);
280 config = NULL;
281 }
282
284err_out3:
286err_out2:
288err_out1:
289 free(opts.mountpoint);
290 fuse_opt_free_args(&args);
291
292 return ret ? 1 : 0;
293}
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_ASYNC_READ
const char * fuse_pkgversion(void)
Definition fuse.c:5236
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.17.2/doc/html/helper_8c_source.html0000644000175000017500000031436015002273413017350 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 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 if (cond) conn->want_ext |= (cap)
428#define LL_DISABLE(cond,cap) \
429 if (cond) conn->want_ext &= ~(cap)
430
431 LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
432 LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
433
434 LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
435 LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
436
437 LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
438 LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
439
440 LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
441 LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
442
443 LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
444 LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
445
446 LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
447 LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
448
449 LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
450 LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
451
452 LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
453 LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
454
455 LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
456 LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
457}
458
459struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
460{
461 struct fuse_conn_info_opts *opts;
462
463 opts = calloc(1, sizeof(struct fuse_conn_info_opts));
464 if(opts == NULL) {
465 fuse_log(FUSE_LOG_ERR, "calloc failed\n");
466 return NULL;
467 }
468 if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
469 free(opts);
470 return NULL;
471 }
472 return opts;
473}
474
475int fuse_open_channel(const char *mountpoint, const char* options)
476{
477 struct mount_opts *opts = NULL;
478 int fd = -1;
479 const char *argv[] = { "", "-o", options };
480 int argc = sizeof(argv) / sizeof(argv[0]);
481 struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv);
482
483 opts = parse_mount_opts(&args);
484 if (opts == NULL)
485 return -1;
486
487 fd = fuse_kern_mount(mountpoint, opts);
488 destroy_mount_opts(opts);
489
490 return fd;
491}
int fuse_mount(struct fuse *f, const char *mountpoint)
Definition fuse.c:5222
void fuse_destroy(struct fuse *f)
Definition fuse.c:5171
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:4602
void fuse_lib_help(struct fuse_args *args)
Definition fuse.c:4769
int fuse_open_channel(const char *mountpoint, const char *options)
Definition helper.c:475
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4545
void fuse_unmount(struct fuse *f)
Definition fuse.c:5227
#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
void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
Definition helper.c:412
#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:459
#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
int fuse_parse_cmdline_30(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition helper.c:237
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
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.17.2/doc/html/iconv_8c_source.html0000644000175000017500000034537715002273413017223 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 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:4669
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:4879
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.17.2/doc/html/index.html0000644000175000017500000001043615002273413015223 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.17.2/doc/html/invalidate__path_8c.html0000644000175000017500000012021215002273413017773 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 COPYING.
*/
#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:5222
void fuse_destroy(struct fuse *f)
Definition fuse.c:5171
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4698
int fuse_loop(struct fuse *f)
Definition fuse.c:4602
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:4769
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4545
void fuse_unmount(struct fuse *f)
Definition fuse.c:5227
@ 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:5236
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:75
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.17.2/doc/html/invalidate__path_8c_source.html0000644000175000017500000016077715002273413021377 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 COPYING.
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:5222
void fuse_destroy(struct fuse *f)
Definition fuse.c:5171
int fuse_invalidate_path(struct fuse *f, const char *path)
Definition fuse.c:4698
int fuse_loop(struct fuse *f)
Definition fuse.c:4602
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:4769
struct fuse_session * fuse_get_session(struct fuse *f)
Definition fuse.c:4545
void fuse_unmount(struct fuse *f)
Definition fuse.c:5227
@ 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:5236
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:75
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.17.2/doc/html/ioctl_8c.html0000644000175000017500000005435315002273413015626 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 COPYING.
*/
#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.17.2/doc/html/ioctl_8c_source.html0000644000175000017500000010633115002273413017200 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 COPYING.
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.17.2/doc/html/ioctl_8h.html0000644000175000017500000001242615002273413015626 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 COPYING.
*/
#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.17.2/doc/html/ioctl_8h_source.html0000644000175000017500000001567315002273413017215 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 COPYING.
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.17.2/doc/html/ioctl__client_8c.html0000644000175000017500000001773215002273413017323 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 tests the ioctl.c example file systsem.
This program can be distributed under the terms of the GNU GPLv2.
See the file COPYING.
*/
#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.17.2/doc/html/ioctl__client_8c_source.html0000644000175000017500000002642615002273413020703 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 tests the ioctl.c example file systsem.
7
8 This program can be distributed under the terms of the GNU GPLv2.
9 See the file COPYING.
10*/
11
24#include <sys/types.h>
25#include <fcntl.h>
26#include <sys/stat.h>
27#include <sys/ioctl.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <ctype.h>
31#include <errno.h>
32#include <unistd.h>
33#include "ioctl.h"
34
35const char *usage =
36"Usage: fioclient FIOC_FILE [size]\n"
37"\n"
38"Get size if <size> is omitted, set size otherwise\n"
39"\n";
40
41int main(int argc, char **argv)
42{
43 size_t size;
44 int fd;
45 int ret = 0;
46
47 if (argc < 2) {
48 fprintf(stderr, "%s", usage);
49 return 1;
50 }
51
52 fd = open(argv[1], O_RDWR);
53 if (fd < 0) {
54 perror("open");
55 return 1;
56 }
57
58 if (argc == 2) {
59 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
60 perror("ioctl");
61 ret = 1;
62 goto out;
63 }
64 printf("%zu\n", size);
65 } else {
66 size = strtoul(argv[2], NULL, 0);
67 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
68 perror("ioctl");
69 ret = 1;
70 goto out;
71 }
72 }
73out:
74 close(fd);
75 return ret;
76}
fuse-3.17.2/doc/html/jquery.js0000644000175000017500000053076615002273413015120 0ustar berndbernd/*! 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+~]|"+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="",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="";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":{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:\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)[^>]*|#([\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\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="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_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",""]);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\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").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="
",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").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",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=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.widthx(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?0n?0=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").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"),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),st.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").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.maxWidthd,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=f[g]?0:Math.min(f[g],n));!a&&1-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",{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.topviewportBottom||Math.abs(coords.bottom-session.windowHeight)>viewportBottom){collisions|=Collision.bottom}if(coords.leftviewportRight){collisions|=Collision.left}if(coords.left+elementWidth>viewportRight||coords.right1)){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=$("").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=$('
').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",$([$('')[0],$('')[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.y0;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.17.2/doc/html/menu.js0000644000175000017500000001344515002273413014533 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 */ function initMenu(relPath,searchEnabled,serverSide,searchPage,search) { function makeTree(data,relPath) { var result=''; if ('children' in data) { result+='
    '; 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+='
  • '+ data.children[i].text+''+ makeTree(data.children[i],relPath)+'
  • '; } result+='
'; } return result; } var searchBoxHtml; if (searchEnabled) { if (serverSide) { searchBoxHtml='
'+ '
'+ '
 '+ ''+ '
'+ '
'+ '
'+ '
'; } else { searchBoxHtml='
'+ ''+ ' '+ ''+ ''+ ''+ ''+ ''+ '
'; } } $('#main-nav').before('
'+ ''+ ''+ '
'); $('#main-nav').append(makeTree(menudata,relPath)); $('#main-nav').children(':first').addClass('sm sm-dox').attr('id','main-menu'); if (searchBoxHtml) { $('#main-menu').append('
  • '); } 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.17.2/doc/html/menudata.js0000644000175000017500000000762415002273413015367 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.17.2/doc/html/mount_8c_source.html0000644000175000017500000034731615002273413017242 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 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.17.2/doc/html/mount_8fuse_8c_source.html0000644000175000017500000021154615002273413020347 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 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:475
    fuse-3.17.2/doc/html/mount__bsd_8c_source.html0000644000175000017500000013017415002273413020221 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 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.17.2/doc/html/mount__util_8c_source.html0000644000175000017500000015674715002273413020444 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 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
    80 return 1;
    81}
    82#endif /* IGNORE_MTAB */
    83
    84static int add_mount(const char *progname, const char *fsname,
    85 const char *mnt, const char *type, const char *opts)
    86{
    87 int res;
    88 int status;
    89 sigset_t blockmask;
    90 sigset_t oldmask;
    91
    92 sigemptyset(&blockmask);
    93 sigaddset(&blockmask, SIGCHLD);
    94 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    95 if (res == -1) {
    96 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    97 return -1;
    98 }
    99
    100 res = fork();
    101 if (res == -1) {
    102 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    103 goto out_restore;
    104 }
    105 if (res == 0) {
    106 char *env = NULL;
    107
    108 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    109
    110 if(setuid(geteuid()) == -1) {
    111 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    112 res = -1;
    113 goto out_restore;
    114 }
    115
    116 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
    117 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
    118 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
    119 progname, strerror(errno));
    120 exit(1);
    121 }
    122 res = waitpid(res, &status, 0);
    123 if (res == -1)
    124 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    125
    126 if (status != 0)
    127 res = -1;
    128
    129 out_restore:
    130 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    131
    132 return res;
    133}
    134
    135int fuse_mnt_add_mount(const char *progname, const char *fsname,
    136 const char *mnt, const char *type, const char *opts)
    137{
    138 if (!mtab_needs_update(mnt))
    139 return 0;
    140
    141 return add_mount(progname, fsname, mnt, type, opts);
    142}
    143
    144static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
    145{
    146 int res;
    147 int status;
    148 sigset_t blockmask;
    149 sigset_t oldmask;
    150
    151 sigemptyset(&blockmask);
    152 sigaddset(&blockmask, SIGCHLD);
    153 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    154 if (res == -1) {
    155 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    156 return -1;
    157 }
    158
    159 res = fork();
    160 if (res == -1) {
    161 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    162 goto out_restore;
    163 }
    164 if (res == 0) {
    165 char *env = NULL;
    166
    167 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    168
    169 if(setuid(geteuid()) == -1) {
    170 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    171 res = -1;
    172 goto out_restore;
    173 }
    174
    175 if (lazy) {
    176 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
    177 "-l", NULL, &env);
    178 } else {
    179 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
    180 NULL, &env);
    181 }
    182 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
    183 progname, strerror(errno));
    184 exit(1);
    185 }
    186 res = waitpid(res, &status, 0);
    187 if (res == -1)
    188 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    189
    190 if (status != 0) {
    191 res = -1;
    192 }
    193
    194 out_restore:
    195 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    196 return res;
    197
    198}
    199
    200int fuse_mnt_umount(const char *progname, const char *abs_mnt,
    201 const char *rel_mnt, int lazy)
    202{
    203 int res;
    204
    205 if (!mtab_needs_update(abs_mnt)) {
    206 res = umount2(rel_mnt, lazy ? 2 : 0);
    207 if (res == -1)
    208 fprintf(stderr, "%s: failed to unmount %s: %s\n",
    209 progname, abs_mnt, strerror(errno));
    210 return res;
    211 }
    212
    213 return exec_umount(progname, rel_mnt, lazy);
    214}
    215
    216static int remove_mount(const char *progname, const char *mnt)
    217{
    218 int res;
    219 int status;
    220 sigset_t blockmask;
    221 sigset_t oldmask;
    222
    223 sigemptyset(&blockmask);
    224 sigaddset(&blockmask, SIGCHLD);
    225 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    226 if (res == -1) {
    227 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    228 return -1;
    229 }
    230
    231 res = fork();
    232 if (res == -1) {
    233 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    234 goto out_restore;
    235 }
    236 if (res == 0) {
    237 char *env = NULL;
    238
    239 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    240
    241 if(setuid(geteuid()) == -1) {
    242 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    243 res = -1;
    244 goto out_restore;
    245 }
    246
    247 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
    248 "--fake", mnt, NULL, &env);
    249 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
    250 progname, strerror(errno));
    251 exit(1);
    252 }
    253 res = waitpid(res, &status, 0);
    254 if (res == -1)
    255 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    256
    257 if (status != 0)
    258 res = -1;
    259
    260 out_restore:
    261 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    262 return res;
    263}
    264
    265int fuse_mnt_remove_mount(const char *progname, const char *mnt)
    266{
    267 if (!mtab_needs_update(mnt))
    268 return 0;
    269
    270 return remove_mount(progname, mnt);
    271}
    272
    273char *fuse_mnt_resolve_path(const char *progname, const char *orig)
    274{
    275 char buf[PATH_MAX];
    276 char *copy;
    277 char *dst;
    278 char *end;
    279 char *lastcomp;
    280 const char *toresolv;
    281
    282 if (!orig[0]) {
    283 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
    284 orig);
    285 return NULL;
    286 }
    287
    288 copy = strdup(orig);
    289 if (copy == NULL) {
    290 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    291 return NULL;
    292 }
    293
    294 toresolv = copy;
    295 lastcomp = NULL;
    296 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
    297 if (end[0] != '/') {
    298 char *tmp;
    299 end[1] = '\0';
    300 tmp = strrchr(copy, '/');
    301 if (tmp == NULL) {
    302 lastcomp = copy;
    303 toresolv = ".";
    304 } else {
    305 lastcomp = tmp + 1;
    306 if (tmp == copy)
    307 toresolv = "/";
    308 }
    309 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
    310 lastcomp = NULL;
    311 toresolv = copy;
    312 }
    313 else if (tmp)
    314 tmp[0] = '\0';
    315 }
    316 if (realpath(toresolv, buf) == NULL) {
    317 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
    318 strerror(errno));
    319 free(copy);
    320 return NULL;
    321 }
    322 if (lastcomp == NULL)
    323 dst = strdup(buf);
    324 else {
    325 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
    326 if (dst) {
    327 unsigned buflen = strlen(buf);
    328 if (buflen && buf[buflen-1] == '/')
    329 sprintf(dst, "%s%s", buf, lastcomp);
    330 else
    331 sprintf(dst, "%s/%s", buf, lastcomp);
    332 }
    333 }
    334 free(copy);
    335 if (dst == NULL)
    336 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    337 return dst;
    338}
    339
    340int fuse_mnt_check_fuseblk(void)
    341{
    342 char buf[256];
    343 FILE *f = fopen("/proc/filesystems", "r");
    344 if (!f)
    345 return 1;
    346
    347 while (fgets(buf, sizeof(buf), f))
    348 if (strstr(buf, "fuseblk\n")) {
    349 fclose(f);
    350 return 1;
    351 }
    352
    353 fclose(f);
    354 return 0;
    355}
    356
    357int fuse_mnt_parse_fuse_fd(const char *mountpoint)
    358{
    359 int fd = -1;
    360 int len = 0;
    361
    362 if (mountpoint == NULL) {
    363 fprintf(stderr, "Invalid null-ptr mount-point!\n");
    364 return -1;
    365 }
    366
    367 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
    368 len == strlen(mountpoint)) {
    369 return fd;
    370 }
    371
    372 return -1;
    373}
    fuse-3.17.2/doc/html/mount__util_8h_source.html0000644000175000017500000001345115002273413020431 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 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.17.2/doc/html/nav_f.png0000644000175000017500000000023115002273413015015 0ustar berndberndPNG  IHDR8`IDATxK Eі[BmkHprӼ.ꎤR6Z VIE5jliIJ0/u޿6sH yIENDB`fuse-3.17.2/doc/html/nav_g.png0000644000175000017500000000013715002273413015023 0ustar berndberndPNG  IHDR1&IDATx1 OHf_ ->~M iMS<IENDB`fuse-3.17.2/doc/html/nav_h.png0000644000175000017500000000014215002273413015020 0ustar berndberndPNG  IHDR ,@)IDATxA @BQۛТ) ) aܿoRlIENDB`fuse-3.17.2/doc/html/notify__inval__entry_8c.html0000644000175000017500000014075015002273413020731 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

    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 COPYING.
    */
    #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;
    };
    static struct options options = {
    .timeout = 5,
    .no_notify = 0,
    .update_interval = 1,
    .only_expire = 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),
    };
    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;
    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");
    printf("Exiting...\n");
    // Make sure to exit now, rather than on next request from userspace
    pthread_kill(main_thread, SIGPIPE);
    break;
    }
    // 1) ret == 0: successful expire of an existing entry
    // 2) ret == -ENOENT: kernel has already expired the entry /
    // entry does not exist anymore in the kernel
    assert(ret == 0 || ret == -ENOENT);
    } else { // invalidate entry
    (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
    }
    }
    free(old_name);
    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"
    " --timeout=<secs> Timeout for kernel caches\n"
    " --update-interval=<secs> Update-rate of file system contents\n"
    " --no-notify Disable kernel notifications\n"
    " --only-expire Expire entries instead of invalidating them\n"
    "\n");
    }
    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;
    }
    /* 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:5236
    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)
    #define FUSE_ROOT_ID
    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
    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.17.2/doc/html/notify__inval__entry_8c_source.html0000644000175000017500000020327615002273413022314 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 COPYING.
    7*/
    8
    79#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    80
    81#include <fuse_lowlevel.h>
    82#include <stdio.h>
    83#include <stdlib.h>
    84#include <string.h>
    85#include <errno.h>
    86#include <fcntl.h>
    87#include <assert.h>
    88#include <signal.h>
    89#include <stddef.h>
    90#include <sys/stat.h>
    91#include <unistd.h>
    92#include <pthread.h>
    93
    94#define MAX_STR_LEN 128
    95static char file_name[MAX_STR_LEN];
    96static fuse_ino_t file_ino = 2;
    97static int lookup_cnt = 0;
    98static pthread_t main_thread;
    99
    100/* Command line parsing */
    101struct options {
    102 int no_notify;
    103 float timeout;
    104 int update_interval;
    105 int only_expire;
    106};
    107static struct options options = {
    108 .timeout = 5,
    109 .no_notify = 0,
    110 .update_interval = 1,
    111 .only_expire = 0,
    112};
    113
    114#define OPTION(t, p) \
    115 { t, offsetof(struct options, p), 1 }
    116static const struct fuse_opt option_spec[] = {
    117 OPTION("--no-notify", no_notify),
    118 OPTION("--update-interval=%d", update_interval),
    119 OPTION("--timeout=%f", timeout),
    120 OPTION("--only-expire", only_expire),
    122};
    123
    124static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
    125 stbuf->st_ino = ino;
    126 if (ino == FUSE_ROOT_ID) {
    127 stbuf->st_mode = S_IFDIR | 0755;
    128 stbuf->st_nlink = 1;
    129 }
    130
    131 else if (ino == file_ino) {
    132 stbuf->st_mode = S_IFREG | 0000;
    133 stbuf->st_nlink = 1;
    134 stbuf->st_size = 0;
    135 }
    136
    137 else
    138 return -1;
    139
    140 return 0;
    141}
    142
    143static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
    144 (void)userdata;
    145
    146 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    147 conn->no_interrupt = 1;
    148}
    149
    150static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
    151 const char *name) {
    152 struct fuse_entry_param e;
    153 memset(&e, 0, sizeof(e));
    154
    155 if (parent != FUSE_ROOT_ID)
    156 goto err_out;
    157 else if (strcmp(name, file_name) == 0) {
    158 e.ino = file_ino;
    159 lookup_cnt++;
    160 } else
    161 goto err_out;
    162
    163 e.attr_timeout = options.timeout;
    164 e.entry_timeout = options.timeout;
    165 if (tfs_stat(e.ino, &e.attr) != 0)
    166 goto err_out;
    167 fuse_reply_entry(req, &e);
    168 return;
    169
    170err_out:
    171 fuse_reply_err(req, ENOENT);
    172}
    173
    174static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
    175 uint64_t nlookup) {
    176 (void) req;
    177 if(ino == file_ino)
    178 lookup_cnt -= nlookup;
    179 else
    180 assert(ino == FUSE_ROOT_ID);
    181 fuse_reply_none(req);
    182}
    183
    184static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
    185 struct fuse_file_info *fi) {
    186 struct stat stbuf;
    187
    188 (void) fi;
    189
    190 memset(&stbuf, 0, sizeof(stbuf));
    191 if (tfs_stat(ino, &stbuf) != 0)
    192 fuse_reply_err(req, ENOENT);
    193 else
    194 fuse_reply_attr(req, &stbuf, options.timeout);
    195}
    196
    197struct dirbuf {
    198 char *p;
    199 size_t size;
    200};
    201
    202static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
    203 fuse_ino_t ino) {
    204 struct stat stbuf;
    205 size_t oldsize = b->size;
    206 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
    207 b->p = (char *) realloc(b->p, b->size);
    208 memset(&stbuf, 0, sizeof(stbuf));
    209 stbuf.st_ino = ino;
    210 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
    211 b->size);
    212}
    213
    214#define min(x, y) ((x) < (y) ? (x) : (y))
    215
    216static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
    217 off_t off, size_t maxsize) {
    218 if (off < bufsize)
    219 return fuse_reply_buf(req, buf + off,
    220 min(bufsize - off, maxsize));
    221 else
    222 return fuse_reply_buf(req, NULL, 0);
    223}
    224
    225static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    226 off_t off, struct fuse_file_info *fi) {
    227 (void) fi;
    228
    229 if (ino != FUSE_ROOT_ID)
    230 fuse_reply_err(req, ENOTDIR);
    231 else {
    232 struct dirbuf b;
    233
    234 memset(&b, 0, sizeof(b));
    235 dirbuf_add(req, &b, file_name, file_ino);
    236 reply_buf_limited(req, b.p, b.size, off, size);
    237 free(b.p);
    238 }
    239}
    240
    241static const struct fuse_lowlevel_ops tfs_oper = {
    242 .init = tfs_init,
    243 .lookup = tfs_lookup,
    244 .getattr = tfs_getattr,
    245 .readdir = tfs_readdir,
    246 .forget = tfs_forget,
    247};
    248
    249static void update_fs(void) {
    250 time_t t;
    251 struct tm *now;
    252 ssize_t ret;
    253
    254 t = time(NULL);
    255 now = localtime(&t);
    256 assert(now != NULL);
    257
    258 ret = strftime(file_name, MAX_STR_LEN,
    259 "Time_is_%Hh_%Mm_%Ss", now);
    260 assert(ret != 0);
    261}
    262
    263static void* update_fs_loop(void *data) {
    264 struct fuse_session *se = (struct fuse_session*) data;
    265 char *old_name;
    266
    267
    268 while(!fuse_session_exited(se)) {
    269 old_name = strdup(file_name);
    270 update_fs();
    271
    272 if (!options.no_notify && lookup_cnt) {
    273 if(options.only_expire) { // expire entry
    275 (se, FUSE_ROOT_ID, old_name, strlen(old_name));
    276
    277 // no kernel support
    278 if (ret == -ENOSYS) {
    279 printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n");
    280 printf("Exiting...\n");
    281
    283 // Make sure to exit now, rather than on next request from userspace
    284 pthread_kill(main_thread, SIGPIPE);
    285
    286 break;
    287 }
    288 // 1) ret == 0: successful expire of an existing entry
    289 // 2) ret == -ENOENT: kernel has already expired the entry /
    290 // entry does not exist anymore in the kernel
    291 assert(ret == 0 || ret == -ENOENT);
    292 } else { // invalidate entry
    294 (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
    295 }
    296 }
    297 free(old_name);
    298 sleep(options.update_interval);
    299 }
    300 return NULL;
    301}
    302
    303static void show_help(const char *progname)
    304{
    305 printf("usage: %s [options] <mountpoint>\n\n", progname);
    306 printf("File-system specific options:\n"
    307 " --timeout=<secs> Timeout for kernel caches\n"
    308 " --update-interval=<secs> Update-rate of file system contents\n"
    309 " --no-notify Disable kernel notifications\n"
    310 " --only-expire Expire entries instead of invalidating them\n"
    311 "\n");
    312}
    313
    314int main(int argc, char *argv[]) {
    315 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    316 struct fuse_session *se;
    317 struct fuse_cmdline_opts opts;
    318 struct fuse_loop_config *config;
    319 pthread_t updater;
    320 int ret = -1;
    321
    322 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
    323 return 1;
    324
    325 if (fuse_parse_cmdline(&args, &opts) != 0)
    326 return 1;
    327 if (opts.show_help) {
    328 show_help(argv[0]);
    331 ret = 0;
    332 goto err_out1;
    333 } else if (opts.show_version) {
    334 printf("FUSE library version %s\n", fuse_pkgversion());
    336 ret = 0;
    337 goto err_out1;
    338 }
    339
    340 /* Initial contents */
    341 update_fs();
    342
    343 se = fuse_session_new(&args, &tfs_oper,
    344 sizeof(tfs_oper), &se);
    345 if (se == NULL)
    346 goto err_out1;
    347
    348 if (fuse_set_signal_handlers(se) != 0)
    349 goto err_out2;
    350
    351 if (fuse_session_mount(se, opts.mountpoint) != 0)
    352 goto err_out3;
    353
    354 fuse_daemonize(opts.foreground);
    355
    356 // Needed to ensure that the main thread continues/restarts processing as soon
    357 // as the fuse session ends (immediately after calling fuse_session_exit() )
    358 // and not only on the next request from userspace
    359 main_thread = pthread_self();
    360
    361 /* Start thread to update file contents */
    362 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
    363 if (ret != 0) {
    364 fprintf(stderr, "pthread_create failed with %s\n",
    365 strerror(ret));
    366 goto err_out3;
    367 }
    368
    369 /* Block until ctrl+c or fusermount -u */
    370 if (opts.singlethread) {
    371 ret = fuse_session_loop(se);
    372 } else {
    373 config = fuse_loop_cfg_create();
    374 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
    375 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
    376 ret = fuse_session_loop_mt(se, config);
    377 fuse_loop_cfg_destroy(config);
    378 config = NULL;
    379 }
    380
    382err_out3:
    384err_out2:
    386err_out1:
    387 free(opts.mountpoint);
    388 fuse_opt_free_args(&args);
    389
    390 return ret ? 1 : 0;
    391}
    392
    393
    int fuse_set_signal_handlers(struct fuse_session *se)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5236
    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)
    #define FUSE_ROOT_ID
    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
    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.17.2/doc/html/notify__inval__inode_8c.html0000644000175000017500000014226115002273413020665 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 COPYING.
    */
    #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:5236
    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)
    #define FUSE_ROOT_ID
    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
    char ** argv
    Definition fuse_opt.h:114
    uint32_t no_interrupt
    fuse_ino_t ino
    uint32_t keep_cache
    Definition fuse_common.h:75
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file notify_inval_inode.c.

    fuse-3.17.2/doc/html/notify__inval__inode_8c_source.html0000644000175000017500000021126515002273413022246 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 COPYING.
    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:5236
    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)
    #define FUSE_ROOT_ID
    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
    char ** argv
    Definition fuse_opt.h:114
    uint32_t no_interrupt
    fuse_ino_t ino
    uint32_t keep_cache
    Definition fuse_common.h:75
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/notify__store__retrieve_8c.html0000644000175000017500000016141515002273413021441 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 COPYING.
    */
    #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:5236
    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)
    #define FUSE_ROOT_ID
    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
    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:75
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file notify_store_retrieve.c.

    fuse-3.17.2/doc/html/notify__store__retrieve_8c_source.html0000644000175000017500000024223115002273413023015 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 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:5236
    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)
    #define FUSE_ROOT_ID
    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
    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:75
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/null_8c.html0000644000175000017500000021330015002273413015453 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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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) */
    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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    static int xmp_fallocate(const char *path, int mode,
    off_t offset, off_t length, struct fuse_file_info *fi)
    {
    (void) path;
    if (mode)
    return -EOPNOTSUPP;
    return -posix_fallocate(fi->fh, offset, length);
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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
    uint32_t direct_io
    Definition fuse_common.h:69
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file null.c.

    fuse-3.17.2/doc/html/null_8c_source.html0000644000175000017500000005706315002273413017047 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 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.17.2/doc/html/open.png0000644000175000017500000000017315002273413014672 0ustar berndberndPNG  IHDR BIDATx 0 ׬ՙ\39b!9{|I>$#ߴ8/z/>2[giU,/~\ 9ٸIENDB`fuse-3.17.2/doc/html/passthrough_8c.html0000644000175000017500000015114615002273413017061 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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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>
    #ifdef __FreeBSD__
    #include <sys/socket.h>
    #include <sys/un.h>
    #endif
    #include <sys/time.h>
    #ifdef HAVE_SETXATTR
    #include <sys/xattr.h>
    #endif
    #include "passthrough_helpers.h"
    static int fill_dir_plus = 0;
    static void *xmp_init(struct fuse_conn_info *conn,
    struct fuse_config *cfg)
    {
    (void) conn;
    cfg->use_ino = 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. */
    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 (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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    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 (mode)
    return -EOPNOTSUPP;
    if(fi == NULL)
    fd = open(path, O_WRONLY);
    else
    fd = fi->fh;
    if (fd == -1)
    return -errno;
    res = -posix_fallocate(fd, offset, length);
    if(fi == NULL)
    close(fd);
    return res;
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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 {
    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
    uint32_t direct_io
    Definition fuse_common.h:69
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file passthrough.c.

    fuse-3.17.2/doc/html/passthrough_8c_source.html0000644000175000017500000026073415002273413020445 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 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
    uint32_t direct_io
    Definition fuse_common.h:69
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641
    fuse-3.17.2/doc/html/passthrough__fh_8c.html0000644000175000017500000021404215002273413017670 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>

    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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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) */
    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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    static int xmp_fallocate(const char *path, int mode,
    off_t offset, off_t length, struct fuse_file_info *fi)
    {
    (void) path;
    if (mode)
    return -EOPNOTSUPP;
    return -posix_fallocate(fi->fh, offset, length);
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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
    uint32_t direct_io
    Definition fuse_common.h:69
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file passthrough_fh.c.

    fuse-3.17.2/doc/html/passthrough__fh_8c_source.html0000644000175000017500000033661315002273413021261 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 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
    uint32_t direct_io
    Definition fuse_common.h:69
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641
    fuse-3.17.2/doc/html/passthrough__helpers_8h_source.html0000644000175000017500000003404615002273413022326 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/*
    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.17.2/doc/html/passthrough__ll_8c.html0000644000175000017500000050121015002273413017676 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, 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 COPYING.
    */
    #define _GNU_SOURCE
    #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    #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) {
    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/%lli -> %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=%lu)\n",
    ino, out_buf.buf[0].size, (unsigned long) 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 = EOPNOTSUPP;
    (void) ino;
    #ifdef HAVE_FALLOCATE
    err = fallocate(fi->fh, mode, offset, length);
    if (err < 0)
    err = errno;
    #elif defined(HAVE_POSIX_FALLOCATE)
    if (mode) {
    fuse_reply_err(req, EOPNOTSUPP);
    return;
    }
    err = posix_fallocate(fi->fh, offset, length);
    #endif
    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, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
    "off=%lu, ino=%" PRIu64 "/fd=%lu, "
    "off=%lu, size=%zd, flags=0x%x)\n",
    ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, 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);
    }
    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,
    };
    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;
    }
    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:5236
    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:95
    uint32_t parallel_direct_writes
    uint32_t direct_io
    Definition fuse_common.h:69
    uint32_t keep_cache
    Definition fuse_common.h:75
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file passthrough_ll.c.

    fuse-3.17.2/doc/html/passthrough__ll_8c_source.html0000644000175000017500000075643015002273413021276 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 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) {
    175 has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
    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) {
    181 has_flag = fuse_set_feature_flag(conn, 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}
    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:5236
    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:95
    uint32_t parallel_direct_writes
    uint32_t direct_io
    Definition fuse_common.h:69
    uint32_t keep_cache
    Definition fuse_common.h:75
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/poll_8c.html0000644000175000017500000007657215002273413015471 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 COPYING.
    */
    #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:4669
    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:84
    uint32_t direct_io
    Definition fuse_common.h:69
    void(* destroy)(void *private_data)
    Definition fuse.h:649

    Definition in file poll.c.

    fuse-3.17.2/doc/html/poll_8c_source.html0000644000175000017500000014213015002273413017031 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 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:4669
    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:84
    uint32_t direct_io
    Definition fuse_common.h:69
    void(* destroy)(void *private_data)
    Definition fuse.h:649
    fuse-3.17.2/doc/html/poll__client_8c.html0000644000175000017500000002112715002273413017150 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 COPYING.
    */
    #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.17.2/doc/html/poll__client_8c_source.html0000644000175000017500000003062415002273413020532 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 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.17.2/doc/html/printcap_8c.html0000644000175000017500000010425615002273413016332 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 COPYING.
    */
    #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
    #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:5236
    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.17.2/doc/html/printcap_8c_source.html0000644000175000017500000012273715002273413017716 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 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
    #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:5236
    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.17.2/doc/html/readdir__inode_8c_source.html0000644000175000017500000002513115002273413021013 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.17.2/doc/html/splitbar.png0000644000175000017500000000047215002273413015553 0ustar berndberndPNG  IHDRMIDATxݡ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=[fIENDB`fuse-3.17.2/doc/html/stracedecode_8c_source.html0000644000175000017500000010603715002273413020516 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.17.2/doc/html/structfuse__args.html0000644000175000017500000001341715002273413017500 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 file:
    fuse-3.17.2/doc/html/structfuse__buf.html0000644000175000017500000002260315002273413017315 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 884 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 907 of file fuse_common.h.

    ◆ flags

    enum fuse_buf_flags fuse_buf::flags

    Buffer flags

    Definition at line 893 of file fuse_common.h.

    ◆ mem

    void* fuse_buf::mem

    Memory pointer

    Used unless FUSE_BUF_IS_FD flag is set.

    Definition at line 900 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 922 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 914 of file fuse_common.h.

    ◆ size

    size_t fuse_buf::size

    Size of data in bytes

    Definition at line 888 of file fuse_common.h.


    The documentation for this struct was generated from the following file:
    fuse-3.17.2/doc/html/structfuse__bufvec.html0000644000175000017500000001623315002273413020015 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 933 of file fuse_common.h.

    Field Documentation

    ◆ buf

    struct fuse_buf fuse_bufvec::buf[1]

    Array of buffers

    Definition at line 952 of file fuse_common.h.

    ◆ count

    size_t fuse_bufvec::count

    Number of buffers in the array

    Definition at line 937 of file fuse_common.h.

    ◆ idx

    size_t fuse_bufvec::idx

    Index of current buffer within the array

    Definition at line 942 of file fuse_common.h.

    ◆ off

    size_t fuse_bufvec::off

    Current offset within the current buffer

    Definition at line 947 of file fuse_common.h.


    The documentation for this struct was generated from the following file:
    fuse-3.17.2/doc/html/structfuse__cmdline__opts.html0000644000175000017500000000517415002273413021364 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 file:
    fuse-3.17.2/doc/html/structfuse__config.html0000644000175000017500000007632015002273413020013 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[48]

    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 file:
    fuse-3.17.2/doc/html/structfuse__conn__info.html0000644000175000017500000005313415002273413020653 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]
     

    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 546 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 586 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 694 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 638 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 628 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 574 of file fuse_common.h.

    ◆ max_readahead

    uint32_t fuse_conn_info::max_readahead

    Maximum readahead

    Definition at line 579 of file fuse_common.h.

    ◆ max_write

    uint32_t fuse_conn_info::max_write

    Maximum size of the write buffer

    Definition at line 560 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 685 of file fuse_common.h.

    ◆ proto_major

    uint32_t fuse_conn_info::proto_major

    Major version of the protocol (read-only)

    Definition at line 550 of file fuse_common.h.

    ◆ proto_minor

    uint32_t fuse_conn_info::proto_minor

    Minor version of the protocol (read-only)

    Definition at line 555 of file fuse_common.h.

    ◆ reserved

    uint32_t fuse_conn_info::reserved[16]

    For future use.

    Definition at line 709 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 655 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 598 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 704 of file fuse_common.h.


    The documentation for this struct was generated from the following file:
    fuse-3.17.2/doc/html/structfuse__context.html0000644000175000017500000002170215002273413020224 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 file:
    fuse-3.17.2/doc/html/structfuse__ctx.html0000644000175000017500000001626115002273413017342 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 file:
    fuse-3.17.2/doc/html/structfuse__entry__param.html0000644000175000017500000002262215002273413021222 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 file:
    fuse-3.17.2/doc/html/structfuse__file__info.html0000644000175000017500000005124015002273413020631 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 56 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 125 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 95 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 128 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 69 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 113 of file fuse_common.h.

    ◆ flags

    int32_t fuse_file_info::flags

    Open flags. Available in open(), release() and create()

    Definition at line 58 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 80 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 75 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 116 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 99 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 84 of file fuse_common.h.

    ◆ padding

    uint32_t fuse_file_info::padding

    Padding. Reserved for future use

    Definition at line 106 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 103 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 120 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 66 of file fuse_common.h.


    The documentation for this struct was generated from the following file:
    fuse-3.17.2/doc/html/structfuse__loop__config.html0000644000175000017500000002036415002273413021200 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 147 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 155 of file fuse_common.h.

    ◆ max_idle_threads [1/2]

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

    Definition at line 167 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 151 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 158 of file fuse_i.h.


    The documentation for this struct was generated from the following files:
    fuse-3.17.2/doc/html/structfuse__lowlevel__ops.html0000644000175000017500000035651015002273413021421 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)
     

    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

    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

    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.

    ◆ 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 file:
    fuse-3.17.2/doc/html/structfuse__module.html0000644000175000017500000000506115002273413020025 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 103 of file fuse_i.h.


    The documentation for this struct was generated from the following file:
    fuse-3.17.2/doc/html/structfuse__operations.html0000644000175000017500000021304715002273413020730 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 *)
     

    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.

    ◆ 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 file:
    fuse-3.17.2/doc/html/structfuse__opt.html0000644000175000017500000001732415002273413017347 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 file:
    fuse-3.17.2/doc/html/subdir_8c_source.html0000644000175000017500000033120515002273413017356 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 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:4669
    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:4879
    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.17.2/doc/html/sync_off.png0000644000175000017500000000152515002273413015541 0ustar berndberndPNG  IHDRw=IDATxKhTW1I&38MII3b$c I1V1-(T.* t!K[čf`l(l"Y6gT}sgܹ d{8?̝;u`:!FB?Űm'y>ѝlU_?]Y(N8f1qn-etm 0}b%׌=0?1s08;_ W|%\Zð >舽lnp.a{ )t; b n652?>Oдunm`׭ZWjC~>־0+ {{fMŕټ` ݛ%uA6,]kWu]7ihu1 l Ҷ̺:\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| /cfOIENDB`fuse-3.17.2/doc/html/sync_on.png0000644000175000017500000000151515002273413015402 0ustar berndberndPNG  IHDRw=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=1&m8SxLU޲iEOsnxKN~jIENDB`fuse-3.17.2/doc/html/tab_h.png0000644000175000017500000000026115002273413015004 0ustar berndberndPNG  IHDR$[xIDATxM@~ΒEv"!d*rGq={SݧH uO^[_Xvyұ=VCff{R%_rug(?gh\i>|sIENDB`fuse-3.17.2/doc/html/tab_s.png0000644000175000017500000000027015002273413015017 0ustar berndberndPNG  IHDR$[IDATx݁ @@ѣ?Q"%If6[HQ<]dr s?O=w'F -~rÍ[芭m֬ݯнF)Y% `n,9B!ь\<#IENDB`fuse-3.17.2/doc/html/tabs.css0000644000175000017500000002450015002273413014666 0ustar berndbernd.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.17.2/doc/html/test__setattr_8c_source.html0000644000175000017500000011605515002273413020756 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 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)
    #define FUSE_ROOT_ID
    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
    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.17.2/doc/html/test__syscalls_8c_source.html0000644000175000017500000114254715002273413021133 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 <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.17.2/doc/html/test__write__cache_8c_source.html0000644000175000017500000016567615002273413021721 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 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) {
    71 assert(fuse_get_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE));
    72 fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
    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
    int fuse_set_signal_handlers(struct fuse_session *se)
    #define FUSE_CAP_WRITEBACK_CACHE
    void fuse_remove_signal_handlers(struct fuse_session *se)
    void fuse_session_destroy(struct fuse_session *se)
    #define FUSE_ROOT_ID
    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
    char ** argv
    Definition fuse_opt.h:114
    fuse_ino_t ino
    uint32_t noflush
    Definition fuse_common.h:99
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/wrong__command_8c_source.html0000644000175000017500000001164715002273413021064 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.17.2/doc/html/tab_ad.png0000644000175000017500000000020715002273413015141 0ustar berndberndPNG  IHDR$[NIDATxa P" ޟ.!L/nEX2i̝^ rV}r>=>bHa5IENDB`fuse-3.17.2/doc/html/tab_bd.png0000644000175000017500000000025515002273413015145 0ustar berndberndPNG  IHDR$[tIDATx[ 0Л66)IZ_%)(.(z* u97d:.Lqwg?8LWxʐOЧVql` 1+S^Z ~dA .YBIENDB`fuse-3.17.2/doc/html/tab_hd.png0000644000175000017500000000026415002273413015153 0ustar berndberndPNG  IHDR$[{IDATxK @D̀""I w1E.gABntX\?,oۺ5:=}`V5!0݇CD*Dm#JI24eVKIENDB`fuse-3.17.2/doc/html/tab_sd.png0000644000175000017500000000027415002273413015167 0ustar berndberndPNG  IHDR$[IDATx{ 0/ir:'V3"t.3$?)P/LĴG_Nm2ڜQ>ٓ莤HYlKKH2C)rBM08?j HZ-IENDB`fuse-3.17.2/doc/html/nav_hd.png0000644000175000017500000000016215002273413015166 0ustar berndberndPNG  IHDR ,@9IDATxݻ Q ;r5 W v?P_E27jA v #IENDB`fuse-3.17.2/doc/html/nav_fd.png0000644000175000017500000000025115002273413015163 0ustar berndberndPNG  IHDR8pIDATxM F ((jMM[73o@s ´K̑ y=[>P\U/gdHȢ(zNC.??;y@AKIENDB`fuse-3.17.2/doc/html/bc_sd.png0000644000175000017500000000117315002273413015004 0ustar berndberndPNG  IHDR_ BIDATxkQƿ;3$4b+J-XBP u#q7 ntV@ӂ- q#3? 7;gwXA~*gm,.7'oN<2 .#umx^Xzdis=1SryTY}ŧUKm߼ĘpHL穨`nbOMgVAr kAOopGOOE>PnN,=P fuse-3.17.2/doc/html/minus.svg0000644000175000017500000000110615002273413015074 0ustar berndbernd fuse-3.17.2/doc/html/plusd.svg0000644000175000017500000000127015002273413015072 0ustar berndbernd fuse-3.17.2/doc/html/minusd.svg0000644000175000017500000000110615002273413015240 0ustar berndbernd fuse-3.17.2/doc/html/doc.svg0000644000175000017500000000273715002273413014521 0ustar berndbernd fuse-3.17.2/doc/html/docd.svg0000644000175000017500000000273715002273413014665 0ustar berndbernd fuse-3.17.2/doc/html/folderopen.svg0000644000175000017500000000630515002273413016104 0ustar berndbernd fuse-3.17.2/doc/html/folderopend.svg0000644000175000017500000000621615002273413016251 0ustar berndbernd fuse-3.17.2/doc/html/folderclosed.svg0000644000175000017500000000371415002273413016415 0ustar berndbernd fuse-3.17.2/doc/html/folderclosedd.svg0000644000175000017500000000371415002273413016561 0ustar berndbernd fuse-3.17.2/doc/html/splitbard.png0000644000175000017500000000043215002273413015713 0ustar berndberndPNG  IHDRMIDATx1jPFE$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.17.2/doc/html/fuse__config_8h_source.html0000644000175000017500000001714215002273413020522 0ustar berndbernd libfuse: build-ubuntu/fuse_config.h Source File
    libfuse
    fuse_config.h
    1/*
    2 * Autogenerated by the Meson build system.
    3 * Do not edit, your changes will be lost.
    4 */
    5
    6#pragma once
    7
    8#define HAVE_BACKTRACE
    9
    10#define HAVE_CLOSE_RANGE
    11
    12#define HAVE_COPY_FILE_RANGE
    13
    14#define HAVE_FALLOCATE
    15
    16#define HAVE_FDATASYNC
    17
    18#define HAVE_FORK
    19
    20#define HAVE_FSTATAT
    21
    22#define HAVE_ICONV
    23
    24#define HAVE_OPENAT
    25
    26#define HAVE_PIPE2
    27
    28#define HAVE_POSIX_FALLOCATE
    29
    30#define HAVE_READLINKAT
    31
    32#define HAVE_SETXATTR
    33
    34#define HAVE_SPLICE
    35
    36#define HAVE_STRUCT_STAT_ST_ATIM
    37
    38#undef HAVE_STRUCT_STAT_ST_ATIMESPEC
    39
    40#define HAVE_UTIMENSAT
    41
    42#define HAVE_VMSPLICE
    43
    44#define PACKAGE_VERSION "3.17.1-rc1"
    45
    fuse-3.17.2/doc/html/libfuse__config_8h_source.html0000644000175000017500000001053715002273413021212 0ustar berndbernd libfuse: build-ubuntu/libfuse_config.h Source File
    libfuse
    libfuse_config.h
    1/*
    2 * Autogenerated by the Meson build system.
    3 * Do not edit, your changes will be lost.
    4 */
    5
    6#pragma once
    7
    8#define FUSE_HOTFIX_VERSION 1
    9
    10#define FUSE_MAJOR_VERSION 3
    11
    12#define FUSE_MINOR_VERSION 17
    13
    14#define FUSE_RC_VERSION rc1
    15
    16#define LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS 1
    17
    fuse-3.17.2/doc/html/sanitycheckc_8c_source.html0000644000175000017500000000530115002273413020531 0ustar berndbernd libfuse: build-ubuntu/meson-private/sanitycheckc.c Source File
    libfuse
    sanitycheckc.c
    1int main(void) { int class=0; return class; }
    fuse-3.17.2/doc/html/hello__ll__uds_8c_source.html0000644000175000017500000020321415002273413021027 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 COPYING.
    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:5236
    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.17.2/doc/html/fuse__lowlevel_8h_source.html0000644000175000017500000047442615002273413021122 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 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)
    mode_t umask
    double entry_timeout
    fuse_ino_t ino
    uint64_t generation
    double attr_timeout
    struct stat attr
    int32_t backing_id
    void(* fsync)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
    void(* bmap)(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx)
    void(* unlink)(fuse_req_t req, fuse_ino_t parent, const char *name)
    void(* getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
    void(* rename)(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname, unsigned int flags)
    void(* releasedir)(fuse_req_t req, fuse_ino_t ino, 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(* write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, off_t off, struct fuse_file_info *fi)
    void(* symlink)(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)
    void(* statfs)(fuse_req_t req, fuse_ino_t ino)
    void(* tmpfile)(fuse_req_t req, fuse_ino_t parent, mode_t mode, struct fuse_file_info *fi)
    void(* release)(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(* fallocate)(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, off_t length, struct fuse_file_info *fi)
    void(* create)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi)
    void(* link)(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname)
    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(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
    void(* readdirplus)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
    void(* flock)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op)
    void(* getattr)(fuse_req_t req, fuse_ino_t ino, 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(* setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)
    void(* open)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
    void(* removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name)
    void(* forget)(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
    void(* poll)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct fuse_pollhandle *ph)
    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(* rmdir)(fuse_req_t req, fuse_ino_t parent, const char *name)
    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)
    void(* retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv)
    void(* fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi)
    void(* destroy)(void *userdata)
    void(* mkdir)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
    void(* access)(fuse_req_t req, fuse_ino_t ino, int mask)
    void(* mknod)(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)
    void(* lseek)(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info *fi)
    void(* getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, struct flock *lock)
    void(* readlink)(fuse_req_t req, fuse_ino_t ino)
    void(* read)(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(* listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size)
    void(* opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
    void(* forget_multi)(fuse_req_t req, size_t count, struct fuse_forget_data *forgets)
    fuse-3.17.2/doc/html/fuse__mount__compat_8h_source.html0000644000175000017500000002231015002273413022112 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.17.2/doc/html/compat_8c_source.html0000644000175000017500000004473115002273413017356 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 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.17.2/doc/html/fuse__misc_8h_source.html0000644000175000017500000002416115002273413020207 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 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.17.2/doc/html/util_8c_source.html0000644000175000017500000001324215002273413017041 0ustar berndbernd libfuse: lib/util.c Source File
    libfuse
    util.c
    1#include <stdlib.h>
    2#include <errno.h>
    3
    4#include "util.h"
    5
    6int libfuse_strtol(const char *str, long *res)
    7{
    8 char *endptr;
    9 int base = 10;
    10 long val;
    11
    12 errno = 0;
    13
    14 if (!str)
    15 return -EINVAL;
    16
    17 val = strtol(str, &endptr, base);
    18
    19 if (errno)
    20 return -errno;
    21
    22 if (endptr == str || *endptr != '\0')
    23 return -EINVAL;
    24
    25 *res = val;
    26 return 0;
    27}
    fuse-3.17.2/doc/html/util_8h_source.html0000644000175000017500000001351315002273413017047 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
    6#define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
    7
    8#define likely(x) __builtin_expect(!!(x), 1)
    9#define unlikely(x) __builtin_expect(!!(x), 0)
    10
    11int libfuse_strtol(const char *str, long *res);
    12
    16static inline uint32_t fuse_lower_32_bits(uint64_t nr)
    17{
    18 return (uint32_t)(nr & 0xffffffff);
    19}
    20
    24static inline uint64_t fuse_higher_32_bits(uint64_t nr)
    25{
    26 return nr & ~0xffffffffULL;
    27}
    28
    29#ifndef FUSE_VAR_UNUSED
    30#define FUSE_VAR_UNUSED(var) (__attribute__((unused)) var)
    31#endif
    32
    33#endif
    fuse-3.17.2/doc/html/release__unlink__race_8c_source.html0000644000175000017500000005356715002273413022372 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 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.17.2/doc/html/hello__ll__uds_8c.html0000644000175000017500000012352115002273413017451 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 COPYING.
    */
    #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:5236
    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.17.2/doc/html/structfuse__ext__header.html0000644000175000017500000000531615002273413021012 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 file:
    fuse-3.17.2/doc/html/structfuse__supp__groups.html0000644000175000017500000000517015002273413021266 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 file:
    fuse-3.17.2/doc/html/structlibfuse__version.html0000644000175000017500000000512615002273413020716 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 959 of file fuse_common.h.


    The documentation for this struct was generated from the following file:
    fuse-3.17.2/doc/html/example_2cuse_8c_source.html0000644000175000017500000016664115002273247020641 0ustar berndbernd 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 COPYING.
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2cuse_8c_source.html0000644000175000017500000016673314770250311023476 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    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.17.2/doc/html/example_2cuse__client_8c_source.html0000644000175000017500000005041615002273247022326 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 COPYING.
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2cuse__client_8c_source.html0000644000175000017500000005063114770250311025160 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    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.17.2/doc/html/example_2hello_8c_source.html0000644000175000017500000011145015002273413020764 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 COPYING.
    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 */
    62 fuse_set_feature_flag(conn, FUSE_CAP_ASYNC_READ);
    63 fuse_unset_feature_flag(conn, FUSE_CAP_ASYNC_READ);
    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
    #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.17.2/doc/html/fuse-3_817_81-rc0_2example_2hello_8c_source.html0000644000175000017500000011157514770250311023634 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    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 return NULL;
    61}
    62
    63static int hello_getattr(const char *path, struct stat *stbuf,
    64 struct fuse_file_info *fi)
    65{
    66 (void) fi;
    67 int res = 0;
    68
    69 memset(stbuf, 0, sizeof(struct stat));
    70 if (strcmp(path, "/") == 0) {
    71 stbuf->st_mode = S_IFDIR | 0755;
    72 stbuf->st_nlink = 2;
    73 } else if (strcmp(path+1, options.filename) == 0) {
    74 stbuf->st_mode = S_IFREG | 0444;
    75 stbuf->st_nlink = 1;
    76 stbuf->st_size = strlen(options.contents);
    77 } else
    78 res = -ENOENT;
    79
    80 return res;
    81}
    82
    83static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
    84 off_t offset, struct fuse_file_info *fi,
    85 enum fuse_readdir_flags flags)
    86{
    87 (void) offset;
    88 (void) fi;
    89 (void) flags;
    90
    91 if (strcmp(path, "/") != 0)
    92 return -ENOENT;
    93
    94 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
    95 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
    96 filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
    97
    98 return 0;
    99}
    100
    101static int hello_open(const char *path, struct fuse_file_info *fi)
    102{
    103 if (strcmp(path+1, options.filename) != 0)
    104 return -ENOENT;
    105
    106 if ((fi->flags & O_ACCMODE) != O_RDONLY)
    107 return -EACCES;
    108
    109 return 0;
    110}
    111
    112static int hello_read(const char *path, char *buf, size_t size, off_t offset,
    113 struct fuse_file_info *fi)
    114{
    115 size_t len;
    116 (void) fi;
    117 if(strcmp(path+1, options.filename) != 0)
    118 return -ENOENT;
    119
    120 len = strlen(options.contents);
    121 if (offset < len) {
    122 if (offset + size > len)
    123 size = len - offset;
    124 memcpy(buf, options.contents + offset, size);
    125 } else
    126 size = 0;
    127
    128 return size;
    129}
    130
    131static const struct fuse_operations hello_oper = {
    132 .init = hello_init,
    133 .getattr = hello_getattr,
    134 .readdir = hello_readdir,
    135 .open = hello_open,
    136 .read = hello_read,
    137};
    138
    139static void show_help(const char *progname)
    140{
    141 printf("usage: %s [options] <mountpoint>\n\n", progname);
    142 printf("File-system specific options:\n"
    143 " --name=<s> Name of the \"hello\" file\n"
    144 " (default: \"hello\")\n"
    145 " --contents=<s> Contents \"hello\" file\n"
    146 " (default \"Hello, World!\\n\")\n"
    147 "\n");
    148}
    149
    150int main(int argc, char *argv[])
    151{
    152 int ret;
    153 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    154
    155 /* Set defaults -- we have to use strdup so that
    156 fuse_opt_parse can free the defaults if other
    157 values are specified */
    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 if (options.show_help) {
    171 show_help(argv[0]);
    172 assert(fuse_opt_add_arg(&args, "--help") == 0);
    173 args.argv[0][0] = '\0';
    174 }
    175
    176 ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
    177 fuse_opt_free_args(&args);
    178 return ret;
    179}
    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
    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.17.2/doc/html/example_2hello__ll_8c_source.html0000644000175000017500000017470415002273247021632 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 COPYING.
    7*/
    8
    21#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    22
    23#include <fuse_lowlevel.h>
    24#include <stdio.h>
    25#include <stdlib.h>
    26#include <string.h>
    27#include <errno.h>
    28#include <fcntl.h>
    29#include <unistd.h>
    30#include <assert.h>
    31
    32static const char *hello_str = "Hello World!\n";
    33static const char *hello_name = "hello";
    34
    35static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
    36{
    37 stbuf->st_ino = ino;
    38 switch (ino) {
    39 case 1:
    40 stbuf->st_mode = S_IFDIR | 0755;
    41 stbuf->st_nlink = 2;
    42 break;
    43
    44 case 2:
    45 stbuf->st_mode = S_IFREG | 0444;
    46 stbuf->st_nlink = 1;
    47 stbuf->st_size = strlen(hello_str);
    48 break;
    49
    50 default:
    51 return -1;
    52 }
    53 return 0;
    54}
    55
    56static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
    57{
    58 (void)userdata;
    59
    60 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    61 conn->no_interrupt = 1;
    62
    63 /* Test setting flags the old way */
    65 conn->want &= ~FUSE_CAP_ASYNC_READ;
    66}
    67
    68static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
    69 struct fuse_file_info *fi)
    70{
    71 struct stat stbuf;
    72
    73 (void) fi;
    74
    75 memset(&stbuf, 0, sizeof(stbuf));
    76 if (hello_stat(ino, &stbuf) == -1)
    77 fuse_reply_err(req, ENOENT);
    78 else
    79 fuse_reply_attr(req, &stbuf, 1.0);
    80}
    81
    82static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
    83{
    84 struct fuse_entry_param e;
    85
    86 if (parent != 1 || strcmp(name, hello_name) != 0)
    87 fuse_reply_err(req, ENOENT);
    88 else {
    89 memset(&e, 0, sizeof(e));
    90 e.ino = 2;
    91 e.attr_timeout = 1.0;
    92 e.entry_timeout = 1.0;
    93 hello_stat(e.ino, &e.attr);
    94
    95 fuse_reply_entry(req, &e);
    96 }
    97}
    98
    99struct dirbuf {
    100 char *p;
    101 size_t size;
    102};
    103
    104static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
    105 fuse_ino_t ino)
    106{
    107 struct stat stbuf;
    108 size_t oldsize = b->size;
    109 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
    110 b->p = (char *) realloc(b->p, b->size);
    111 memset(&stbuf, 0, sizeof(stbuf));
    112 stbuf.st_ino = ino;
    113 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
    114 b->size);
    115}
    116
    117#define min(x, y) ((x) < (y) ? (x) : (y))
    118
    119static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
    120 off_t off, size_t maxsize)
    121{
    122 if (off < bufsize)
    123 return fuse_reply_buf(req, buf + off,
    124 min(bufsize - off, maxsize));
    125 else
    126 return fuse_reply_buf(req, NULL, 0);
    127}
    128
    129static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    130 off_t off, struct fuse_file_info *fi)
    131{
    132 (void) fi;
    133
    134 if (ino != 1)
    135 fuse_reply_err(req, ENOTDIR);
    136 else {
    137 struct dirbuf b;
    138
    139 memset(&b, 0, sizeof(b));
    140 dirbuf_add(req, &b, ".", 1);
    141 dirbuf_add(req, &b, "..", 1);
    142 dirbuf_add(req, &b, hello_name, 2);
    143 reply_buf_limited(req, b.p, b.size, off, size);
    144 free(b.p);
    145 }
    146}
    147
    148static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
    149 struct fuse_file_info *fi)
    150{
    151 if (ino != 2)
    152 fuse_reply_err(req, EISDIR);
    153 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
    154 fuse_reply_err(req, EACCES);
    155 else
    156 fuse_reply_open(req, fi);
    157}
    158
    159static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
    160 off_t off, struct fuse_file_info *fi)
    161{
    162 (void) fi;
    163
    164 assert(ino == 2);
    165 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
    166}
    167
    168static void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    169 size_t size)
    170{
    171 (void)size;
    172 assert(ino == 1 || ino == 2);
    173 if (strcmp(name, "hello_ll_getxattr_name") == 0)
    174 {
    175 const char *buf = "hello_ll_getxattr_value";
    176 fuse_reply_buf(req, buf, strlen(buf));
    177 }
    178 else
    179 {
    180 fuse_reply_err(req, ENOTSUP);
    181 }
    182}
    183
    184static void hello_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    185 const char *value, size_t size, int flags)
    186{
    187 (void)flags;
    188 (void)size;
    189 assert(ino == 1 || ino == 2);
    190 const char* exp_val = "hello_ll_setxattr_value";
    191 if (strcmp(name, "hello_ll_setxattr_name") == 0 &&
    192 strlen(exp_val) == size &&
    193 strncmp(value, exp_val, size) == 0)
    194 {
    195 fuse_reply_err(req, 0);
    196 }
    197 else
    198 {
    199 fuse_reply_err(req, ENOTSUP);
    200 }
    201}
    202
    203static void hello_ll_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
    204{
    205 assert(ino == 1 || ino == 2);
    206 if (strcmp(name, "hello_ll_removexattr_name") == 0)
    207 {
    208 fuse_reply_err(req, 0);
    209 }
    210 else
    211 {
    212 fuse_reply_err(req, ENOTSUP);
    213 }
    214}
    215
    216static const struct fuse_lowlevel_ops hello_ll_oper = {
    217 .init = hello_ll_init,
    218 .lookup = hello_ll_lookup,
    219 .getattr = hello_ll_getattr,
    220 .readdir = hello_ll_readdir,
    221 .open = hello_ll_open,
    222 .read = hello_ll_read,
    223 .setxattr = hello_ll_setxattr,
    224 .getxattr = hello_ll_getxattr,
    225 .removexattr = hello_ll_removexattr,
    226};
    227
    228int main(int argc, char *argv[])
    229{
    230 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    231 struct fuse_session *se;
    232 struct fuse_cmdline_opts opts;
    233 struct fuse_loop_config *config;
    234 int ret = -1;
    235
    236 if (fuse_parse_cmdline(&args, &opts) != 0)
    237 return 1;
    238 if (opts.show_help) {
    239 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
    242 ret = 0;
    243 goto err_out1;
    244 } else if (opts.show_version) {
    245 printf("FUSE library version %s\n", fuse_pkgversion());
    247 ret = 0;
    248 goto err_out1;
    249 }
    250
    251 if(opts.mountpoint == NULL) {
    252 printf("usage: %s [options] <mountpoint>\n", argv[0]);
    253 printf(" %s --help\n", argv[0]);
    254 ret = 1;
    255 goto err_out1;
    256 }
    257
    258 se = fuse_session_new(&args, &hello_ll_oper,
    259 sizeof(hello_ll_oper), NULL);
    260 if (se == NULL)
    261 goto err_out1;
    262
    263 if (fuse_set_signal_handlers(se) != 0)
    264 goto err_out2;
    265
    266 if (fuse_session_mount(se, opts.mountpoint) != 0)
    267 goto err_out3;
    268
    269 fuse_daemonize(opts.foreground);
    270
    271 /* Block until ctrl+c or fusermount -u */
    272 if (opts.singlethread)
    273 ret = fuse_session_loop(se);
    274 else {
    275 config = fuse_loop_cfg_create();
    276 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
    277 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
    278 ret = fuse_session_loop_mt(se, config);
    279 fuse_loop_cfg_destroy(config);
    280 config = NULL;
    281 }
    282
    284err_out3:
    286err_out2:
    288err_out1:
    289 free(opts.mountpoint);
    290 fuse_opt_free_args(&args);
    291
    292 return ret ? 1 : 0;
    293}
    int fuse_set_signal_handlers(struct fuse_session *se)
    #define FUSE_CAP_ASYNC_READ
    const char * fuse_pkgversion(void)
    Definition fuse.c:5234
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2hello__ll_8c_source.html0000644000175000017500000017146614770250311024467 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    7*/
    8
    21#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    22
    23#include <fuse_lowlevel.h>
    24#include <stdio.h>
    25#include <stdlib.h>
    26#include <string.h>
    27#include <errno.h>
    28#include <fcntl.h>
    29#include <unistd.h>
    30#include <assert.h>
    31
    32static const char *hello_str = "Hello World!\n";
    33static const char *hello_name = "hello";
    34
    35static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
    36{
    37 stbuf->st_ino = ino;
    38 switch (ino) {
    39 case 1:
    40 stbuf->st_mode = S_IFDIR | 0755;
    41 stbuf->st_nlink = 2;
    42 break;
    43
    44 case 2:
    45 stbuf->st_mode = S_IFREG | 0444;
    46 stbuf->st_nlink = 1;
    47 stbuf->st_size = strlen(hello_str);
    48 break;
    49
    50 default:
    51 return -1;
    52 }
    53 return 0;
    54}
    55
    56static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
    57{
    58 (void)userdata;
    59
    60 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    61 conn->no_interrupt = 1;
    62}
    63
    64static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
    65 struct fuse_file_info *fi)
    66{
    67 struct stat stbuf;
    68
    69 (void) fi;
    70
    71 memset(&stbuf, 0, sizeof(stbuf));
    72 if (hello_stat(ino, &stbuf) == -1)
    73 fuse_reply_err(req, ENOENT);
    74 else
    75 fuse_reply_attr(req, &stbuf, 1.0);
    76}
    77
    78static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
    79{
    80 struct fuse_entry_param e;
    81
    82 if (parent != 1 || strcmp(name, hello_name) != 0)
    83 fuse_reply_err(req, ENOENT);
    84 else {
    85 memset(&e, 0, sizeof(e));
    86 e.ino = 2;
    87 e.attr_timeout = 1.0;
    88 e.entry_timeout = 1.0;
    89 hello_stat(e.ino, &e.attr);
    90
    91 fuse_reply_entry(req, &e);
    92 }
    93}
    94
    95struct dirbuf {
    96 char *p;
    97 size_t size;
    98};
    99
    100static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
    101 fuse_ino_t ino)
    102{
    103 struct stat stbuf;
    104 size_t oldsize = b->size;
    105 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
    106 b->p = (char *) realloc(b->p, b->size);
    107 memset(&stbuf, 0, sizeof(stbuf));
    108 stbuf.st_ino = ino;
    109 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
    110 b->size);
    111}
    112
    113#define min(x, y) ((x) < (y) ? (x) : (y))
    114
    115static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
    116 off_t off, size_t maxsize)
    117{
    118 if (off < bufsize)
    119 return fuse_reply_buf(req, buf + off,
    120 min(bufsize - off, maxsize));
    121 else
    122 return fuse_reply_buf(req, NULL, 0);
    123}
    124
    125static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    126 off_t off, struct fuse_file_info *fi)
    127{
    128 (void) fi;
    129
    130 if (ino != 1)
    131 fuse_reply_err(req, ENOTDIR);
    132 else {
    133 struct dirbuf b;
    134
    135 memset(&b, 0, sizeof(b));
    136 dirbuf_add(req, &b, ".", 1);
    137 dirbuf_add(req, &b, "..", 1);
    138 dirbuf_add(req, &b, hello_name, 2);
    139 reply_buf_limited(req, b.p, b.size, off, size);
    140 free(b.p);
    141 }
    142}
    143
    144static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
    145 struct fuse_file_info *fi)
    146{
    147 if (ino != 2)
    148 fuse_reply_err(req, EISDIR);
    149 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
    150 fuse_reply_err(req, EACCES);
    151 else
    152 fuse_reply_open(req, fi);
    153}
    154
    155static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
    156 off_t off, struct fuse_file_info *fi)
    157{
    158 (void) fi;
    159
    160 assert(ino == 2);
    161 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
    162}
    163
    164static void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    165 size_t size)
    166{
    167 (void)size;
    168 assert(ino == 1 || ino == 2);
    169 if (strcmp(name, "hello_ll_getxattr_name") == 0)
    170 {
    171 const char *buf = "hello_ll_getxattr_value";
    172 fuse_reply_buf(req, buf, strlen(buf));
    173 }
    174 else
    175 {
    176 fuse_reply_err(req, ENOTSUP);
    177 }
    178}
    179
    180static void hello_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    181 const char *value, size_t size, int flags)
    182{
    183 (void)flags;
    184 (void)size;
    185 assert(ino == 1 || ino == 2);
    186 const char* exp_val = "hello_ll_setxattr_value";
    187 if (strcmp(name, "hello_ll_setxattr_name") == 0 &&
    188 strlen(exp_val) == size &&
    189 strncmp(value, exp_val, size) == 0)
    190 {
    191 fuse_reply_err(req, 0);
    192 }
    193 else
    194 {
    195 fuse_reply_err(req, ENOTSUP);
    196 }
    197}
    198
    199static void hello_ll_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
    200{
    201 assert(ino == 1 || ino == 2);
    202 if (strcmp(name, "hello_ll_removexattr_name") == 0)
    203 {
    204 fuse_reply_err(req, 0);
    205 }
    206 else
    207 {
    208 fuse_reply_err(req, ENOTSUP);
    209 }
    210}
    211
    212static const struct fuse_lowlevel_ops hello_ll_oper = {
    213 .init = hello_ll_init,
    214 .lookup = hello_ll_lookup,
    215 .getattr = hello_ll_getattr,
    216 .readdir = hello_ll_readdir,
    217 .open = hello_ll_open,
    218 .read = hello_ll_read,
    219 .setxattr = hello_ll_setxattr,
    220 .getxattr = hello_ll_getxattr,
    221 .removexattr = hello_ll_removexattr,
    222};
    223
    224int main(int argc, char *argv[])
    225{
    226 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    227 struct fuse_session *se;
    228 struct fuse_cmdline_opts opts;
    229 struct fuse_loop_config *config;
    230 int ret = -1;
    231
    232 if (fuse_parse_cmdline(&args, &opts) != 0)
    233 return 1;
    234 if (opts.show_help) {
    235 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
    238 ret = 0;
    239 goto err_out1;
    240 } else if (opts.show_version) {
    241 printf("FUSE library version %s\n", fuse_pkgversion());
    243 ret = 0;
    244 goto err_out1;
    245 }
    246
    247 if(opts.mountpoint == NULL) {
    248 printf("usage: %s [options] <mountpoint>\n", argv[0]);
    249 printf(" %s --help\n", argv[0]);
    250 ret = 1;
    251 goto err_out1;
    252 }
    253
    254 se = fuse_session_new(&args, &hello_ll_oper,
    255 sizeof(hello_ll_oper), NULL);
    256 if (se == NULL)
    257 goto err_out1;
    258
    259 if (fuse_set_signal_handlers(se) != 0)
    260 goto err_out2;
    261
    262 if (fuse_session_mount(se, opts.mountpoint) != 0)
    263 goto err_out3;
    264
    265 fuse_daemonize(opts.foreground);
    266
    267 /* Block until ctrl+c or fusermount -u */
    268 if (opts.singlethread)
    269 ret = fuse_session_loop(se);
    270 else {
    271 config = fuse_loop_cfg_create();
    272 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
    273 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
    274 ret = fuse_session_loop_mt(se, config);
    275 fuse_loop_cfg_destroy(config);
    276 config = NULL;
    277 }
    278
    280err_out3:
    282err_out2:
    284err_out1:
    285 free(opts.mountpoint);
    286 fuse_opt_free_args(&args);
    287
    288 return ret ? 1 : 0;
    289}
    int fuse_set_signal_handlers(struct fuse_session *se)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    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.17.2/doc/html/example_2hello__ll__uds_8c_source.html0000644000175000017500000021024515002273247022633 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 COPYING.
    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:5234
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2hello__ll__uds_8c_source.html0000644000175000017500000021032414770250311025464 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    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 %lu 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:5218
    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.17.2/doc/html/example_2invalidate__path_8c_source.html0000644000175000017500000016560615002273247023175 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 COPYING.
    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:5220
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5169
    int fuse_invalidate_path(struct fuse *f, const char *path)
    Definition fuse.c:4698
    int fuse_loop(struct fuse *f)
    Definition fuse.c:4602
    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:4769
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4545
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5225
    @ 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:5234
    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:77
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2invalidate__path_8c_source.html0000644000175000017500000016566314770250311026033 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    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:5204
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5153
    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:5209
    @ 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:5218
    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:77
    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.17.2/doc/html/example_2ioctl_8c_source.html0000644000175000017500000010741415002273247021005 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 COPYING.
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2ioctl_8c_source.html0000644000175000017500000010760214770250311023637 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    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.17.2/doc/html/example_2ioctl_8h_source.html0000644000175000017500000001570415002273247021012 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 COPYING.
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2ioctl_8h_source.html0000644000175000017500000001612214770250311023640 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    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.17.2/doc/html/example_2ioctl__client_8c_source.html0000644000175000017500000002656615002273247022512 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 tests the ioctl.c example file systsem.
    7
    8 This program can be distributed under the terms of the GNU GPLv2.
    9 See the file COPYING.
    10*/
    11
    24#include <sys/types.h>
    25#include <fcntl.h>
    26#include <sys/stat.h>
    27#include <sys/ioctl.h>
    28#include <stdio.h>
    29#include <stdlib.h>
    30#include <ctype.h>
    31#include <errno.h>
    32#include <unistd.h>
    33#include "ioctl.h"
    34
    35const char *usage =
    36"Usage: fioclient FIOC_FILE [size]\n"
    37"\n"
    38"Get size if <size> is omitted, set size otherwise\n"
    39"\n";
    40
    41int main(int argc, char **argv)
    42{
    43 size_t size;
    44 int fd;
    45 int ret = 0;
    46
    47 if (argc < 2) {
    48 fprintf(stderr, "%s", usage);
    49 return 1;
    50 }
    51
    52 fd = open(argv[1], O_RDWR);
    53 if (fd < 0) {
    54 perror("open");
    55 return 1;
    56 }
    57
    58 if (argc == 2) {
    59 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
    60 perror("ioctl");
    61 ret = 1;
    62 goto out;
    63 }
    64 printf("%zu\n", size);
    65 } else {
    66 size = strtoul(argv[2], NULL, 0);
    67 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
    68 perror("ioctl");
    69 ret = 1;
    70 goto out;
    71 }
    72 }
    73out:
    74 close(fd);
    75 return ret;
    76}
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2ioctl__client_8c_source.html0000644000175000017500000002700114770250311025326 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 tests the ioctl.c example file systsem.
    7
    8 This program can be distributed under the terms of the GNU GPLv2.
    9 See the file COPYING.
    10*/
    11
    24#include <sys/types.h>
    25#include <fcntl.h>
    26#include <sys/stat.h>
    27#include <sys/ioctl.h>
    28#include <stdio.h>
    29#include <stdlib.h>
    30#include <ctype.h>
    31#include <errno.h>
    32#include <unistd.h>
    33#include "ioctl.h"
    34
    35const char *usage =
    36"Usage: fioclient FIOC_FILE [size]\n"
    37"\n"
    38"Get size if <size> is omitted, set size otherwise\n"
    39"\n";
    40
    41int main(int argc, char **argv)
    42{
    43 size_t size;
    44 int fd;
    45 int ret = 0;
    46
    47 if (argc < 2) {
    48 fprintf(stderr, "%s", usage);
    49 return 1;
    50 }
    51
    52 fd = open(argv[1], O_RDWR);
    53 if (fd < 0) {
    54 perror("open");
    55 return 1;
    56 }
    57
    58 if (argc == 2) {
    59 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
    60 perror("ioctl");
    61 ret = 1;
    62 goto out;
    63 }
    64 printf("%zu\n", size);
    65 } else {
    66 size = strtoul(argv[2], NULL, 0);
    67 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
    68 perror("ioctl");
    69 ret = 1;
    70 goto out;
    71 }
    72 }
    73out:
    74 close(fd);
    75 return ret;
    76}
    fuse-3.17.2/doc/html/example_2notify__inval__entry_8c_source.html0000644000175000017500000021243115002273247024107 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 COPYING.
    7*/
    8
    79#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    80
    81#include <fuse_lowlevel.h>
    82#include <stdio.h>
    83#include <stdlib.h>
    84#include <string.h>
    85#include <errno.h>
    86#include <fcntl.h>
    87#include <assert.h>
    88#include <signal.h>
    89#include <stddef.h>
    90#include <sys/stat.h>
    91#include <unistd.h>
    92#include <pthread.h>
    93
    94#define MAX_STR_LEN 128
    95static char file_name[MAX_STR_LEN];
    96static fuse_ino_t file_ino = 2;
    97static int lookup_cnt = 0;
    98static pthread_t main_thread;
    99
    100/* Command line parsing */
    101struct options {
    102 int no_notify;
    103 float timeout;
    104 int update_interval;
    105 int only_expire;
    106};
    107static struct options options = {
    108 .timeout = 5,
    109 .no_notify = 0,
    110 .update_interval = 1,
    111 .only_expire = 0,
    112};
    113
    114#define OPTION(t, p) \
    115 { t, offsetof(struct options, p), 1 }
    116static const struct fuse_opt option_spec[] = {
    117 OPTION("--no-notify", no_notify),
    118 OPTION("--update-interval=%d", update_interval),
    119 OPTION("--timeout=%f", timeout),
    120 OPTION("--only-expire", only_expire),
    122};
    123
    124static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
    125 stbuf->st_ino = ino;
    126 if (ino == FUSE_ROOT_ID) {
    127 stbuf->st_mode = S_IFDIR | 0755;
    128 stbuf->st_nlink = 1;
    129 }
    130
    131 else if (ino == file_ino) {
    132 stbuf->st_mode = S_IFREG | 0000;
    133 stbuf->st_nlink = 1;
    134 stbuf->st_size = 0;
    135 }
    136
    137 else
    138 return -1;
    139
    140 return 0;
    141}
    142
    143static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
    144 (void)userdata;
    145
    146 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    147 conn->no_interrupt = 1;
    148}
    149
    150static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
    151 const char *name) {
    152 struct fuse_entry_param e;
    153 memset(&e, 0, sizeof(e));
    154
    155 if (parent != FUSE_ROOT_ID)
    156 goto err_out;
    157 else if (strcmp(name, file_name) == 0) {
    158 e.ino = file_ino;
    159 lookup_cnt++;
    160 } else
    161 goto err_out;
    162
    163 e.attr_timeout = options.timeout;
    164 e.entry_timeout = options.timeout;
    165 if (tfs_stat(e.ino, &e.attr) != 0)
    166 goto err_out;
    167 fuse_reply_entry(req, &e);
    168 return;
    169
    170err_out:
    171 fuse_reply_err(req, ENOENT);
    172}
    173
    174static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
    175 uint64_t nlookup) {
    176 (void) req;
    177 if(ino == file_ino)
    178 lookup_cnt -= nlookup;
    179 else
    180 assert(ino == FUSE_ROOT_ID);
    181 fuse_reply_none(req);
    182}
    183
    184static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
    185 struct fuse_file_info *fi) {
    186 struct stat stbuf;
    187
    188 (void) fi;
    189
    190 memset(&stbuf, 0, sizeof(stbuf));
    191 if (tfs_stat(ino, &stbuf) != 0)
    192 fuse_reply_err(req, ENOENT);
    193 else
    194 fuse_reply_attr(req, &stbuf, options.timeout);
    195}
    196
    197struct dirbuf {
    198 char *p;
    199 size_t size;
    200};
    201
    202static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
    203 fuse_ino_t ino) {
    204 struct stat stbuf;
    205 size_t oldsize = b->size;
    206 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
    207 b->p = (char *) realloc(b->p, b->size);
    208 memset(&stbuf, 0, sizeof(stbuf));
    209 stbuf.st_ino = ino;
    210 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
    211 b->size);
    212}
    213
    214#define min(x, y) ((x) < (y) ? (x) : (y))
    215
    216static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
    217 off_t off, size_t maxsize) {
    218 if (off < bufsize)
    219 return fuse_reply_buf(req, buf + off,
    220 min(bufsize - off, maxsize));
    221 else
    222 return fuse_reply_buf(req, NULL, 0);
    223}
    224
    225static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    226 off_t off, struct fuse_file_info *fi) {
    227 (void) fi;
    228
    229 if (ino != FUSE_ROOT_ID)
    230 fuse_reply_err(req, ENOTDIR);
    231 else {
    232 struct dirbuf b;
    233
    234 memset(&b, 0, sizeof(b));
    235 dirbuf_add(req, &b, file_name, file_ino);
    236 reply_buf_limited(req, b.p, b.size, off, size);
    237 free(b.p);
    238 }
    239}
    240
    241static const struct fuse_lowlevel_ops tfs_oper = {
    242 .init = tfs_init,
    243 .lookup = tfs_lookup,
    244 .getattr = tfs_getattr,
    245 .readdir = tfs_readdir,
    246 .forget = tfs_forget,
    247};
    248
    249static void update_fs(void) {
    250 time_t t;
    251 struct tm *now;
    252 ssize_t ret;
    253
    254 t = time(NULL);
    255 now = localtime(&t);
    256 assert(now != NULL);
    257
    258 ret = strftime(file_name, MAX_STR_LEN,
    259 "Time_is_%Hh_%Mm_%Ss", now);
    260 assert(ret != 0);
    261}
    262
    263static void* update_fs_loop(void *data) {
    264 struct fuse_session *se = (struct fuse_session*) data;
    265 char *old_name;
    266
    267
    268 while(!fuse_session_exited(se)) {
    269 old_name = strdup(file_name);
    270 update_fs();
    271
    272 if (!options.no_notify && lookup_cnt) {
    273 if(options.only_expire) { // expire entry
    275 (se, FUSE_ROOT_ID, old_name, strlen(old_name));
    276
    277 // no kernel support
    278 if (ret == -ENOSYS) {
    279 printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n");
    280 printf("Exiting...\n");
    281
    283 // Make sure to exit now, rather than on next request from userspace
    284 pthread_kill(main_thread, SIGPIPE);
    285
    286 break;
    287 }
    288 // 1) ret == 0: successful expire of an existing entry
    289 // 2) ret == -ENOENT: kernel has already expired the entry /
    290 // entry does not exist anymore in the kernel
    291 assert(ret == 0 || ret == -ENOENT);
    292 } else { // invalidate entry
    294 (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
    295 }
    296 }
    297 free(old_name);
    298 sleep(options.update_interval);
    299 }
    300 return NULL;
    301}
    302
    303static void show_help(const char *progname)
    304{
    305 printf("usage: %s [options] <mountpoint>\n\n", progname);
    306 printf("File-system specific options:\n"
    307 " --timeout=<secs> Timeout for kernel caches\n"
    308 " --update-interval=<secs> Update-rate of file system contents\n"
    309 " --no-notify Disable kernel notifications\n"
    310 " --only-expire Expire entries instead of invalidating them\n"
    311 "\n");
    312}
    313
    314int main(int argc, char *argv[]) {
    315 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    316 struct fuse_session *se;
    317 struct fuse_cmdline_opts opts;
    318 struct fuse_loop_config *config;
    319 pthread_t updater;
    320 int ret = -1;
    321
    322 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
    323 return 1;
    324
    325 if (fuse_parse_cmdline(&args, &opts) != 0)
    326 return 1;
    327 if (opts.show_help) {
    328 show_help(argv[0]);
    331 ret = 0;
    332 goto err_out1;
    333 } else if (opts.show_version) {
    334 printf("FUSE library version %s\n", fuse_pkgversion());
    336 ret = 0;
    337 goto err_out1;
    338 }
    339
    340 /* Initial contents */
    341 update_fs();
    342
    343 se = fuse_session_new(&args, &tfs_oper,
    344 sizeof(tfs_oper), &se);
    345 if (se == NULL)
    346 goto err_out1;
    347
    348 if (fuse_set_signal_handlers(se) != 0)
    349 goto err_out2;
    350
    351 if (fuse_session_mount(se, opts.mountpoint) != 0)
    352 goto err_out3;
    353
    354 fuse_daemonize(opts.foreground);
    355
    356 // Needed to ensure that the main thread continues/restarts processing as soon
    357 // as the fuse session ends (immediately after calling fuse_session_exit() )
    358 // and not only on the next request from userspace
    359 main_thread = pthread_self();
    360
    361 /* Start thread to update file contents */
    362 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
    363 if (ret != 0) {
    364 fprintf(stderr, "pthread_create failed with %s\n",
    365 strerror(ret));
    366 goto err_out3;
    367 }
    368
    369 /* Block until ctrl+c or fusermount -u */
    370 if (opts.singlethread) {
    371 ret = fuse_session_loop(se);
    372 } else {
    373 config = fuse_loop_cfg_create();
    374 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
    375 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
    376 ret = fuse_session_loop_mt(se, config);
    377 fuse_loop_cfg_destroy(config);
    378 config = NULL;
    379 }
    380
    382err_out3:
    384err_out2:
    386err_out1:
    387 free(opts.mountpoint);
    388 fuse_opt_free_args(&args);
    389
    390 return ret ? 1 : 0;
    391}
    392
    393
    int fuse_set_signal_handlers(struct fuse_session *se)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5234
    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
    #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.17.2/doc/html/fuse-3_817_81-rc0_2example_2notify__inval__entry_8c_source.html0000644000175000017500000021244114770250311026743 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    7*/
    8
    79#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    80
    81#include <fuse_lowlevel.h>
    82#include <stdio.h>
    83#include <stdlib.h>
    84#include <string.h>
    85#include <errno.h>
    86#include <fcntl.h>
    87#include <assert.h>
    88#include <signal.h>
    89#include <stddef.h>
    90#include <sys/stat.h>
    91#include <unistd.h>
    92#include <pthread.h>
    93
    94#define MAX_STR_LEN 128
    95static char file_name[MAX_STR_LEN];
    96static fuse_ino_t file_ino = 2;
    97static int lookup_cnt = 0;
    98static pthread_t main_thread;
    99
    100/* Command line parsing */
    101struct options {
    102 int no_notify;
    103 float timeout;
    104 int update_interval;
    105 int only_expire;
    106};
    107static struct options options = {
    108 .timeout = 5,
    109 .no_notify = 0,
    110 .update_interval = 1,
    111 .only_expire = 0,
    112};
    113
    114#define OPTION(t, p) \
    115 { t, offsetof(struct options, p), 1 }
    116static const struct fuse_opt option_spec[] = {
    117 OPTION("--no-notify", no_notify),
    118 OPTION("--update-interval=%d", update_interval),
    119 OPTION("--timeout=%f", timeout),
    120 OPTION("--only-expire", only_expire),
    122};
    123
    124static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
    125 stbuf->st_ino = ino;
    126 if (ino == FUSE_ROOT_ID) {
    127 stbuf->st_mode = S_IFDIR | 0755;
    128 stbuf->st_nlink = 1;
    129 }
    130
    131 else if (ino == file_ino) {
    132 stbuf->st_mode = S_IFREG | 0000;
    133 stbuf->st_nlink = 1;
    134 stbuf->st_size = 0;
    135 }
    136
    137 else
    138 return -1;
    139
    140 return 0;
    141}
    142
    143static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
    144 (void)userdata;
    145
    146 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    147 conn->no_interrupt = 1;
    148}
    149
    150static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
    151 const char *name) {
    152 struct fuse_entry_param e;
    153 memset(&e, 0, sizeof(e));
    154
    155 if (parent != FUSE_ROOT_ID)
    156 goto err_out;
    157 else if (strcmp(name, file_name) == 0) {
    158 e.ino = file_ino;
    159 lookup_cnt++;
    160 } else
    161 goto err_out;
    162
    163 e.attr_timeout = options.timeout;
    164 e.entry_timeout = options.timeout;
    165 if (tfs_stat(e.ino, &e.attr) != 0)
    166 goto err_out;
    167 fuse_reply_entry(req, &e);
    168 return;
    169
    170err_out:
    171 fuse_reply_err(req, ENOENT);
    172}
    173
    174static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
    175 uint64_t nlookup) {
    176 (void) req;
    177 if(ino == file_ino)
    178 lookup_cnt -= nlookup;
    179 else
    180 assert(ino == FUSE_ROOT_ID);
    181 fuse_reply_none(req);
    182}
    183
    184static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
    185 struct fuse_file_info *fi) {
    186 struct stat stbuf;
    187
    188 (void) fi;
    189
    190 memset(&stbuf, 0, sizeof(stbuf));
    191 if (tfs_stat(ino, &stbuf) != 0)
    192 fuse_reply_err(req, ENOENT);
    193 else
    194 fuse_reply_attr(req, &stbuf, options.timeout);
    195}
    196
    197struct dirbuf {
    198 char *p;
    199 size_t size;
    200};
    201
    202static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
    203 fuse_ino_t ino) {
    204 struct stat stbuf;
    205 size_t oldsize = b->size;
    206 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
    207 b->p = (char *) realloc(b->p, b->size);
    208 memset(&stbuf, 0, sizeof(stbuf));
    209 stbuf.st_ino = ino;
    210 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
    211 b->size);
    212}
    213
    214#define min(x, y) ((x) < (y) ? (x) : (y))
    215
    216static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
    217 off_t off, size_t maxsize) {
    218 if (off < bufsize)
    219 return fuse_reply_buf(req, buf + off,
    220 min(bufsize - off, maxsize));
    221 else
    222 return fuse_reply_buf(req, NULL, 0);
    223}
    224
    225static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    226 off_t off, struct fuse_file_info *fi) {
    227 (void) fi;
    228
    229 if (ino != FUSE_ROOT_ID)
    230 fuse_reply_err(req, ENOTDIR);
    231 else {
    232 struct dirbuf b;
    233
    234 memset(&b, 0, sizeof(b));
    235 dirbuf_add(req, &b, file_name, file_ino);
    236 reply_buf_limited(req, b.p, b.size, off, size);
    237 free(b.p);
    238 }
    239}
    240
    241static const struct fuse_lowlevel_ops tfs_oper = {
    242 .init = tfs_init,
    243 .lookup = tfs_lookup,
    244 .getattr = tfs_getattr,
    245 .readdir = tfs_readdir,
    246 .forget = tfs_forget,
    247};
    248
    249static void update_fs(void) {
    250 time_t t;
    251 struct tm *now;
    252 ssize_t ret;
    253
    254 t = time(NULL);
    255 now = localtime(&t);
    256 assert(now != NULL);
    257
    258 ret = strftime(file_name, MAX_STR_LEN,
    259 "Time_is_%Hh_%Mm_%Ss", now);
    260 assert(ret != 0);
    261}
    262
    263static void* update_fs_loop(void *data) {
    264 struct fuse_session *se = (struct fuse_session*) data;
    265 char *old_name;
    266
    267
    268 while(!fuse_session_exited(se)) {
    269 old_name = strdup(file_name);
    270 update_fs();
    271
    272 if (!options.no_notify && lookup_cnt) {
    273 if(options.only_expire) { // expire entry
    275 (se, FUSE_ROOT_ID, old_name, strlen(old_name));
    276
    277 // no kernel support
    278 if (ret == -ENOSYS) {
    279 printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n");
    280 printf("Exiting...\n");
    281
    283 // Make sure to exit now, rather than on next request from userspace
    284 pthread_kill(main_thread, SIGPIPE);
    285
    286 break;
    287 }
    288 // 1) ret == 0: successful expire of an existing entry
    289 // 2) ret == -ENOENT: kernel has already expired the entry /
    290 // entry does not exist anymore in the kernel
    291 assert(ret == 0 || ret == -ENOENT);
    292 } else { // invalidate entry
    294 (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
    295 }
    296 }
    297 free(old_name);
    298 sleep(options.update_interval);
    299 }
    300 return NULL;
    301}
    302
    303static void show_help(const char *progname)
    304{
    305 printf("usage: %s [options] <mountpoint>\n\n", progname);
    306 printf("File-system specific options:\n"
    307 " --timeout=<secs> Timeout for kernel caches\n"
    308 " --update-interval=<secs> Update-rate of file system contents\n"
    309 " --no-notify Disable kernel notifications\n"
    310 " --only-expire Expire entries instead of invalidating them\n"
    311 "\n");
    312}
    313
    314int main(int argc, char *argv[]) {
    315 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    316 struct fuse_session *se;
    317 struct fuse_cmdline_opts opts;
    318 struct fuse_loop_config *config;
    319 pthread_t updater;
    320 int ret = -1;
    321
    322 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
    323 return 1;
    324
    325 if (fuse_parse_cmdline(&args, &opts) != 0)
    326 return 1;
    327 if (opts.show_help) {
    328 show_help(argv[0]);
    331 ret = 0;
    332 goto err_out1;
    333 } else if (opts.show_version) {
    334 printf("FUSE library version %s\n", fuse_pkgversion());
    336 ret = 0;
    337 goto err_out1;
    338 }
    339
    340 /* Initial contents */
    341 update_fs();
    342
    343 se = fuse_session_new(&args, &tfs_oper,
    344 sizeof(tfs_oper), &se);
    345 if (se == NULL)
    346 goto err_out1;
    347
    348 if (fuse_set_signal_handlers(se) != 0)
    349 goto err_out2;
    350
    351 if (fuse_session_mount(se, opts.mountpoint) != 0)
    352 goto err_out3;
    353
    354 fuse_daemonize(opts.foreground);
    355
    356 // Needed to ensure that the main thread continues/restarts processing as soon
    357 // as the fuse session ends (immediately after calling fuse_session_exit() )
    358 // and not only on the next request from userspace
    359 main_thread = pthread_self();
    360
    361 /* Start thread to update file contents */
    362 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
    363 if (ret != 0) {
    364 fprintf(stderr, "pthread_create failed with %s\n",
    365 strerror(ret));
    366 goto err_out3;
    367 }
    368
    369 /* Block until ctrl+c or fusermount -u */
    370 if (opts.singlethread) {
    371 ret = fuse_session_loop(se);
    372 } else {
    373 config = fuse_loop_cfg_create();
    374 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
    375 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
    376 ret = fuse_session_loop_mt(se, config);
    377 fuse_loop_cfg_destroy(config);
    378 config = NULL;
    379 }
    380
    382err_out3:
    384err_out2:
    386err_out1:
    387 free(opts.mountpoint);
    388 fuse_opt_free_args(&args);
    389
    390 return ret ? 1 : 0;
    391}
    392
    393
    int fuse_set_signal_handlers(struct fuse_session *se)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    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
    #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.17.2/doc/html/example_2notify__inval__inode_8c_source.html0000644000175000017500000022051215002273247024043 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 COPYING.
    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:5234
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2notify__inval__inode_8c_source.html0000644000175000017500000022052014770250311026675 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    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:5218
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/example_2notify__store__retrieve_8c_source.html0000644000175000017500000025227415002273247024627 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 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:5234
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2notify__store__retrieve_8c_source.html0000644000175000017500000025226414770250311027461 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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:5218
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/example_2null_8c_source.html0000644000175000017500000005725015002273247020647 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 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.17.2/doc/html/fuse-3_817_81-rc0_2example_2null_8c_source.html0000644000175000017500000005744714770250311023512 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/example_2passthrough_8c_source.html0000644000175000017500000026176215002273247022251 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 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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2passthrough_8c_source.html0000644000175000017500000026215114770250311025075 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641
    fuse-3.17.2/doc/html/example_2passthrough__fh_8c_source.html0000644000175000017500000034171215002273247023057 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 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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2passthrough__fh_8c_source.html0000644000175000017500000034203414770250311025710 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641
    fuse-3.17.2/doc/html/example_2passthrough__helpers_8h_source.html0000644000175000017500000003404615002273247024130 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/*
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2passthrough__helpers_8h_source.html0000644000175000017500000003424114770250311026760 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/example_2passthrough__ll_8c_source.html0000644000175000017500000100162215002273247023063 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 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) {
    175 has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
    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) {
    181 has_flag = fuse_set_feature_flag(conn, 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}
    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:5234
    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:97
    uint32_t parallel_direct_writes
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2passthrough__ll_8c_source.html0000644000175000017500000100102714770250311025715 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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) {
    175 has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
    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) {
    181 has_flag = fuse_set_feature_flag(conn, 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}
    int fuse_set_signal_handlers(struct fuse_session *se)
    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
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    void fuse_remove_signal_handlers(struct fuse_session *se)
    @ FUSE_BUF_SPLICE_MOVE
    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_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
    #define FUSE_CAP_WRITEBACK_CACHE
    #define FUSE_CAP_FLOCK_LOCKS
    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:97
    uint32_t parallel_direct_writes
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/example_2poll_8c_source.html0000644000175000017500000014346115002273247020643 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 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:4669
    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:86
    uint32_t direct_io
    Definition fuse_common.h:71
    void(* destroy)(void *private_data)
    Definition fuse.h:649
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2poll_8c_source.html0000644000175000017500000014364114770250311023476 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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:86
    uint32_t direct_io
    Definition fuse_common.h:71
    void(* destroy)(void *private_data)
    Definition fuse.h:649
    fuse-3.17.2/doc/html/example_2poll__client_8c_source.html0000644000175000017500000003063515002273247022336 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 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.17.2/doc/html/fuse-3_817_81-rc0_2example_2poll__client_8c_source.html0000644000175000017500000003105314770250311025164 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/example_2printcap_8c_source.html0000644000175000017500000013360615002273247021515 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 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
    #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:5234
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2printcap_8c_source.html0000644000175000017500000012744114770250311024350 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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}
    int fuse_set_signal_handlers(struct fuse_session *se)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    void fuse_remove_signal_handlers(struct fuse_session *se)
    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
    #define FUSE_CAP_IOCTL_DIR
    #define FUSE_CAP_DONT_MASK
    #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
    #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
    #define FUSE_CAP_PASSTHROUGH
    #define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
    #define FUSE_CAP_NO_OPEN_SUPPORT
    #define FUSE_CAP_READDIRPLUS
    #define FUSE_CAP_SETXATTR_EXT
    #define FUSE_CAP_SPLICE_MOVE
    #define FUSE_CAP_NO_EXPORT_SUPPORT
    #define FUSE_CAP_FLOCK_LOCKS
    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.17.2/doc/html/fuse-3_817_81-rc0_2include_2cuse__lowlevel_8h_source.html0000644000175000017500000004611314770250311025530 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/include_2cuse__lowlevel_8h_source.html0000644000175000017500000004573615002273247022707 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 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.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse_8h_source.html0000644000175000017500000044614214770250311023471 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    885static inline int fuse_main_real(int argc, char *argv[],
    886 const struct fuse_operations *op,
    887 size_t op_size, void *user_data)
    888{
    889 struct libfuse_version version = { .major = FUSE_MAJOR_VERSION,
    890 .minor = FUSE_MINOR_VERSION,
    891 .hotfix = FUSE_HOTFIX_VERSION,
    892 .padding = 0 };
    893
    894 fuse_log(FUSE_LOG_ERR,
    895 "%s is a libfuse internal function, please use fuse_main()\n",
    896 __func__);
    897
    898 /* not declared globally, to restrict usage of this function */
    899 int fuse_main_real_versioned(int argc, char *argv[],
    900 const struct fuse_operations *op,
    901 size_t op_size,
    902 struct libfuse_version *version,
    903 void *user_data);
    904
    905 return fuse_main_real_versioned(argc, argv, op, op_size, &version,
    906 user_data);
    907}
    908
    963static inline int fuse_main_fn(int argc, char *argv[],
    964 const struct fuse_operations *op,
    965 void *user_data)
    966{
    967 struct libfuse_version version = {
    968 .major = FUSE_MAJOR_VERSION,
    969 .minor = FUSE_MINOR_VERSION,
    970 .hotfix = FUSE_HOTFIX_VERSION,
    971 .padding = 0
    972 };
    973
    974 /* not declared globally, to restrict usage of this function */
    975 int fuse_main_real_versioned(int argc, char *argv[],
    976 const struct fuse_operations *op,
    977 size_t op_size,
    978 struct libfuse_version *version,
    979 void *user_data);
    980 return fuse_main_real_versioned(argc, argv, op, sizeof(*(op)), &version,
    981 user_data);
    982}
    983#define fuse_main(argc, argv, op, user_data) \
    984 fuse_main_fn(argc, argv, op, user_data)
    985
    986/* ----------------------------------------------------------- *
    987 * More detailed API *
    988 * ----------------------------------------------------------- */
    989
    1001void fuse_lib_help(struct fuse_args *args);
    1002
    1030#if FUSE_USE_VERSION == 30
    1031struct fuse *_fuse_new_30(struct fuse_args *args,
    1032 const struct fuse_operations *op,
    1033 size_t op_size,
    1034 struct libfuse_version *version,
    1035 void *user_data);
    1036static inline struct fuse *
    1037fuse_new_fn(struct fuse_args *args,
    1038 const struct fuse_operations *op, size_t op_size,
    1039 void *user_data)
    1040{
    1041 /* not declared globally, to restrict usage of this function */
    1042 struct fuse *_fuse_new_30(struct fuse_args *args,
    1043 const struct fuse_operations *op, size_t op_size,
    1044 struct libfuse_version *version,
    1045 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_30(args, op, op_size, &version, user_data);
    1055}
    1056#else /* FUSE_USE_VERSION */
    1057static inline struct fuse *
    1058fuse_new_fn(struct fuse_args *args,
    1059 const struct fuse_operations *op, size_t op_size,
    1060 void *user_data)
    1061{
    1062 struct libfuse_version version = {
    1063 .major = FUSE_MAJOR_VERSION,
    1064 .minor = FUSE_MINOR_VERSION,
    1065 .hotfix = FUSE_HOTFIX_VERSION,
    1066 .padding = 0
    1067 };
    1068
    1069 /* not declared globally, to restrict usage of this function */
    1070 struct fuse *_fuse_new_31(struct fuse_args *args,
    1071 const struct fuse_operations *op,
    1072 size_t op_size, struct libfuse_version *version,
    1073 void *user_data);
    1074 return _fuse_new_31(args, op, op_size, &version, user_data);
    1075}
    1076#endif
    1077#define fuse_new(args, op, size, data) fuse_new_fn(args, op, size, data)
    1078
    1087int fuse_mount(struct fuse *f, const char *mountpoint);
    1088
    1096void fuse_unmount(struct fuse *f);
    1097
    1106void fuse_destroy(struct fuse *f);
    1107
    1123int fuse_loop(struct fuse *f);
    1124
    1133void fuse_exit(struct fuse *f);
    1134
    1135#if FUSE_USE_VERSION < 32
    1136int fuse_loop_mt_31(struct fuse *f, int clone_fd);
    1137#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd)
    1138#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
    1139int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
    1140#define fuse_loop_mt(f, config) fuse_loop_mt_32(f, config)
    1141#else
    1173#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
    1174int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config);
    1175#else
    1176#define fuse_loop_mt(f, config) fuse_loop_mt_312(f, config)
    1177#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
    1178#endif
    1179
    1180
    1189struct fuse_context *fuse_get_context(void);
    1190
    1209int fuse_getgroups(int size, gid_t list[]);
    1210
    1216int fuse_interrupted(void);
    1217
    1229int fuse_invalidate_path(struct fuse *f, const char *path);
    1230
    1239
    1246void fuse_stop_cleanup_thread(struct fuse *fuse);
    1247
    1257int fuse_clean_cache(struct fuse *fuse);
    1258
    1259/*
    1260 * Stacking API
    1261 */
    1262
    1268struct fuse_fs;
    1269
    1270/*
    1271 * These functions call the relevant filesystem operation, and return
    1272 * the result.
    1273 *
    1274 * If the operation is not defined, they return -ENOSYS, with the
    1275 * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
    1276 * fuse_fs_releasedir and fuse_fs_statfs, which return 0.
    1277 */
    1278
    1279int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
    1280 struct fuse_file_info *fi);
    1281int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
    1282 const char *newpath, unsigned int flags);
    1283int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
    1284int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
    1285int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
    1286 const char *path);
    1287int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
    1288int fuse_fs_release(struct fuse_fs *fs, const char *path,
    1289 struct fuse_file_info *fi);
    1290int fuse_fs_open(struct fuse_fs *fs, const char *path,
    1291 struct fuse_file_info *fi);
    1292int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
    1293 off_t off, struct fuse_file_info *fi);
    1294int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
    1295 struct fuse_bufvec **bufp, size_t size, off_t off,
    1296 struct fuse_file_info *fi);
    1297int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
    1298 size_t size, off_t off, struct fuse_file_info *fi);
    1299int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
    1300 struct fuse_bufvec *buf, off_t off,
    1301 struct fuse_file_info *fi);
    1302int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
    1303 struct fuse_file_info *fi);
    1304int fuse_fs_flush(struct fuse_fs *fs, const char *path,
    1305 struct fuse_file_info *fi);
    1306int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
    1307int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
    1308 struct fuse_file_info *fi);
    1309int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
    1310 fuse_fill_dir_t filler, off_t off,
    1311 struct fuse_file_info *fi, enum fuse_readdir_flags flags);
    1312int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
    1313 struct fuse_file_info *fi);
    1314int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
    1315 struct fuse_file_info *fi);
    1316int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
    1317 struct fuse_file_info *fi);
    1318int fuse_fs_lock(struct fuse_fs *fs, const char *path,
    1319 struct fuse_file_info *fi, int cmd, struct flock *lock);
    1320int fuse_fs_flock(struct fuse_fs *fs, const char *path,
    1321 struct fuse_file_info *fi, int op);
    1322int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
    1323 struct fuse_file_info *fi);
    1324int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid,
    1325 struct fuse_file_info *fi);
    1326int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
    1327 struct fuse_file_info *fi);
    1328int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
    1329 const struct timespec tv[2], struct fuse_file_info *fi);
    1330int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
    1331int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
    1332 size_t len);
    1333int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
    1334 dev_t rdev);
    1335int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
    1336int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
    1337 const char *value, size_t size, int flags);
    1338int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
    1339 char *value, size_t size);
    1340int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
    1341 size_t size);
    1342int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
    1343 const char *name);
    1344int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
    1345 uint64_t *idx);
    1346#if FUSE_USE_VERSION < 35
    1347int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd,
    1348 void *arg, struct fuse_file_info *fi, unsigned int flags,
    1349 void *data);
    1350#else
    1351int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
    1352 void *arg, struct fuse_file_info *fi, unsigned int flags,
    1353 void *data);
    1354#endif
    1355int fuse_fs_poll(struct fuse_fs *fs, const char *path,
    1356 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
    1357 unsigned *reventsp);
    1358int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
    1359 off_t offset, off_t length, struct fuse_file_info *fi);
    1360ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
    1361 struct fuse_file_info *fi_in, off_t off_in,
    1362 const char *path_out,
    1363 struct fuse_file_info *fi_out, off_t off_out,
    1364 size_t len, int flags);
    1365off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
    1366 struct fuse_file_info *fi);
    1367void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
    1368 struct fuse_config *cfg);
    1369void fuse_fs_destroy(struct fuse_fs *fs);
    1370
    1371int fuse_notify_poll(struct fuse_pollhandle *ph);
    1372
    1386struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
    1387 void *private_data);
    1388
    1403typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args,
    1404 struct fuse_fs *fs[]);
    1415#define FUSE_REGISTER_MODULE(name_, factory_) \
    1416 fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
    1417
    1419struct fuse_session *fuse_get_session(struct fuse *f);
    1420
    1429int fuse_open_channel(const char *mountpoint, const char *options);
    1430
    1431#ifdef __cplusplus
    1432}
    1433#endif
    1434
    1435#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:5204
    int fuse_interrupted(void)
    Definition fuse.c:4663
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5153
    int fuse_start_cleanup_thread(struct fuse *fuse)
    Definition fuse.c:4904
    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:479
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4520
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5209
    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:4912
    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:87
    fuse_readdir_flags
    Definition fuse.h:42
    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.17.2/doc/html/include_2fuse_8h_source.html0000644000175000017500000042412715002273247020635 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 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);
    101struct fuse_config {
    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
    127 double entry_timeout;
    128
    137 double negative_timeout;
    138
    143 double attr_timeout;
    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
    245 int32_t kernel_cache;
    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
    297 int32_t no_rofd_flush;
    298
    313
    314
    318 uint32_t flags;
    319
    323 uint64_t reserved[48];
    324};
    325
    326
    349struct fuse_operations {
    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
    860struct fuse_context {
    862 struct fuse *fuse;
    863
    865 uid_t uid;
    866
    868 gid_t gid;
    869
    871 pid_t pid;
    872
    874 void *private_data;
    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:4679
    int fuse_mount(struct fuse *f, const char *mountpoint)
    Definition fuse.c:5220
    int fuse_interrupted(void)
    Definition fuse.c:4688
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5169
    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:4929
    int fuse_invalidate_path(struct fuse *f, const char *path)
    Definition fuse.c:4698
    struct fuse_context * fuse_get_context(void)
    Definition fuse.c:4669
    int fuse_loop(struct fuse *f)
    Definition fuse.c:4602
    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:4664
    int fuse_clean_cache(struct fuse *fuse)
    Definition fuse.c:4458
    void fuse_lib_help(struct fuse_args *args)
    Definition fuse.c:4769
    int fuse_open_channel(const char *mountpoint, const char *options)
    Definition helper.c:475
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4545
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5225
    struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
    Definition fuse.c:4879
    void fuse_stop_cleanup_thread(struct fuse *fuse)
    Definition fuse.c:4937
    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:87
    fuse_readdir_flags
    Definition fuse.h:42
    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.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse__common_8h_source.html0000644000175000017500000034126214770250311025175 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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#if (defined(__cplusplus) && __cplusplus >= 201103L) || \
    34 (!defined(__cplusplus) && defined(__STDC_VERSION__) && \
    35 __STDC_VERSION__ >= 201112L)
    36#define fuse_static_assert(condition, message) static_assert(condition, message)
    37#else
    38#define fuse_static_assert(condition, message)
    39#endif
    40
    41#ifdef __cplusplus
    42extern "C" {
    43#endif
    44
    60 int32_t flags;
    61
    68 uint32_t writepage : 1;
    69
    71 uint32_t direct_io : 1;
    72
    77 uint32_t keep_cache : 1;
    78
    82 uint32_t flush : 1;
    83
    86 uint32_t nonseekable : 1;
    87
    88 /* Indicates that flock locks for this file should be
    89 released. If set, lock_owner shall contain a valid value.
    90 May only be set in ->release(). */
    91 uint32_t flock_release : 1;
    92
    97 uint32_t cache_readdir : 1;
    98
    101 uint32_t noflush : 1;
    102
    106
    108 uint32_t padding : 23;
    109 uint32_t padding2 : 32;
    110 uint32_t padding3 : 32;
    111
    115 uint64_t fh;
    116
    118 uint64_t lock_owner;
    119
    122 uint32_t poll_events;
    123
    127 int32_t backing_id;
    128
    130 uint64_t compat_flags;
    131
    132 uint64_t reserved[2];
    133};
    134fuse_static_assert(sizeof(struct fuse_file_info) == 64,
    135 "fuse_file_info size mismatch");
    136
    147#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
    148struct fuse_loop_config_v1; /* forward declaration */
    150#else
    151struct fuse_loop_config_v1 {
    152#endif
    158
    169 unsigned int max_idle_threads;
    170};
    171
    172
    173/**************************************************************************
    174 * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
    175 **************************************************************************/
    176
    526
    537#define FUSE_IOCTL_COMPAT (1 << 0)
    538#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
    539#define FUSE_IOCTL_RETRY (1 << 2)
    540#define FUSE_IOCTL_DIR (1 << 4)
    541
    542#define FUSE_IOCTL_MAX_IOV 256
    543
    559 uint32_t proto_major;
    560
    564 uint32_t proto_minor;
    565
    569 uint32_t max_write;
    570
    583 uint32_t max_read;
    584
    589
    595 uint32_t capable;
    596
    607 uint32_t want;
    608
    638
    648
    664 uint32_t time_gran;
    665
    682#define FUSE_BACKING_STACKED_UNDER (0)
    683#define FUSE_BACKING_STACKED_OVER (1)
    684 uint32_t max_backing_stack_depth;
    685
    694 uint32_t no_interrupt : 1;
    695
    696 /* reserved bits for future use */
    697 uint32_t padding : 31;
    698
    703 uint64_t capable_ext;
    704
    713 uint64_t want_ext;
    714
    718 uint32_t reserved[16];
    719};
    720fuse_static_assert(sizeof(struct fuse_conn_info) == 128,
    721 "Size of struct fuse_conn_info must be 128 bytes");
    722
    723struct fuse_session;
    724struct fuse_pollhandle;
    725struct fuse_conn_info_opts;
    726
    769struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
    770
    778void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
    779 struct fuse_conn_info *conn);
    780
    787int fuse_daemonize(int foreground);
    788
    794int fuse_version(void);
    795
    801const char *fuse_pkgversion(void);
    802
    808void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
    809
    810/* ----------------------------------------------------------- *
    811 * Data buffer *
    812 * ----------------------------------------------------------- */
    813
    824 FUSE_BUF_IS_FD = (1 << 1),
    825
    834
    842 FUSE_BUF_FD_RETRY = (1 << 3)
    844
    886
    893struct fuse_buf {
    897 size_t size;
    898
    903
    909 void *mem;
    910
    916 int fd;
    917
    923 off_t pos;
    924
    931 size_t mem_size;
    932};
    933
    946 size_t count;
    947
    951 size_t idx;
    952
    956 size_t off;
    957
    961 struct fuse_buf buf[1];
    962};
    963
    969{
    970 uint32_t major;
    971 uint32_t minor;
    972 uint32_t hotfix;
    973 uint32_t padding;
    974};
    975
    976/* Initialize bufvec with a single buffer of given size */
    977#define FUSE_BUFVEC_INIT(size__) \
    978 ((struct fuse_bufvec) { \
    979 /* .count= */ 1, \
    980 /* .idx = */ 0, \
    981 /* .off = */ 0, \
    982 /* .buf = */ { /* [0] = */ { \
    983 /* .size = */ (size__), \
    984 /* .flags = */ (enum fuse_buf_flags) 0, \
    985 /* .mem = */ NULL, \
    986 /* .fd = */ -1, \
    987 /* .pos = */ 0, \
    988 /* .mem_size = */ 0, \
    989 } } \
    990 } )
    991
    998size_t fuse_buf_size(const struct fuse_bufvec *bufv);
    999
    1008ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
    1009 enum fuse_buf_copy_flags flags);
    1010
    1011/* ----------------------------------------------------------- *
    1012 * Signal handling *
    1013 * ----------------------------------------------------------- */
    1014
    1030int fuse_set_signal_handlers(struct fuse_session *se);
    1031
    1047int fuse_set_fail_signal_handlers(struct fuse_session *se);
    1048
    1060void fuse_remove_signal_handlers(struct fuse_session *se);
    1061
    1067#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
    1073struct fuse_loop_config *fuse_loop_cfg_create(void);
    1074
    1078void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
    1079
    1083void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
    1084 unsigned int value);
    1085
    1089void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
    1090 unsigned int value);
    1091
    1095void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
    1096 unsigned int value);
    1097
    1104void fuse_loop_cfg_convert(struct fuse_loop_config *config,
    1105 struct fuse_loop_config_v1 *v1_conf);
    1106#endif
    1107
    1108
    1109static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn,
    1110 uint64_t flag)
    1111{
    1112 if (conn->capable_ext & flag) {
    1113 conn->want_ext |= flag;
    1114 return true;
    1115 }
    1116 return false;
    1117}
    1118
    1119static inline void fuse_unset_feature_flag(struct fuse_conn_info *conn,
    1120 uint64_t flag)
    1121{
    1122 conn->want_ext &= ~flag;
    1123}
    1124
    1125static inline bool fuse_get_feature_flag(struct fuse_conn_info *conn,
    1126 uint64_t flag)
    1127{
    1128 return conn->capable_ext & flag ? true : false;
    1129}
    1130
    1131/* ----------------------------------------------------------- *
    1132 * Compatibility stuff *
    1133 * ----------------------------------------------------------- */
    1134
    1135#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30
    1136# error only API version 30 or greater is supported
    1137#endif
    1138
    1139#ifdef __cplusplus
    1140}
    1141#endif
    1142
    1143
    1144/*
    1145 * This interface uses 64 bit off_t.
    1146 *
    1147 * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags!
    1148 */
    1149
    1150#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
    1151_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit");
    1152#else
    1153struct _fuse_off_t_must_be_64bit_dummy_struct \
    1154 { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); };
    1155#endif
    1156
    1157#endif /* FUSE_COMMON_H_ */
    int fuse_set_fail_signal_handlers(struct fuse_session *se)
    int fuse_set_signal_handlers(struct fuse_session *se)
    fuse_capability
    @ FUSE_CAP_CURRENT_MAX
    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:416
    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:463
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
    int fuse_version(void)
    Definition fuse.c:5213
    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
    #define FUSE_CAP_IOCTL_DIR
    #define FUSE_CAP_DONT_MASK
    #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
    #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_flags
    #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
    #define FUSE_CAP_PASSTHROUGH
    #define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
    #define FUSE_CAP_NO_OPEN_SUPPORT
    #define FUSE_CAP_READDIRPLUS
    #define FUSE_CAP_SETXATTR_EXT
    #define FUSE_CAP_SPLICE_MOVE
    #define FUSE_CAP_NO_EXPORT_SUPPORT
    #define FUSE_CAP_FLOCK_LOCKS
    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:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t nonseekable
    Definition fuse_common.h:86
    int32_t backing_id
    uint32_t parallel_direct_writes
    uint32_t padding
    uint32_t noflush
    uint64_t compat_flags
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    unsigned int max_idle_threads
    fuse-3.17.2/doc/html/include_2fuse__common_8h_source.html0000644000175000017500000024115115002273247022336 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 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 HAVE_STATIC_ASSERT
    34#define fuse_static_assert(condition, message) static_assert(condition, message)
    35#else
    36#define fuse_static_assert(condition, message)
    37#endif
    38
    39#ifdef __cplusplus
    40extern "C" {
    41#endif
    42
    56struct fuse_file_info {
    58 int32_t flags;
    59
    66 uint32_t writepage : 1;
    67
    69 uint32_t direct_io : 1;
    70
    75 uint32_t keep_cache : 1;
    76
    80 uint32_t flush : 1;
    81
    84 uint32_t nonseekable : 1;
    85
    86 /* Indicates that flock locks for this file should be
    87 released. If set, lock_owner shall contain a valid value.
    88 May only be set in ->release(). */
    89 uint32_t flock_release : 1;
    90
    95 uint32_t cache_readdir : 1;
    96
    99 uint32_t noflush : 1;
    100
    103 uint32_t parallel_direct_writes : 1;
    104
    106 uint32_t padding : 23;
    107 uint32_t padding2 : 32;
    108 uint32_t padding3 : 32;
    109
    113 uint64_t fh;
    114
    116 uint64_t lock_owner;
    117
    120 uint32_t poll_events;
    121
    125 int32_t backing_id;
    126
    128 uint64_t compat_flags;
    129
    130 uint64_t reserved[2];
    131};
    132fuse_static_assert(sizeof(struct fuse_file_info) == 64,
    133 "fuse_file_info size mismatch");
    134
    145#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
    146struct fuse_loop_config_v1; /* forward declaration */
    147struct fuse_loop_config {
    148#else
    149struct fuse_loop_config_v1 {
    150#endif
    155 int clone_fd;
    156
    167 unsigned int max_idle_threads;
    168};
    169
    170
    171/**************************************************************************
    172 * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
    173 **************************************************************************/
    174
    185#define FUSE_CAP_ASYNC_READ (1 << 0)
    186
    193#define FUSE_CAP_POSIX_LOCKS (1 << 1)
    194
    202#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
    203
    214#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
    215
    222#define FUSE_CAP_DONT_MASK (1 << 6)
    223
    230#define FUSE_CAP_SPLICE_WRITE (1 << 7)
    231
    238#define FUSE_CAP_SPLICE_MOVE (1 << 8)
    239
    247#define FUSE_CAP_SPLICE_READ (1 << 9)
    248
    260#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
    261
    267#define FUSE_CAP_IOCTL_DIR (1 << 11)
    268
    289#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12)
    290
    297#define FUSE_CAP_READDIRPLUS (1 << 13)
    298
    325#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14)
    326
    336#define FUSE_CAP_ASYNC_DIO (1 << 15)
    337
    345#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
    346
    360#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17)
    361
    368#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
    369
    387#define FUSE_CAP_POSIX_ACL (1 << 19)
    388
    396#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20)
    397
    413#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1 << 21)
    414
    426#define FUSE_CAP_CACHE_SYMLINKS (1 << 23)
    427
    441#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24)
    442
    464#define FUSE_CAP_EXPLICIT_INVAL_DATA (1 << 25)
    465
    480#define FUSE_CAP_EXPIRE_ONLY (1 << 26)
    481
    487#define FUSE_CAP_SETXATTR_EXT (1 << 27)
    488
    496#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1 << 28)
    497
    508#define FUSE_CAP_PASSTHROUGH (1 << 29)
    509
    516#define FUSE_CAP_NO_EXPORT_SUPPORT (1 << 30)
    517
    528#define FUSE_IOCTL_COMPAT (1 << 0)
    529#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
    530#define FUSE_IOCTL_RETRY (1 << 2)
    531#define FUSE_IOCTL_DIR (1 << 4)
    532
    533#define FUSE_IOCTL_MAX_IOV 256
    534
    546struct fuse_conn_info {
    550 uint32_t proto_major;
    551
    555 uint32_t proto_minor;
    556
    560 uint32_t max_write;
    561
    574 uint32_t max_read;
    575
    579 uint32_t max_readahead;
    580
    586 uint32_t capable;
    587
    598 uint32_t want;
    599
    628 uint32_t max_background;
    629
    638 uint32_t congestion_threshold;
    639
    655 uint32_t time_gran;
    656
    673#define FUSE_BACKING_STACKED_UNDER (0)
    674#define FUSE_BACKING_STACKED_OVER (1)
    675 uint32_t max_backing_stack_depth;
    676
    685 uint32_t no_interrupt : 1;
    686
    687 /* reserved bits for future use */
    688 uint32_t padding : 31;
    689
    694 uint64_t capable_ext;
    695
    704 uint64_t want_ext;
    705
    709 uint32_t reserved[16];
    710};
    711fuse_static_assert(sizeof(struct fuse_conn_info) == 128,
    712 "Size of struct fuse_conn_info must be 128 bytes");
    713
    714struct fuse_session;
    715struct fuse_pollhandle;
    716struct fuse_conn_info_opts;
    717
    760struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
    761
    769void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
    770 struct fuse_conn_info *conn);
    771
    778int fuse_daemonize(int foreground);
    779
    785int fuse_version(void);
    786
    792const char *fuse_pkgversion(void);
    793
    799void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
    800
    801/* ----------------------------------------------------------- *
    802 * Data buffer *
    803 * ----------------------------------------------------------- */
    804
    815 FUSE_BUF_IS_FD = (1 << 1),
    816
    825
    833 FUSE_BUF_FD_RETRY = (1 << 3)
    835
    877
    884struct fuse_buf {
    888 size_t size;
    889
    893 enum fuse_buf_flags flags;
    894
    900 void *mem;
    901
    907 int fd;
    908
    914 off_t pos;
    915
    922 size_t mem_size;
    923};
    924
    933struct fuse_bufvec {
    937 size_t count;
    938
    942 size_t idx;
    943
    947 size_t off;
    948
    952 struct fuse_buf buf[1];
    953};
    954
    959struct libfuse_version
    960{
    961 uint32_t major;
    962 uint32_t minor;
    963 uint32_t hotfix;
    964 uint32_t padding;
    965};
    966
    967/* Initialize bufvec with a single buffer of given size */
    968#define FUSE_BUFVEC_INIT(size__) \
    969 ((struct fuse_bufvec) { \
    970 /* .count= */ 1, \
    971 /* .idx = */ 0, \
    972 /* .off = */ 0, \
    973 /* .buf = */ { /* [0] = */ { \
    974 /* .size = */ (size__), \
    975 /* .flags = */ (enum fuse_buf_flags) 0, \
    976 /* .mem = */ NULL, \
    977 /* .fd = */ -1, \
    978 /* .pos = */ 0, \
    979 /* .mem_size = */ 0, \
    980 } } \
    981 } )
    982
    989size_t fuse_buf_size(const struct fuse_bufvec *bufv);
    990
    999ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
    1000 enum fuse_buf_copy_flags flags);
    1001
    1002/* ----------------------------------------------------------- *
    1003 * Signal handling *
    1004 * ----------------------------------------------------------- */
    1005
    1021int fuse_set_signal_handlers(struct fuse_session *se);
    1022
    1038int fuse_set_fail_signal_handlers(struct fuse_session *se);
    1039
    1051void fuse_remove_signal_handlers(struct fuse_session *se);
    1052
    1058#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
    1064struct fuse_loop_config *fuse_loop_cfg_create(void);
    1065
    1069void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
    1070
    1074void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
    1075 unsigned int value);
    1076
    1080void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
    1081 unsigned int value);
    1082
    1086void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
    1087 unsigned int value);
    1088
    1095void fuse_loop_cfg_convert(struct fuse_loop_config *config,
    1096 struct fuse_loop_config_v1 *v1_conf);
    1097#endif
    1098
    1099
    1100static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn,
    1101 uint64_t flag)
    1102{
    1103 if (conn->capable_ext & flag) {
    1104 conn->want_ext |= flag;
    1105 return true;
    1106 }
    1107 return false;
    1108}
    1109
    1110static inline void fuse_unset_feature_flag(struct fuse_conn_info *conn,
    1111 uint64_t flag)
    1112{
    1113 conn->want_ext &= ~flag;
    1114}
    1115
    1116static inline bool fuse_get_feature_flag(struct fuse_conn_info *conn,
    1117 uint64_t flag)
    1118{
    1119 return conn->capable_ext & flag ? true : false;
    1120}
    1121
    1122/* ----------------------------------------------------------- *
    1123 * Compatibility stuff *
    1124 * ----------------------------------------------------------- */
    1125
    1126#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30
    1127# error only API version 30 or greater is supported
    1128#endif
    1129
    1130#ifdef __cplusplus
    1131}
    1132#endif
    1133
    1134
    1135/*
    1136 * This interface uses 64 bit off_t.
    1137 *
    1138 * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags!
    1139 */
    1140
    1141#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
    1142_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit");
    1143#else
    1144struct _fuse_off_t_must_be_64bit_dummy_struct \
    1145 { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); };
    1146#endif
    1147
    1148#endif /* FUSE_COMMON_H_ */
    int fuse_set_fail_signal_handlers(struct fuse_session *se)
    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:459
    const char * fuse_pkgversion(void)
    Definition fuse.c:5234
    void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
    int fuse_version(void)
    Definition fuse.c:5229
    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 capable_ext
    uint64_t want_ext
    uint64_t lock_owner
    uint32_t writepage
    Definition fuse_common.h:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t nonseekable
    Definition fuse_common.h:86
    int32_t backing_id
    uint32_t parallel_direct_writes
    uint32_t padding
    uint32_t noflush
    uint64_t compat_flags
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse__kernel_8h_source.html0000644000175000017500000042346314770250311025171 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/include_2fuse__kernel_8h_source.html0000644000175000017500000042265515002273247022340 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
    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
    1174struct fuse_ext_header {
    1175 uint32_t size;
    1176 uint32_t type;
    1177};
    1178
    1184struct fuse_supp_groups {
    1185 uint32_t nr_groups;
    1186 uint32_t groups[];
    1187};
    1188
    1189#endif /* _LINUX_FUSE_H */
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse__log_8h_source.html0000644000175000017500000003045714770250311024467 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/include_2fuse__log_8h_source.html0000644000175000017500000003022615002273247021626 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 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.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse__lowlevel_8h_source.html0000644000175000017500000052145214770250311025537 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    1437int fuse_passthrough_open(fuse_req_t req, int fd);
    1438int fuse_passthrough_close(fuse_req_t req, int backing_id);
    1439
    1454int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi);
    1455
    1466int fuse_reply_write(fuse_req_t req, size_t count);
    1467
    1479int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size);
    1480
    1524int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
    1526
    1538int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count);
    1539
    1550int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf);
    1551
    1562int fuse_reply_xattr(fuse_req_t req, size_t count);
    1563
    1574int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
    1575
    1586int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
    1587
    1588/* ----------------------------------------------------------- *
    1589 * Filling a buffer in readdir *
    1590 * ----------------------------------------------------------- */
    1591
    1619size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
    1620 const char *name, const struct stat *stbuf,
    1621 off_t off);
    1622
    1636size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
    1637 const char *name,
    1638 const struct fuse_entry_param *e, off_t off);
    1639
    1656 const struct iovec *in_iov, size_t in_count,
    1657 const struct iovec *out_iov, size_t out_count);
    1658
    1670int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
    1671
    1683int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
    1684 int count);
    1685
    1692int fuse_reply_poll(fuse_req_t req, unsigned revents);
    1693
    1704int fuse_reply_lseek(fuse_req_t req, off_t off);
    1705
    1706/* ----------------------------------------------------------- *
    1707 * Notification *
    1708 * ----------------------------------------------------------- */
    1709
    1717int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph);
    1718
    1742int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
    1743 off_t off, off_t len);
    1744
    1769int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
    1770 const char *name, size_t namelen);
    1771
    1800int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
    1801 const char *name, size_t namelen);
    1802
    1831int fuse_lowlevel_notify_delete(struct fuse_session *se,
    1832 fuse_ino_t parent, fuse_ino_t child,
    1833 const char *name, size_t namelen);
    1834
    1860int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
    1861 off_t offset, struct fuse_bufvec *bufv,
    1892int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
    1893 size_t size, off_t offset, void *cookie);
    1894
    1895
    1896/* ----------------------------------------------------------- *
    1897 * Utility functions *
    1898 * ----------------------------------------------------------- */
    1899
    1906void *fuse_req_userdata(fuse_req_t req);
    1907
    1917const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
    1918
    1938int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
    1939
    1946typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data);
    1947
    1960 void *data);
    1961
    1969
    1970
    1971/* ----------------------------------------------------------- *
    1972 * Inquiry functions *
    1973 * ----------------------------------------------------------- */
    1974
    1978void fuse_lowlevel_version(void);
    1979
    1985void fuse_lowlevel_help(void);
    1986
    1990void fuse_cmdline_help(void);
    1991
    1992/* ----------------------------------------------------------- *
    1993 * Filesystem setup & teardown *
    1994 * ----------------------------------------------------------- */
    1995
    2002 int singlethread;
    2003 int foreground;
    2004 int debug;
    2005 int nodefault_subtype;
    2006 char *mountpoint;
    2007 int show_version;
    2008 int show_help;
    2009 int clone_fd;
    2010 unsigned int max_idle_threads; /* discouraged, due to thread
    2011 * destruct overhead */
    2012
    2013 /* Added in libfuse-3.12 */
    2014 unsigned int max_threads;
    2015};
    2016
    2035#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
    2036int fuse_parse_cmdline(struct fuse_args *args,
    2037 struct fuse_cmdline_opts *opts);
    2038#else
    2039#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
    2040int fuse_parse_cmdline_30(struct fuse_args *args,
    2041 struct fuse_cmdline_opts *opts);
    2042#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_30(args, opts)
    2043#else
    2044int fuse_parse_cmdline_312(struct fuse_args *args,
    2045 struct fuse_cmdline_opts *opts);
    2046#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_312(args, opts)
    2047#endif
    2048#endif
    2049
    2078static inline struct fuse_session *
    2079fuse_session_new_fn(struct fuse_args *args, const struct fuse_lowlevel_ops *op,
    2080 size_t op_size, void *userdata)
    2081{
    2082 struct libfuse_version version = {
    2083 .major = FUSE_MAJOR_VERSION,
    2084 .minor = FUSE_MINOR_VERSION,
    2085 .hotfix = FUSE_HOTFIX_VERSION,
    2086 .padding = 0
    2087 };
    2088
    2089 /* not declared globally, to restrict usage of this function */
    2090 struct fuse_session *fuse_session_new_versioned(
    2091 struct fuse_args *args, const struct fuse_lowlevel_ops *op,
    2092 size_t op_size, struct libfuse_version *version,
    2093 void *userdata);
    2094
    2095 return fuse_session_new_versioned(args, op, op_size, &version,
    2096 userdata);
    2097}
    2098#define fuse_session_new(args, op, op_size, userdata) \
    2099 fuse_session_new_fn(args, op, op_size, userdata)
    2100
    2101/*
    2102 * This should mostly not be called directly, but instead the
    2103 * fuse_session_custom_io() should be used.
    2104 */
    2105int fuse_session_custom_io_317(struct fuse_session *se,
    2106 const struct fuse_custom_io *io, size_t op_size, int fd);
    2107
    2135#if FUSE_MAKE_VERSION(3, 17) <= FUSE_USE_VERSION
    2136static inline int fuse_session_custom_io(struct fuse_session *se,
    2137 const struct fuse_custom_io *io, size_t op_size, int fd)
    2138{
    2139 return fuse_session_custom_io_317(se, io, op_size, fd);
    2140}
    2141#else
    2142static inline int fuse_session_custom_io(struct fuse_session *se,
    2143 const struct fuse_custom_io *io, int fd)
    2144{
    2145 return fuse_session_custom_io_317(se, io,
    2146 offsetof(struct fuse_custom_io, clone_fd), fd);
    2147}
    2148#endif
    2149
    2158int fuse_session_mount(struct fuse_session *se, const char *mountpoint);
    2159
    2182int fuse_session_loop(struct fuse_session *se);
    2183
    2184#if FUSE_USE_VERSION < 32
    2185 int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
    2186 #define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd)
    2187#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
    2188 int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
    2189 #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_32(se, config)
    2190#else
    2191 #if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
    2203 int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config);
    2204 #else
    2205 int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
    2206 #define fuse_session_loop_mt(se, config) fuse_session_loop_mt_312(se, config)
    2207 #endif
    2208#endif
    2209
    2222void fuse_session_exit(struct fuse_session *se);
    2223
    2229void fuse_session_reset(struct fuse_session *se);
    2230
    2237int fuse_session_exited(struct fuse_session *se);
    2238
    2263void fuse_session_unmount(struct fuse_session *se);
    2264
    2270void fuse_session_destroy(struct fuse_session *se);
    2271
    2272/* ----------------------------------------------------------- *
    2273 * Custom event loop support *
    2274 * ----------------------------------------------------------- */
    2275
    2290int fuse_session_fd(struct fuse_session *se);
    2291
    2300void fuse_session_process_buf(struct fuse_session *se,
    2301 const struct fuse_buf *buf);
    2302
    2314int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf);
    2315
    2316#ifdef __cplusplus
    2317}
    2318#endif
    2319
    2320#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.17.2/doc/html/include_2fuse__lowlevel_8h_source.html0000644000175000017500000051101115002273247022672 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 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
    60struct fuse_entry_param {
    69
    80 uint64_t generation;
    81
    89 struct stat attr;
    90
    95 double attr_timeout;
    96
    101 double entry_timeout;
    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
    206struct fuse_lowlevel_ops {
    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
    799 void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
    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
    1147 void (*write_buf) (fuse_req_t req, fuse_ino_t ino,
    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
    1279 void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in,
    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
    2003struct fuse_cmdline_opts {
    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)
    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.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse__mount__compat_8h_source.html0000644000175000017500000002250314770250311026543 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/include_2fuse__mount__compat_8h_source.html0000644000175000017500000002231015002273247023704 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.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse__opt_8h_source.html0000644000175000017500000005411414770250311024504 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/include_2fuse__opt_8h_source.html0000644000175000017500000005211715002273247021652 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 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
    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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2buffer_8c_source.html0000644000175000017500000020667714770250311023125 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/lib_2buffer_8c_source.html0000644000175000017500000020601115002273247020250 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 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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2compat_8c_source.html0000644000175000017500000004540014770250311023120 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/lib_2compat_8c_source.html0000644000175000017500000004521215002273247020266 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 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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2cuse__lowlevel_8c_source.html0000644000175000017500000021333714770250311024652 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/lib_2cuse__lowlevel_8c_source.html0000644000175000017500000021331315002273247022011 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 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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2fuse_8c_source.html0000644000175000017500000330240514770250311022603 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    14#include "fuse_config.h"
    15#include "fuse_i.h"
    16#include "fuse_lowlevel.h"
    17#include "fuse_opt.h"
    18#include "fuse_misc.h"
    19#include "fuse_kernel.h"
    20
    21#include <stdio.h>
    22#include <string.h>
    23#include <stdlib.h>
    24#include <stddef.h>
    25#include <stdbool.h>
    26#include <unistd.h>
    27#include <time.h>
    28#include <fcntl.h>
    29#include <limits.h>
    30#include <errno.h>
    31#include <signal.h>
    32#include <dlfcn.h>
    33#include <assert.h>
    34#include <poll.h>
    35#include <sys/param.h>
    36#include <sys/uio.h>
    37#include <sys/time.h>
    38#include <sys/mman.h>
    39#include <sys/file.h>
    40
    41#define FUSE_NODE_SLAB 1
    42
    43#ifndef MAP_ANONYMOUS
    44#undef FUSE_NODE_SLAB
    45#endif
    46
    47#ifndef RENAME_EXCHANGE
    48#define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
    49#endif
    50
    51#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
    52
    53#define FUSE_UNKNOWN_INO 0xffffffff
    54#define OFFSET_MAX 0x7fffffffffffffffLL
    55
    56#define NODE_TABLE_MIN_SIZE 8192
    57
    58struct fuse_fs {
    59 struct fuse_operations op;
    60 void *user_data;
    61 int debug;
    62};
    63
    64struct fusemod_so {
    65 void *handle;
    66 int ctr;
    67};
    68
    69struct lock_queue_element {
    70 struct lock_queue_element *next;
    71 pthread_cond_t cond;
    72 fuse_ino_t nodeid1;
    73 const char *name1;
    74 char **path1;
    75 struct node **wnode1;
    76 fuse_ino_t nodeid2;
    77 const char *name2;
    78 char **path2;
    79 struct node **wnode2;
    80 int err;
    81 bool done : 1;
    82};
    83
    84struct node_table {
    85 struct node **array;
    86 size_t use;
    87 size_t size;
    88 size_t split;
    89};
    90
    91#define container_of(ptr, type, member) ({ \
    92 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    93 (type *)( (char *)__mptr - offsetof(type,member) );})
    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)
    2609 conn->want &= ~FUSE_CAP_SPLICE_READ;
    2610 if (!fs->op.lock)
    2611 conn->want &= ~FUSE_CAP_POSIX_LOCKS;
    2612 if (!fs->op.flock)
    2613 conn->want &= ~FUSE_CAP_FLOCK_LOCKS;
    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);
    2625 fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
    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 pthread_setname_np(pthread_self(), "fuse_prune_nodes");
    4896
    4897 while(1) {
    4898 sleep_time = fuse_clean_cache(f);
    4899 sleep(sleep_time);
    4900 }
    4901 return NULL;
    4902}
    4903
    4904int fuse_start_cleanup_thread(struct fuse *f)
    4905{
    4906 if (lru_enabled(f))
    4907 return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
    4908
    4909 return 0;
    4910}
    4911
    4912void fuse_stop_cleanup_thread(struct fuse *f)
    4913{
    4914 if (lru_enabled(f)) {
    4915 pthread_mutex_lock(&f->lock);
    4916 pthread_cancel(f->prune_thread);
    4917 pthread_mutex_unlock(&f->lock);
    4918 pthread_join(f->prune_thread, NULL);
    4919 }
    4920}
    4921
    4922/*
    4923 * Not supposed to be called directly, but supposed to be called
    4924 * through the fuse_new macro
    4925 */
    4926struct fuse *_fuse_new_31(struct fuse_args *args,
    4927 const struct fuse_operations *op,
    4928 size_t op_size, struct libfuse_version *version,
    4929 void *user_data);
    4930struct fuse *_fuse_new_31(struct fuse_args *args,
    4931 const struct fuse_operations *op,
    4932 size_t op_size, struct libfuse_version *version,
    4933 void *user_data)
    4934{
    4935 struct fuse *f;
    4936 struct node *root;
    4937 struct fuse_fs *fs;
    4938 struct fuse_lowlevel_ops llop = fuse_path_ops;
    4939
    4940 f = (struct fuse *) calloc(1, sizeof(struct fuse));
    4941 if (f == NULL) {
    4942 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
    4943 goto out;
    4944 }
    4945
    4946 f->conf.entry_timeout = 1.0;
    4947 f->conf.attr_timeout = 1.0;
    4948 f->conf.negative_timeout = 0.0;
    4949 f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
    4950
    4951 /* Parse options */
    4952 if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
    4953 fuse_lib_opt_proc) == -1)
    4954 goto out_free;
    4955
    4956 pthread_mutex_lock(&fuse_context_lock);
    4957 static int builtin_modules_registered = 0;
    4958 /* Have the builtin modules already been registered? */
    4959 if (builtin_modules_registered == 0) {
    4960 /* If not, register them. */
    4961 fuse_register_module("subdir", fuse_module_subdir_factory, NULL);
    4962#ifdef HAVE_ICONV
    4963 fuse_register_module("iconv", fuse_module_iconv_factory, NULL);
    4964#endif
    4965 builtin_modules_registered= 1;
    4966 }
    4967 pthread_mutex_unlock(&fuse_context_lock);
    4968
    4969 if (fuse_create_context_key() == -1)
    4970 goto out_free;
    4971
    4972 fs = fuse_fs_new(op, op_size, user_data);
    4973 if (!fs)
    4974 goto out_delete_context_key;
    4975
    4976 f->fs = fs;
    4977
    4978 /* Oh f**k, this is ugly! */
    4979 if (!fs->op.lock) {
    4980 llop.getlk = NULL;
    4981 llop.setlk = NULL;
    4982 }
    4983
    4984 f->pagesize = getpagesize();
    4985 init_list_head(&f->partial_slabs);
    4986 init_list_head(&f->full_slabs);
    4987 init_list_head(&f->lru_table);
    4988
    4989 if (f->conf.modules) {
    4990 char *module;
    4991 char *next;
    4992
    4993 for (module = f->conf.modules; module; module = next) {
    4994 char *p;
    4995 for (p = module; *p && *p != ':'; p++);
    4996 next = *p ? p + 1 : NULL;
    4997 *p = '\0';
    4998 if (module[0] &&
    4999 fuse_push_module(f, module, args) == -1)
    5000 goto out_free_fs;
    5001 }
    5002 }
    5003
    5004 if (!f->conf.ac_attr_timeout_set)
    5005 f->conf.ac_attr_timeout = f->conf.attr_timeout;
    5006
    5007#if defined(__FreeBSD__) || defined(__NetBSD__)
    5008 /*
    5009 * In FreeBSD, we always use these settings as inode numbers
    5010 * are needed to make getcwd(3) work.
    5011 */
    5012 f->conf.readdir_ino = 1;
    5013#endif
    5014
    5015 /* not declared globally, to restrict usage of this function */
    5016 struct fuse_session *fuse_session_new_versioned(
    5017 struct fuse_args *args, const struct fuse_lowlevel_ops *op,
    5018 size_t op_size, struct libfuse_version *version,
    5019 void *userdata);
    5020 f->se = fuse_session_new_versioned(args, &llop, sizeof(llop), version,
    5021 f);
    5022 if (f->se == NULL)
    5023 goto out_free_fs;
    5024
    5025 if (f->conf.debug) {
    5026 fuse_log(FUSE_LOG_DEBUG, "nullpath_ok: %i\n", f->conf.nullpath_ok);
    5027 }
    5028
    5029 /* Trace topmost layer by default */
    5030 f->fs->debug = f->conf.debug;
    5031 f->ctr = 0;
    5032 f->generation = 0;
    5033 if (node_table_init(&f->name_table) == -1)
    5034 goto out_free_session;
    5035
    5036 if (node_table_init(&f->id_table) == -1)
    5037 goto out_free_name_table;
    5038
    5039 pthread_mutex_init(&f->lock, NULL);
    5040
    5041 root = alloc_node(f);
    5042 if (root == NULL) {
    5043 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
    5044 goto out_free_id_table;
    5045 }
    5046 if (lru_enabled(f)) {
    5047 struct node_lru *lnode = node_lru(root);
    5048 init_list_head(&lnode->lru);
    5049 }
    5050
    5051 strcpy(root->inline_name, "/");
    5052 root->name = root->inline_name;
    5053 root->parent = NULL;
    5054 root->nodeid = FUSE_ROOT_ID;
    5055 inc_nlookup(root);
    5056 hash_id(f, root);
    5057
    5058 return f;
    5059
    5060out_free_id_table:
    5061 free(f->id_table.array);
    5062out_free_name_table:
    5063 free(f->name_table.array);
    5064out_free_session:
    5065 fuse_session_destroy(f->se);
    5066out_free_fs:
    5067 free(f->fs);
    5068 free(f->conf.modules);
    5069out_delete_context_key:
    5070 fuse_delete_context_key();
    5071out_free:
    5072 free(f);
    5073out:
    5074 return NULL;
    5075}
    5076
    5077/* Emulates 3.0-style fuse_new(), which processes --help */
    5078struct fuse *_fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
    5079 size_t op_size,
    5080 struct libfuse_version *version,
    5081 void *user_data);
    5082FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0")
    5083struct fuse *_fuse_new_30(struct fuse_args *args,
    5084 const struct fuse_operations *op,
    5085 size_t op_size,
    5086 struct libfuse_version *version,
    5087 void *user_data)
    5088{
    5089 struct fuse_config conf = {0};
    5090
    5091 const struct fuse_opt opts[] = {
    5092 FUSE_LIB_OPT("-h", show_help, 1),
    5093 FUSE_LIB_OPT("--help", show_help, 1),
    5095 };
    5096
    5097 if (fuse_opt_parse(args, &conf, opts,
    5098 fuse_lib_opt_proc) == -1)
    5099 return NULL;
    5100
    5101 if (conf.show_help) {
    5102 fuse_lib_help(args);
    5103 return NULL;
    5104 } else
    5105 return _fuse_new_31(args, op, op_size, version, user_data);
    5106}
    5107
    5108/* ABI compat version */
    5109struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
    5110 size_t op_size, void *user_data);
    5111FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1")
    5112struct fuse *fuse_new_31(struct fuse_args *args,
    5113 const struct fuse_operations *op,
    5114 size_t op_size, void *user_data)
    5115{
    5116 /* unknown version */
    5117 struct libfuse_version version = { 0 };
    5118
    5119 return _fuse_new_31(args, op, op_size, &version, user_data);
    5120}
    5121
    5122/*
    5123 * ABI compat version
    5124 * Emulates 3.0-style fuse_new(), which processes --help
    5125 */
    5126struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
    5127 size_t op_size, void *user_data);
    5128FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0")
    5129struct fuse *fuse_new_30(struct fuse_args *args,
    5130 const struct fuse_operations *op,
    5131 size_t op_size, void *user_data)
    5132{
    5133 struct fuse_config conf = {0};
    5134
    5135 const struct fuse_opt opts[] = {
    5136 FUSE_LIB_OPT("-h", show_help, 1),
    5137 FUSE_LIB_OPT("--help", show_help, 1),
    5139 };
    5140
    5141 if (fuse_opt_parse(args, &conf, opts,
    5142 fuse_lib_opt_proc) == -1)
    5143 return NULL;
    5144
    5145 if (conf.show_help) {
    5146 fuse_lib_help(args);
    5147 return NULL;
    5148 } else
    5149 return fuse_new_31(args, op, op_size, user_data);
    5150}
    5151
    5152
    5153void fuse_destroy(struct fuse *f)
    5154{
    5155 size_t i;
    5156
    5157 if (f->conf.intr && f->intr_installed)
    5158 fuse_restore_intr_signal(f->conf.intr_signal);
    5159
    5160 if (f->fs) {
    5161 fuse_create_context(f);
    5162
    5163 for (i = 0; i < f->id_table.size; i++) {
    5164 struct node *node;
    5165
    5166 for (node = f->id_table.array[i]; node != NULL;
    5167 node = node->id_next) {
    5168 if (node->is_hidden) {
    5169 char *path;
    5170 if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
    5171 fuse_fs_unlink(f->fs, path);
    5172 free(path);
    5173 }
    5174 }
    5175 }
    5176 }
    5177 }
    5178 for (i = 0; i < f->id_table.size; i++) {
    5179 struct node *node;
    5180 struct node *next;
    5181
    5182 for (node = f->id_table.array[i]; node != NULL; node = next) {
    5183 next = node->id_next;
    5184 free_node(f, node);
    5185 f->id_table.use--;
    5186 }
    5187 }
    5188 assert(list_empty(&f->partial_slabs));
    5189 assert(list_empty(&f->full_slabs));
    5190
    5191 while (fuse_modules) {
    5192 fuse_put_module(fuse_modules);
    5193 }
    5194 free(f->id_table.array);
    5195 free(f->name_table.array);
    5196 pthread_mutex_destroy(&f->lock);
    5197 fuse_session_destroy(f->se);
    5198 free(f->fs);
    5199 free(f->conf.modules);
    5200 free(f);
    5201 fuse_delete_context_key();
    5202}
    5203
    5204int fuse_mount(struct fuse *f, const char *mountpoint) {
    5205 return fuse_session_mount(fuse_get_session(f), mountpoint);
    5206}
    5207
    5208
    5209void fuse_unmount(struct fuse *f) {
    5211}
    5212
    5214{
    5215 return FUSE_VERSION;
    5216}
    5217
    5218const char *fuse_pkgversion(void)
    5219{
    5220 return PACKAGE_VERSION;
    5221}
    int fuse_getgroups(int size, gid_t list[])
    Definition fuse.c:4654
    int fuse_mount(struct fuse *f, const char *mountpoint)
    Definition fuse.c:5204
    int fuse_interrupted(void)
    Definition fuse.c:4663
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5153
    int fuse_start_cleanup_thread(struct fuse *fuse)
    Definition fuse.c:4904
    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:1403
    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:5209
    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:4912
    fuse_fill_dir_flags
    Definition fuse.h:58
    fuse_readdir_flags
    Definition fuse.h:42
    size_t fuse_buf_size(const struct fuse_bufvec *bufv)
    Definition buffer.c:22
    @ 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:5218
    int fuse_version(void)
    Definition fuse.c:5213
    @ FUSE_BUF_SPLICE_MOVE
    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
    #define FUSE_CAP_EXPORT_SUPPORT
    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:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t parallel_direct_writes
    uint32_t noflush
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    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.17.2/doc/html/lib_2fuse_8c_source.html0000644000175000017500000330552415002273247017755 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 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 container_of(ptr, type, member) ({ \
    96 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    97 (type *)( (char *)__mptr - offsetof(type,member) );})
    98
    99#define list_entry(ptr, type, member) \
    100 container_of(ptr, type, member)
    101
    102struct list_head {
    103 struct list_head *next;
    104 struct list_head *prev;
    105};
    106
    107struct node_slab {
    108 struct list_head list; /* must be the first member */
    109 struct list_head freelist;
    110 int used;
    111};
    112
    113struct fuse {
    114 struct fuse_session *se;
    115 struct node_table name_table;
    116 struct node_table id_table;
    117 struct list_head lru_table;
    118 fuse_ino_t ctr;
    119 unsigned int generation;
    120 unsigned int hidectr;
    121 pthread_mutex_t lock;
    122 struct fuse_config conf;
    123 int intr_installed;
    124 struct fuse_fs *fs;
    125 struct lock_queue_element *lockq;
    126 int pagesize;
    127 struct list_head partial_slabs;
    128 struct list_head full_slabs;
    129 pthread_t prune_thread;
    130};
    131
    132struct lock {
    133 int type;
    134 off_t start;
    135 off_t end;
    136 pid_t pid;
    137 uint64_t owner;
    138 struct lock *next;
    139};
    140
    141struct node {
    142 struct node *name_next;
    143 struct node *id_next;
    144 fuse_ino_t nodeid;
    145 unsigned int generation;
    146 int refctr;
    147 struct node *parent;
    148 char *name;
    149 uint64_t nlookup;
    150 int open_count;
    151 struct timespec stat_updated;
    152 struct timespec mtime;
    153 off_t size;
    154 struct lock *locks;
    155 unsigned int is_hidden : 1;
    156 unsigned int cache_valid : 1;
    157 int treelock;
    158 char inline_name[32];
    159};
    160
    161#define TREELOCK_WRITE -1
    162#define TREELOCK_WAIT_OFFSET INT_MIN
    163
    164struct node_lru {
    165 struct node node;
    166 struct list_head lru;
    167 struct timespec forget_time;
    168};
    169
    170struct fuse_direntry {
    171 struct stat stat;
    172 enum fuse_fill_dir_flags flags;
    173 char *name;
    174 struct fuse_direntry *next;
    175};
    176
    177struct fuse_dh {
    178 pthread_mutex_t lock;
    179 struct fuse *fuse;
    180 fuse_req_t req;
    181 char *contents;
    182 struct fuse_direntry *first;
    183 struct fuse_direntry **last;
    184 unsigned len;
    185 unsigned size;
    186 unsigned needlen;
    187 int filled;
    188 uint64_t fh;
    189 int error;
    190 fuse_ino_t nodeid;
    191};
    192
    193struct fuse_context_i {
    194 struct fuse_context ctx;
    195 fuse_req_t req;
    196};
    197
    198/* Defined by FUSE_REGISTER_MODULE() in lib/modules/subdir.c and iconv.c. */
    199extern fuse_module_factory_t fuse_module_subdir_factory;
    200#ifdef HAVE_ICONV
    201extern fuse_module_factory_t fuse_module_iconv_factory;
    202#endif
    203
    204static pthread_key_t fuse_context_key;
    205static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
    206static int fuse_context_ref;
    207static struct fuse_module *fuse_modules = NULL;
    208
    209static int fuse_register_module(const char *name,
    210 fuse_module_factory_t factory,
    211 struct fusemod_so *so)
    212{
    213 struct fuse_module *mod;
    214
    215 mod = calloc(1, sizeof(struct fuse_module));
    216 if (!mod) {
    217 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module\n");
    218 return -1;
    219 }
    220 mod->name = strdup(name);
    221 if (!mod->name) {
    222 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module name\n");
    223 free(mod);
    224 return -1;
    225 }
    226 mod->factory = factory;
    227 mod->ctr = 0;
    228 mod->so = so;
    229 if (mod->so)
    230 mod->so->ctr++;
    231 mod->next = fuse_modules;
    232 fuse_modules = mod;
    233
    234 return 0;
    235}
    236
    237static void fuse_unregister_module(struct fuse_module *m)
    238{
    239 struct fuse_module **mp;
    240 for (mp = &fuse_modules; *mp; mp = &(*mp)->next) {
    241 if (*mp == m) {
    242 *mp = (*mp)->next;
    243 break;
    244 }
    245 }
    246 free(m->name);
    247 free(m);
    248}
    249
    250static int fuse_load_so_module(const char *module)
    251{
    252 int ret = -1;
    253 char *tmp;
    254 struct fusemod_so *so;
    255 fuse_module_factory_t *factory;
    256
    257 tmp = malloc(strlen(module) + 64);
    258 if (!tmp) {
    259 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
    260 return -1;
    261 }
    262 sprintf(tmp, "libfusemod_%s.so", module);
    263 so = calloc(1, sizeof(struct fusemod_so));
    264 if (!so) {
    265 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module so\n");
    266 goto out;
    267 }
    268
    269 so->handle = dlopen(tmp, RTLD_NOW);
    270 if (so->handle == NULL) {
    271 fuse_log(FUSE_LOG_ERR, "fuse: dlopen(%s) failed: %s\n",
    272 tmp, dlerror());
    273 goto out_free_so;
    274 }
    275
    276 sprintf(tmp, "fuse_module_%s_factory", module);
    277 factory = (fuse_module_factory_t*)dlsym(so->handle, tmp);
    278 if (factory == NULL) {
    279 fuse_log(FUSE_LOG_ERR, "fuse: symbol <%s> not found in module: %s\n",
    280 tmp, dlerror());
    281 goto out_dlclose;
    282 }
    283 ret = fuse_register_module(module, *factory, so);
    284 if (ret)
    285 goto out_dlclose;
    286
    287out:
    288 free(tmp);
    289 return ret;
    290
    291out_dlclose:
    292 dlclose(so->handle);
    293out_free_so:
    294 free(so);
    295 goto out;
    296}
    297
    298static struct fuse_module *fuse_find_module(const char *module)
    299{
    300 struct fuse_module *m;
    301 for (m = fuse_modules; m; m = m->next) {
    302 if (strcmp(module, m->name) == 0) {
    303 m->ctr++;
    304 break;
    305 }
    306 }
    307 return m;
    308}
    309
    310static struct fuse_module *fuse_get_module(const char *module)
    311{
    312 struct fuse_module *m;
    313
    314 pthread_mutex_lock(&fuse_context_lock);
    315 m = fuse_find_module(module);
    316 if (!m) {
    317 int err = fuse_load_so_module(module);
    318 if (!err)
    319 m = fuse_find_module(module);
    320 }
    321 pthread_mutex_unlock(&fuse_context_lock);
    322 return m;
    323}
    324
    325static void fuse_put_module(struct fuse_module *m)
    326{
    327 pthread_mutex_lock(&fuse_context_lock);
    328 if (m->so)
    329 assert(m->ctr > 0);
    330 /* Builtin modules may already have m->ctr == 0 */
    331 if (m->ctr > 0)
    332 m->ctr--;
    333 if (!m->ctr && m->so) {
    334 struct fusemod_so *so = m->so;
    335 assert(so->ctr > 0);
    336 so->ctr--;
    337 if (!so->ctr) {
    338 struct fuse_module **mp;
    339 for (mp = &fuse_modules; *mp;) {
    340 if ((*mp)->so == so)
    341 fuse_unregister_module(*mp);
    342 else
    343 mp = &(*mp)->next;
    344 }
    345 dlclose(so->handle);
    346 free(so);
    347 }
    348 } else if (!m->ctr) {
    349 fuse_unregister_module(m);
    350 }
    351 pthread_mutex_unlock(&fuse_context_lock);
    352}
    353
    354static void init_list_head(struct list_head *list)
    355{
    356 list->next = list;
    357 list->prev = list;
    358}
    359
    360static int list_empty(const struct list_head *head)
    361{
    362 return head->next == head;
    363}
    364
    365static void list_add(struct list_head *new, struct list_head *prev,
    366 struct list_head *next)
    367{
    368 next->prev = new;
    369 new->next = next;
    370 new->prev = prev;
    371 prev->next = new;
    372}
    373
    374static inline void list_add_head(struct list_head *new, struct list_head *head)
    375{
    376 list_add(new, head, head->next);
    377}
    378
    379static inline void list_add_tail(struct list_head *new, struct list_head *head)
    380{
    381 list_add(new, head->prev, head);
    382}
    383
    384static inline void list_del(struct list_head *entry)
    385{
    386 struct list_head *prev = entry->prev;
    387 struct list_head *next = entry->next;
    388
    389 next->prev = prev;
    390 prev->next = next;
    391}
    392
    393static inline int lru_enabled(struct fuse *f)
    394{
    395 return f->conf.remember > 0;
    396}
    397
    398static struct node_lru *node_lru(struct node *node)
    399{
    400 return (struct node_lru *) node;
    401}
    402
    403static size_t get_node_size(struct fuse *f)
    404{
    405 if (lru_enabled(f))
    406 return sizeof(struct node_lru);
    407 else
    408 return sizeof(struct node);
    409}
    410
    411#ifdef FUSE_NODE_SLAB
    412static struct node_slab *list_to_slab(struct list_head *head)
    413{
    414 return (struct node_slab *) head;
    415}
    416
    417static struct node_slab *node_to_slab(struct fuse *f, struct node *node)
    418{
    419 return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1));
    420}
    421
    422static int alloc_slab(struct fuse *f)
    423{
    424 void *mem;
    425 struct node_slab *slab;
    426 char *start;
    427 size_t num;
    428 size_t i;
    429 size_t node_size = get_node_size(f);
    430
    431 mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE,
    432 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    433
    434 if (mem == MAP_FAILED)
    435 return -1;
    436
    437 slab = mem;
    438 init_list_head(&slab->freelist);
    439 slab->used = 0;
    440 num = (f->pagesize - sizeof(struct node_slab)) / node_size;
    441
    442 start = (char *) mem + f->pagesize - num * node_size;
    443 for (i = 0; i < num; i++) {
    444 struct list_head *n;
    445
    446 n = (struct list_head *) (start + i * node_size);
    447 list_add_tail(n, &slab->freelist);
    448 }
    449 list_add_tail(&slab->list, &f->partial_slabs);
    450
    451 return 0;
    452}
    453
    454static struct node *alloc_node(struct fuse *f)
    455{
    456 struct node_slab *slab;
    457 struct list_head *node;
    458
    459 if (list_empty(&f->partial_slabs)) {
    460 int res = alloc_slab(f);
    461 if (res != 0)
    462 return NULL;
    463 }
    464 slab = list_to_slab(f->partial_slabs.next);
    465 slab->used++;
    466 node = slab->freelist.next;
    467 list_del(node);
    468 if (list_empty(&slab->freelist)) {
    469 list_del(&slab->list);
    470 list_add_tail(&slab->list, &f->full_slabs);
    471 }
    472 memset(node, 0, sizeof(struct node));
    473
    474 return (struct node *) node;
    475}
    476
    477static void free_slab(struct fuse *f, struct node_slab *slab)
    478{
    479 int res;
    480
    481 list_del(&slab->list);
    482 res = munmap(slab, f->pagesize);
    483 if (res == -1)
    484 fuse_log(FUSE_LOG_WARNING, "fuse warning: munmap(%p) failed\n",
    485 slab);
    486}
    487
    488static void free_node_mem(struct fuse *f, struct node *node)
    489{
    490 struct node_slab *slab = node_to_slab(f, node);
    491 struct list_head *n = (struct list_head *) node;
    492
    493 slab->used--;
    494 if (slab->used) {
    495 if (list_empty(&slab->freelist)) {
    496 list_del(&slab->list);
    497 list_add_tail(&slab->list, &f->partial_slabs);
    498 }
    499 list_add_head(n, &slab->freelist);
    500 } else {
    501 free_slab(f, slab);
    502 }
    503}
    504#else
    505static struct node *alloc_node(struct fuse *f)
    506{
    507 return (struct node *) calloc(1, get_node_size(f));
    508}
    509
    510static void free_node_mem(struct fuse *f, struct node *node)
    511{
    512 (void) f;
    513 free(node);
    514}
    515#endif
    516
    517static size_t id_hash(struct fuse *f, fuse_ino_t ino)
    518{
    519 uint64_t hash = ((uint32_t) ino * 2654435761U) % f->id_table.size;
    520 uint64_t oldhash = hash % (f->id_table.size / 2);
    521
    522 if (oldhash >= f->id_table.split)
    523 return oldhash;
    524 else
    525 return hash;
    526}
    527
    528static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
    529{
    530 size_t hash = id_hash(f, nodeid);
    531 struct node *node;
    532
    533 for (node = f->id_table.array[hash]; node != NULL; node = node->id_next)
    534 if (node->nodeid == nodeid)
    535 return node;
    536
    537 return NULL;
    538}
    539
    540static struct node *get_node(struct fuse *f, fuse_ino_t nodeid)
    541{
    542 struct node *node = get_node_nocheck(f, nodeid);
    543 if (!node) {
    544 fuse_log(FUSE_LOG_ERR, "fuse internal error: node %llu not found\n",
    545 (unsigned long long) nodeid);
    546 abort();
    547 }
    548 return node;
    549}
    550
    551static void curr_time(struct timespec *now);
    552static double diff_timespec(const struct timespec *t1,
    553 const struct timespec *t2);
    554
    555static void remove_node_lru(struct node *node)
    556{
    557 struct node_lru *lnode = node_lru(node);
    558 list_del(&lnode->lru);
    559 init_list_head(&lnode->lru);
    560}
    561
    562static void set_forget_time(struct fuse *f, struct node *node)
    563{
    564 struct node_lru *lnode = node_lru(node);
    565
    566 list_del(&lnode->lru);
    567 list_add_tail(&lnode->lru, &f->lru_table);
    568 curr_time(&lnode->forget_time);
    569}
    570
    571static void free_node(struct fuse *f, struct node *node)
    572{
    573 if (node->name != node->inline_name)
    574 free(node->name);
    575 free_node_mem(f, node);
    576}
    577
    578static void node_table_reduce(struct node_table *t)
    579{
    580 size_t newsize = t->size / 2;
    581 void *newarray;
    582
    583 if (newsize < NODE_TABLE_MIN_SIZE)
    584 return;
    585
    586 newarray = realloc(t->array, sizeof(struct node *) * newsize);
    587 if (newarray != NULL)
    588 t->array = newarray;
    589
    590 t->size = newsize;
    591 t->split = t->size / 2;
    592}
    593
    594static void remerge_id(struct fuse *f)
    595{
    596 struct node_table *t = &f->id_table;
    597 int iter;
    598
    599 if (t->split == 0)
    600 node_table_reduce(t);
    601
    602 for (iter = 8; t->split > 0 && iter; iter--) {
    603 struct node **upper;
    604
    605 t->split--;
    606 upper = &t->array[t->split + t->size / 2];
    607 if (*upper) {
    608 struct node **nodep;
    609
    610 for (nodep = &t->array[t->split]; *nodep;
    611 nodep = &(*nodep)->id_next);
    612
    613 *nodep = *upper;
    614 *upper = NULL;
    615 break;
    616 }
    617 }
    618}
    619
    620static void unhash_id(struct fuse *f, struct node *node)
    621{
    622 struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)];
    623
    624 for (; *nodep != NULL; nodep = &(*nodep)->id_next)
    625 if (*nodep == node) {
    626 *nodep = node->id_next;
    627 f->id_table.use--;
    628
    629 if(f->id_table.use < f->id_table.size / 4)
    630 remerge_id(f);
    631 return;
    632 }
    633}
    634
    635static int node_table_resize(struct node_table *t)
    636{
    637 size_t newsize = t->size * 2;
    638 void *newarray;
    639
    640 newarray = realloc(t->array, sizeof(struct node *) * newsize);
    641 if (newarray == NULL)
    642 return -1;
    643
    644 t->array = newarray;
    645 memset(t->array + t->size, 0, t->size * sizeof(struct node *));
    646 t->size = newsize;
    647 t->split = 0;
    648
    649 return 0;
    650}
    651
    652static void rehash_id(struct fuse *f)
    653{
    654 struct node_table *t = &f->id_table;
    655 struct node **nodep;
    656 struct node **next;
    657 size_t hash;
    658
    659 if (t->split == t->size / 2)
    660 return;
    661
    662 hash = t->split;
    663 t->split++;
    664 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
    665 struct node *node = *nodep;
    666 size_t newhash = id_hash(f, node->nodeid);
    667
    668 if (newhash != hash) {
    669 next = nodep;
    670 *nodep = node->id_next;
    671 node->id_next = t->array[newhash];
    672 t->array[newhash] = node;
    673 } else {
    674 next = &node->id_next;
    675 }
    676 }
    677 if (t->split == t->size / 2)
    678 node_table_resize(t);
    679}
    680
    681static void hash_id(struct fuse *f, struct node *node)
    682{
    683 size_t hash = id_hash(f, node->nodeid);
    684 node->id_next = f->id_table.array[hash];
    685 f->id_table.array[hash] = node;
    686 f->id_table.use++;
    687
    688 if (f->id_table.use >= f->id_table.size / 2)
    689 rehash_id(f);
    690}
    691
    692static size_t name_hash(struct fuse *f, fuse_ino_t parent,
    693 const char *name)
    694{
    695 uint64_t hash = parent;
    696 uint64_t oldhash;
    697
    698 for (; *name; name++)
    699 hash = hash * 31 + (unsigned char) *name;
    700
    701 hash %= f->name_table.size;
    702 oldhash = hash % (f->name_table.size / 2);
    703 if (oldhash >= f->name_table.split)
    704 return oldhash;
    705 else
    706 return hash;
    707}
    708
    709static void unref_node(struct fuse *f, struct node *node);
    710
    711static void remerge_name(struct fuse *f)
    712{
    713 struct node_table *t = &f->name_table;
    714 int iter;
    715
    716 if (t->split == 0)
    717 node_table_reduce(t);
    718
    719 for (iter = 8; t->split > 0 && iter; iter--) {
    720 struct node **upper;
    721
    722 t->split--;
    723 upper = &t->array[t->split + t->size / 2];
    724 if (*upper) {
    725 struct node **nodep;
    726
    727 for (nodep = &t->array[t->split]; *nodep;
    728 nodep = &(*nodep)->name_next);
    729
    730 *nodep = *upper;
    731 *upper = NULL;
    732 break;
    733 }
    734 }
    735}
    736
    737static void unhash_name(struct fuse *f, struct node *node)
    738{
    739 if (node->name) {
    740 size_t hash = name_hash(f, node->parent->nodeid, node->name);
    741 struct node **nodep = &f->name_table.array[hash];
    742
    743 for (; *nodep != NULL; nodep = &(*nodep)->name_next)
    744 if (*nodep == node) {
    745 *nodep = node->name_next;
    746 node->name_next = NULL;
    747 unref_node(f, node->parent);
    748 if (node->name != node->inline_name)
    749 free(node->name);
    750 node->name = NULL;
    751 node->parent = NULL;
    752 f->name_table.use--;
    753
    754 if (f->name_table.use < f->name_table.size / 4)
    755 remerge_name(f);
    756 return;
    757 }
    758 fuse_log(FUSE_LOG_ERR,
    759 "fuse internal error: unable to unhash node: %llu\n",
    760 (unsigned long long) node->nodeid);
    761 abort();
    762 }
    763}
    764
    765static void rehash_name(struct fuse *f)
    766{
    767 struct node_table *t = &f->name_table;
    768 struct node **nodep;
    769 struct node **next;
    770 size_t hash;
    771
    772 if (t->split == t->size / 2)
    773 return;
    774
    775 hash = t->split;
    776 t->split++;
    777 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
    778 struct node *node = *nodep;
    779 size_t newhash = name_hash(f, node->parent->nodeid, node->name);
    780
    781 if (newhash != hash) {
    782 next = nodep;
    783 *nodep = node->name_next;
    784 node->name_next = t->array[newhash];
    785 t->array[newhash] = node;
    786 } else {
    787 next = &node->name_next;
    788 }
    789 }
    790 if (t->split == t->size / 2)
    791 node_table_resize(t);
    792}
    793
    794static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
    795 const char *name)
    796{
    797 size_t hash = name_hash(f, parentid, name);
    798 struct node *parent = get_node(f, parentid);
    799 if (strlen(name) < sizeof(node->inline_name)) {
    800 strcpy(node->inline_name, name);
    801 node->name = node->inline_name;
    802 } else {
    803 node->name = strdup(name);
    804 if (node->name == NULL)
    805 return -1;
    806 }
    807
    808 parent->refctr ++;
    809 node->parent = parent;
    810 node->name_next = f->name_table.array[hash];
    811 f->name_table.array[hash] = node;
    812 f->name_table.use++;
    813
    814 if (f->name_table.use >= f->name_table.size / 2)
    815 rehash_name(f);
    816
    817 return 0;
    818}
    819
    820static void delete_node(struct fuse *f, struct node *node)
    821{
    822 if (f->conf.debug)
    823 fuse_log(FUSE_LOG_DEBUG, "DELETE: %llu\n",
    824 (unsigned long long) node->nodeid);
    825
    826 assert(node->treelock == 0);
    827 unhash_name(f, node);
    828 if (lru_enabled(f))
    829 remove_node_lru(node);
    830 unhash_id(f, node);
    831 free_node(f, node);
    832}
    833
    834static void unref_node(struct fuse *f, struct node *node)
    835{
    836 assert(node->refctr > 0);
    837 node->refctr --;
    838 if (!node->refctr)
    839 delete_node(f, node);
    840}
    841
    842static fuse_ino_t next_id(struct fuse *f)
    843{
    844 do {
    845 f->ctr = (f->ctr + 1) & 0xffffffff;
    846 if (!f->ctr)
    847 f->generation ++;
    848 } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO ||
    849 get_node_nocheck(f, f->ctr) != NULL);
    850 return f->ctr;
    851}
    852
    853static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
    854 const char *name)
    855{
    856 size_t hash = name_hash(f, parent, name);
    857 struct node *node;
    858
    859 for (node = f->name_table.array[hash]; node != NULL; node = node->name_next)
    860 if (node->parent->nodeid == parent &&
    861 strcmp(node->name, name) == 0)
    862 return node;
    863
    864 return NULL;
    865}
    866
    867static void inc_nlookup(struct node *node)
    868{
    869 if (!node->nlookup)
    870 node->refctr++;
    871 node->nlookup++;
    872}
    873
    874static struct node *find_node(struct fuse *f, fuse_ino_t parent,
    875 const char *name)
    876{
    877 struct node *node;
    878
    879 pthread_mutex_lock(&f->lock);
    880 if (!name)
    881 node = get_node(f, parent);
    882 else
    883 node = lookup_node(f, parent, name);
    884 if (node == NULL) {
    885 node = alloc_node(f);
    886 if (node == NULL)
    887 goto out_err;
    888
    889 node->nodeid = next_id(f);
    890 node->generation = f->generation;
    891 if (f->conf.remember)
    892 inc_nlookup(node);
    893
    894 if (hash_name(f, node, parent, name) == -1) {
    895 free_node(f, node);
    896 node = NULL;
    897 goto out_err;
    898 }
    899 hash_id(f, node);
    900 if (lru_enabled(f)) {
    901 struct node_lru *lnode = node_lru(node);
    902 init_list_head(&lnode->lru);
    903 }
    904 } else if (lru_enabled(f) && node->nlookup == 1) {
    905 remove_node_lru(node);
    906 }
    907 inc_nlookup(node);
    908out_err:
    909 pthread_mutex_unlock(&f->lock);
    910 return node;
    911}
    912
    913static int lookup_path_in_cache(struct fuse *f,
    914 const char *path, fuse_ino_t *inop)
    915{
    916 char *tmp = strdup(path);
    917 if (!tmp)
    918 return -ENOMEM;
    919
    920 pthread_mutex_lock(&f->lock);
    921 fuse_ino_t ino = FUSE_ROOT_ID;
    922
    923 int err = 0;
    924 char *save_ptr;
    925 char *path_element = strtok_r(tmp, "/", &save_ptr);
    926 while (path_element != NULL) {
    927 struct node *node = lookup_node(f, ino, path_element);
    928 if (node == NULL) {
    929 err = -ENOENT;
    930 break;
    931 }
    932 ino = node->nodeid;
    933 path_element = strtok_r(NULL, "/", &save_ptr);
    934 }
    935 pthread_mutex_unlock(&f->lock);
    936 free(tmp);
    937
    938 if (!err)
    939 *inop = ino;
    940 return err;
    941}
    942
    943static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name)
    944{
    945 size_t len = strlen(name);
    946
    947 if (s - len <= *buf) {
    948 unsigned pathlen = *bufsize - (s - *buf);
    949 unsigned newbufsize = *bufsize;
    950 char *newbuf;
    951
    952 while (newbufsize < pathlen + len + 1) {
    953 if (newbufsize >= 0x80000000)
    954 newbufsize = 0xffffffff;
    955 else
    956 newbufsize *= 2;
    957 }
    958
    959 newbuf = realloc(*buf, newbufsize);
    960 if (newbuf == NULL)
    961 return NULL;
    962
    963 *buf = newbuf;
    964 s = newbuf + newbufsize - pathlen;
    965 memmove(s, newbuf + *bufsize - pathlen, pathlen);
    966 *bufsize = newbufsize;
    967 }
    968 s -= len;
    969 memcpy(s, name, len);
    970 s--;
    971 *s = '/';
    972
    973 return s;
    974}
    975
    976static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode,
    977 struct node *end)
    978{
    979 struct node *node;
    980
    981 if (wnode) {
    982 assert(wnode->treelock == TREELOCK_WRITE);
    983 wnode->treelock = 0;
    984 }
    985
    986 for (node = get_node(f, nodeid);
    987 node != end && node->nodeid != FUSE_ROOT_ID; node = node->parent) {
    988 assert(node->treelock != 0);
    989 assert(node->treelock != TREELOCK_WAIT_OFFSET);
    990 assert(node->treelock != TREELOCK_WRITE);
    991 node->treelock--;
    992 if (node->treelock == TREELOCK_WAIT_OFFSET)
    993 node->treelock = 0;
    994 }
    995}
    996
    997static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
    998 char **path, struct node **wnodep, bool need_lock)
    999{
    1000 unsigned bufsize = 256;
    1001 char *buf;
    1002 char *s;
    1003 struct node *node;
    1004 struct node *wnode = NULL;
    1005 int err;
    1006
    1007 *path = NULL;
    1008
    1009 err = -ENOMEM;
    1010 buf = malloc(bufsize);
    1011 if (buf == NULL)
    1012 goto out_err;
    1013
    1014 s = buf + bufsize - 1;
    1015 *s = '\0';
    1016
    1017 if (name != NULL) {
    1018 s = add_name(&buf, &bufsize, s, name);
    1019 err = -ENOMEM;
    1020 if (s == NULL)
    1021 goto out_free;
    1022 }
    1023
    1024 if (wnodep) {
    1025 assert(need_lock);
    1026 wnode = lookup_node(f, nodeid, name);
    1027 if (wnode) {
    1028 if (wnode->treelock != 0) {
    1029 if (wnode->treelock > 0)
    1030 wnode->treelock += TREELOCK_WAIT_OFFSET;
    1031 err = -EAGAIN;
    1032 goto out_free;
    1033 }
    1034 wnode->treelock = TREELOCK_WRITE;
    1035 }
    1036 }
    1037
    1038 for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
    1039 node = node->parent) {
    1040 err = -ESTALE;
    1041 if (node->name == NULL || node->parent == NULL)
    1042 goto out_unlock;
    1043
    1044 err = -ENOMEM;
    1045 s = add_name(&buf, &bufsize, s, node->name);
    1046 if (s == NULL)
    1047 goto out_unlock;
    1048
    1049 if (need_lock) {
    1050 err = -EAGAIN;
    1051 if (node->treelock < 0)
    1052 goto out_unlock;
    1053
    1054 node->treelock++;
    1055 }
    1056 }
    1057
    1058 if (s[0])
    1059 memmove(buf, s, bufsize - (s - buf));
    1060 else
    1061 strcpy(buf, "/");
    1062
    1063 *path = buf;
    1064 if (wnodep)
    1065 *wnodep = wnode;
    1066
    1067 return 0;
    1068
    1069 out_unlock:
    1070 if (need_lock)
    1071 unlock_path(f, nodeid, wnode, node);
    1072 out_free:
    1073 free(buf);
    1074
    1075 out_err:
    1076 return err;
    1077}
    1078
    1079static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
    1080 fuse_ino_t nodeid2, const char *name2,
    1081 char **path1, char **path2,
    1082 struct node **wnode1, struct node **wnode2)
    1083{
    1084 int err;
    1085
    1086 /* FIXME: locking two paths needs deadlock checking */
    1087 err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
    1088 if (!err) {
    1089 err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
    1090 if (err) {
    1091 struct node *wn1 = wnode1 ? *wnode1 : NULL;
    1092
    1093 unlock_path(f, nodeid1, wn1, NULL);
    1094 free(*path1);
    1095 }
    1096 }
    1097 return err;
    1098}
    1099
    1100static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
    1101{
    1102 int err;
    1103
    1104 if (!qe->path1) {
    1105 /* Just waiting for it to be unlocked */
    1106 if (get_node(f, qe->nodeid1)->treelock == 0)
    1107 pthread_cond_signal(&qe->cond);
    1108
    1109 return;
    1110 }
    1111
    1112 if (qe->done)
    1113 return; // Don't try to double-lock the element
    1114
    1115 if (!qe->path2) {
    1116 err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
    1117 qe->wnode1, true);
    1118 } else {
    1119 err = try_get_path2(f, qe->nodeid1, qe->name1, qe->nodeid2,
    1120 qe->name2, qe->path1, qe->path2, qe->wnode1,
    1121 qe->wnode2);
    1122 }
    1123
    1124 if (err == -EAGAIN)
    1125 return; /* keep trying */
    1126
    1127 qe->err = err;
    1128 qe->done = true;
    1129 pthread_cond_signal(&qe->cond);
    1130}
    1131
    1132static void wake_up_queued(struct fuse *f)
    1133{
    1134 struct lock_queue_element *qe;
    1135
    1136 for (qe = f->lockq; qe != NULL; qe = qe->next)
    1137 queue_element_wakeup(f, qe);
    1138}
    1139
    1140static void debug_path(struct fuse *f, const char *msg, fuse_ino_t nodeid,
    1141 const char *name, bool wr)
    1142{
    1143 if (f->conf.debug) {
    1144 struct node *wnode = NULL;
    1145
    1146 if (wr)
    1147 wnode = lookup_node(f, nodeid, name);
    1148
    1149 if (wnode) {
    1150 fuse_log(FUSE_LOG_DEBUG, "%s %llu (w)\n",
    1151 msg, (unsigned long long) wnode->nodeid);
    1152 } else {
    1153 fuse_log(FUSE_LOG_DEBUG, "%s %llu\n",
    1154 msg, (unsigned long long) nodeid);
    1155 }
    1156 }
    1157}
    1158
    1159static void queue_path(struct fuse *f, struct lock_queue_element *qe)
    1160{
    1161 struct lock_queue_element **qp;
    1162
    1163 qe->done = false;
    1164 pthread_cond_init(&qe->cond, NULL);
    1165 qe->next = NULL;
    1166 for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
    1167 *qp = qe;
    1168}
    1169
    1170static void dequeue_path(struct fuse *f, struct lock_queue_element *qe)
    1171{
    1172 struct lock_queue_element **qp;
    1173
    1174 pthread_cond_destroy(&qe->cond);
    1175 for (qp = &f->lockq; *qp != qe; qp = &(*qp)->next);
    1176 *qp = qe->next;
    1177}
    1178
    1179static int wait_path(struct fuse *f, struct lock_queue_element *qe)
    1180{
    1181 queue_path(f, qe);
    1182
    1183 do {
    1184 pthread_cond_wait(&qe->cond, &f->lock);
    1185 } while (!qe->done);
    1186
    1187 dequeue_path(f, qe);
    1188
    1189 return qe->err;
    1190}
    1191
    1192static int get_path_common(struct fuse *f, fuse_ino_t nodeid, const char *name,
    1193 char **path, struct node **wnode)
    1194{
    1195 int err;
    1196
    1197 pthread_mutex_lock(&f->lock);
    1198 err = try_get_path(f, nodeid, name, path, wnode, true);
    1199 if (err == -EAGAIN) {
    1200 struct lock_queue_element qe = {
    1201 .nodeid1 = nodeid,
    1202 .name1 = name,
    1203 .path1 = path,
    1204 .wnode1 = wnode,
    1205 };
    1206 debug_path(f, "QUEUE PATH", nodeid, name, !!wnode);
    1207 err = wait_path(f, &qe);
    1208 debug_path(f, "DEQUEUE PATH", nodeid, name, !!wnode);
    1209 }
    1210 pthread_mutex_unlock(&f->lock);
    1211
    1212 return err;
    1213}
    1214
    1215static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path)
    1216{
    1217 return get_path_common(f, nodeid, NULL, path, NULL);
    1218}
    1219
    1220static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
    1221{
    1222 int err = 0;
    1223
    1224 if (f->conf.nullpath_ok) {
    1225 *path = NULL;
    1226 } else {
    1227 err = get_path_common(f, nodeid, NULL, path, NULL);
    1228 if (err == -ESTALE)
    1229 err = 0;
    1230 }
    1231
    1232 return err;
    1233}
    1234
    1235static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name,
    1236 char **path)
    1237{
    1238 return get_path_common(f, nodeid, name, path, NULL);
    1239}
    1240
    1241static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name,
    1242 char **path, struct node **wnode)
    1243{
    1244 return get_path_common(f, nodeid, name, path, wnode);
    1245}
    1246
    1247#if defined(__FreeBSD__)
    1248#define CHECK_DIR_LOOP
    1249#endif
    1250
    1251#if defined(CHECK_DIR_LOOP)
    1252static int check_dir_loop(struct fuse *f,
    1253 fuse_ino_t nodeid1, const char *name1,
    1254 fuse_ino_t nodeid2, const char *name2)
    1255{
    1256 struct node *node, *node1, *node2;
    1257 fuse_ino_t id1, id2;
    1258
    1259 node1 = lookup_node(f, nodeid1, name1);
    1260 id1 = node1 ? node1->nodeid : nodeid1;
    1261
    1262 node2 = lookup_node(f, nodeid2, name2);
    1263 id2 = node2 ? node2->nodeid : nodeid2;
    1264
    1265 for (node = get_node(f, id2); node->nodeid != FUSE_ROOT_ID;
    1266 node = node->parent) {
    1267 if (node->name == NULL || node->parent == NULL)
    1268 break;
    1269
    1270 if (node->nodeid != id2 && node->nodeid == id1)
    1271 return -EINVAL;
    1272 }
    1273
    1274 if (node2)
    1275 {
    1276 for (node = get_node(f, id1); node->nodeid != FUSE_ROOT_ID;
    1277 node = node->parent) {
    1278 if (node->name == NULL || node->parent == NULL)
    1279 break;
    1280
    1281 if (node->nodeid != id1 && node->nodeid == id2)
    1282 return -ENOTEMPTY;
    1283 }
    1284 }
    1285
    1286 return 0;
    1287}
    1288#endif
    1289
    1290static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
    1291 fuse_ino_t nodeid2, const char *name2,
    1292 char **path1, char **path2,
    1293 struct node **wnode1, struct node **wnode2)
    1294{
    1295 int err;
    1296
    1297 pthread_mutex_lock(&f->lock);
    1298
    1299#if defined(CHECK_DIR_LOOP)
    1300 if (name1)
    1301 {
    1302 // called during rename; perform dir loop check
    1303 err = check_dir_loop(f, nodeid1, name1, nodeid2, name2);
    1304 if (err)
    1305 goto out_unlock;
    1306 }
    1307#endif
    1308
    1309 err = try_get_path2(f, nodeid1, name1, nodeid2, name2,
    1310 path1, path2, wnode1, wnode2);
    1311 if (err == -EAGAIN) {
    1312 struct lock_queue_element qe = {
    1313 .nodeid1 = nodeid1,
    1314 .name1 = name1,
    1315 .path1 = path1,
    1316 .wnode1 = wnode1,
    1317 .nodeid2 = nodeid2,
    1318 .name2 = name2,
    1319 .path2 = path2,
    1320 .wnode2 = wnode2,
    1321 };
    1322
    1323 debug_path(f, "QUEUE PATH1", nodeid1, name1, !!wnode1);
    1324 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
    1325 err = wait_path(f, &qe);
    1326 debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1);
    1327 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
    1328 }
    1329
    1330#if defined(CHECK_DIR_LOOP)
    1331out_unlock:
    1332#endif
    1333 pthread_mutex_unlock(&f->lock);
    1334
    1335 return err;
    1336}
    1337
    1338static void free_path_wrlock(struct fuse *f, fuse_ino_t nodeid,
    1339 struct node *wnode, char *path)
    1340{
    1341 pthread_mutex_lock(&f->lock);
    1342 unlock_path(f, nodeid, wnode, NULL);
    1343 if (f->lockq)
    1344 wake_up_queued(f);
    1345 pthread_mutex_unlock(&f->lock);
    1346 free(path);
    1347}
    1348
    1349static void free_path(struct fuse *f, fuse_ino_t nodeid, char *path)
    1350{
    1351 if (path)
    1352 free_path_wrlock(f, nodeid, NULL, path);
    1353}
    1354
    1355static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2,
    1356 struct node *wnode1, struct node *wnode2,
    1357 char *path1, char *path2)
    1358{
    1359 pthread_mutex_lock(&f->lock);
    1360 unlock_path(f, nodeid1, wnode1, NULL);
    1361 unlock_path(f, nodeid2, wnode2, NULL);
    1362 wake_up_queued(f);
    1363 pthread_mutex_unlock(&f->lock);
    1364 free(path1);
    1365 free(path2);
    1366}
    1367
    1368static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
    1369{
    1370 struct node *node;
    1371 if (nodeid == FUSE_ROOT_ID)
    1372 return;
    1373 pthread_mutex_lock(&f->lock);
    1374 node = get_node(f, nodeid);
    1375
    1376 /*
    1377 * Node may still be locked due to interrupt idiocy in open,
    1378 * create and opendir
    1379 */
    1380 while (node->nlookup == nlookup && node->treelock) {
    1381 struct lock_queue_element qe = {
    1382 .nodeid1 = nodeid,
    1383 };
    1384
    1385 debug_path(f, "QUEUE PATH (forget)", nodeid, NULL, false);
    1386 queue_path(f, &qe);
    1387
    1388 do {
    1389 pthread_cond_wait(&qe.cond, &f->lock);
    1390 } while (node->nlookup == nlookup && node->treelock);
    1391
    1392 dequeue_path(f, &qe);
    1393 debug_path(f, "DEQUEUE_PATH (forget)", nodeid, NULL, false);
    1394 }
    1395
    1396 assert(node->nlookup >= nlookup);
    1397 node->nlookup -= nlookup;
    1398 if (!node->nlookup) {
    1399 unref_node(f, node);
    1400 } else if (lru_enabled(f) && node->nlookup == 1) {
    1401 set_forget_time(f, node);
    1402 }
    1403 pthread_mutex_unlock(&f->lock);
    1404}
    1405
    1406static void unlink_node(struct fuse *f, struct node *node)
    1407{
    1408 if (f->conf.remember) {
    1409 assert(node->nlookup > 1);
    1410 node->nlookup--;
    1411 }
    1412 unhash_name(f, node);
    1413}
    1414
    1415static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name)
    1416{
    1417 struct node *node;
    1418
    1419 pthread_mutex_lock(&f->lock);
    1420 node = lookup_node(f, dir, name);
    1421 if (node != NULL)
    1422 unlink_node(f, node);
    1423 pthread_mutex_unlock(&f->lock);
    1424}
    1425
    1426static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
    1427 fuse_ino_t newdir, const char *newname, int hide)
    1428{
    1429 struct node *node;
    1430 struct node *newnode;
    1431 int err = 0;
    1432
    1433 pthread_mutex_lock(&f->lock);
    1434 node = lookup_node(f, olddir, oldname);
    1435 newnode = lookup_node(f, newdir, newname);
    1436 if (node == NULL)
    1437 goto out;
    1438
    1439 if (newnode != NULL) {
    1440 if (hide) {
    1441 fuse_log(FUSE_LOG_ERR, "fuse: hidden file got created during hiding\n");
    1442 err = -EBUSY;
    1443 goto out;
    1444 }
    1445 unlink_node(f, newnode);
    1446 }
    1447
    1448 unhash_name(f, node);
    1449 if (hash_name(f, node, newdir, newname) == -1) {
    1450 err = -ENOMEM;
    1451 goto out;
    1452 }
    1453
    1454 if (hide)
    1455 node->is_hidden = 1;
    1456
    1457out:
    1458 pthread_mutex_unlock(&f->lock);
    1459 return err;
    1460}
    1461
    1462static int exchange_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
    1463 fuse_ino_t newdir, const char *newname)
    1464{
    1465 struct node *oldnode;
    1466 struct node *newnode;
    1467 int err;
    1468
    1469 pthread_mutex_lock(&f->lock);
    1470 oldnode = lookup_node(f, olddir, oldname);
    1471 newnode = lookup_node(f, newdir, newname);
    1472
    1473 if (oldnode)
    1474 unhash_name(f, oldnode);
    1475 if (newnode)
    1476 unhash_name(f, newnode);
    1477
    1478 err = -ENOMEM;
    1479 if (oldnode) {
    1480 if (hash_name(f, oldnode, newdir, newname) == -1)
    1481 goto out;
    1482 }
    1483 if (newnode) {
    1484 if (hash_name(f, newnode, olddir, oldname) == -1)
    1485 goto out;
    1486 }
    1487 err = 0;
    1488out:
    1489 pthread_mutex_unlock(&f->lock);
    1490 return err;
    1491}
    1492
    1493static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
    1494{
    1495 if (!f->conf.use_ino)
    1496 stbuf->st_ino = nodeid;
    1497 if (f->conf.set_mode) {
    1498 if (f->conf.dmask && S_ISDIR(stbuf->st_mode))
    1499 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
    1500 (0777 & ~f->conf.dmask);
    1501 else if (f->conf.fmask)
    1502 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
    1503 (0777 & ~f->conf.fmask);
    1504 else
    1505 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
    1506 (0777 & ~f->conf.umask);
    1507 }
    1508 if (f->conf.set_uid)
    1509 stbuf->st_uid = f->conf.uid;
    1510 if (f->conf.set_gid)
    1511 stbuf->st_gid = f->conf.gid;
    1512}
    1513
    1514static struct fuse *req_fuse(fuse_req_t req)
    1515{
    1516 return (struct fuse *) fuse_req_userdata(req);
    1517}
    1518
    1519static void fuse_intr_sighandler(int sig)
    1520{
    1521 (void) sig;
    1522 /* Nothing to do */
    1523}
    1524
    1525struct fuse_intr_data {
    1526 pthread_t id;
    1527 pthread_cond_t cond;
    1528 int finished;
    1529};
    1530
    1531static void fuse_interrupt(fuse_req_t req, void *d_)
    1532{
    1533 struct fuse_intr_data *d = d_;
    1534 struct fuse *f = req_fuse(req);
    1535
    1536 if (d->id == pthread_self())
    1537 return;
    1538
    1539 pthread_mutex_lock(&f->lock);
    1540 while (!d->finished) {
    1541 struct timeval now;
    1542 struct timespec timeout;
    1543
    1544 pthread_kill(d->id, f->conf.intr_signal);
    1545 gettimeofday(&now, NULL);
    1546 timeout.tv_sec = now.tv_sec + 1;
    1547 timeout.tv_nsec = now.tv_usec * 1000;
    1548 pthread_cond_timedwait(&d->cond, &f->lock, &timeout);
    1549 }
    1550 pthread_mutex_unlock(&f->lock);
    1551}
    1552
    1553static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req,
    1554 struct fuse_intr_data *d)
    1555{
    1556 pthread_mutex_lock(&f->lock);
    1557 d->finished = 1;
    1558 pthread_cond_broadcast(&d->cond);
    1559 pthread_mutex_unlock(&f->lock);
    1560 fuse_req_interrupt_func(req, NULL, NULL);
    1561 pthread_cond_destroy(&d->cond);
    1562}
    1563
    1564static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d)
    1565{
    1566 d->id = pthread_self();
    1567 pthread_cond_init(&d->cond, NULL);
    1568 d->finished = 0;
    1569 fuse_req_interrupt_func(req, fuse_interrupt, d);
    1570}
    1571
    1572static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req,
    1573 struct fuse_intr_data *d)
    1574{
    1575 if (f->conf.intr)
    1576 fuse_do_finish_interrupt(f, req, d);
    1577}
    1578
    1579static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req,
    1580 struct fuse_intr_data *d)
    1581{
    1582 if (f->conf.intr)
    1583 fuse_do_prepare_interrupt(req, d);
    1584}
    1585
    1586static const char* file_info_string(struct fuse_file_info *fi,
    1587 char* buf, size_t len)
    1588{
    1589 if(fi == NULL)
    1590 return "NULL";
    1591 snprintf(buf, len, "%llu", (unsigned long long) fi->fh);
    1592 return buf;
    1593}
    1594
    1595int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
    1596 struct fuse_file_info *fi)
    1597{
    1598 fuse_get_context()->private_data = fs->user_data;
    1599 if (fs->op.getattr) {
    1600 if (fs->debug) {
    1601 char buf[10];
    1602 fuse_log(FUSE_LOG_DEBUG, "getattr[%s] %s\n",
    1603 file_info_string(fi, buf, sizeof(buf)),
    1604 path);
    1605 }
    1606 return fs->op.getattr(path, buf, fi);
    1607 } else {
    1608 return -ENOSYS;
    1609 }
    1610}
    1611
    1612int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
    1613 const char *newpath, unsigned int flags)
    1614{
    1615 fuse_get_context()->private_data = fs->user_data;
    1616 if (fs->op.rename) {
    1617 if (fs->debug)
    1618 fuse_log(FUSE_LOG_DEBUG, "rename %s %s 0x%x\n", oldpath, newpath,
    1619 flags);
    1620
    1621 return fs->op.rename(oldpath, newpath, flags);
    1622 } else {
    1623 return -ENOSYS;
    1624 }
    1625}
    1626
    1627int fuse_fs_unlink(struct fuse_fs *fs, const char *path)
    1628{
    1629 fuse_get_context()->private_data = fs->user_data;
    1630 if (fs->op.unlink) {
    1631 if (fs->debug)
    1632 fuse_log(FUSE_LOG_DEBUG, "unlink %s\n", path);
    1633
    1634 return fs->op.unlink(path);
    1635 } else {
    1636 return -ENOSYS;
    1637 }
    1638}
    1639
    1640int fuse_fs_rmdir(struct fuse_fs *fs, const char *path)
    1641{
    1642 fuse_get_context()->private_data = fs->user_data;
    1643 if (fs->op.rmdir) {
    1644 if (fs->debug)
    1645 fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path);
    1646
    1647 return fs->op.rmdir(path);
    1648 } else {
    1649 return -ENOSYS;
    1650 }
    1651}
    1652
    1653int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path)
    1654{
    1655 fuse_get_context()->private_data = fs->user_data;
    1656 if (fs->op.symlink) {
    1657 if (fs->debug)
    1658 fuse_log(FUSE_LOG_DEBUG, "symlink %s %s\n", linkname, path);
    1659
    1660 return fs->op.symlink(linkname, path);
    1661 } else {
    1662 return -ENOSYS;
    1663 }
    1664}
    1665
    1666int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath)
    1667{
    1668 fuse_get_context()->private_data = fs->user_data;
    1669 if (fs->op.link) {
    1670 if (fs->debug)
    1671 fuse_log(FUSE_LOG_DEBUG, "link %s %s\n", oldpath, newpath);
    1672
    1673 return fs->op.link(oldpath, newpath);
    1674 } else {
    1675 return -ENOSYS;
    1676 }
    1677}
    1678
    1679int fuse_fs_release(struct fuse_fs *fs, const char *path,
    1680 struct fuse_file_info *fi)
    1681{
    1682 fuse_get_context()->private_data = fs->user_data;
    1683 if (fs->op.release) {
    1684 if (fs->debug)
    1685 fuse_log(FUSE_LOG_DEBUG, "release%s[%llu] flags: 0x%x\n",
    1686 fi->flush ? "+flush" : "",
    1687 (unsigned long long) fi->fh, fi->flags);
    1688
    1689 return fs->op.release(path, fi);
    1690 } else {
    1691 return 0;
    1692 }
    1693}
    1694
    1695int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
    1696 struct fuse_file_info *fi)
    1697{
    1698 fuse_get_context()->private_data = fs->user_data;
    1699 if (fs->op.opendir) {
    1700 int err;
    1701
    1702 if (fs->debug)
    1703 fuse_log(FUSE_LOG_DEBUG, "opendir flags: 0x%x %s\n", fi->flags,
    1704 path);
    1705
    1706 err = fs->op.opendir(path, fi);
    1707
    1708 if (fs->debug && !err)
    1709 fuse_log(FUSE_LOG_DEBUG, " opendir[%llu] flags: 0x%x %s\n",
    1710 (unsigned long long) fi->fh, fi->flags, path);
    1711
    1712 return err;
    1713 } else {
    1714 return 0;
    1715 }
    1716}
    1717
    1718int fuse_fs_open(struct fuse_fs *fs, const char *path,
    1719 struct fuse_file_info *fi)
    1720{
    1721 fuse_get_context()->private_data = fs->user_data;
    1722 if (fs->op.open) {
    1723 int err;
    1724
    1725 if (fs->debug)
    1726 fuse_log(FUSE_LOG_DEBUG, "open flags: 0x%x %s\n", fi->flags,
    1727 path);
    1728
    1729 err = fs->op.open(path, fi);
    1730
    1731 if (fs->debug && !err)
    1732 fuse_log(FUSE_LOG_DEBUG, " open[%llu] flags: 0x%x %s\n",
    1733 (unsigned long long) fi->fh, fi->flags, path);
    1734
    1735 return err;
    1736 } else {
    1737 return 0;
    1738 }
    1739}
    1740
    1741static void fuse_free_buf(struct fuse_bufvec *buf)
    1742{
    1743 if (buf != NULL) {
    1744 size_t i;
    1745
    1746 for (i = 0; i < buf->count; i++)
    1747 if (!(buf->buf[i].flags & FUSE_BUF_IS_FD))
    1748 free(buf->buf[i].mem);
    1749 free(buf);
    1750 }
    1751}
    1752
    1753int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
    1754 struct fuse_bufvec **bufp, size_t size, off_t off,
    1755 struct fuse_file_info *fi)
    1756{
    1757 fuse_get_context()->private_data = fs->user_data;
    1758 if (fs->op.read || fs->op.read_buf) {
    1759 int res;
    1760
    1761 if (fs->debug)
    1762 fuse_log(FUSE_LOG_DEBUG,
    1763 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
    1764 (unsigned long long) fi->fh,
    1765 size, (unsigned long long) off, fi->flags);
    1766
    1767 if (fs->op.read_buf) {
    1768 res = fs->op.read_buf(path, bufp, size, off, fi);
    1769 } else {
    1770 struct fuse_bufvec *buf;
    1771 void *mem;
    1772
    1773 buf = malloc(sizeof(struct fuse_bufvec));
    1774 if (buf == NULL)
    1775 return -ENOMEM;
    1776
    1777 mem = malloc(size);
    1778 if (mem == NULL) {
    1779 free(buf);
    1780 return -ENOMEM;
    1781 }
    1782 *buf = FUSE_BUFVEC_INIT(size);
    1783 buf->buf[0].mem = mem;
    1784 *bufp = buf;
    1785
    1786 res = fs->op.read(path, mem, size, off, fi);
    1787 if (res >= 0)
    1788 buf->buf[0].size = res;
    1789 }
    1790
    1791 if (fs->debug && res >= 0)
    1792 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %zu bytes from %llu\n",
    1793 (unsigned long long) fi->fh,
    1794 fuse_buf_size(*bufp),
    1795 (unsigned long long) off);
    1796 if (res >= 0 && fuse_buf_size(*bufp) > size)
    1797 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
    1798
    1799 if (res < 0)
    1800 return res;
    1801
    1802 return 0;
    1803 } else {
    1804 return -ENOSYS;
    1805 }
    1806}
    1807
    1808int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size,
    1809 off_t off, struct fuse_file_info *fi)
    1810{
    1811 fuse_get_context()->private_data = fs->user_data;
    1812 if (fs->op.read || fs->op.read_buf) {
    1813 int res;
    1814
    1815 if (fs->debug)
    1816 fuse_log(FUSE_LOG_DEBUG,
    1817 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
    1818 (unsigned long long) fi->fh,
    1819 size, (unsigned long long) off, fi->flags);
    1820
    1821 if (fs->op.read_buf) {
    1822 struct fuse_bufvec *buf = NULL;
    1823
    1824 res = fs->op.read_buf(path, &buf, size, off, fi);
    1825 if (res == 0) {
    1826 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size);
    1827
    1828 dst.buf[0].mem = mem;
    1829 res = fuse_buf_copy(&dst, buf, 0);
    1830 }
    1831 fuse_free_buf(buf);
    1832 } else {
    1833 res = fs->op.read(path, mem, size, off, fi);
    1834 }
    1835
    1836 if (fs->debug && res >= 0)
    1837 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %u bytes from %llu\n",
    1838 (unsigned long long) fi->fh,
    1839 res,
    1840 (unsigned long long) off);
    1841 if (res >= 0 && res > (int) size)
    1842 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
    1843
    1844 return res;
    1845 } else {
    1846 return -ENOSYS;
    1847 }
    1848}
    1849
    1850int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
    1851 struct fuse_bufvec *buf, off_t off,
    1852 struct fuse_file_info *fi)
    1853{
    1854 fuse_get_context()->private_data = fs->user_data;
    1855 if (fs->op.write_buf || fs->op.write) {
    1856 int res;
    1857 size_t size = fuse_buf_size(buf);
    1858
    1859 assert(buf->idx == 0 && buf->off == 0);
    1860 if (fs->debug)
    1861 fuse_log(FUSE_LOG_DEBUG,
    1862 "write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
    1863 fi->writepage ? "page" : "",
    1864 (unsigned long long) fi->fh,
    1865 size,
    1866 (unsigned long long) off,
    1867 fi->flags);
    1868
    1869 if (fs->op.write_buf) {
    1870 res = fs->op.write_buf(path, buf, off, fi);
    1871 } else {
    1872 void *mem = NULL;
    1873 struct fuse_buf *flatbuf;
    1874 struct fuse_bufvec tmp = FUSE_BUFVEC_INIT(size);
    1875
    1876 if (buf->count == 1 &&
    1877 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
    1878 flatbuf = &buf->buf[0];
    1879 } else {
    1880 res = -ENOMEM;
    1881 mem = malloc(size);
    1882 if (mem == NULL)
    1883 goto out;
    1884
    1885 tmp.buf[0].mem = mem;
    1886 res = fuse_buf_copy(&tmp, buf, 0);
    1887 if (res <= 0)
    1888 goto out_free;
    1889
    1890 tmp.buf[0].size = res;
    1891 flatbuf = &tmp.buf[0];
    1892 }
    1893
    1894 res = fs->op.write(path, flatbuf->mem, flatbuf->size,
    1895 off, fi);
    1896out_free:
    1897 free(mem);
    1898 }
    1899out:
    1900 if (fs->debug && res >= 0)
    1901 fuse_log(FUSE_LOG_DEBUG, " write%s[%llu] %u bytes to %llu\n",
    1902 fi->writepage ? "page" : "",
    1903 (unsigned long long) fi->fh, res,
    1904 (unsigned long long) off);
    1905 if (res > (int) size)
    1906 fuse_log(FUSE_LOG_ERR, "fuse: wrote too many bytes\n");
    1907
    1908 return res;
    1909 } else {
    1910 return -ENOSYS;
    1911 }
    1912}
    1913
    1914int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *mem,
    1915 size_t size, off_t off, struct fuse_file_info *fi)
    1916{
    1917 struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(size);
    1918
    1919 bufv.buf[0].mem = (void *) mem;
    1920
    1921 return fuse_fs_write_buf(fs, path, &bufv, off, fi);
    1922}
    1923
    1924int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
    1925 struct fuse_file_info *fi)
    1926{
    1927 fuse_get_context()->private_data = fs->user_data;
    1928 if (fs->op.fsync) {
    1929 if (fs->debug)
    1930 fuse_log(FUSE_LOG_DEBUG, "fsync[%llu] datasync: %i\n",
    1931 (unsigned long long) fi->fh, datasync);
    1932
    1933 return fs->op.fsync(path, datasync, fi);
    1934 } else {
    1935 return -ENOSYS;
    1936 }
    1937}
    1938
    1939int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
    1940 struct fuse_file_info *fi)
    1941{
    1942 fuse_get_context()->private_data = fs->user_data;
    1943 if (fs->op.fsyncdir) {
    1944 if (fs->debug)
    1945 fuse_log(FUSE_LOG_DEBUG, "fsyncdir[%llu] datasync: %i\n",
    1946 (unsigned long long) fi->fh, datasync);
    1947
    1948 return fs->op.fsyncdir(path, datasync, fi);
    1949 } else {
    1950 return -ENOSYS;
    1951 }
    1952}
    1953
    1954int fuse_fs_flush(struct fuse_fs *fs, const char *path,
    1955 struct fuse_file_info *fi)
    1956{
    1957 fuse_get_context()->private_data = fs->user_data;
    1958 if (fs->op.flush) {
    1959 if (fs->debug)
    1960 fuse_log(FUSE_LOG_DEBUG, "flush[%llu]\n",
    1961 (unsigned long long) fi->fh);
    1962
    1963 return fs->op.flush(path, fi);
    1964 } else {
    1965 return -ENOSYS;
    1966 }
    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 if (fs->debug)
    1990 fuse_log(FUSE_LOG_DEBUG, "releasedir[%llu] flags: 0x%x\n",
    1991 (unsigned long long) fi->fh, fi->flags);
    1992
    1993 return fs->op.releasedir(path, fi);
    1994 } else {
    1995 return 0;
    1996 }
    1997}
    1998
    1999int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
    2000 fuse_fill_dir_t filler, off_t off,
    2001 struct fuse_file_info *fi,
    2002 enum fuse_readdir_flags flags)
    2003{
    2004 fuse_get_context()->private_data = fs->user_data;
    2005 if (fs->op.readdir) {
    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 } else {
    2015 return -ENOSYS;
    2016 }
    2017}
    2018
    2019int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
    2020 struct fuse_file_info *fi)
    2021{
    2022 fuse_get_context()->private_data = fs->user_data;
    2023 if (fs->op.create) {
    2024 int err;
    2025
    2026 if (fs->debug)
    2027 fuse_log(FUSE_LOG_DEBUG,
    2028 "create flags: 0x%x %s 0%o umask=0%03o\n",
    2029 fi->flags, path, mode,
    2030 fuse_get_context()->umask);
    2031
    2032 err = fs->op.create(path, mode, fi);
    2033
    2034 if (fs->debug && !err)
    2035 fuse_log(FUSE_LOG_DEBUG, " create[%llu] flags: 0x%x %s\n",
    2036 (unsigned long long) fi->fh, fi->flags, path);
    2037
    2038 return err;
    2039 } else {
    2040 return -ENOSYS;
    2041 }
    2042}
    2043
    2044int fuse_fs_lock(struct fuse_fs *fs, const char *path,
    2045 struct fuse_file_info *fi, int cmd, struct flock *lock)
    2046{
    2047 fuse_get_context()->private_data = fs->user_data;
    2048 if (fs->op.lock) {
    2049 if (fs->debug)
    2050 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
    2051 (unsigned long long) fi->fh,
    2052 (cmd == F_GETLK ? "F_GETLK" :
    2053 (cmd == F_SETLK ? "F_SETLK" :
    2054 (cmd == F_SETLKW ? "F_SETLKW" : "???"))),
    2055 (lock->l_type == F_RDLCK ? "F_RDLCK" :
    2056 (lock->l_type == F_WRLCK ? "F_WRLCK" :
    2057 (lock->l_type == F_UNLCK ? "F_UNLCK" :
    2058 "???"))),
    2059 (unsigned long long) lock->l_start,
    2060 (unsigned long long) lock->l_len,
    2061 (unsigned long long) lock->l_pid);
    2062
    2063 return fs->op.lock(path, fi, cmd, lock);
    2064 } else {
    2065 return -ENOSYS;
    2066 }
    2067}
    2068
    2069int fuse_fs_flock(struct fuse_fs *fs, const char *path,
    2070 struct fuse_file_info *fi, int op)
    2071{
    2072 fuse_get_context()->private_data = fs->user_data;
    2073 if (fs->op.flock) {
    2074 if (fs->debug) {
    2075 int xop = op & ~LOCK_NB;
    2076
    2077 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s%s\n",
    2078 (unsigned long long) fi->fh,
    2079 xop == LOCK_SH ? "LOCK_SH" :
    2080 (xop == LOCK_EX ? "LOCK_EX" :
    2081 (xop == LOCK_UN ? "LOCK_UN" : "???")),
    2082 (op & LOCK_NB) ? "|LOCK_NB" : "");
    2083 }
    2084 return fs->op.flock(path, fi, op);
    2085 } else {
    2086 return -ENOSYS;
    2087 }
    2088}
    2089
    2090int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid,
    2091 gid_t gid, struct fuse_file_info *fi)
    2092{
    2093 fuse_get_context()->private_data = fs->user_data;
    2094 if (fs->op.chown) {
    2095 if (fs->debug) {
    2096 char buf[10];
    2097 fuse_log(FUSE_LOG_DEBUG, "chown[%s] %s %lu %lu\n",
    2098 file_info_string(fi, buf, sizeof(buf)),
    2099 path, (unsigned long) uid, (unsigned long) gid);
    2100 }
    2101 return fs->op.chown(path, uid, gid, fi);
    2102 } else {
    2103 return -ENOSYS;
    2104 }
    2105}
    2106
    2107int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
    2108 struct fuse_file_info *fi)
    2109{
    2110 fuse_get_context()->private_data = fs->user_data;
    2111 if (fs->op.truncate) {
    2112 if (fs->debug) {
    2113 char buf[10];
    2114 fuse_log(FUSE_LOG_DEBUG, "truncate[%s] %llu\n",
    2115 file_info_string(fi, buf, sizeof(buf)),
    2116 (unsigned long long) size);
    2117 }
    2118 return fs->op.truncate(path, size, fi);
    2119 } else {
    2120 return -ENOSYS;
    2121 }
    2122}
    2123
    2124int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
    2125 const struct timespec tv[2], struct fuse_file_info *fi)
    2126{
    2127 fuse_get_context()->private_data = fs->user_data;
    2128 if (fs->op.utimens) {
    2129 if (fs->debug) {
    2130 char buf[10];
    2131 fuse_log(FUSE_LOG_DEBUG, "utimens[%s] %s %li.%09lu %li.%09lu\n",
    2132 file_info_string(fi, buf, sizeof(buf)),
    2133 path, tv[0].tv_sec, tv[0].tv_nsec,
    2134 tv[1].tv_sec, tv[1].tv_nsec);
    2135 }
    2136 return fs->op.utimens(path, tv, fi);
    2137 } else {
    2138 return -ENOSYS;
    2139 }
    2140}
    2141
    2142int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask)
    2143{
    2144 fuse_get_context()->private_data = fs->user_data;
    2145 if (fs->op.access) {
    2146 if (fs->debug)
    2147 fuse_log(FUSE_LOG_DEBUG, "access %s 0%o\n", path, mask);
    2148
    2149 return fs->op.access(path, mask);
    2150 } else {
    2151 return -ENOSYS;
    2152 }
    2153}
    2154
    2155int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
    2156 size_t len)
    2157{
    2158 fuse_get_context()->private_data = fs->user_data;
    2159 if (fs->op.readlink) {
    2160 if (fs->debug)
    2161 fuse_log(FUSE_LOG_DEBUG, "readlink %s %lu\n", path,
    2162 (unsigned long) len);
    2163
    2164 return fs->op.readlink(path, buf, len);
    2165 } else {
    2166 return -ENOSYS;
    2167 }
    2168}
    2169
    2170int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
    2171 dev_t rdev)
    2172{
    2173 fuse_get_context()->private_data = fs->user_data;
    2174 if (fs->op.mknod) {
    2175 if (fs->debug)
    2176 fuse_log(FUSE_LOG_DEBUG, "mknod %s 0%o 0x%llx umask=0%03o\n",
    2177 path, mode, (unsigned long long) rdev,
    2178 fuse_get_context()->umask);
    2179
    2180 return fs->op.mknod(path, mode, rdev);
    2181 } else {
    2182 return -ENOSYS;
    2183 }
    2184}
    2185
    2186int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode)
    2187{
    2188 fuse_get_context()->private_data = fs->user_data;
    2189 if (fs->op.mkdir) {
    2190 if (fs->debug)
    2191 fuse_log(FUSE_LOG_DEBUG, "mkdir %s 0%o umask=0%03o\n",
    2192 path, mode, fuse_get_context()->umask);
    2193
    2194 return fs->op.mkdir(path, mode);
    2195 } else {
    2196 return -ENOSYS;
    2197 }
    2198}
    2199
    2200int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
    2201 const char *value, size_t size, int flags)
    2202{
    2203 fuse_get_context()->private_data = fs->user_data;
    2204 if (fs->op.setxattr) {
    2205 if (fs->debug)
    2206 fuse_log(FUSE_LOG_DEBUG, "setxattr %s %s %lu 0x%x\n",
    2207 path, name, (unsigned long) size, flags);
    2208
    2209 return fs->op.setxattr(path, name, value, size, flags);
    2210 } else {
    2211 return -ENOSYS;
    2212 }
    2213}
    2214
    2215int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
    2216 char *value, size_t size)
    2217{
    2218 fuse_get_context()->private_data = fs->user_data;
    2219 if (fs->op.getxattr) {
    2220 if (fs->debug)
    2221 fuse_log(FUSE_LOG_DEBUG, "getxattr %s %s %lu\n",
    2222 path, name, (unsigned long) size);
    2223
    2224 return fs->op.getxattr(path, name, value, size);
    2225 } else {
    2226 return -ENOSYS;
    2227 }
    2228}
    2229
    2230int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
    2231 size_t size)
    2232{
    2233 fuse_get_context()->private_data = fs->user_data;
    2234 if (fs->op.listxattr) {
    2235 if (fs->debug)
    2236 fuse_log(FUSE_LOG_DEBUG, "listxattr %s %lu\n",
    2237 path, (unsigned long) size);
    2238
    2239 return fs->op.listxattr(path, list, size);
    2240 } else {
    2241 return -ENOSYS;
    2242 }
    2243}
    2244
    2245int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
    2246 uint64_t *idx)
    2247{
    2248 fuse_get_context()->private_data = fs->user_data;
    2249 if (fs->op.bmap) {
    2250 if (fs->debug)
    2251 fuse_log(FUSE_LOG_DEBUG, "bmap %s blocksize: %lu index: %llu\n",
    2252 path, (unsigned long) blocksize,
    2253 (unsigned long long) *idx);
    2254
    2255 return fs->op.bmap(path, blocksize, idx);
    2256 } else {
    2257 return -ENOSYS;
    2258 }
    2259}
    2260
    2261int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
    2262{
    2263 fuse_get_context()->private_data = fs->user_data;
    2264 if (fs->op.removexattr) {
    2265 if (fs->debug)
    2266 fuse_log(FUSE_LOG_DEBUG, "removexattr %s %s\n", path, name);
    2267
    2268 return fs->op.removexattr(path, name);
    2269 } else {
    2270 return -ENOSYS;
    2271 }
    2272}
    2273
    2274int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
    2275 void *arg, struct fuse_file_info *fi, unsigned int flags,
    2276 void *data)
    2277{
    2278 fuse_get_context()->private_data = fs->user_data;
    2279 if (fs->op.ioctl) {
    2280 if (fs->debug)
    2281 fuse_log(FUSE_LOG_DEBUG, "ioctl[%llu] 0x%x flags: 0x%x\n",
    2282 (unsigned long long) fi->fh, cmd, flags);
    2283
    2284 return fs->op.ioctl(path, cmd, arg, fi, flags, data);
    2285 } else
    2286 return -ENOSYS;
    2287}
    2288
    2289int fuse_fs_poll(struct fuse_fs *fs, const char *path,
    2290 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
    2291 unsigned *reventsp)
    2292{
    2293 fuse_get_context()->private_data = fs->user_data;
    2294 if (fs->op.poll) {
    2295 int res;
    2296
    2297 if (fs->debug)
    2298 fuse_log(FUSE_LOG_DEBUG, "poll[%llu] ph: %p, events 0x%x\n",
    2299 (unsigned long long) fi->fh, ph,
    2300 fi->poll_events);
    2301
    2302 res = fs->op.poll(path, fi, ph, reventsp);
    2303
    2304 if (fs->debug && !res)
    2305 fuse_log(FUSE_LOG_DEBUG, " poll[%llu] revents: 0x%x\n",
    2306 (unsigned long long) fi->fh, *reventsp);
    2307
    2308 return res;
    2309 } else
    2310 return -ENOSYS;
    2311}
    2312
    2313int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
    2314 off_t offset, off_t length, struct fuse_file_info *fi)
    2315{
    2316 fuse_get_context()->private_data = fs->user_data;
    2317 if (fs->op.fallocate) {
    2318 if (fs->debug)
    2319 fuse_log(FUSE_LOG_DEBUG, "fallocate %s mode %x, offset: %llu, length: %llu\n",
    2320 path,
    2321 mode,
    2322 (unsigned long long) offset,
    2323 (unsigned long long) length);
    2324
    2325 return fs->op.fallocate(path, mode, offset, length, fi);
    2326 } else
    2327 return -ENOSYS;
    2328}
    2329
    2330ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
    2331 struct fuse_file_info *fi_in, off_t off_in,
    2332 const char *path_out,
    2333 struct fuse_file_info *fi_out, off_t off_out,
    2334 size_t len, int flags)
    2335{
    2336 fuse_get_context()->private_data = fs->user_data;
    2337 if (fs->op.copy_file_range) {
    2338 if (fs->debug)
    2339 fuse_log(FUSE_LOG_DEBUG, "copy_file_range from %s:%llu to "
    2340 "%s:%llu, length: %llu\n",
    2341 path_in,
    2342 (unsigned long long) off_in,
    2343 path_out,
    2344 (unsigned long long) off_out,
    2345 (unsigned long long) len);
    2346
    2347 return fs->op.copy_file_range(path_in, fi_in, off_in, path_out,
    2348 fi_out, off_out, len, flags);
    2349 } else
    2350 return -ENOSYS;
    2351}
    2352
    2353off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
    2354 struct fuse_file_info *fi)
    2355{
    2356 fuse_get_context()->private_data = fs->user_data;
    2357 if (fs->op.lseek) {
    2358 if (fs->debug) {
    2359 char buf[10];
    2360 fuse_log(FUSE_LOG_DEBUG, "lseek[%s] %llu %d\n",
    2361 file_info_string(fi, buf, sizeof(buf)),
    2362 (unsigned long long) off, whence);
    2363 }
    2364 return fs->op.lseek(path, off, whence, fi);
    2365 } else {
    2366 return -ENOSYS;
    2367 }
    2368}
    2369
    2370static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
    2371{
    2372 struct node *node;
    2373 int isopen = 0;
    2374 pthread_mutex_lock(&f->lock);
    2375 node = lookup_node(f, dir, name);
    2376 if (node && node->open_count > 0)
    2377 isopen = 1;
    2378 pthread_mutex_unlock(&f->lock);
    2379 return isopen;
    2380}
    2381
    2382static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname,
    2383 char *newname, size_t bufsize)
    2384{
    2385 struct stat buf;
    2386 struct node *node;
    2387 struct node *newnode;
    2388 char *newpath;
    2389 int res;
    2390 int failctr = 10;
    2391
    2392 do {
    2393 pthread_mutex_lock(&f->lock);
    2394 node = lookup_node(f, dir, oldname);
    2395 if (node == NULL) {
    2396 pthread_mutex_unlock(&f->lock);
    2397 return NULL;
    2398 }
    2399 do {
    2400 f->hidectr ++;
    2401 snprintf(newname, bufsize, ".fuse_hidden%08x%08x",
    2402 (unsigned int) node->nodeid, f->hidectr);
    2403 newnode = lookup_node(f, dir, newname);
    2404 } while(newnode);
    2405
    2406 res = try_get_path(f, dir, newname, &newpath, NULL, false);
    2407 pthread_mutex_unlock(&f->lock);
    2408 if (res)
    2409 break;
    2410
    2411 memset(&buf, 0, sizeof(buf));
    2412 res = fuse_fs_getattr(f->fs, newpath, &buf, NULL);
    2413 if (res == -ENOENT)
    2414 break;
    2415 free(newpath);
    2416 newpath = NULL;
    2417 } while(res == 0 && --failctr);
    2418
    2419 return newpath;
    2420}
    2421
    2422static int hide_node(struct fuse *f, const char *oldpath,
    2423 fuse_ino_t dir, const char *oldname)
    2424{
    2425 char newname[64];
    2426 char *newpath;
    2427 int err = -EBUSY;
    2428
    2429 newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
    2430 if (newpath) {
    2431 err = fuse_fs_rename(f->fs, oldpath, newpath, 0);
    2432 if (!err)
    2433 err = rename_node(f, dir, oldname, dir, newname, 1);
    2434 free(newpath);
    2435 }
    2436 return err;
    2437}
    2438
    2439static int mtime_eq(const struct stat *stbuf, const struct timespec *ts)
    2440{
    2441 return stbuf->st_mtime == ts->tv_sec &&
    2442 ST_MTIM_NSEC(stbuf) == ts->tv_nsec;
    2443}
    2444
    2445#ifndef CLOCK_MONOTONIC
    2446#define CLOCK_MONOTONIC CLOCK_REALTIME
    2447#endif
    2448
    2449static void curr_time(struct timespec *now)
    2450{
    2451 static clockid_t clockid = CLOCK_MONOTONIC;
    2452 int res = clock_gettime(clockid, now);
    2453 if (res == -1 && errno == EINVAL) {
    2454 clockid = CLOCK_REALTIME;
    2455 res = clock_gettime(clockid, now);
    2456 }
    2457 if (res == -1) {
    2458 perror("fuse: clock_gettime");
    2459 abort();
    2460 }
    2461}
    2462
    2463static void update_stat(struct node *node, const struct stat *stbuf)
    2464{
    2465 if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) ||
    2466 stbuf->st_size != node->size))
    2467 node->cache_valid = 0;
    2468 node->mtime.tv_sec = stbuf->st_mtime;
    2469 node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf);
    2470 node->size = stbuf->st_size;
    2471 curr_time(&node->stat_updated);
    2472}
    2473
    2474static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name,
    2475 struct fuse_entry_param *e)
    2476{
    2477 struct node *node;
    2478
    2479 node = find_node(f, nodeid, name);
    2480 if (node == NULL)
    2481 return -ENOMEM;
    2482
    2483 e->ino = node->nodeid;
    2484 e->generation = node->generation;
    2485 e->entry_timeout = f->conf.entry_timeout;
    2486 e->attr_timeout = f->conf.attr_timeout;
    2487 if (f->conf.auto_cache) {
    2488 pthread_mutex_lock(&f->lock);
    2489 update_stat(node, &e->attr);
    2490 pthread_mutex_unlock(&f->lock);
    2491 }
    2492 set_stat(f, e->ino, &e->attr);
    2493 return 0;
    2494}
    2495
    2496static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
    2497 const char *name, const char *path,
    2498 struct fuse_entry_param *e, struct fuse_file_info *fi)
    2499{
    2500 int res;
    2501
    2502 memset(e, 0, sizeof(struct fuse_entry_param));
    2503 res = fuse_fs_getattr(f->fs, path, &e->attr, fi);
    2504 if (res == 0) {
    2505 res = do_lookup(f, nodeid, name, e);
    2506 if (res == 0 && f->conf.debug) {
    2507 fuse_log(FUSE_LOG_DEBUG, " NODEID: %llu\n",
    2508 (unsigned long long) e->ino);
    2509 }
    2510 }
    2511 return res;
    2512}
    2513
    2514static struct fuse_context_i *fuse_get_context_internal(void)
    2515{
    2516 return (struct fuse_context_i *) pthread_getspecific(fuse_context_key);
    2517}
    2518
    2519static struct fuse_context_i *fuse_create_context(struct fuse *f)
    2520{
    2521 struct fuse_context_i *c = fuse_get_context_internal();
    2522 if (c == NULL) {
    2523 c = (struct fuse_context_i *)
    2524 calloc(1, sizeof(struct fuse_context_i));
    2525 if (c == NULL) {
    2526 /* This is hard to deal with properly, so just
    2527 abort. If memory is so low that the
    2528 context cannot be allocated, there's not
    2529 much hope for the filesystem anyway */
    2530 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate thread specific data\n");
    2531 abort();
    2532 }
    2533 pthread_setspecific(fuse_context_key, c);
    2534 } else {
    2535 memset(c, 0, sizeof(*c));
    2536 }
    2537 c->ctx.fuse = f;
    2538
    2539 return c;
    2540}
    2541
    2542static void fuse_freecontext(void *data)
    2543{
    2544 free(data);
    2545}
    2546
    2547static int fuse_create_context_key(void)
    2548{
    2549 int err = 0;
    2550 pthread_mutex_lock(&fuse_context_lock);
    2551 if (!fuse_context_ref) {
    2552 err = pthread_key_create(&fuse_context_key, fuse_freecontext);
    2553 if (err) {
    2554 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
    2555 strerror(err));
    2556 pthread_mutex_unlock(&fuse_context_lock);
    2557 return -1;
    2558 }
    2559 }
    2560 fuse_context_ref++;
    2561 pthread_mutex_unlock(&fuse_context_lock);
    2562 return 0;
    2563}
    2564
    2565static void fuse_delete_context_key(void)
    2566{
    2567 pthread_mutex_lock(&fuse_context_lock);
    2568 fuse_context_ref--;
    2569 if (!fuse_context_ref) {
    2570 free(pthread_getspecific(fuse_context_key));
    2571 pthread_key_delete(fuse_context_key);
    2572 }
    2573 pthread_mutex_unlock(&fuse_context_lock);
    2574}
    2575
    2576static struct fuse *req_fuse_prepare(fuse_req_t req)
    2577{
    2578 struct fuse_context_i *c = fuse_create_context(req_fuse(req));
    2579 const struct fuse_ctx *ctx = fuse_req_ctx(req);
    2580 c->req = req;
    2581 c->ctx.uid = ctx->uid;
    2582 c->ctx.gid = ctx->gid;
    2583 c->ctx.pid = ctx->pid;
    2584 c->ctx.umask = ctx->umask;
    2585 return c->ctx.fuse;
    2586}
    2587
    2588static inline void reply_err(fuse_req_t req, int err)
    2589{
    2590 /* fuse_reply_err() uses non-negated errno values */
    2591 fuse_reply_err(req, -err);
    2592}
    2593
    2594static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
    2595 int err)
    2596{
    2597 if (!err) {
    2598 struct fuse *f = req_fuse(req);
    2599 if (fuse_reply_entry(req, e) == -ENOENT) {
    2600 /* Skip forget for negative result */
    2601 if (e->ino != 0)
    2602 forget_node(f, e->ino, 1);
    2603 }
    2604 } else
    2605 reply_err(req, err);
    2606}
    2607
    2608void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
    2609 struct fuse_config *cfg)
    2610{
    2611 fuse_get_context()->private_data = fs->user_data;
    2612 if (!fs->op.write_buf)
    2613 fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ);
    2614 if (!fs->op.lock)
    2615 fuse_unset_feature_flag(conn, FUSE_CAP_POSIX_LOCKS);
    2616 if (!fs->op.flock)
    2617 fuse_unset_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS);
    2618 if (fs->op.init) {
    2619 uint64_t want_ext_default = conn->want_ext;
    2620 uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
    2621 int rc;
    2622
    2623 conn->want = want_default;
    2624 fs->user_data = fs->op.init(conn, cfg);
    2625
    2626 rc = convert_to_conn_want_ext(conn, want_ext_default,
    2627 want_default);
    2628
    2629 if (rc != 0) {
    2630 /*
    2631 * This is a grave developer error, but
    2632 * we cannot return an error here, as the function
    2633 * signature does not allow it.
    2634 */
    2635 fuse_log(
    2636 FUSE_LOG_ERR,
    2637 "fuse: Aborting due to invalid conn want flags.\n");
    2638 _exit(EXIT_FAILURE);
    2639 }
    2640 }
    2641}
    2642
    2643static int fuse_init_intr_signal(int signum, int *installed);
    2644
    2645static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
    2646{
    2647 struct fuse *f = (struct fuse *) data;
    2648
    2649 fuse_create_context(f);
    2650 fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
    2651 fuse_fs_init(f->fs, conn, &f->conf);
    2652
    2653 if (f->conf.intr) {
    2654 if (fuse_init_intr_signal(f->conf.intr_signal,
    2655 &f->intr_installed) == -1)
    2656 fuse_log(FUSE_LOG_ERR, "fuse: failed to init interrupt signal\n");
    2657 } else {
    2658 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    2659 conn->no_interrupt = 1;
    2660 }
    2661}
    2662
    2663void fuse_fs_destroy(struct fuse_fs *fs)
    2664{
    2665 fuse_get_context()->private_data = fs->user_data;
    2666 if (fs->op.destroy)
    2667 fs->op.destroy(fs->user_data);
    2668}
    2669
    2670static void fuse_lib_destroy(void *data)
    2671{
    2672 struct fuse *f = (struct fuse *) data;
    2673
    2674 fuse_create_context(f);
    2675 fuse_fs_destroy(f->fs);
    2676}
    2677
    2678static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
    2679 const char *name)
    2680{
    2681 struct fuse *f = req_fuse_prepare(req);
    2682 struct fuse_entry_param e;
    2683 char *path;
    2684 int err;
    2685 struct node *dot = NULL;
    2686
    2687 if (name[0] == '.') {
    2688 int len = strlen(name);
    2689
    2690 if (len == 1 || (name[1] == '.' && len == 2)) {
    2691 pthread_mutex_lock(&f->lock);
    2692 if (len == 1) {
    2693 if (f->conf.debug)
    2694 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOT\n");
    2695 dot = get_node_nocheck(f, parent);
    2696 if (dot == NULL) {
    2697 pthread_mutex_unlock(&f->lock);
    2698 reply_entry(req, &e, -ESTALE);
    2699 return;
    2700 }
    2701 dot->refctr++;
    2702 } else {
    2703 if (f->conf.debug)
    2704 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOTDOT\n");
    2705 parent = get_node(f, parent)->parent->nodeid;
    2706 }
    2707 pthread_mutex_unlock(&f->lock);
    2708 name = NULL;
    2709 }
    2710 }
    2711
    2712 err = get_path_name(f, parent, name, &path);
    2713 if (!err) {
    2714 struct fuse_intr_data d;
    2715 if (f->conf.debug)
    2716 fuse_log(FUSE_LOG_DEBUG, "LOOKUP %s\n", path);
    2717 fuse_prepare_interrupt(f, req, &d);
    2718 err = lookup_path(f, parent, name, path, &e, NULL);
    2719 if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
    2720 e.ino = 0;
    2721 e.entry_timeout = f->conf.negative_timeout;
    2722 err = 0;
    2723 }
    2724 fuse_finish_interrupt(f, req, &d);
    2725 free_path(f, parent, path);
    2726 }
    2727 if (dot) {
    2728 pthread_mutex_lock(&f->lock);
    2729 unref_node(f, dot);
    2730 pthread_mutex_unlock(&f->lock);
    2731 }
    2732 reply_entry(req, &e, err);
    2733}
    2734
    2735static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup)
    2736{
    2737 if (f->conf.debug)
    2738 fuse_log(FUSE_LOG_DEBUG, "FORGET %llu/%llu\n", (unsigned long long)ino,
    2739 (unsigned long long) nlookup);
    2740 forget_node(f, ino, nlookup);
    2741}
    2742
    2743static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
    2744{
    2745 do_forget(req_fuse(req), ino, nlookup);
    2746 fuse_reply_none(req);
    2747}
    2748
    2749static void fuse_lib_forget_multi(fuse_req_t req, size_t count,
    2750 struct fuse_forget_data *forgets)
    2751{
    2752 struct fuse *f = req_fuse(req);
    2753 size_t i;
    2754
    2755 for (i = 0; i < count; i++)
    2756 do_forget(f, forgets[i].ino, forgets[i].nlookup);
    2757
    2758 fuse_reply_none(req);
    2759}
    2760
    2761
    2762static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino,
    2763 struct fuse_file_info *fi)
    2764{
    2765 struct fuse *f = req_fuse_prepare(req);
    2766 struct stat buf;
    2767 char *path;
    2768 int err;
    2769
    2770 memset(&buf, 0, sizeof(buf));
    2771
    2772 if (fi != NULL)
    2773 err = get_path_nullok(f, ino, &path);
    2774 else
    2775 err = get_path(f, ino, &path);
    2776 if (!err) {
    2777 struct fuse_intr_data d;
    2778 fuse_prepare_interrupt(f, req, &d);
    2779 err = fuse_fs_getattr(f->fs, path, &buf, fi);
    2780 fuse_finish_interrupt(f, req, &d);
    2781 free_path(f, ino, path);
    2782 }
    2783 if (!err) {
    2784 struct node *node;
    2785
    2786 pthread_mutex_lock(&f->lock);
    2787 node = get_node(f, ino);
    2788 if (node->is_hidden && buf.st_nlink > 0)
    2789 buf.st_nlink--;
    2790 if (f->conf.auto_cache)
    2791 update_stat(node, &buf);
    2792 pthread_mutex_unlock(&f->lock);
    2793 set_stat(f, ino, &buf);
    2794 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
    2795 } else
    2796 reply_err(req, err);
    2797}
    2798
    2799int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
    2800 struct fuse_file_info *fi)
    2801{
    2802 fuse_get_context()->private_data = fs->user_data;
    2803 if (fs->op.chmod) {
    2804 if (fs->debug) {
    2805 char buf[10];
    2806 fuse_log(FUSE_LOG_DEBUG, "chmod[%s] %s %llo\n",
    2807 file_info_string(fi, buf, sizeof(buf)),
    2808 path, (unsigned long long) mode);
    2809 }
    2810 return fs->op.chmod(path, mode, fi);
    2811 }
    2812 else
    2813 return -ENOSYS;
    2814}
    2815
    2816static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
    2817 int valid, struct fuse_file_info *fi)
    2818{
    2819 struct fuse *f = req_fuse_prepare(req);
    2820 struct stat buf;
    2821 char *path;
    2822 int err;
    2823
    2824 memset(&buf, 0, sizeof(buf));
    2825 if (fi != NULL)
    2826 err = get_path_nullok(f, ino, &path);
    2827 else
    2828 err = get_path(f, ino, &path);
    2829 if (!err) {
    2830 struct fuse_intr_data d;
    2831 fuse_prepare_interrupt(f, req, &d);
    2832 err = 0;
    2833 if (!err && (valid & FUSE_SET_ATTR_MODE))
    2834 err = fuse_fs_chmod(f->fs, path, attr->st_mode, fi);
    2835 if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) {
    2836 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
    2837 attr->st_uid : (uid_t) -1;
    2838 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
    2839 attr->st_gid : (gid_t) -1;
    2840 err = fuse_fs_chown(f->fs, path, uid, gid, fi);
    2841 }
    2842 if (!err && (valid & FUSE_SET_ATTR_SIZE)) {
    2843 err = fuse_fs_truncate(f->fs, path,
    2844 attr->st_size, fi);
    2845 }
    2846#ifdef HAVE_UTIMENSAT
    2847 if (!err &&
    2848 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
    2849 struct timespec tv[2];
    2850
    2851 tv[0].tv_sec = 0;
    2852 tv[1].tv_sec = 0;
    2853 tv[0].tv_nsec = UTIME_OMIT;
    2854 tv[1].tv_nsec = UTIME_OMIT;
    2855
    2856 if (valid & FUSE_SET_ATTR_ATIME_NOW)
    2857 tv[0].tv_nsec = UTIME_NOW;
    2858 else if (valid & FUSE_SET_ATTR_ATIME)
    2859 tv[0] = attr->st_atim;
    2860
    2861 if (valid & FUSE_SET_ATTR_MTIME_NOW)
    2862 tv[1].tv_nsec = UTIME_NOW;
    2863 else if (valid & FUSE_SET_ATTR_MTIME)
    2864 tv[1] = attr->st_mtim;
    2865
    2866 err = fuse_fs_utimens(f->fs, path, tv, fi);
    2867 } else
    2868#endif
    2869 if (!err &&
    2870 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ==
    2871 (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
    2872 struct timespec tv[2];
    2873 tv[0].tv_sec = attr->st_atime;
    2874 tv[0].tv_nsec = ST_ATIM_NSEC(attr);
    2875 tv[1].tv_sec = attr->st_mtime;
    2876 tv[1].tv_nsec = ST_MTIM_NSEC(attr);
    2877 err = fuse_fs_utimens(f->fs, path, tv, fi);
    2878 }
    2879 if (!err) {
    2880 err = fuse_fs_getattr(f->fs, path, &buf, fi);
    2881 }
    2882 fuse_finish_interrupt(f, req, &d);
    2883 free_path(f, ino, path);
    2884 }
    2885 if (!err) {
    2886 if (f->conf.auto_cache) {
    2887 pthread_mutex_lock(&f->lock);
    2888 update_stat(get_node(f, ino), &buf);
    2889 pthread_mutex_unlock(&f->lock);
    2890 }
    2891 set_stat(f, ino, &buf);
    2892 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
    2893 } else
    2894 reply_err(req, err);
    2895}
    2896
    2897static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask)
    2898{
    2899 struct fuse *f = req_fuse_prepare(req);
    2900 char *path;
    2901 int err;
    2902
    2903 err = get_path(f, ino, &path);
    2904 if (!err) {
    2905 struct fuse_intr_data d;
    2906
    2907 fuse_prepare_interrupt(f, req, &d);
    2908 err = fuse_fs_access(f->fs, path, mask);
    2909 fuse_finish_interrupt(f, req, &d);
    2910 free_path(f, ino, path);
    2911 }
    2912 reply_err(req, err);
    2913}
    2914
    2915static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino)
    2916{
    2917 struct fuse *f = req_fuse_prepare(req);
    2918 char linkname[PATH_MAX + 1];
    2919 char *path;
    2920 int err;
    2921
    2922 err = get_path(f, ino, &path);
    2923 if (!err) {
    2924 struct fuse_intr_data d;
    2925 fuse_prepare_interrupt(f, req, &d);
    2926 err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname));
    2927 fuse_finish_interrupt(f, req, &d);
    2928 free_path(f, ino, path);
    2929 }
    2930 if (!err) {
    2931 linkname[PATH_MAX] = '\0';
    2932 fuse_reply_readlink(req, linkname);
    2933 } else
    2934 reply_err(req, err);
    2935}
    2936
    2937static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
    2938 mode_t mode, dev_t rdev)
    2939{
    2940 struct fuse *f = req_fuse_prepare(req);
    2941 struct fuse_entry_param e;
    2942 char *path;
    2943 int err;
    2944
    2945 err = get_path_name(f, parent, name, &path);
    2946 if (!err) {
    2947 struct fuse_intr_data d;
    2948
    2949 fuse_prepare_interrupt(f, req, &d);
    2950 err = -ENOSYS;
    2951 if (S_ISREG(mode)) {
    2952 struct fuse_file_info fi;
    2953
    2954 memset(&fi, 0, sizeof(fi));
    2955 fi.flags = O_CREAT | O_EXCL | O_WRONLY;
    2956 err = fuse_fs_create(f->fs, path, mode, &fi);
    2957 if (!err) {
    2958 err = lookup_path(f, parent, name, path, &e,
    2959 &fi);
    2960 fuse_fs_release(f->fs, path, &fi);
    2961 }
    2962 }
    2963 if (err == -ENOSYS) {
    2964 err = fuse_fs_mknod(f->fs, path, mode, rdev);
    2965 if (!err)
    2966 err = lookup_path(f, parent, name, path, &e,
    2967 NULL);
    2968 }
    2969 fuse_finish_interrupt(f, req, &d);
    2970 free_path(f, parent, path);
    2971 }
    2972 reply_entry(req, &e, err);
    2973}
    2974
    2975static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
    2976 mode_t mode)
    2977{
    2978 struct fuse *f = req_fuse_prepare(req);
    2979 struct fuse_entry_param e;
    2980 char *path;
    2981 int err;
    2982
    2983 err = get_path_name(f, parent, name, &path);
    2984 if (!err) {
    2985 struct fuse_intr_data d;
    2986
    2987 fuse_prepare_interrupt(f, req, &d);
    2988 err = fuse_fs_mkdir(f->fs, path, mode);
    2989 if (!err)
    2990 err = lookup_path(f, parent, name, path, &e, NULL);
    2991 fuse_finish_interrupt(f, req, &d);
    2992 free_path(f, parent, path);
    2993 }
    2994 reply_entry(req, &e, err);
    2995}
    2996
    2997static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent,
    2998 const char *name)
    2999{
    3000 struct fuse *f = req_fuse_prepare(req);
    3001 struct node *wnode;
    3002 char *path;
    3003 int err;
    3004
    3005 err = get_path_wrlock(f, parent, name, &path, &wnode);
    3006 if (!err) {
    3007 struct fuse_intr_data d;
    3008
    3009 fuse_prepare_interrupt(f, req, &d);
    3010 if (!f->conf.hard_remove && is_open(f, parent, name)) {
    3011 err = hide_node(f, path, parent, name);
    3012 if (!err) {
    3013 /* we have hidden the node so now check again under a lock in case it is not used any more */
    3014 if (!is_open(f, parent, wnode->name)) {
    3015 char *unlinkpath;
    3016
    3017 /* get the hidden file path, to unlink it */
    3018 if (try_get_path(f, wnode->nodeid, NULL, &unlinkpath, NULL, false) == 0) {
    3019 err = fuse_fs_unlink(f->fs, unlinkpath);
    3020 if (!err)
    3021 remove_node(f, parent, wnode->name);
    3022 free(unlinkpath);
    3023 }
    3024 }
    3025 }
    3026 } else {
    3027 err = fuse_fs_unlink(f->fs, path);
    3028 if (!err)
    3029 remove_node(f, parent, name);
    3030 }
    3031 fuse_finish_interrupt(f, req, &d);
    3032 free_path_wrlock(f, parent, wnode, path);
    3033 }
    3034 reply_err(req, err);
    3035}
    3036
    3037static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
    3038{
    3039 struct fuse *f = req_fuse_prepare(req);
    3040 struct node *wnode;
    3041 char *path;
    3042 int err;
    3043
    3044 err = get_path_wrlock(f, parent, name, &path, &wnode);
    3045 if (!err) {
    3046 struct fuse_intr_data d;
    3047
    3048 fuse_prepare_interrupt(f, req, &d);
    3049 err = fuse_fs_rmdir(f->fs, path);
    3050 fuse_finish_interrupt(f, req, &d);
    3051 if (!err)
    3052 remove_node(f, parent, name);
    3053 free_path_wrlock(f, parent, wnode, path);
    3054 }
    3055 reply_err(req, err);
    3056}
    3057
    3058static void fuse_lib_symlink(fuse_req_t req, const char *linkname,
    3059 fuse_ino_t parent, const char *name)
    3060{
    3061 struct fuse *f = req_fuse_prepare(req);
    3062 struct fuse_entry_param e;
    3063 char *path;
    3064 int err;
    3065
    3066 err = get_path_name(f, parent, name, &path);
    3067 if (!err) {
    3068 struct fuse_intr_data d;
    3069
    3070 fuse_prepare_interrupt(f, req, &d);
    3071 err = fuse_fs_symlink(f->fs, linkname, path);
    3072 if (!err)
    3073 err = lookup_path(f, parent, name, path, &e, NULL);
    3074 fuse_finish_interrupt(f, req, &d);
    3075 free_path(f, parent, path);
    3076 }
    3077 reply_entry(req, &e, err);
    3078}
    3079
    3080static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
    3081 const char *oldname, fuse_ino_t newdir,
    3082 const char *newname, unsigned int flags)
    3083{
    3084 struct fuse *f = req_fuse_prepare(req);
    3085 char *oldpath;
    3086 char *newpath;
    3087 struct node *wnode1;
    3088 struct node *wnode2;
    3089 int err;
    3090
    3091 err = get_path2(f, olddir, oldname, newdir, newname,
    3092 &oldpath, &newpath, &wnode1, &wnode2);
    3093 if (!err) {
    3094 struct fuse_intr_data d;
    3095 err = 0;
    3096 fuse_prepare_interrupt(f, req, &d);
    3097 if (!f->conf.hard_remove && !(flags & RENAME_EXCHANGE) &&
    3098 is_open(f, newdir, newname))
    3099 err = hide_node(f, newpath, newdir, newname);
    3100 if (!err) {
    3101 err = fuse_fs_rename(f->fs, oldpath, newpath, flags);
    3102 if (!err) {
    3103 if (flags & RENAME_EXCHANGE) {
    3104 err = exchange_node(f, olddir, oldname,
    3105 newdir, newname);
    3106 } else {
    3107 err = rename_node(f, olddir, oldname,
    3108 newdir, newname, 0);
    3109 }
    3110 }
    3111 }
    3112 fuse_finish_interrupt(f, req, &d);
    3113 free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath);
    3114 }
    3115 reply_err(req, err);
    3116}
    3117
    3118static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
    3119 const char *newname)
    3120{
    3121 struct fuse *f = req_fuse_prepare(req);
    3122 struct fuse_entry_param e;
    3123 char *oldpath;
    3124 char *newpath;
    3125 int err;
    3126
    3127 err = get_path2(f, ino, NULL, newparent, newname,
    3128 &oldpath, &newpath, NULL, NULL);
    3129 if (!err) {
    3130 struct fuse_intr_data d;
    3131
    3132 fuse_prepare_interrupt(f, req, &d);
    3133 err = fuse_fs_link(f->fs, oldpath, newpath);
    3134 if (!err)
    3135 err = lookup_path(f, newparent, newname, newpath,
    3136 &e, NULL);
    3137 fuse_finish_interrupt(f, req, &d);
    3138 free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath);
    3139 }
    3140 reply_entry(req, &e, err);
    3141}
    3142
    3143static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path,
    3144 struct fuse_file_info *fi)
    3145{
    3146 struct node *node;
    3147 int unlink_hidden = 0;
    3148
    3149 fuse_fs_release(f->fs, path, fi);
    3150
    3151 pthread_mutex_lock(&f->lock);
    3152 node = get_node(f, ino);
    3153 assert(node->open_count > 0);
    3154 --node->open_count;
    3155 if (node->is_hidden && !node->open_count) {
    3156 unlink_hidden = 1;
    3157 node->is_hidden = 0;
    3158 }
    3159 pthread_mutex_unlock(&f->lock);
    3160
    3161 if(unlink_hidden) {
    3162 if (path) {
    3163 fuse_fs_unlink(f->fs, path);
    3164 } else if (f->conf.nullpath_ok) {
    3165 char *unlinkpath;
    3166
    3167 if (get_path(f, ino, &unlinkpath) == 0)
    3168 fuse_fs_unlink(f->fs, unlinkpath);
    3169
    3170 free_path(f, ino, unlinkpath);
    3171 }
    3172 }
    3173}
    3174
    3175static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent,
    3176 const char *name, mode_t mode,
    3177 struct fuse_file_info *fi)
    3178{
    3179 struct fuse *f = req_fuse_prepare(req);
    3180 struct fuse_intr_data d;
    3181 struct fuse_entry_param e;
    3182 char *path;
    3183 int err;
    3184
    3185 err = get_path_name(f, parent, name, &path);
    3186 if (!err) {
    3187 fuse_prepare_interrupt(f, req, &d);
    3188 err = fuse_fs_create(f->fs, path, mode, fi);
    3189 if (!err) {
    3190 err = lookup_path(f, parent, name, path, &e, fi);
    3191 if (err)
    3192 fuse_fs_release(f->fs, path, fi);
    3193 else if (!S_ISREG(e.attr.st_mode)) {
    3194 err = -EIO;
    3195 fuse_fs_release(f->fs, path, fi);
    3196 forget_node(f, e.ino, 1);
    3197 } else {
    3198 if (f->conf.direct_io)
    3199 fi->direct_io = 1;
    3200 if (f->conf.kernel_cache)
    3201 fi->keep_cache = 1;
    3202 if (fi->direct_io &&
    3203 f->conf.parallel_direct_writes)
    3204 fi->parallel_direct_writes = 1;
    3205 }
    3206 }
    3207 fuse_finish_interrupt(f, req, &d);
    3208 }
    3209 if (!err) {
    3210 pthread_mutex_lock(&f->lock);
    3211 get_node(f, e.ino)->open_count++;
    3212 pthread_mutex_unlock(&f->lock);
    3213 if (fuse_reply_create(req, &e, fi) == -ENOENT) {
    3214 /* The open syscall was interrupted, so it
    3215 must be cancelled */
    3216 fuse_do_release(f, e.ino, path, fi);
    3217 forget_node(f, e.ino, 1);
    3218 }
    3219 } else {
    3220 reply_err(req, err);
    3221 }
    3222
    3223 free_path(f, parent, path);
    3224}
    3225
    3226static double diff_timespec(const struct timespec *t1,
    3227 const struct timespec *t2)
    3228{
    3229 return (t1->tv_sec - t2->tv_sec) +
    3230 ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0;
    3231}
    3232
    3233static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path,
    3234 struct fuse_file_info *fi)
    3235{
    3236 struct node *node;
    3237
    3238 pthread_mutex_lock(&f->lock);
    3239 node = get_node(f, ino);
    3240 if (node->cache_valid) {
    3241 struct timespec now;
    3242
    3243 curr_time(&now);
    3244 if (diff_timespec(&now, &node->stat_updated) >
    3245 f->conf.ac_attr_timeout) {
    3246 struct stat stbuf;
    3247 int err;
    3248 pthread_mutex_unlock(&f->lock);
    3249 err = fuse_fs_getattr(f->fs, path, &stbuf, fi);
    3250 pthread_mutex_lock(&f->lock);
    3251 if (!err)
    3252 update_stat(node, &stbuf);
    3253 else
    3254 node->cache_valid = 0;
    3255 }
    3256 }
    3257 if (node->cache_valid)
    3258 fi->keep_cache = 1;
    3259
    3260 node->cache_valid = 1;
    3261 pthread_mutex_unlock(&f->lock);
    3262}
    3263
    3264static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino,
    3265 struct fuse_file_info *fi)
    3266{
    3267 struct fuse *f = req_fuse_prepare(req);
    3268 struct fuse_intr_data d;
    3269 char *path;
    3270 int err;
    3271
    3272 err = get_path(f, ino, &path);
    3273 if (!err) {
    3274 fuse_prepare_interrupt(f, req, &d);
    3275 err = fuse_fs_open(f->fs, path, fi);
    3276 if (!err) {
    3277 if (f->conf.direct_io)
    3278 fi->direct_io = 1;
    3279 if (f->conf.kernel_cache)
    3280 fi->keep_cache = 1;
    3281
    3282 if (f->conf.auto_cache)
    3283 open_auto_cache(f, ino, path, fi);
    3284
    3285 if (f->conf.no_rofd_flush &&
    3286 (fi->flags & O_ACCMODE) == O_RDONLY)
    3287 fi->noflush = 1;
    3288
    3289 if (fi->direct_io && f->conf.parallel_direct_writes)
    3290 fi->parallel_direct_writes = 1;
    3291
    3292 }
    3293 fuse_finish_interrupt(f, req, &d);
    3294 }
    3295 if (!err) {
    3296 pthread_mutex_lock(&f->lock);
    3297 get_node(f, ino)->open_count++;
    3298 pthread_mutex_unlock(&f->lock);
    3299 if (fuse_reply_open(req, fi) == -ENOENT) {
    3300 /* The open syscall was interrupted, so it
    3301 must be cancelled */
    3302 fuse_do_release(f, ino, path, fi);
    3303 }
    3304 } else
    3305 reply_err(req, err);
    3306
    3307 free_path(f, ino, path);
    3308}
    3309
    3310static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
    3311 off_t off, struct fuse_file_info *fi)
    3312{
    3313 struct fuse *f = req_fuse_prepare(req);
    3314 struct fuse_bufvec *buf = NULL;
    3315 char *path;
    3316 int res;
    3317
    3318 res = get_path_nullok(f, ino, &path);
    3319 if (res == 0) {
    3320 struct fuse_intr_data d;
    3321
    3322 fuse_prepare_interrupt(f, req, &d);
    3323 res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi);
    3324 fuse_finish_interrupt(f, req, &d);
    3325 free_path(f, ino, path);
    3326 }
    3327
    3328 if (res == 0)
    3330 else
    3331 reply_err(req, res);
    3332
    3333 fuse_free_buf(buf);
    3334}
    3335
    3336static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino,
    3337 struct fuse_bufvec *buf, off_t off,
    3338 struct fuse_file_info *fi)
    3339{
    3340 struct fuse *f = req_fuse_prepare(req);
    3341 char *path;
    3342 int res;
    3343
    3344 res = get_path_nullok(f, ino, &path);
    3345 if (res == 0) {
    3346 struct fuse_intr_data d;
    3347
    3348 fuse_prepare_interrupt(f, req, &d);
    3349 res = fuse_fs_write_buf(f->fs, path, buf, off, fi);
    3350 fuse_finish_interrupt(f, req, &d);
    3351 free_path(f, ino, path);
    3352 }
    3353
    3354 if (res >= 0)
    3355 fuse_reply_write(req, res);
    3356 else
    3357 reply_err(req, res);
    3358}
    3359
    3360static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
    3361 struct fuse_file_info *fi)
    3362{
    3363 struct fuse *f = req_fuse_prepare(req);
    3364 char *path;
    3365 int err;
    3366
    3367 err = get_path_nullok(f, ino, &path);
    3368 if (!err) {
    3369 struct fuse_intr_data d;
    3370
    3371 fuse_prepare_interrupt(f, req, &d);
    3372 err = fuse_fs_fsync(f->fs, path, datasync, fi);
    3373 fuse_finish_interrupt(f, req, &d);
    3374 free_path(f, ino, path);
    3375 }
    3376 reply_err(req, err);
    3377}
    3378
    3379static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi,
    3380 struct fuse_file_info *fi)
    3381{
    3382 struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh;
    3383 memset(fi, 0, sizeof(struct fuse_file_info));
    3384 fi->fh = dh->fh;
    3385 return dh;
    3386}
    3387
    3388static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
    3389 struct fuse_file_info *llfi)
    3390{
    3391 struct fuse *f = req_fuse_prepare(req);
    3392 struct fuse_intr_data d;
    3393 struct fuse_dh *dh;
    3394 struct fuse_file_info fi;
    3395 char *path;
    3396 int err;
    3397
    3398 dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
    3399 if (dh == NULL) {
    3400 reply_err(req, -ENOMEM);
    3401 return;
    3402 }
    3403 memset(dh, 0, sizeof(struct fuse_dh));
    3404 dh->fuse = f;
    3405 dh->contents = NULL;
    3406 dh->first = NULL;
    3407 dh->len = 0;
    3408 dh->filled = 0;
    3409 dh->nodeid = ino;
    3410 pthread_mutex_init(&dh->lock, NULL);
    3411
    3412 llfi->fh = (uintptr_t) dh;
    3413
    3414 memset(&fi, 0, sizeof(fi));
    3415 fi.flags = llfi->flags;
    3416
    3417 err = get_path(f, ino, &path);
    3418 if (!err) {
    3419 fuse_prepare_interrupt(f, req, &d);
    3420 err = fuse_fs_opendir(f->fs, path, &fi);
    3421 fuse_finish_interrupt(f, req, &d);
    3422 dh->fh = fi.fh;
    3423 llfi->cache_readdir = fi.cache_readdir;
    3424 llfi->keep_cache = fi.keep_cache;
    3425 }
    3426 if (!err) {
    3427 if (fuse_reply_open(req, llfi) == -ENOENT) {
    3428 /* The opendir syscall was interrupted, so it
    3429 must be cancelled */
    3430 fuse_fs_releasedir(f->fs, path, &fi);
    3431 pthread_mutex_destroy(&dh->lock);
    3432 free(dh);
    3433 }
    3434 } else {
    3435 reply_err(req, err);
    3436 pthread_mutex_destroy(&dh->lock);
    3437 free(dh);
    3438 }
    3439 free_path(f, ino, path);
    3440}
    3441
    3442static int extend_contents(struct fuse_dh *dh, unsigned minsize)
    3443{
    3444 if (minsize > dh->size) {
    3445 char *newptr;
    3446 unsigned newsize = dh->size;
    3447 if (!newsize)
    3448 newsize = 1024;
    3449 while (newsize < minsize) {
    3450 if (newsize >= 0x80000000)
    3451 newsize = 0xffffffff;
    3452 else
    3453 newsize *= 2;
    3454 }
    3455
    3456 newptr = (char *) realloc(dh->contents, newsize);
    3457 if (!newptr) {
    3458 dh->error = -ENOMEM;
    3459 return -1;
    3460 }
    3461 dh->contents = newptr;
    3462 dh->size = newsize;
    3463 }
    3464 return 0;
    3465}
    3466
    3467static int fuse_add_direntry_to_dh(struct fuse_dh *dh, const char *name,
    3468 struct stat *st, enum fuse_fill_dir_flags flags)
    3469{
    3470 struct fuse_direntry *de;
    3471
    3472 de = malloc(sizeof(struct fuse_direntry));
    3473 if (!de) {
    3474 dh->error = -ENOMEM;
    3475 return -1;
    3476 }
    3477 de->name = strdup(name);
    3478 if (!de->name) {
    3479 dh->error = -ENOMEM;
    3480 free(de);
    3481 return -1;
    3482 }
    3483 de->flags = flags;
    3484 de->stat = *st;
    3485 de->next = NULL;
    3486
    3487 *dh->last = de;
    3488 dh->last = &de->next;
    3489
    3490 return 0;
    3491}
    3492
    3493static fuse_ino_t lookup_nodeid(struct fuse *f, fuse_ino_t parent,
    3494 const char *name)
    3495{
    3496 struct node *node;
    3497 fuse_ino_t res = FUSE_UNKNOWN_INO;
    3498
    3499 pthread_mutex_lock(&f->lock);
    3500 node = lookup_node(f, parent, name);
    3501 if (node)
    3502 res = node->nodeid;
    3503 pthread_mutex_unlock(&f->lock);
    3504
    3505 return res;
    3506}
    3507
    3508static int fill_dir(void *dh_, const char *name, const struct stat *statp,
    3509 off_t off, enum fuse_fill_dir_flags flags)
    3510{
    3511 struct fuse_dh *dh = (struct fuse_dh *) dh_;
    3512 struct stat stbuf;
    3513
    3514 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
    3515 dh->error = -EIO;
    3516 return 1;
    3517 }
    3518
    3519 if (statp)
    3520 stbuf = *statp;
    3521 else {
    3522 memset(&stbuf, 0, sizeof(stbuf));
    3523 stbuf.st_ino = FUSE_UNKNOWN_INO;
    3524 }
    3525
    3526 if (!dh->fuse->conf.use_ino) {
    3527 stbuf.st_ino = FUSE_UNKNOWN_INO;
    3528 if (dh->fuse->conf.readdir_ino) {
    3529 stbuf.st_ino = (ino_t)
    3530 lookup_nodeid(dh->fuse, dh->nodeid, name);
    3531 }
    3532 }
    3533
    3534 if (off) {
    3535 size_t newlen;
    3536
    3537 if (dh->filled) {
    3538 dh->error = -EIO;
    3539 return 1;
    3540 }
    3541
    3542 if (dh->first) {
    3543 dh->error = -EIO;
    3544 return 1;
    3545 }
    3546
    3547 if (extend_contents(dh, dh->needlen) == -1)
    3548 return 1;
    3549
    3550 newlen = dh->len +
    3551 fuse_add_direntry(dh->req, dh->contents + dh->len,
    3552 dh->needlen - dh->len, name,
    3553 &stbuf, off);
    3554 if (newlen > dh->needlen)
    3555 return 1;
    3556
    3557 dh->len = newlen;
    3558 } else {
    3559 dh->filled = 1;
    3560
    3561 if (fuse_add_direntry_to_dh(dh, name, &stbuf, flags) == -1)
    3562 return 1;
    3563 }
    3564 return 0;
    3565}
    3566
    3567static int is_dot_or_dotdot(const char *name)
    3568{
    3569 return name[0] == '.' && (name[1] == '\0' ||
    3570 (name[1] == '.' && name[2] == '\0'));
    3571}
    3572
    3573static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp,
    3574 off_t off, enum fuse_fill_dir_flags flags)
    3575{
    3576 struct fuse_dh *dh = (struct fuse_dh *) dh_;
    3577 struct fuse_entry_param e = {
    3578 /* ino=0 tells the kernel to ignore readdirplus stat info */
    3579 .ino = 0,
    3580 };
    3581 struct fuse *f = dh->fuse;
    3582 int res;
    3583
    3584 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
    3585 dh->error = -EIO;
    3586 return 1;
    3587 }
    3588
    3589 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
    3590 e.attr = *statp;
    3591 } else {
    3592 e.attr.st_ino = FUSE_UNKNOWN_INO;
    3593 if (statp) {
    3594 e.attr.st_mode = statp->st_mode;
    3595 if (f->conf.use_ino)
    3596 e.attr.st_ino = statp->st_ino;
    3597 }
    3598 if (!f->conf.use_ino && f->conf.readdir_ino) {
    3599 e.attr.st_ino = (ino_t)
    3600 lookup_nodeid(f, dh->nodeid, name);
    3601 }
    3602 }
    3603
    3604 if (off) {
    3605 size_t newlen;
    3606
    3607 if (dh->filled) {
    3608 dh->error = -EIO;
    3609 return 1;
    3610 }
    3611
    3612 if (dh->first) {
    3613 dh->error = -EIO;
    3614 return 1;
    3615 }
    3616 if (extend_contents(dh, dh->needlen) == -1)
    3617 return 1;
    3618
    3619 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
    3620 if (!is_dot_or_dotdot(name)) {
    3621 res = do_lookup(f, dh->nodeid, name, &e);
    3622 if (res) {
    3623 dh->error = res;
    3624 return 1;
    3625 }
    3626 }
    3627 }
    3628
    3629 newlen = dh->len +
    3630 fuse_add_direntry_plus(dh->req, dh->contents + dh->len,
    3631 dh->needlen - dh->len, name,
    3632 &e, off);
    3633 if (newlen > dh->needlen)
    3634 return 1;
    3635 dh->len = newlen;
    3636 } else {
    3637 dh->filled = 1;
    3638
    3639 if (fuse_add_direntry_to_dh(dh, name, &e.attr, flags) == -1)
    3640 return 1;
    3641 }
    3642
    3643 return 0;
    3644}
    3645
    3646static void free_direntries(struct fuse_direntry *de)
    3647{
    3648 while (de) {
    3649 struct fuse_direntry *next = de->next;
    3650 free(de->name);
    3651 free(de);
    3652 de = next;
    3653 }
    3654}
    3655
    3656static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
    3657 size_t size, off_t off, struct fuse_dh *dh,
    3658 struct fuse_file_info *fi,
    3659 enum fuse_readdir_flags flags)
    3660{
    3661 char *path;
    3662 int err;
    3663
    3664 if (f->fs->op.readdir)
    3665 err = get_path_nullok(f, ino, &path);
    3666 else
    3667 err = get_path(f, ino, &path);
    3668 if (!err) {
    3669 struct fuse_intr_data d;
    3670 fuse_fill_dir_t filler = fill_dir;
    3671
    3672 if (flags & FUSE_READDIR_PLUS)
    3673 filler = fill_dir_plus;
    3674
    3675 free_direntries(dh->first);
    3676 dh->first = NULL;
    3677 dh->last = &dh->first;
    3678 dh->len = 0;
    3679 dh->error = 0;
    3680 dh->needlen = size;
    3681 dh->filled = 0;
    3682 dh->req = req;
    3683 fuse_prepare_interrupt(f, req, &d);
    3684 err = fuse_fs_readdir(f->fs, path, dh, filler, off, fi, flags);
    3685 fuse_finish_interrupt(f, req, &d);
    3686 dh->req = NULL;
    3687 if (!err)
    3688 err = dh->error;
    3689 if (err)
    3690 dh->filled = 0;
    3691 free_path(f, ino, path);
    3692 }
    3693 return err;
    3694}
    3695
    3696static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh,
    3697 off_t off, enum fuse_readdir_flags flags)
    3698{
    3699 off_t pos;
    3700 struct fuse_direntry *de = dh->first;
    3701 int res;
    3702
    3703 dh->len = 0;
    3704
    3705 if (extend_contents(dh, dh->needlen) == -1)
    3706 return dh->error;
    3707
    3708 for (pos = 0; pos < off; pos++) {
    3709 if (!de)
    3710 break;
    3711
    3712 de = de->next;
    3713 }
    3714 while (de) {
    3715 char *p = dh->contents + dh->len;
    3716 unsigned rem = dh->needlen - dh->len;
    3717 unsigned thislen;
    3718 unsigned newlen;
    3719 pos++;
    3720
    3721 if (flags & FUSE_READDIR_PLUS) {
    3722 struct fuse_entry_param e = {
    3723 .ino = 0,
    3724 .attr = de->stat,
    3725 };
    3726
    3727 if (de->flags & FUSE_FILL_DIR_PLUS &&
    3728 !is_dot_or_dotdot(de->name)) {
    3729 res = do_lookup(dh->fuse, dh->nodeid,
    3730 de->name, &e);
    3731 if (res) {
    3732 dh->error = res;
    3733 return 1;
    3734 }
    3735 }
    3736
    3737 thislen = fuse_add_direntry_plus(req, p, rem,
    3738 de->name, &e, pos);
    3739 } else {
    3740 thislen = fuse_add_direntry(req, p, rem,
    3741 de->name, &de->stat, pos);
    3742 }
    3743 newlen = dh->len + thislen;
    3744 if (newlen > dh->needlen)
    3745 break;
    3746 dh->len = newlen;
    3747 de = de->next;
    3748 }
    3749 return 0;
    3750}
    3751
    3752static void fuse_readdir_common(fuse_req_t req, fuse_ino_t ino, size_t size,
    3753 off_t off, struct fuse_file_info *llfi,
    3754 enum fuse_readdir_flags flags)
    3755{
    3756 struct fuse *f = req_fuse_prepare(req);
    3757 struct fuse_file_info fi;
    3758 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
    3759 int err;
    3760
    3761 pthread_mutex_lock(&dh->lock);
    3762 /* According to SUS, directory contents need to be refreshed on
    3763 rewinddir() */
    3764 if (!off)
    3765 dh->filled = 0;
    3766
    3767 if (!dh->filled) {
    3768 err = readdir_fill(f, req, ino, size, off, dh, &fi, flags);
    3769 if (err) {
    3770 reply_err(req, err);
    3771 goto out;
    3772 }
    3773 }
    3774 if (dh->filled) {
    3775 dh->needlen = size;
    3776 err = readdir_fill_from_list(req, dh, off, flags);
    3777 if (err) {
    3778 reply_err(req, err);
    3779 goto out;
    3780 }
    3781 }
    3782 fuse_reply_buf(req, dh->contents, dh->len);
    3783out:
    3784 pthread_mutex_unlock(&dh->lock);
    3785}
    3786
    3787static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    3788 off_t off, struct fuse_file_info *llfi)
    3789{
    3790 fuse_readdir_common(req, ino, size, off, llfi, 0);
    3791}
    3792
    3793static void fuse_lib_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
    3794 off_t off, struct fuse_file_info *llfi)
    3795{
    3796 fuse_readdir_common(req, ino, size, off, llfi, FUSE_READDIR_PLUS);
    3797}
    3798
    3799static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
    3800 struct fuse_file_info *llfi)
    3801{
    3802 struct fuse *f = req_fuse_prepare(req);
    3803 struct fuse_intr_data d;
    3804 struct fuse_file_info fi;
    3805 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
    3806 char *path;
    3807
    3808 get_path_nullok(f, ino, &path);
    3809
    3810 fuse_prepare_interrupt(f, req, &d);
    3811 fuse_fs_releasedir(f->fs, path, &fi);
    3812 fuse_finish_interrupt(f, req, &d);
    3813 free_path(f, ino, path);
    3814
    3815 pthread_mutex_lock(&dh->lock);
    3816 pthread_mutex_unlock(&dh->lock);
    3817 pthread_mutex_destroy(&dh->lock);
    3818 free_direntries(dh->first);
    3819 free(dh->contents);
    3820 free(dh);
    3821 reply_err(req, 0);
    3822}
    3823
    3824static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
    3825 struct fuse_file_info *llfi)
    3826{
    3827 struct fuse *f = req_fuse_prepare(req);
    3828 struct fuse_file_info fi;
    3829 char *path;
    3830 int err;
    3831
    3832 get_dirhandle(llfi, &fi);
    3833
    3834 err = get_path_nullok(f, ino, &path);
    3835 if (!err) {
    3836 struct fuse_intr_data d;
    3837 fuse_prepare_interrupt(f, req, &d);
    3838 err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi);
    3839 fuse_finish_interrupt(f, req, &d);
    3840 free_path(f, ino, path);
    3841 }
    3842 reply_err(req, err);
    3843}
    3844
    3845static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino)
    3846{
    3847 struct fuse *f = req_fuse_prepare(req);
    3848 struct statvfs buf;
    3849 char *path = NULL;
    3850 int err = 0;
    3851
    3852 memset(&buf, 0, sizeof(buf));
    3853 if (ino)
    3854 err = get_path(f, ino, &path);
    3855
    3856 if (!err) {
    3857 struct fuse_intr_data d;
    3858 fuse_prepare_interrupt(f, req, &d);
    3859 err = fuse_fs_statfs(f->fs, path ? path : "/", &buf);
    3860 fuse_finish_interrupt(f, req, &d);
    3861 free_path(f, ino, path);
    3862 }
    3863
    3864 if (!err)
    3865 fuse_reply_statfs(req, &buf);
    3866 else
    3867 reply_err(req, err);
    3868}
    3869
    3870static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    3871 const char *value, size_t size, int flags)
    3872{
    3873 struct fuse *f = req_fuse_prepare(req);
    3874 char *path;
    3875 int err;
    3876
    3877 err = get_path(f, ino, &path);
    3878 if (!err) {
    3879 struct fuse_intr_data d;
    3880 fuse_prepare_interrupt(f, req, &d);
    3881 err = fuse_fs_setxattr(f->fs, path, name, value, size, flags);
    3882 fuse_finish_interrupt(f, req, &d);
    3883 free_path(f, ino, path);
    3884 }
    3885 reply_err(req, err);
    3886}
    3887
    3888static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
    3889 const char *name, char *value, size_t size)
    3890{
    3891 int err;
    3892 char *path;
    3893
    3894 err = get_path(f, ino, &path);
    3895 if (!err) {
    3896 struct fuse_intr_data d;
    3897 fuse_prepare_interrupt(f, req, &d);
    3898 err = fuse_fs_getxattr(f->fs, path, name, value, size);
    3899 fuse_finish_interrupt(f, req, &d);
    3900 free_path(f, ino, path);
    3901 }
    3902 return err;
    3903}
    3904
    3905static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    3906 size_t size)
    3907{
    3908 struct fuse *f = req_fuse_prepare(req);
    3909 int res;
    3910
    3911 if (size) {
    3912 char *value = (char *) malloc(size);
    3913 if (value == NULL) {
    3914 reply_err(req, -ENOMEM);
    3915 return;
    3916 }
    3917 res = common_getxattr(f, req, ino, name, value, size);
    3918 if (res > 0)
    3919 fuse_reply_buf(req, value, res);
    3920 else
    3921 reply_err(req, res);
    3922 free(value);
    3923 } else {
    3924 res = common_getxattr(f, req, ino, name, NULL, 0);
    3925 if (res >= 0)
    3926 fuse_reply_xattr(req, res);
    3927 else
    3928 reply_err(req, res);
    3929 }
    3930}
    3931
    3932static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
    3933 char *list, size_t size)
    3934{
    3935 char *path;
    3936 int err;
    3937
    3938 err = get_path(f, ino, &path);
    3939 if (!err) {
    3940 struct fuse_intr_data d;
    3941 fuse_prepare_interrupt(f, req, &d);
    3942 err = fuse_fs_listxattr(f->fs, path, list, size);
    3943 fuse_finish_interrupt(f, req, &d);
    3944 free_path(f, ino, path);
    3945 }
    3946 return err;
    3947}
    3948
    3949static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
    3950{
    3951 struct fuse *f = req_fuse_prepare(req);
    3952 int res;
    3953
    3954 if (size) {
    3955 char *list = (char *) malloc(size);
    3956 if (list == NULL) {
    3957 reply_err(req, -ENOMEM);
    3958 return;
    3959 }
    3960 res = common_listxattr(f, req, ino, list, size);
    3961 if (res > 0)
    3962 fuse_reply_buf(req, list, res);
    3963 else
    3964 reply_err(req, res);
    3965 free(list);
    3966 } else {
    3967 res = common_listxattr(f, req, ino, NULL, 0);
    3968 if (res >= 0)
    3969 fuse_reply_xattr(req, res);
    3970 else
    3971 reply_err(req, res);
    3972 }
    3973}
    3974
    3975static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino,
    3976 const char *name)
    3977{
    3978 struct fuse *f = req_fuse_prepare(req);
    3979 char *path;
    3980 int err;
    3981
    3982 err = get_path(f, ino, &path);
    3983 if (!err) {
    3984 struct fuse_intr_data d;
    3985 fuse_prepare_interrupt(f, req, &d);
    3986 err = fuse_fs_removexattr(f->fs, path, name);
    3987 fuse_finish_interrupt(f, req, &d);
    3988 free_path(f, ino, path);
    3989 }
    3990 reply_err(req, err);
    3991}
    3992
    3993static struct lock *locks_conflict(struct node *node, const struct lock *lock)
    3994{
    3995 struct lock *l;
    3996
    3997 for (l = node->locks; l; l = l->next)
    3998 if (l->owner != lock->owner &&
    3999 lock->start <= l->end && l->start <= lock->end &&
    4000 (l->type == F_WRLCK || lock->type == F_WRLCK))
    4001 break;
    4002
    4003 return l;
    4004}
    4005
    4006static void delete_lock(struct lock **lockp)
    4007{
    4008 struct lock *l = *lockp;
    4009 *lockp = l->next;
    4010 free(l);
    4011}
    4012
    4013static void insert_lock(struct lock **pos, struct lock *lock)
    4014{
    4015 lock->next = *pos;
    4016 *pos = lock;
    4017}
    4018
    4019static int locks_insert(struct node *node, struct lock *lock)
    4020{
    4021 struct lock **lp;
    4022 struct lock *newl1 = NULL;
    4023 struct lock *newl2 = NULL;
    4024
    4025 if (lock->type != F_UNLCK || lock->start != 0 ||
    4026 lock->end != OFFSET_MAX) {
    4027 newl1 = malloc(sizeof(struct lock));
    4028 newl2 = malloc(sizeof(struct lock));
    4029
    4030 if (!newl1 || !newl2) {
    4031 free(newl1);
    4032 free(newl2);
    4033 return -ENOLCK;
    4034 }
    4035 }
    4036
    4037 for (lp = &node->locks; *lp;) {
    4038 struct lock *l = *lp;
    4039 if (l->owner != lock->owner)
    4040 goto skip;
    4041
    4042 if (lock->type == l->type) {
    4043 if (l->end < lock->start - 1)
    4044 goto skip;
    4045 if (lock->end < l->start - 1)
    4046 break;
    4047 if (l->start <= lock->start && lock->end <= l->end)
    4048 goto out;
    4049 if (l->start < lock->start)
    4050 lock->start = l->start;
    4051 if (lock->end < l->end)
    4052 lock->end = l->end;
    4053 goto delete;
    4054 } else {
    4055 if (l->end < lock->start)
    4056 goto skip;
    4057 if (lock->end < l->start)
    4058 break;
    4059 if (lock->start <= l->start && l->end <= lock->end)
    4060 goto delete;
    4061 if (l->end <= lock->end) {
    4062 l->end = lock->start - 1;
    4063 goto skip;
    4064 }
    4065 if (lock->start <= l->start) {
    4066 l->start = lock->end + 1;
    4067 break;
    4068 }
    4069 *newl2 = *l;
    4070 newl2->start = lock->end + 1;
    4071 l->end = lock->start - 1;
    4072 insert_lock(&l->next, newl2);
    4073 newl2 = NULL;
    4074 }
    4075 skip:
    4076 lp = &l->next;
    4077 continue;
    4078
    4079 delete:
    4080 delete_lock(lp);
    4081 }
    4082 if (lock->type != F_UNLCK) {
    4083 *newl1 = *lock;
    4084 insert_lock(lp, newl1);
    4085 newl1 = NULL;
    4086 }
    4087out:
    4088 free(newl1);
    4089 free(newl2);
    4090 return 0;
    4091}
    4092
    4093static void flock_to_lock(struct flock *flock, struct lock *lock)
    4094{
    4095 memset(lock, 0, sizeof(struct lock));
    4096 lock->type = flock->l_type;
    4097 lock->start = flock->l_start;
    4098 lock->end =
    4099 flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX;
    4100 lock->pid = flock->l_pid;
    4101}
    4102
    4103static void lock_to_flock(struct lock *lock, struct flock *flock)
    4104{
    4105 flock->l_type = lock->type;
    4106 flock->l_start = lock->start;
    4107 flock->l_len =
    4108 (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1;
    4109 flock->l_pid = lock->pid;
    4110}
    4111
    4112static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
    4113 const char *path, struct fuse_file_info *fi)
    4114{
    4115 struct fuse_intr_data d;
    4116 struct flock lock;
    4117 struct lock l;
    4118 int err;
    4119 int errlock;
    4120
    4121 fuse_prepare_interrupt(f, req, &d);
    4122 memset(&lock, 0, sizeof(lock));
    4123 lock.l_type = F_UNLCK;
    4124 lock.l_whence = SEEK_SET;
    4125 err = fuse_fs_flush(f->fs, path, fi);
    4126 errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock);
    4127 fuse_finish_interrupt(f, req, &d);
    4128
    4129 if (errlock != -ENOSYS) {
    4130 flock_to_lock(&lock, &l);
    4131 l.owner = fi->lock_owner;
    4132 pthread_mutex_lock(&f->lock);
    4133 locks_insert(get_node(f, ino), &l);
    4134 pthread_mutex_unlock(&f->lock);
    4135
    4136 /* if op.lock() is defined FLUSH is needed regardless
    4137 of op.flush() */
    4138 if (err == -ENOSYS)
    4139 err = 0;
    4140 }
    4141 return err;
    4142}
    4143
    4144static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino,
    4145 struct fuse_file_info *fi)
    4146{
    4147 struct fuse *f = req_fuse_prepare(req);
    4148 struct fuse_intr_data d;
    4149 char *path;
    4150 int err = 0;
    4151
    4152 get_path_nullok(f, ino, &path);
    4153 if (fi->flush) {
    4154 err = fuse_flush_common(f, req, ino, path, fi);
    4155 if (err == -ENOSYS)
    4156 err = 0;
    4157 }
    4158
    4159 fuse_prepare_interrupt(f, req, &d);
    4160 fuse_do_release(f, ino, path, fi);
    4161 fuse_finish_interrupt(f, req, &d);
    4162 free_path(f, ino, path);
    4163
    4164 reply_err(req, err);
    4165}
    4166
    4167static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino,
    4168 struct fuse_file_info *fi)
    4169{
    4170 struct fuse *f = req_fuse_prepare(req);
    4171 char *path;
    4172 int err;
    4173
    4174 get_path_nullok(f, ino, &path);
    4175 err = fuse_flush_common(f, req, ino, path, fi);
    4176 free_path(f, ino, path);
    4177
    4178 reply_err(req, err);
    4179}
    4180
    4181static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino,
    4182 struct fuse_file_info *fi, struct flock *lock,
    4183 int cmd)
    4184{
    4185 struct fuse *f = req_fuse_prepare(req);
    4186 char *path;
    4187 int err;
    4188
    4189 err = get_path_nullok(f, ino, &path);
    4190 if (!err) {
    4191 struct fuse_intr_data d;
    4192 fuse_prepare_interrupt(f, req, &d);
    4193 err = fuse_fs_lock(f->fs, path, fi, cmd, lock);
    4194 fuse_finish_interrupt(f, req, &d);
    4195 free_path(f, ino, path);
    4196 }
    4197 return err;
    4198}
    4199
    4200static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino,
    4201 struct fuse_file_info *fi, struct flock *lock)
    4202{
    4203 int err;
    4204 struct lock l;
    4205 struct lock *conflict;
    4206 struct fuse *f = req_fuse(req);
    4207
    4208 flock_to_lock(lock, &l);
    4209 l.owner = fi->lock_owner;
    4210 pthread_mutex_lock(&f->lock);
    4211 conflict = locks_conflict(get_node(f, ino), &l);
    4212 if (conflict)
    4213 lock_to_flock(conflict, lock);
    4214 pthread_mutex_unlock(&f->lock);
    4215 if (!conflict)
    4216 err = fuse_lock_common(req, ino, fi, lock, F_GETLK);
    4217 else
    4218 err = 0;
    4219
    4220 if (!err)
    4221 fuse_reply_lock(req, lock);
    4222 else
    4223 reply_err(req, err);
    4224}
    4225
    4226static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino,
    4227 struct fuse_file_info *fi, struct flock *lock,
    4228 int sleep)
    4229{
    4230 int err = fuse_lock_common(req, ino, fi, lock,
    4231 sleep ? F_SETLKW : F_SETLK);
    4232 if (!err) {
    4233 struct fuse *f = req_fuse(req);
    4234 struct lock l;
    4235 flock_to_lock(lock, &l);
    4236 l.owner = fi->lock_owner;
    4237 pthread_mutex_lock(&f->lock);
    4238 locks_insert(get_node(f, ino), &l);
    4239 pthread_mutex_unlock(&f->lock);
    4240 }
    4241 reply_err(req, err);
    4242}
    4243
    4244static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino,
    4245 struct fuse_file_info *fi, int op)
    4246{
    4247 struct fuse *f = req_fuse_prepare(req);
    4248 char *path;
    4249 int err;
    4250
    4251 err = get_path_nullok(f, ino, &path);
    4252 if (err == 0) {
    4253 struct fuse_intr_data d;
    4254 fuse_prepare_interrupt(f, req, &d);
    4255 err = fuse_fs_flock(f->fs, path, fi, op);
    4256 fuse_finish_interrupt(f, req, &d);
    4257 free_path(f, ino, path);
    4258 }
    4259 reply_err(req, err);
    4260}
    4261
    4262static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
    4263 uint64_t idx)
    4264{
    4265 struct fuse *f = req_fuse_prepare(req);
    4266 struct fuse_intr_data d;
    4267 char *path;
    4268 int err;
    4269
    4270 err = get_path(f, ino, &path);
    4271 if (!err) {
    4272 fuse_prepare_interrupt(f, req, &d);
    4273 err = fuse_fs_bmap(f->fs, path, blocksize, &idx);
    4274 fuse_finish_interrupt(f, req, &d);
    4275 free_path(f, ino, path);
    4276 }
    4277 if (!err)
    4278 fuse_reply_bmap(req, idx);
    4279 else
    4280 reply_err(req, err);
    4281}
    4282
    4283static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
    4284 void *arg, struct fuse_file_info *llfi,
    4285 unsigned int flags, const void *in_buf,
    4286 size_t in_bufsz, size_t out_bufsz)
    4287{
    4288 struct fuse *f = req_fuse_prepare(req);
    4289 struct fuse_intr_data d;
    4290 struct fuse_file_info fi;
    4291 char *path, *out_buf = NULL;
    4292 int err;
    4293
    4294 err = -EPERM;
    4295 if (flags & FUSE_IOCTL_UNRESTRICTED)
    4296 goto err;
    4297
    4298 if (flags & FUSE_IOCTL_DIR)
    4299 get_dirhandle(llfi, &fi);
    4300 else
    4301 fi = *llfi;
    4302
    4303 if (out_bufsz) {
    4304 err = -ENOMEM;
    4305 out_buf = malloc(out_bufsz);
    4306 if (!out_buf)
    4307 goto err;
    4308 }
    4309
    4310 assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
    4311 if (out_buf && in_bufsz)
    4312 memcpy(out_buf, in_buf, in_bufsz);
    4313
    4314 err = get_path_nullok(f, ino, &path);
    4315 if (err)
    4316 goto err;
    4317
    4318 fuse_prepare_interrupt(f, req, &d);
    4319
    4320 err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags,
    4321 out_buf ? out_buf : (void *)in_buf);
    4322
    4323 fuse_finish_interrupt(f, req, &d);
    4324 free_path(f, ino, path);
    4325
    4326 if (err < 0)
    4327 goto err;
    4328 fuse_reply_ioctl(req, err, out_buf, out_bufsz);
    4329 goto out;
    4330err:
    4331 reply_err(req, err);
    4332out:
    4333 free(out_buf);
    4334}
    4335
    4336static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
    4337 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
    4338{
    4339 struct fuse *f = req_fuse_prepare(req);
    4340 struct fuse_intr_data d;
    4341 char *path;
    4342 int err;
    4343 unsigned revents = 0;
    4344
    4345 err = get_path_nullok(f, ino, &path);
    4346 if (!err) {
    4347 fuse_prepare_interrupt(f, req, &d);
    4348 err = fuse_fs_poll(f->fs, path, fi, ph, &revents);
    4349 fuse_finish_interrupt(f, req, &d);
    4350 free_path(f, ino, path);
    4351 }
    4352 if (!err)
    4353 fuse_reply_poll(req, revents);
    4354 else
    4355 reply_err(req, err);
    4356}
    4357
    4358static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
    4359 off_t offset, off_t length, struct fuse_file_info *fi)
    4360{
    4361 struct fuse *f = req_fuse_prepare(req);
    4362 struct fuse_intr_data d;
    4363 char *path;
    4364 int err;
    4365
    4366 err = get_path_nullok(f, ino, &path);
    4367 if (!err) {
    4368 fuse_prepare_interrupt(f, req, &d);
    4369 err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi);
    4370 fuse_finish_interrupt(f, req, &d);
    4371 free_path(f, ino, path);
    4372 }
    4373 reply_err(req, err);
    4374}
    4375
    4376static void fuse_lib_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
    4377 off_t off_in, struct fuse_file_info *fi_in,
    4378 fuse_ino_t nodeid_out, off_t off_out,
    4379 struct fuse_file_info *fi_out, size_t len,
    4380 int flags)
    4381{
    4382 struct fuse *f = req_fuse_prepare(req);
    4383 struct fuse_intr_data d;
    4384 char *path_in, *path_out;
    4385 int err;
    4386 ssize_t res;
    4387
    4388 err = get_path_nullok(f, nodeid_in, &path_in);
    4389 if (err) {
    4390 reply_err(req, err);
    4391 return;
    4392 }
    4393
    4394 err = get_path_nullok(f, nodeid_out, &path_out);
    4395 if (err) {
    4396 free_path(f, nodeid_in, path_in);
    4397 reply_err(req, err);
    4398 return;
    4399 }
    4400
    4401 fuse_prepare_interrupt(f, req, &d);
    4402 res = fuse_fs_copy_file_range(f->fs, path_in, fi_in, off_in, path_out,
    4403 fi_out, off_out, len, flags);
    4404 fuse_finish_interrupt(f, req, &d);
    4405
    4406 if (res >= 0)
    4407 fuse_reply_write(req, res);
    4408 else
    4409 reply_err(req, res);
    4410
    4411 free_path(f, nodeid_in, path_in);
    4412 free_path(f, nodeid_out, path_out);
    4413}
    4414
    4415static void fuse_lib_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
    4416 struct fuse_file_info *fi)
    4417{
    4418 struct fuse *f = req_fuse_prepare(req);
    4419 struct fuse_intr_data d;
    4420 char *path;
    4421 int err;
    4422 off_t res;
    4423
    4424 err = get_path(f, ino, &path);
    4425 if (err) {
    4426 reply_err(req, err);
    4427 return;
    4428 }
    4429
    4430 fuse_prepare_interrupt(f, req, &d);
    4431 res = fuse_fs_lseek(f->fs, path, off, whence, fi);
    4432 fuse_finish_interrupt(f, req, &d);
    4433 free_path(f, ino, path);
    4434 if (res >= 0)
    4435 fuse_reply_lseek(req, res);
    4436 else
    4437 reply_err(req, res);
    4438}
    4439
    4440static int clean_delay(struct fuse *f)
    4441{
    4442 /*
    4443 * This is calculating the delay between clean runs. To
    4444 * reduce the number of cleans we are doing them 10 times
    4445 * within the remember window.
    4446 */
    4447 int min_sleep = 60;
    4448 int max_sleep = 3600;
    4449 int sleep_time = f->conf.remember / 10;
    4450
    4451 if (sleep_time > max_sleep)
    4452 return max_sleep;
    4453 if (sleep_time < min_sleep)
    4454 return min_sleep;
    4455 return sleep_time;
    4456}
    4457
    4458int fuse_clean_cache(struct fuse *f)
    4459{
    4460 struct node_lru *lnode;
    4461 struct list_head *curr, *next;
    4462 struct node *node;
    4463 struct timespec now;
    4464
    4465 pthread_mutex_lock(&f->lock);
    4466
    4467 curr_time(&now);
    4468
    4469 for (curr = f->lru_table.next; curr != &f->lru_table; curr = next) {
    4470 double age;
    4471
    4472 next = curr->next;
    4473 lnode = list_entry(curr, struct node_lru, lru);
    4474 node = &lnode->node;
    4475
    4476 age = diff_timespec(&now, &lnode->forget_time);
    4477 if (age <= f->conf.remember)
    4478 break;
    4479
    4480 assert(node->nlookup == 1);
    4481
    4482 /* Don't forget active directories */
    4483 if (node->refctr > 1)
    4484 continue;
    4485
    4486 node->nlookup = 0;
    4487 unhash_name(f, node);
    4488 unref_node(f, node);
    4489 }
    4490 pthread_mutex_unlock(&f->lock);
    4491
    4492 return clean_delay(f);
    4493}
    4494
    4495static struct fuse_lowlevel_ops fuse_path_ops = {
    4496 .init = fuse_lib_init,
    4497 .destroy = fuse_lib_destroy,
    4498 .lookup = fuse_lib_lookup,
    4499 .forget = fuse_lib_forget,
    4500 .forget_multi = fuse_lib_forget_multi,
    4501 .getattr = fuse_lib_getattr,
    4502 .setattr = fuse_lib_setattr,
    4503 .access = fuse_lib_access,
    4504 .readlink = fuse_lib_readlink,
    4505 .mknod = fuse_lib_mknod,
    4506 .mkdir = fuse_lib_mkdir,
    4507 .unlink = fuse_lib_unlink,
    4508 .rmdir = fuse_lib_rmdir,
    4509 .symlink = fuse_lib_symlink,
    4510 .rename = fuse_lib_rename,
    4511 .link = fuse_lib_link,
    4512 .create = fuse_lib_create,
    4513 .open = fuse_lib_open,
    4514 .read = fuse_lib_read,
    4515 .write_buf = fuse_lib_write_buf,
    4516 .flush = fuse_lib_flush,
    4517 .release = fuse_lib_release,
    4518 .fsync = fuse_lib_fsync,
    4519 .opendir = fuse_lib_opendir,
    4520 .readdir = fuse_lib_readdir,
    4521 .readdirplus = fuse_lib_readdirplus,
    4522 .releasedir = fuse_lib_releasedir,
    4523 .fsyncdir = fuse_lib_fsyncdir,
    4524 .statfs = fuse_lib_statfs,
    4525 .setxattr = fuse_lib_setxattr,
    4526 .getxattr = fuse_lib_getxattr,
    4527 .listxattr = fuse_lib_listxattr,
    4528 .removexattr = fuse_lib_removexattr,
    4529 .getlk = fuse_lib_getlk,
    4530 .setlk = fuse_lib_setlk,
    4531 .flock = fuse_lib_flock,
    4532 .bmap = fuse_lib_bmap,
    4533 .ioctl = fuse_lib_ioctl,
    4534 .poll = fuse_lib_poll,
    4535 .fallocate = fuse_lib_fallocate,
    4536 .copy_file_range = fuse_lib_copy_file_range,
    4537 .lseek = fuse_lib_lseek,
    4538};
    4539
    4540int fuse_notify_poll(struct fuse_pollhandle *ph)
    4541{
    4542 return fuse_lowlevel_notify_poll(ph);
    4543}
    4544
    4545struct fuse_session *fuse_get_session(struct fuse *f)
    4546{
    4547 return f->se;
    4548}
    4549
    4550static int fuse_session_loop_remember(struct fuse *f)
    4551{
    4552 struct fuse_session *se = f->se;
    4553 int res = 0;
    4554 struct timespec now;
    4555 time_t next_clean;
    4556 struct pollfd fds = {
    4557 .fd = se->fd,
    4558 .events = POLLIN
    4559 };
    4560 struct fuse_buf fbuf = {
    4561 .mem = NULL,
    4562 };
    4563
    4564 curr_time(&now);
    4565 next_clean = now.tv_sec;
    4566 while (!fuse_session_exited(se)) {
    4567 unsigned timeout;
    4568
    4569 curr_time(&now);
    4570 if (now.tv_sec < next_clean)
    4571 timeout = next_clean - now.tv_sec;
    4572 else
    4573 timeout = 0;
    4574
    4575 res = poll(&fds, 1, timeout * 1000);
    4576 if (res == -1) {
    4577 if (errno == EINTR)
    4578 continue;
    4579 else
    4580 break;
    4581 } else if (res > 0) {
    4582 res = fuse_session_receive_buf_internal(se, &fbuf,
    4583 NULL);
    4584 if (res == -EINTR)
    4585 continue;
    4586 if (res <= 0)
    4587 break;
    4588
    4589 fuse_session_process_buf_internal(se, &fbuf, NULL);
    4590 } else {
    4591 timeout = fuse_clean_cache(f);
    4592 curr_time(&now);
    4593 next_clean = now.tv_sec + timeout;
    4594 }
    4595 }
    4596
    4597 free(fbuf.mem);
    4599 return res < 0 ? -1 : 0;
    4600}
    4601
    4602int fuse_loop(struct fuse *f)
    4603{
    4604 if (!f)
    4605 return -1;
    4606
    4607 if (lru_enabled(f))
    4608 return fuse_session_loop_remember(f);
    4609
    4610 return fuse_session_loop(f->se);
    4611}
    4612
    4613FUSE_SYMVER("fuse_loop_mt_312", "fuse_loop_mt@@FUSE_3.12")
    4614int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config)
    4615{
    4616 if (f == NULL)
    4617 return -1;
    4618
    4619 int res = fuse_start_cleanup_thread(f);
    4620 if (res)
    4621 return -1;
    4622
    4623 res = fuse_session_loop_mt_312(fuse_get_session(f), config);
    4625 return res;
    4626}
    4627
    4628int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1);
    4629FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@FUSE_3.2")
    4630int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1)
    4631{
    4632 struct fuse_loop_config *config = fuse_loop_cfg_create();
    4633 if (config == NULL)
    4634 return ENOMEM;
    4635
    4636 fuse_loop_cfg_convert(config, config_v1);
    4637
    4638 int res = fuse_loop_mt_312(f, config);
    4639
    4640 fuse_loop_cfg_destroy(config);
    4641
    4642 return res;
    4643}
    4644
    4645int fuse_loop_mt_31(struct fuse *f, int clone_fd);
    4646FUSE_SYMVER("fuse_loop_mt_31", "fuse_loop_mt@FUSE_3.0")
    4647int fuse_loop_mt_31(struct fuse *f, int clone_fd)
    4648{
    4649 int err;
    4650 struct fuse_loop_config *config = fuse_loop_cfg_create();
    4651
    4652 if (config == NULL)
    4653 return ENOMEM;
    4654
    4655 fuse_loop_cfg_set_clone_fd(config, clone_fd);
    4656
    4657 err = fuse_loop_mt_312(f, config);
    4658
    4659 fuse_loop_cfg_destroy(config);
    4660
    4661 return err;
    4662}
    4663
    4664void fuse_exit(struct fuse *f)
    4665{
    4666 fuse_session_exit(f->se);
    4667}
    4668
    4669struct fuse_context *fuse_get_context(void)
    4670{
    4671 struct fuse_context_i *c = fuse_get_context_internal();
    4672
    4673 if (c)
    4674 return &c->ctx;
    4675 else
    4676 return NULL;
    4677}
    4678
    4679int fuse_getgroups(int size, gid_t list[])
    4680{
    4681 struct fuse_context_i *c = fuse_get_context_internal();
    4682 if (!c)
    4683 return -EINVAL;
    4684
    4685 return fuse_req_getgroups(c->req, size, list);
    4686}
    4687
    4688int fuse_interrupted(void)
    4689{
    4690 struct fuse_context_i *c = fuse_get_context_internal();
    4691
    4692 if (c)
    4693 return fuse_req_interrupted(c->req);
    4694 else
    4695 return 0;
    4696}
    4697
    4698int fuse_invalidate_path(struct fuse *f, const char *path) {
    4699 fuse_ino_t ino;
    4700 int err = lookup_path_in_cache(f, path, &ino);
    4701 if (err) {
    4702 return err;
    4703 }
    4704
    4705 return fuse_lowlevel_notify_inval_inode(f->se, ino, 0, 0);
    4706}
    4707
    4708#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
    4709
    4710static const struct fuse_opt fuse_lib_opts[] = {
    4713 FUSE_LIB_OPT("debug", debug, 1),
    4714 FUSE_LIB_OPT("-d", debug, 1),
    4715 FUSE_LIB_OPT("kernel_cache", kernel_cache, 1),
    4716 FUSE_LIB_OPT("auto_cache", auto_cache, 1),
    4717 FUSE_LIB_OPT("noauto_cache", auto_cache, 0),
    4718 FUSE_LIB_OPT("no_rofd_flush", no_rofd_flush, 1),
    4719 FUSE_LIB_OPT("umask=", set_mode, 1),
    4720 FUSE_LIB_OPT("umask=%o", umask, 0),
    4721 FUSE_LIB_OPT("fmask=", set_mode, 1),
    4722 FUSE_LIB_OPT("fmask=%o", fmask, 0),
    4723 FUSE_LIB_OPT("dmask=", set_mode, 1),
    4724 FUSE_LIB_OPT("dmask=%o", dmask, 0),
    4725 FUSE_LIB_OPT("uid=", set_uid, 1),
    4726 FUSE_LIB_OPT("uid=%d", uid, 0),
    4727 FUSE_LIB_OPT("gid=", set_gid, 1),
    4728 FUSE_LIB_OPT("gid=%d", gid, 0),
    4729 FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0),
    4730 FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0),
    4731 FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0),
    4732 FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1),
    4733 FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0),
    4734 FUSE_LIB_OPT("noforget", remember, -1),
    4735 FUSE_LIB_OPT("remember=%u", remember, 0),
    4736 FUSE_LIB_OPT("modules=%s", modules, 0),
    4737 FUSE_LIB_OPT("parallel_direct_write=%d", parallel_direct_writes, 0),
    4739};
    4740
    4741static int fuse_lib_opt_proc(void *data, const char *arg, int key,
    4742 struct fuse_args *outargs)
    4743{
    4744 (void) arg; (void) outargs; (void) data; (void) key;
    4745
    4746 /* Pass through unknown options */
    4747 return 1;
    4748}
    4749
    4750
    4751static const struct fuse_opt fuse_help_opts[] = {
    4752 FUSE_LIB_OPT("modules=%s", modules, 1),
    4753 FUSE_OPT_KEY("modules=%s", FUSE_OPT_KEY_KEEP),
    4755};
    4756
    4757static void print_module_help(const char *name,
    4759{
    4760 struct fuse_args a = FUSE_ARGS_INIT(0, NULL);
    4761 if (fuse_opt_add_arg(&a, "") == -1 ||
    4762 fuse_opt_add_arg(&a, "-h") == -1)
    4763 return;
    4764 printf("\nOptions for %s module:\n", name);
    4765 (*fac)(&a, NULL);
    4767}
    4768
    4769void fuse_lib_help(struct fuse_args *args)
    4770{
    4771 /* These are not all options, but only the ones that
    4772 may be of interest to an end-user */
    4773 printf(
    4774" -o kernel_cache cache files in kernel\n"
    4775" -o [no]auto_cache enable caching based on modification times (off)\n"
    4776" -o no_rofd_flush disable flushing of read-only fd on close (off)\n"
    4777" -o umask=M set file permissions (octal)\n"
    4778" -o fmask=M set file permissions (octal)\n"
    4779" -o dmask=M set dir permissions (octal)\n"
    4780" -o uid=N set file owner\n"
    4781" -o gid=N set file group\n"
    4782" -o entry_timeout=T cache timeout for names (1.0s)\n"
    4783" -o negative_timeout=T cache timeout for deleted names (0.0s)\n"
    4784" -o attr_timeout=T cache timeout for attributes (1.0s)\n"
    4785" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n"
    4786" -o noforget never forget cached inodes\n"
    4787" -o remember=T remember cached inodes for T seconds (0s)\n"
    4788" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n");
    4789
    4790
    4791 /* Print low-level help */
    4793
    4794 /* Print help for builtin modules */
    4795 print_module_help("subdir", &fuse_module_subdir_factory);
    4796#ifdef HAVE_ICONV
    4797 print_module_help("iconv", &fuse_module_iconv_factory);
    4798#endif
    4799
    4800 /* Parse command line options in case we need to
    4801 activate more modules */
    4802 struct fuse_config conf = { .modules = NULL };
    4803 if (fuse_opt_parse(args, &conf, fuse_help_opts,
    4804 fuse_lib_opt_proc) == -1
    4805 || !conf.modules)
    4806 return;
    4807
    4808 char *module;
    4809 char *next;
    4810 struct fuse_module *m;
    4811
    4812 // Iterate over all modules
    4813 for (module = conf.modules; module; module = next) {
    4814 char *p;
    4815 for (p = module; *p && *p != ':'; p++);
    4816 next = *p ? p + 1 : NULL;
    4817 *p = '\0';
    4818
    4819 m = fuse_get_module(module);
    4820 if (m)
    4821 print_module_help(module, &m->factory);
    4822 }
    4823}
    4824
    4825static int fuse_init_intr_signal(int signum, int *installed)
    4826{
    4827 struct sigaction old_sa;
    4828
    4829 if (sigaction(signum, NULL, &old_sa) == -1) {
    4830 perror("fuse: cannot get old signal handler");
    4831 return -1;
    4832 }
    4833
    4834 if (old_sa.sa_handler == SIG_DFL) {
    4835 struct sigaction sa;
    4836
    4837 memset(&sa, 0, sizeof(struct sigaction));
    4838 sa.sa_handler = fuse_intr_sighandler;
    4839 sigemptyset(&sa.sa_mask);
    4840
    4841 if (sigaction(signum, &sa, NULL) == -1) {
    4842 perror("fuse: cannot set interrupt signal handler");
    4843 return -1;
    4844 }
    4845 *installed = 1;
    4846 }
    4847 return 0;
    4848}
    4849
    4850static void fuse_restore_intr_signal(int signum)
    4851{
    4852 struct sigaction sa;
    4853
    4854 memset(&sa, 0, sizeof(struct sigaction));
    4855 sa.sa_handler = SIG_DFL;
    4856 sigaction(signum, &sa, NULL);
    4857}
    4858
    4859
    4860static int fuse_push_module(struct fuse *f, const char *module,
    4861 struct fuse_args *args)
    4862{
    4863 struct fuse_fs *fs[2] = { f->fs, NULL };
    4864 struct fuse_fs *newfs;
    4865 struct fuse_module *m = fuse_get_module(module);
    4866
    4867 if (!m)
    4868 return -1;
    4869
    4870 newfs = m->factory(args, fs);
    4871 if (!newfs) {
    4872 fuse_put_module(m);
    4873 return -1;
    4874 }
    4875 f->fs = newfs;
    4876 return 0;
    4877}
    4878
    4879struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
    4880 void *user_data)
    4881{
    4882 struct fuse_fs *fs;
    4883
    4884 if (sizeof(struct fuse_operations) < op_size) {
    4885 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not not work\n");
    4886 op_size = sizeof(struct fuse_operations);
    4887 }
    4888
    4889 fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
    4890 if (!fs) {
    4891 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse_fs object\n");
    4892 return NULL;
    4893 }
    4894
    4895 fs->user_data = user_data;
    4896 if (op)
    4897 memcpy(&fs->op, op, op_size);
    4898 return fs;
    4899}
    4900
    4901static int node_table_init(struct node_table *t)
    4902{
    4903 t->size = NODE_TABLE_MIN_SIZE;
    4904 t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
    4905 if (t->array == NULL) {
    4906 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
    4907 return -1;
    4908 }
    4909 t->use = 0;
    4910 t->split = 0;
    4911
    4912 return 0;
    4913}
    4914
    4915static void *fuse_prune_nodes(void *fuse)
    4916{
    4917 struct fuse *f = fuse;
    4918 int sleep_time;
    4919
    4920#ifdef HAVE_PTHREAD_SETNAME_NP
    4921 pthread_setname_np(pthread_self(), "fuse_prune_nodes");
    4922#endif
    4923
    4924 while(1) {
    4925 sleep_time = fuse_clean_cache(f);
    4926 sleep(sleep_time);
    4927 }
    4928 return NULL;
    4929}
    4930
    4931int fuse_start_cleanup_thread(struct fuse *f)
    4932{
    4933 if (lru_enabled(f))
    4934 return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
    4935
    4936 return 0;
    4937}
    4938
    4939void fuse_stop_cleanup_thread(struct fuse *f)
    4940{
    4941 if (lru_enabled(f)) {
    4942 pthread_mutex_lock(&f->lock);
    4943 pthread_cancel(f->prune_thread);
    4944 pthread_mutex_unlock(&f->lock);
    4945 pthread_join(f->prune_thread, NULL);
    4946 }
    4947}
    4948
    4949/*
    4950 * Not supposed to be called directly, but supposed to be called
    4951 * through the fuse_new macro
    4952 */
    4953struct fuse *_fuse_new_31(struct fuse_args *args,
    4954 const struct fuse_operations *op, size_t op_size,
    4955 struct libfuse_version *version, void *user_data)
    4956{
    4957 struct fuse *f;
    4958 struct node *root;
    4959 struct fuse_fs *fs;
    4960 struct fuse_lowlevel_ops llop = fuse_path_ops;
    4961
    4962 f = (struct fuse *) calloc(1, sizeof(struct fuse));
    4963 if (f == NULL) {
    4964 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
    4965 goto out;
    4966 }
    4967
    4968 f->conf.entry_timeout = 1.0;
    4969 f->conf.attr_timeout = 1.0;
    4970 f->conf.negative_timeout = 0.0;
    4971 f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
    4972
    4973 /* Parse options */
    4974 if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
    4975 fuse_lib_opt_proc) == -1)
    4976 goto out_free;
    4977
    4978 pthread_mutex_lock(&fuse_context_lock);
    4979 static int builtin_modules_registered = 0;
    4980 /* Have the builtin modules already been registered? */
    4981 if (builtin_modules_registered == 0) {
    4982 /* If not, register them. */
    4983 fuse_register_module("subdir", fuse_module_subdir_factory, NULL);
    4984#ifdef HAVE_ICONV
    4985 fuse_register_module("iconv", fuse_module_iconv_factory, NULL);
    4986#endif
    4987 builtin_modules_registered= 1;
    4988 }
    4989 pthread_mutex_unlock(&fuse_context_lock);
    4990
    4991 if (fuse_create_context_key() == -1)
    4992 goto out_free;
    4993
    4994 fs = fuse_fs_new(op, op_size, user_data);
    4995 if (!fs)
    4996 goto out_delete_context_key;
    4997
    4998 f->fs = fs;
    4999
    5000 /* Oh f**k, this is ugly! */
    5001 if (!fs->op.lock) {
    5002 llop.getlk = NULL;
    5003 llop.setlk = NULL;
    5004 }
    5005
    5006 f->pagesize = getpagesize();
    5007 init_list_head(&f->partial_slabs);
    5008 init_list_head(&f->full_slabs);
    5009 init_list_head(&f->lru_table);
    5010
    5011 if (f->conf.modules) {
    5012 char *module;
    5013 char *next;
    5014
    5015 for (module = f->conf.modules; module; module = next) {
    5016 char *p;
    5017 for (p = module; *p && *p != ':'; p++);
    5018 next = *p ? p + 1 : NULL;
    5019 *p = '\0';
    5020 if (module[0] &&
    5021 fuse_push_module(f, module, args) == -1)
    5022 goto out_free_fs;
    5023 }
    5024 }
    5025
    5026 if (!f->conf.ac_attr_timeout_set)
    5027 f->conf.ac_attr_timeout = f->conf.attr_timeout;
    5028
    5029#if defined(__FreeBSD__) || defined(__NetBSD__)
    5030 /*
    5031 * In FreeBSD, we always use these settings as inode numbers
    5032 * are needed to make getcwd(3) work.
    5033 */
    5034 f->conf.readdir_ino = 1;
    5035#endif
    5036
    5037 /* not declared globally, to restrict usage of this function */
    5038 struct fuse_session *fuse_session_new_versioned(
    5039 struct fuse_args *args, const struct fuse_lowlevel_ops *op,
    5040 size_t op_size, struct libfuse_version *version,
    5041 void *userdata);
    5042 f->se = fuse_session_new_versioned(args, &llop, sizeof(llop), version,
    5043 f);
    5044 if (f->se == NULL)
    5045 goto out_free_fs;
    5046
    5047 if (f->conf.debug) {
    5048 fuse_log(FUSE_LOG_DEBUG, "nullpath_ok: %i\n", f->conf.nullpath_ok);
    5049 }
    5050
    5051 /* Trace topmost layer by default */
    5052 f->fs->debug = f->conf.debug;
    5053 f->ctr = 0;
    5054 f->generation = 0;
    5055 if (node_table_init(&f->name_table) == -1)
    5056 goto out_free_session;
    5057
    5058 if (node_table_init(&f->id_table) == -1)
    5059 goto out_free_name_table;
    5060
    5061 pthread_mutex_init(&f->lock, NULL);
    5062
    5063 root = alloc_node(f);
    5064 if (root == NULL) {
    5065 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
    5066 goto out_free_id_table;
    5067 }
    5068 if (lru_enabled(f)) {
    5069 struct node_lru *lnode = node_lru(root);
    5070 init_list_head(&lnode->lru);
    5071 }
    5072
    5073 strcpy(root->inline_name, "/");
    5074 root->name = root->inline_name;
    5075 root->parent = NULL;
    5076 root->nodeid = FUSE_ROOT_ID;
    5077 inc_nlookup(root);
    5078 hash_id(f, root);
    5079
    5080 return f;
    5081
    5082out_free_id_table:
    5083 free(f->id_table.array);
    5084out_free_name_table:
    5085 free(f->name_table.array);
    5086out_free_session:
    5087 fuse_session_destroy(f->se);
    5088out_free_fs:
    5089 free(f->fs);
    5090 free(f->conf.modules);
    5091out_delete_context_key:
    5092 fuse_delete_context_key();
    5093out_free:
    5094 free(f);
    5095out:
    5096 return NULL;
    5097}
    5098
    5099/* Emulates 3.0-style fuse_new(), which processes --help */
    5100FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0")
    5101struct fuse *_fuse_new_30(struct fuse_args *args,
    5102 const struct fuse_operations *op,
    5103 size_t op_size,
    5104 struct libfuse_version *version,
    5105 void *user_data)
    5106{
    5107 struct fuse_config conf = {0};
    5108
    5109 const struct fuse_opt opts[] = {
    5110 FUSE_LIB_OPT("-h", show_help, 1),
    5111 FUSE_LIB_OPT("--help", show_help, 1),
    5113 };
    5114
    5115 if (fuse_opt_parse(args, &conf, opts,
    5116 fuse_lib_opt_proc) == -1)
    5117 return NULL;
    5118
    5119 if (conf.show_help) {
    5120 fuse_lib_help(args);
    5121 return NULL;
    5122 } else
    5123 return _fuse_new_31(args, op, op_size, version, user_data);
    5124}
    5125
    5126/* ABI compat version */
    5127struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
    5128 size_t op_size, void *user_data);
    5129FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1")
    5130struct fuse *fuse_new_31(struct fuse_args *args,
    5131 const struct fuse_operations *op,
    5132 size_t op_size, void *user_data)
    5133{
    5134 /* unknown version */
    5135 struct libfuse_version version = { 0 };
    5136
    5137 return _fuse_new_31(args, op, op_size, &version, user_data);
    5138}
    5139
    5140/*
    5141 * ABI compat version
    5142 * Emulates 3.0-style fuse_new(), which processes --help
    5143 */
    5144struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
    5145 size_t op_size, void *user_data);
    5146FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0")
    5147struct fuse *fuse_new_30(struct fuse_args *args,
    5148 const struct fuse_operations *op,
    5149 size_t op_size, void *user_data)
    5150{
    5151 struct fuse_config conf = {0};
    5152
    5153 const struct fuse_opt opts[] = {
    5154 FUSE_LIB_OPT("-h", show_help, 1),
    5155 FUSE_LIB_OPT("--help", show_help, 1),
    5157 };
    5158
    5159 if (fuse_opt_parse(args, &conf, opts,
    5160 fuse_lib_opt_proc) == -1)
    5161 return NULL;
    5162
    5163 if (conf.show_help) {
    5164 fuse_lib_help(args);
    5165 return NULL;
    5166 } else
    5167 return fuse_new_31(args, op, op_size, user_data);
    5168}
    5169
    5170
    5171void fuse_destroy(struct fuse *f)
    5172{
    5173 size_t i;
    5174
    5175 if (f->conf.intr && f->intr_installed)
    5176 fuse_restore_intr_signal(f->conf.intr_signal);
    5177
    5178 if (f->fs) {
    5179 fuse_create_context(f);
    5180
    5181 for (i = 0; i < f->id_table.size; i++) {
    5182 struct node *node;
    5183
    5184 for (node = f->id_table.array[i]; node != NULL;
    5185 node = node->id_next) {
    5186 if (node->is_hidden) {
    5187 char *path;
    5188 if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
    5189 fuse_fs_unlink(f->fs, path);
    5190 free(path);
    5191 }
    5192 }
    5193 }
    5194 }
    5195 }
    5196 for (i = 0; i < f->id_table.size; i++) {
    5197 struct node *node;
    5198 struct node *next;
    5199
    5200 for (node = f->id_table.array[i]; node != NULL; node = next) {
    5201 next = node->id_next;
    5202 free_node(f, node);
    5203 f->id_table.use--;
    5204 }
    5205 }
    5206 assert(list_empty(&f->partial_slabs));
    5207 assert(list_empty(&f->full_slabs));
    5208
    5209 while (fuse_modules) {
    5210 fuse_put_module(fuse_modules);
    5211 }
    5212 free(f->id_table.array);
    5213 free(f->name_table.array);
    5214 pthread_mutex_destroy(&f->lock);
    5215 fuse_session_destroy(f->se);
    5216 free(f->fs);
    5217 free(f->conf.modules);
    5218 free(f);
    5219 fuse_delete_context_key();
    5220}
    5221
    5222int fuse_mount(struct fuse *f, const char *mountpoint) {
    5223 return fuse_session_mount(fuse_get_session(f), mountpoint);
    5224}
    5225
    5226
    5227void fuse_unmount(struct fuse *f) {
    5229}
    5230
    5231int fuse_version(void)
    5232{
    5233 return FUSE_VERSION;
    5234}
    5235
    5236const char *fuse_pkgversion(void)
    5237{
    5238 return PACKAGE_VERSION;
    5239}
    int fuse_getgroups(int size, gid_t list[])
    Definition fuse.c:4679
    int fuse_mount(struct fuse *f, const char *mountpoint)
    Definition fuse.c:5220
    int fuse_interrupted(void)
    Definition fuse.c:4688
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5169
    int fuse_start_cleanup_thread(struct fuse *fuse)
    Definition fuse.c:4929
    int fuse_invalidate_path(struct fuse *f, const char *path)
    Definition fuse.c:4698
    struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
    Definition fuse.h:1386
    struct fuse_context * fuse_get_context(void)
    Definition fuse.c:4669
    int fuse_loop(struct fuse *f)
    Definition fuse.c:4602
    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:4664
    int fuse_clean_cache(struct fuse *fuse)
    Definition fuse.c:4458
    void fuse_lib_help(struct fuse_args *args)
    Definition fuse.c:4769
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4545
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5225
    struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
    Definition fuse.c:4879
    void fuse_stop_cleanup_thread(struct fuse *fuse)
    Definition fuse.c:4937
    fuse_fill_dir_flags
    Definition fuse.h:58
    fuse_readdir_flags
    Definition fuse.h:42
    #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:5234
    int fuse_version(void)
    Definition fuse.c:5229
    @ 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:4669
    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
    uint64_t want_ext
    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:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t parallel_direct_writes
    uint32_t noflush
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2fuse__i_8h_source.html0000644000175000017500000010753614770250311023264 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    12#include <stdbool.h>
    13
    14#define MIN(a, b) \
    15({ \
    16 typeof(a) _a = (a); \
    17 typeof(b) _b = (b); \
    18 _a < _b ? _a : _b; \
    19})
    20
    21struct mount_opts;
    22
    23struct fuse_req {
    24 struct fuse_session *se;
    25 uint64_t unique;
    26 _Atomic int ref_cnt;
    27 pthread_mutex_t lock;
    28 struct fuse_ctx ctx;
    29 struct fuse_chan *ch;
    30 int interrupted;
    31 unsigned int ioctl_64bit : 1;
    32 union {
    33 struct {
    34 uint64_t unique;
    35 } i;
    36 struct {
    38 void *data;
    39 } ni;
    40 } u;
    41 struct fuse_req *next;
    42 struct fuse_req *prev;
    43};
    44
    45struct fuse_notify_req {
    46 uint64_t unique;
    47 void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
    48 const void *, const struct fuse_buf *);
    49 struct fuse_notify_req *next;
    50 struct fuse_notify_req *prev;
    51};
    52
    53struct fuse_session {
    54 char *mountpoint;
    55 volatile int exited;
    56 int fd;
    57 struct fuse_custom_io *io;
    58 struct mount_opts *mo;
    59 int debug;
    60 int deny_others;
    61 struct fuse_lowlevel_ops op;
    62 int got_init;
    63 struct cuse_data *cuse_data;
    64 void *userdata;
    65 uid_t owner;
    66 struct fuse_conn_info conn;
    67 struct fuse_req list;
    68 struct fuse_req interrupts;
    69 pthread_mutex_t lock;
    70 int got_destroy;
    71 pthread_key_t pipe_key;
    72 int broken_splice_nonblock;
    73 uint64_t notify_ctr;
    74 struct fuse_notify_req notify_list;
    75 size_t bufsize;
    76 int error;
    77
    78 /* This is useful if any kind of ABI incompatibility is found at
    79 * a later version, to 'fix' it at run time.
    80 */
    81 struct libfuse_version version;
    82 bool buf_reallocable;
    83};
    84
    85struct fuse_chan {
    86 pthread_mutex_t lock;
    87 int ctr;
    88 int fd;
    89};
    90
    99 char *name;
    100 fuse_module_factory_t factory;
    101 struct fuse_module *next;
    102 struct fusemod_so *so;
    103 int ctr;
    104};
    105
    114#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
    115struct fuse_loop_config
    116{
    117 /* verififier that a correct struct was was passed. This is especially
    118 * needed, as versions below (3, 12) were using a public struct
    119 * (now called fuse_loop_config_v1), which was hard to extend with
    120 * additional parameters, without risking that file system implementations
    121 * would not have noticed and might either pass uninitialized members
    122 * or even too small structs.
    123 * fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
    124 * or 1. v2 or even higher version just need to set a value here
    125 * which not conflicting and very unlikely as having been set by
    126 * file system implementation.
    127 */
    128 int version_id;
    129
    134 int clone_fd;
    147
    153 unsigned int max_threads;
    154};
    155#endif
    156
    157/* ----------------------------------------------------------- *
    158 * Channel interface (when using -o clone_fd) *
    159 * ----------------------------------------------------------- */
    160
    167struct fuse_chan *fuse_chan_get(struct fuse_chan *ch);
    168
    174void fuse_chan_put(struct fuse_chan *ch);
    175
    176struct mount_opts *parse_mount_opts(struct fuse_args *args);
    177void destroy_mount_opts(struct mount_opts *mo);
    178void fuse_mount_version(void);
    179unsigned get_max_read(struct mount_opts *o);
    180void fuse_kern_unmount(const char *mountpoint, int fd);
    181int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo);
    182
    183int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
    184 int count);
    185void fuse_free_req(fuse_req_t req);
    186
    187void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
    188
    189int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
    190
    191void fuse_buf_free(struct fuse_buf *buf);
    192
    193int fuse_session_receive_buf_internal(struct fuse_session *se,
    194 struct fuse_buf *buf,
    195 struct fuse_chan *ch);
    196void fuse_session_process_buf_internal(struct fuse_session *se,
    197 const struct fuse_buf *buf,
    198 struct fuse_chan *ch);
    199
    200struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
    201 size_t op_size, void *private_data);
    202int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
    203int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
    204
    210int fuse_loop_cfg_verify(struct fuse_loop_config *config);
    211
    212
    213/*
    214 * This can be changed dynamically on recent kernels through the
    215 * /proc/sys/fs/fuse/max_pages_limit interface.
    216 *
    217 * Older kernels will always use the default value.
    218 */
    219#define FUSE_DEFAULT_MAX_PAGES_LIMIT 256
    220#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
    221
    222/* room needed in buffer to accommodate header */
    223#define FUSE_BUFFER_HEADER_SIZE 0x1000
    224
    struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
    Definition fuse.h:1403
    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:153
    int max_idle_threads
    Definition fuse_i.h:146
    fuse-3.17.2/doc/html/lib_2fuse__i_8h_source.html0000644000175000017500000012631215002273247020422 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 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
    17#define MIN(a, b) \
    18({ \
    19 typeof(a) _a = (a); \
    20 typeof(b) _b = (b); \
    21 _a < _b ? _a : _b; \
    22})
    23
    24struct mount_opts;
    25
    26struct fuse_req {
    27 struct fuse_session *se;
    28 uint64_t unique;
    29 _Atomic int ref_cnt;
    30 pthread_mutex_t lock;
    31 struct fuse_ctx ctx;
    32 struct fuse_chan *ch;
    33 int interrupted;
    34 unsigned int ioctl_64bit : 1;
    35 union {
    36 struct {
    37 uint64_t unique;
    38 } i;
    39 struct {
    41 void *data;
    42 } ni;
    43 } u;
    44 struct fuse_req *next;
    45 struct fuse_req *prev;
    46};
    47
    48struct fuse_notify_req {
    49 uint64_t unique;
    50 void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
    51 const void *, const struct fuse_buf *);
    52 struct fuse_notify_req *next;
    53 struct fuse_notify_req *prev;
    54};
    55
    56struct fuse_session {
    57 char *mountpoint;
    58 volatile int exited;
    59 int fd;
    60 struct fuse_custom_io *io;
    61 struct mount_opts *mo;
    62 int debug;
    63 int deny_others;
    64 struct fuse_lowlevel_ops op;
    65 int got_init;
    66 struct cuse_data *cuse_data;
    67 void *userdata;
    68 uid_t owner;
    69 struct fuse_conn_info conn;
    70 struct fuse_req list;
    71 struct fuse_req interrupts;
    72 pthread_mutex_t lock;
    73 int got_destroy;
    74 pthread_key_t pipe_key;
    75 int broken_splice_nonblock;
    76 uint64_t notify_ctr;
    77 struct fuse_notify_req notify_list;
    78 _Atomic size_t bufsize;
    79 int error;
    80
    81 /* This is useful if any kind of ABI incompatibility is found at
    82 * a later version, to 'fix' it at run time.
    83 */
    84 struct libfuse_version version;
    85
    86 /* true if reading requests from /dev/fuse are handled internally */
    87 bool buf_reallocable;
    88};
    89
    90struct fuse_chan {
    91 pthread_mutex_t lock;
    92 int ctr;
    93 int fd;
    94};
    95
    103struct fuse_module {
    104 char *name;
    105 fuse_module_factory_t factory;
    106 struct fuse_module *next;
    107 struct fusemod_so *so;
    108 int ctr;
    109};
    110
    119#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
    120struct fuse_loop_config
    121{
    122 /* verififier that a correct struct was was passed. This is especially
    123 * needed, as versions below (3, 12) were using a public struct
    124 * (now called fuse_loop_config_v1), which was hard to extend with
    125 * additional parameters, without risking that file system implementations
    126 * would not have noticed and might either pass uninitialized members
    127 * or even too small structs.
    128 * fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
    129 * or 1. v2 or even higher version just need to set a value here
    130 * which not conflicting and very unlikely as having been set by
    131 * file system implementation.
    132 */
    133 int version_id;
    134
    139 int clone_fd;
    152
    158 unsigned int max_threads;
    159};
    160#endif
    161
    162/* ----------------------------------------------------------- *
    163 * Channel interface (when using -o clone_fd) *
    164 * ----------------------------------------------------------- */
    165
    172struct fuse_chan *fuse_chan_get(struct fuse_chan *ch);
    173
    179void fuse_chan_put(struct fuse_chan *ch);
    180
    181struct mount_opts *parse_mount_opts(struct fuse_args *args);
    182void destroy_mount_opts(struct mount_opts *mo);
    183void fuse_mount_version(void);
    184unsigned get_max_read(struct mount_opts *o);
    185void fuse_kern_unmount(const char *mountpoint, int fd);
    186int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo);
    187
    188int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
    189 int count);
    190void fuse_free_req(fuse_req_t req);
    191
    192void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
    193
    194int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
    195
    196void fuse_buf_free(struct fuse_buf *buf);
    197
    198int fuse_session_receive_buf_internal(struct fuse_session *se,
    199 struct fuse_buf *buf,
    200 struct fuse_chan *ch);
    201void fuse_session_process_buf_internal(struct fuse_session *se,
    202 const struct fuse_buf *buf,
    203 struct fuse_chan *ch);
    204
    205struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
    206 size_t op_size, void *private_data);
    207int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
    208int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
    209
    215int fuse_loop_cfg_verify(struct fuse_loop_config *config);
    216
    217
    218/*
    219 * This can be changed dynamically on recent kernels through the
    220 * /proc/sys/fs/fuse/max_pages_limit interface.
    221 *
    222 * Older kernels will always use the default value.
    223 */
    224#define FUSE_DEFAULT_MAX_PAGES_LIMIT 256
    225#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
    226
    227/* room needed in buffer to accommodate header */
    228#define FUSE_BUFFER_HEADER_SIZE 0x1000
    229
    233static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn,
    234 uint64_t want_ext_default,
    235 uint32_t want_default)
    236{
    237 /*
    238 * Convert want to want_ext if necessary.
    239 * For the high level interface this function might be called
    240 * twice, once from the high level interface and once from the
    241 * low level interface. Both, with different want_ext_default and
    242 * want_default values. In order to suppress a failure for the
    243 * second call, we check if the lower 32 bits of want_ext are
    244 * already set to the value of want.
    245 */
    246 if (conn->want != want_default &&
    247 fuse_lower_32_bits(conn->want_ext) != conn->want) {
    248 if (conn->want_ext != want_ext_default) {
    249 fuse_log(FUSE_LOG_ERR,
    250 "fuse: both 'want' and 'want_ext' are set\n");
    251 return -EINVAL;
    252 }
    253
    254 /* high bits from want_ext, low bits from want */
    255 conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
    256 conn->want;
    257 }
    258
    259 /* ensure there won't be a second conversion */
    260 conn->want = fuse_lower_32_bits(conn->want_ext);
    261
    262 return 0;
    263}
    struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
    Definition fuse.h:1386
    void fuse_log(enum fuse_log_level level, const char *fmt,...)
    Definition fuse_log.c:77
    void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
    struct fuse_req * fuse_req_t
    uint64_t fuse_ino_t
    uint64_t want_ext
    unsigned int max_threads
    Definition fuse_i.h:156
    unsigned int max_idle_threads
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2lib_2fuse__log_8c_source.html0000644000175000017500000004715014770250311023603 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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}
    76
    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.17.2/doc/html/lib_2fuse__log_8c_source.html0000644000175000017500000004544715002273247020757 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 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}
    76
    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
    93void fuse_log_close_syslog(void)
    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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2fuse__loop_8c_source.html0000644000175000017500000002677614770250311024006 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/lib_2fuse__loop_8c_source.html0000644000175000017500000002632615002273247021142 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 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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2fuse__loop__mt_8c_source.html0000644000175000017500000024321014770250311024625 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 pthread_setname_np(pthread_self(), "fuse_worker");
    136
    137 while (!fuse_session_exited(mt->se)) {
    138 int isforget = 0;
    139 int res;
    140
    141 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    142 res = fuse_session_receive_buf_internal(mt->se, &w->fbuf,
    143 w->ch);
    144 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    145 if (res == -EINTR)
    146 continue;
    147 if (res <= 0) {
    148 if (res < 0) {
    149 fuse_session_exit(mt->se);
    150 mt->error = res;
    151 }
    152 break;
    153 }
    154
    155 pthread_mutex_lock(&mt->lock);
    156 if (mt->exit) {
    157 pthread_mutex_unlock(&mt->lock);
    158 return NULL;
    159 }
    160
    161 /*
    162 * This disgusting hack is needed so that zillions of threads
    163 * are not created on a burst of FORGET messages
    164 */
    165 if (!(w->fbuf.flags & FUSE_BUF_IS_FD)) {
    166 struct fuse_in_header *in = w->fbuf.mem;
    167
    168 if (in->opcode == FUSE_FORGET ||
    169 in->opcode == FUSE_BATCH_FORGET)
    170 isforget = 1;
    171 }
    172
    173 if (!isforget)
    174 mt->numavail--;
    175 if (mt->numavail == 0 && mt->numworker < mt->max_threads)
    176 fuse_loop_start_thread(mt);
    177 pthread_mutex_unlock(&mt->lock);
    178
    179 fuse_session_process_buf_internal(mt->se, &w->fbuf, w->ch);
    180
    181 pthread_mutex_lock(&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 (mt->exit) {
    193 pthread_mutex_unlock(&mt->lock);
    194 return NULL;
    195 }
    196 list_del_worker(w);
    197 mt->numavail--;
    198 mt->numworker--;
    199 pthread_mutex_unlock(&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(&mt->lock);
    208 }
    209
    210 sem_post(&mt->finish);
    211
    212 return NULL;
    213}
    214
    215int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
    216{
    217 sigset_t oldset;
    218 sigset_t newset;
    219 int res;
    220 pthread_attr_t attr;
    221 char *stack_size;
    222
    223 /* Override default stack size
    224 * XXX: This should ideally be a parameter option. It is rather
    225 * well hidden here.
    226 */
    227 pthread_attr_init(&attr);
    228 stack_size = getenv(ENVNAME_THREAD_STACK);
    229 if (stack_size) {
    230 long size;
    231
    232 res = libfuse_strtol(stack_size, &size);
    233 if (res)
    234 fuse_log(FUSE_LOG_ERR, "fuse: invalid stack size: %s\n",
    235 stack_size);
    236 else if (pthread_attr_setstacksize(&attr, size))
    237 fuse_log(FUSE_LOG_ERR, "fuse: could not set stack size: %ld\n",
    238 size);
    239 }
    240
    241 /* Disallow signal reception in worker threads */
    242 sigemptyset(&newset);
    243 sigaddset(&newset, SIGTERM);
    244 sigaddset(&newset, SIGINT);
    245 sigaddset(&newset, SIGHUP);
    246 sigaddset(&newset, SIGQUIT);
    247 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
    248 res = pthread_create(thread_id, &attr, func, arg);
    249 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
    250 pthread_attr_destroy(&attr);
    251 if (res != 0) {
    252 fuse_log(FUSE_LOG_ERR, "fuse: error creating thread: %s\n",
    253 strerror(res));
    254 return -1;
    255 }
    256
    257 return 0;
    258}
    259
    260static int fuse_clone_chan_fd_default(struct fuse_session *se)
    261{
    262 int res;
    263 int clonefd;
    264 uint32_t masterfd;
    265 const char *devname = "/dev/fuse";
    266
    267#ifndef O_CLOEXEC
    268#define O_CLOEXEC 0
    269#endif
    270 clonefd = open(devname, O_RDWR | O_CLOEXEC);
    271 if (clonefd == -1) {
    272 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname,
    273 strerror(errno));
    274 return -1;
    275 }
    276#ifndef O_CLOEXEC
    277 fcntl(clonefd, F_SETFD, FD_CLOEXEC);
    278#endif
    279
    280 masterfd = se->fd;
    281 res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
    282 if (res == -1) {
    283 fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
    284 strerror(errno));
    285 close(clonefd);
    286 return -1;
    287 }
    288 return clonefd;
    289}
    290
    291static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt)
    292{
    293 int clonefd;
    294 struct fuse_session *se = mt->se;
    295 struct fuse_chan *newch;
    296
    297 if (se->io != NULL) {
    298 if (se->io->clone_fd != NULL)
    299 clonefd = se->io->clone_fd(se->fd);
    300 else
    301 return NULL;
    302 } else {
    303 clonefd = fuse_clone_chan_fd_default(se);
    304 }
    305 if (clonefd < 0)
    306 return NULL;
    307
    308 newch = fuse_chan_new(clonefd);
    309 if (newch == NULL)
    310 close(clonefd);
    311
    312 return newch;
    313}
    314
    315static int fuse_loop_start_thread(struct fuse_mt *mt)
    316{
    317 int res;
    318
    319 struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
    320 if (!w) {
    321 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
    322 return -1;
    323 }
    324 memset(w, 0, sizeof(struct fuse_worker));
    325 w->fbuf.mem = NULL;
    326 w->mt = mt;
    327
    328 w->ch = NULL;
    329 if (mt->clone_fd) {
    330 w->ch = fuse_clone_chan(mt);
    331 if(!w->ch) {
    332 /* Don't attempt this again */
    333 fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
    334 "without -o clone_fd.\n");
    335 mt->clone_fd = 0;
    336 }
    337 }
    338
    339 res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
    340 if (res == -1) {
    341 fuse_chan_put(w->ch);
    342 free(w);
    343 return -1;
    344 }
    345 list_add_worker(w, &mt->main);
    346 mt->numavail ++;
    347 mt->numworker ++;
    348
    349 return 0;
    350}
    351
    352static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
    353{
    354 pthread_join(w->thread_id, NULL);
    355 pthread_mutex_lock(&mt->lock);
    356 list_del_worker(w);
    357 pthread_mutex_unlock(&mt->lock);
    358 fuse_buf_free(&w->fbuf);
    359 fuse_chan_put(w->ch);
    360 free(w);
    361}
    362
    363int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
    364FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
    365int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
    366{
    367int err;
    368 struct fuse_mt mt;
    369 struct fuse_worker *w;
    370 int created_config = 0;
    371
    372 if (config) {
    373 err = fuse_loop_cfg_verify(config);
    374 if (err)
    375 return err;
    376 } else {
    377 /* The caller does not care about parameters - use the default */
    378 config = fuse_loop_cfg_create();
    379 created_config = 1;
    380 }
    381
    382
    383 memset(&mt, 0, sizeof(struct fuse_mt));
    384 mt.se = se;
    385 mt.clone_fd = config->clone_fd;
    386 mt.error = 0;
    387 mt.numworker = 0;
    388 mt.numavail = 0;
    389 mt.max_idle = config->max_idle_threads;
    390 mt.max_threads = config->max_threads;
    391 mt.main.thread_id = pthread_self();
    392 mt.main.prev = mt.main.next = &mt.main;
    393 sem_init(&mt.finish, 0, 0);
    394 pthread_mutex_init(&mt.lock, NULL);
    395
    396 pthread_mutex_lock(&mt.lock);
    397 err = fuse_loop_start_thread(&mt);
    398 pthread_mutex_unlock(&mt.lock);
    399 if (!err) {
    400 /* sem_wait() is interruptible */
    401 while (!fuse_session_exited(se))
    402 sem_wait(&mt.finish);
    403
    404 pthread_mutex_lock(&mt.lock);
    405 for (w = mt.main.next; w != &mt.main; w = w->next)
    406 pthread_cancel(w->thread_id);
    407 mt.exit = 1;
    408 pthread_mutex_unlock(&mt.lock);
    409
    410 while (mt.main.next != &mt.main)
    411 fuse_join_worker(&mt, mt.main.next);
    412
    413 err = mt.error;
    414 }
    415
    416 pthread_mutex_destroy(&mt.lock);
    417 sem_destroy(&mt.finish);
    418 if(se->error != 0)
    419 err = se->error;
    421
    422 if (created_config) {
    423 fuse_loop_cfg_destroy(config);
    424 config = NULL;
    425 }
    426
    427 return err;
    428}
    429
    430int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
    431FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
    432int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
    433{
    434 int err;
    435 struct fuse_loop_config *config = NULL;
    436
    437 if (config_v1 != NULL) {
    438 /* convert the given v1 config */
    439 config = fuse_loop_cfg_create();
    440 if (config == NULL)
    441 return ENOMEM;
    442
    443 fuse_loop_cfg_convert(config, config_v1);
    444 }
    445
    446 err = fuse_session_loop_mt_312(se, config);
    447
    448 fuse_loop_cfg_destroy(config);
    449
    450 return err;
    451}
    452
    453
    454int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
    455FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
    456int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
    457{
    458 int err;
    459 struct fuse_loop_config *config = fuse_loop_cfg_create();
    460 if (clone_fd > 0)
    461 fuse_loop_cfg_set_clone_fd(config, clone_fd);
    462 err = fuse_session_loop_mt_312(se, config);
    463
    464 fuse_loop_cfg_destroy(config);
    465
    466 return err;
    467}
    468
    469struct fuse_loop_config *fuse_loop_cfg_create(void)
    470{
    471 struct fuse_loop_config *config = calloc(1, sizeof(*config));
    472 if (config == NULL)
    473 return NULL;
    474
    475 config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
    476 config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
    477 config->max_threads = FUSE_LOOP_MT_DEF_MAX_THREADS;
    478 config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
    479
    480 return config;
    481}
    482
    483void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
    484{
    485 free(config);
    486}
    487
    488int fuse_loop_cfg_verify(struct fuse_loop_config *config)
    489{
    490 if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
    491 return -EINVAL;
    492
    493 return 0;
    494}
    495
    496void fuse_loop_cfg_convert(struct fuse_loop_config *config,
    497 struct fuse_loop_config_v1 *v1_conf)
    498{
    499 fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
    500
    501 fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
    502}
    503
    504void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
    505 unsigned int value)
    506{
    507 if (value > FUSE_LOOP_MT_MAX_THREADS) {
    508 if (value != UINT_MAX)
    509 fuse_log(FUSE_LOG_ERR,
    510 "Ignoring invalid max threads value "
    511 "%u > max (%u).\n", value,
    512 FUSE_LOOP_MT_MAX_THREADS);
    513 return;
    514 }
    515 config->max_idle_threads = value;
    516}
    517
    518void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
    519 unsigned int value)
    520{
    521 config->max_threads = value;
    522}
    523
    524void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
    525 unsigned int value)
    526{
    527 config->clone_fd = value;
    528}
    529
    @ 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:153
    unsigned int max_idle_threads
    fuse-3.17.2/doc/html/lib_2fuse__loop__mt_8c_source.html0000644000175000017500000024351215002273247021777 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 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#ifndef O_CLOEXEC
    279 fcntl(clonefd, F_SETFD, FD_CLOEXEC);
    280#endif
    281
    282 masterfd = se->fd;
    283 res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
    284 if (res == -1) {
    285 fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
    286 strerror(errno));
    287 close(clonefd);
    288 return -1;
    289 }
    290 return clonefd;
    291}
    292
    293static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt)
    294{
    295 int clonefd;
    296 struct fuse_session *se = mt->se;
    297 struct fuse_chan *newch;
    298
    299 if (se->io != NULL) {
    300 if (se->io->clone_fd != NULL)
    301 clonefd = se->io->clone_fd(se->fd);
    302 else
    303 return NULL;
    304 } else {
    305 clonefd = fuse_clone_chan_fd_default(se);
    306 }
    307 if (clonefd < 0)
    308 return NULL;
    309
    310 newch = fuse_chan_new(clonefd);
    311 if (newch == NULL)
    312 close(clonefd);
    313
    314 return newch;
    315}
    316
    317static int fuse_loop_start_thread(struct fuse_mt *mt)
    318{
    319 int res;
    320
    321 struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
    322 if (!w) {
    323 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
    324 return -1;
    325 }
    326 memset(w, 0, sizeof(struct fuse_worker));
    327 w->fbuf.mem = NULL;
    328 w->mt = mt;
    329
    330 w->ch = NULL;
    331 if (mt->clone_fd) {
    332 w->ch = fuse_clone_chan(mt);
    333 if(!w->ch) {
    334 /* Don't attempt this again */
    335 fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
    336 "without -o clone_fd.\n");
    337 mt->clone_fd = 0;
    338 }
    339 }
    340
    341 res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
    342 if (res == -1) {
    343 fuse_chan_put(w->ch);
    344 free(w);
    345 return -1;
    346 }
    347 list_add_worker(w, &mt->main);
    348 mt->numavail ++;
    349 mt->numworker ++;
    350
    351 return 0;
    352}
    353
    354static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
    355{
    356 pthread_join(w->thread_id, NULL);
    357 pthread_mutex_lock(&mt->lock);
    358 list_del_worker(w);
    359 pthread_mutex_unlock(&mt->lock);
    360 fuse_buf_free(&w->fbuf);
    361 fuse_chan_put(w->ch);
    362 free(w);
    363}
    364
    365int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
    366FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
    367int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
    368{
    369int err;
    370 struct fuse_mt mt;
    371 struct fuse_worker *w;
    372 int created_config = 0;
    373
    374 if (config) {
    375 err = fuse_loop_cfg_verify(config);
    376 if (err)
    377 return err;
    378 } else {
    379 /* The caller does not care about parameters - use the default */
    380 config = fuse_loop_cfg_create();
    381 created_config = 1;
    382 }
    383
    384
    385 memset(&mt, 0, sizeof(struct fuse_mt));
    386 mt.se = se;
    387 mt.clone_fd = config->clone_fd;
    388 mt.error = 0;
    389 mt.numworker = 0;
    390 mt.numavail = 0;
    391 mt.max_idle = config->max_idle_threads;
    392 mt.max_threads = config->max_threads;
    393 mt.main.thread_id = pthread_self();
    394 mt.main.prev = mt.main.next = &mt.main;
    395 sem_init(&mt.finish, 0, 0);
    396 pthread_mutex_init(&mt.lock, NULL);
    397
    398 pthread_mutex_lock(&mt.lock);
    399 err = fuse_loop_start_thread(&mt);
    400 pthread_mutex_unlock(&mt.lock);
    401 if (!err) {
    402 /* sem_wait() is interruptible */
    403 while (!fuse_session_exited(se))
    404 sem_wait(&mt.finish);
    405
    406 pthread_mutex_lock(&mt.lock);
    407 for (w = mt.main.next; w != &mt.main; w = w->next)
    408 pthread_cancel(w->thread_id);
    409 mt.exit = 1;
    410 pthread_mutex_unlock(&mt.lock);
    411
    412 while (mt.main.next != &mt.main)
    413 fuse_join_worker(&mt, mt.main.next);
    414
    415 err = mt.error;
    416 }
    417
    418 pthread_mutex_destroy(&mt.lock);
    419 sem_destroy(&mt.finish);
    420 if(se->error != 0)
    421 err = se->error;
    423
    424 if (created_config) {
    425 fuse_loop_cfg_destroy(config);
    426 config = NULL;
    427 }
    428
    429 return err;
    430}
    431
    432int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
    433FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
    434int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
    435{
    436 int err;
    437 struct fuse_loop_config *config = NULL;
    438
    439 if (config_v1 != NULL) {
    440 /* convert the given v1 config */
    441 config = fuse_loop_cfg_create();
    442 if (config == NULL)
    443 return ENOMEM;
    444
    445 fuse_loop_cfg_convert(config, config_v1);
    446 }
    447
    448 err = fuse_session_loop_mt_312(se, config);
    449
    450 fuse_loop_cfg_destroy(config);
    451
    452 return err;
    453}
    454
    455
    456int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
    457FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
    458int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
    459{
    460 int err;
    461 struct fuse_loop_config *config = fuse_loop_cfg_create();
    462 if (clone_fd > 0)
    463 fuse_loop_cfg_set_clone_fd(config, clone_fd);
    464 err = fuse_session_loop_mt_312(se, config);
    465
    466 fuse_loop_cfg_destroy(config);
    467
    468 return err;
    469}
    470
    471struct fuse_loop_config *fuse_loop_cfg_create(void)
    472{
    473 struct fuse_loop_config *config = calloc(1, sizeof(*config));
    474 if (config == NULL)
    475 return NULL;
    476
    477 config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
    478 config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
    479 config->max_threads = FUSE_LOOP_MT_DEF_MAX_THREADS;
    480 config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
    481
    482 return config;
    483}
    484
    485void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
    486{
    487 free(config);
    488}
    489
    490int fuse_loop_cfg_verify(struct fuse_loop_config *config)
    491{
    492 if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
    493 return -EINVAL;
    494
    495 return 0;
    496}
    497
    498void fuse_loop_cfg_convert(struct fuse_loop_config *config,
    499 struct fuse_loop_config_v1 *v1_conf)
    500{
    501 fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
    502
    503 fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
    504}
    505
    506void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
    507 unsigned int value)
    508{
    509 if (value > FUSE_LOOP_MT_MAX_THREADS) {
    510 if (value != UINT_MAX)
    511 fuse_log(FUSE_LOG_ERR,
    512 "Ignoring invalid max threads value "
    513 "%u > max (%u).\n", value,
    514 FUSE_LOOP_MT_MAX_THREADS);
    515 return;
    516 }
    517 config->max_idle_threads = value;
    518}
    519
    520void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
    521 unsigned int value)
    522{
    523 config->max_threads = value;
    524}
    525
    526void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
    527 unsigned int value)
    528{
    529 config->clone_fd = value;
    530}
    531
    @ 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:156
    unsigned int max_idle_threads
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2lib_2fuse__lowlevel_8c_source.html0000644000175000017500000240746514770250311024666 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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#include <stdbool.h>
    13#define _GNU_SOURCE
    14
    15#include "fuse_config.h"
    16#include "fuse_i.h"
    17#include "fuse_kernel.h"
    18#include "fuse_opt.h"
    19#include "fuse_misc.h"
    20#include "mount_util.h"
    21#include "util.h"
    22
    23#include <stdio.h>
    24#include <stdlib.h>
    25#include <stddef.h>
    26#include <stdalign.h>
    27#include <string.h>
    28#include <unistd.h>
    29#include <limits.h>
    30#include <errno.h>
    31#include <assert.h>
    32#include <sys/file.h>
    33#include <sys/ioctl.h>
    34
    35#ifndef F_LINUX_SPECIFIC_BASE
    36#define F_LINUX_SPECIFIC_BASE 1024
    37#endif
    38#ifndef F_SETPIPE_SZ
    39#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
    40#endif
    41
    42
    43#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
    44#define OFFSET_MAX 0x7fffffffffffffffLL
    45
    46#define container_of(ptr, type, member) ({ \
    47 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    48 (type *)( (char *)__mptr - offsetof(type,member) );})
    49
    50struct fuse_pollhandle {
    51 uint64_t kh;
    52 struct fuse_session *se;
    53};
    54
    55static size_t pagesize;
    56
    57static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
    58{
    59 pagesize = getpagesize();
    60}
    61
    62static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
    63{
    64 attr->ino = stbuf->st_ino;
    65 attr->mode = stbuf->st_mode;
    66 attr->nlink = stbuf->st_nlink;
    67 attr->uid = stbuf->st_uid;
    68 attr->gid = stbuf->st_gid;
    69 attr->rdev = stbuf->st_rdev;
    70 attr->size = stbuf->st_size;
    71 attr->blksize = stbuf->st_blksize;
    72 attr->blocks = stbuf->st_blocks;
    73 attr->atime = stbuf->st_atime;
    74 attr->mtime = stbuf->st_mtime;
    75 attr->ctime = stbuf->st_ctime;
    76 attr->atimensec = ST_ATIM_NSEC(stbuf);
    77 attr->mtimensec = ST_MTIM_NSEC(stbuf);
    78 attr->ctimensec = ST_CTIM_NSEC(stbuf);
    79}
    80
    81static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
    82{
    83 stbuf->st_mode = attr->mode;
    84 stbuf->st_uid = attr->uid;
    85 stbuf->st_gid = attr->gid;
    86 stbuf->st_size = attr->size;
    87 stbuf->st_atime = attr->atime;
    88 stbuf->st_mtime = attr->mtime;
    89 stbuf->st_ctime = attr->ctime;
    90 ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
    91 ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
    92 ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
    93}
    94
    95static size_t iov_length(const struct iovec *iov, size_t count)
    96{
    97 size_t seg;
    98 size_t ret = 0;
    99
    100 for (seg = 0; seg < count; seg++)
    101 ret += iov[seg].iov_len;
    102 return ret;
    103}
    104
    105static void list_init_req(struct fuse_req *req)
    106{
    107 req->next = req;
    108 req->prev = req;
    109}
    110
    111static void list_del_req(struct fuse_req *req)
    112{
    113 struct fuse_req *prev = req->prev;
    114 struct fuse_req *next = req->next;
    115 prev->next = next;
    116 next->prev = prev;
    117}
    118
    119static void list_add_req(struct fuse_req *req, struct fuse_req *next)
    120{
    121 struct fuse_req *prev = next->prev;
    122 req->next = next;
    123 req->prev = prev;
    124 prev->next = req;
    125 next->prev = req;
    126}
    127
    128static void destroy_req(fuse_req_t req)
    129{
    130 assert(req->ch == NULL);
    131 pthread_mutex_destroy(&req->lock);
    132 free(req);
    133}
    134
    135void fuse_free_req(fuse_req_t req)
    136{
    137 int ctr;
    138 struct fuse_session *se = req->se;
    139
    140 if (se->conn.no_interrupt) {
    141 ctr = --req->ref_cnt;
    142 fuse_chan_put(req->ch);
    143 req->ch = NULL;
    144 } else {
    145 pthread_mutex_lock(&se->lock);
    146 req->u.ni.func = NULL;
    147 req->u.ni.data = NULL;
    148 list_del_req(req);
    149 ctr = --req->ref_cnt;
    150 fuse_chan_put(req->ch);
    151 req->ch = NULL;
    152 pthread_mutex_unlock(&se->lock);
    153 }
    154 if (!ctr)
    155 destroy_req(req);
    156}
    157
    158static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
    159{
    160 struct fuse_req *req;
    161
    162 req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
    163 if (req == NULL) {
    164 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
    165 } else {
    166 req->se = se;
    167 req->ref_cnt = 1;
    168 list_init_req(req);
    169 pthread_mutex_init(&req->lock, NULL);
    170 }
    171
    172 return req;
    173}
    174
    175/* Send data. If *ch* is NULL, send via session master fd */
    176static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
    177 struct iovec *iov, int count)
    178{
    179 struct fuse_out_header *out = iov[0].iov_base;
    180
    181 assert(se != NULL);
    182 out->len = iov_length(iov, count);
    183 if (se->debug) {
    184 if (out->unique == 0) {
    185 fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
    186 out->error, out->len);
    187 } else if (out->error) {
    188 fuse_log(FUSE_LOG_DEBUG,
    189 " unique: %llu, error: %i (%s), outsize: %i\n",
    190 (unsigned long long) out->unique, out->error,
    191 strerror(-out->error), out->len);
    192 } else {
    193 fuse_log(FUSE_LOG_DEBUG,
    194 " unique: %llu, success, outsize: %i\n",
    195 (unsigned long long) out->unique, out->len);
    196 }
    197 }
    198
    199 ssize_t res;
    200 if (se->io != NULL)
    201 /* se->io->writev is never NULL if se->io is not NULL as
    202 specified by fuse_session_custom_io()*/
    203 res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
    204 se->userdata);
    205 else
    206 res = writev(ch ? ch->fd : se->fd, iov, count);
    207
    208 int err = errno;
    209
    210 if (res == -1) {
    211 /* ENOENT means the operation was interrupted */
    212 if (!fuse_session_exited(se) && err != ENOENT)
    213 perror("fuse: writing device");
    214 return -err;
    215 }
    216
    217 return 0;
    218}
    219
    220
    221int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
    222 int count)
    223{
    224 struct fuse_out_header out;
    225
    226#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
    227 const char *str = strerrordesc_np(error * -1);
    228 if ((str == NULL && error != 0) || error > 0) {
    229#else
    230 if (error <= -1000 || error > 0) {
    231#endif
    232 fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
    233 error = -ERANGE;
    234 }
    235
    236 out.unique = req->unique;
    237 out.error = error;
    238
    239 iov[0].iov_base = &out;
    240 iov[0].iov_len = sizeof(struct fuse_out_header);
    241
    242 return fuse_send_msg(req->se, req->ch, iov, count);
    243}
    244
    245static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
    246 int count)
    247{
    248 int res;
    249
    250 res = fuse_send_reply_iov_nofree(req, error, iov, count);
    251 fuse_free_req(req);
    252 return res;
    253}
    254
    255static int send_reply(fuse_req_t req, int error, const void *arg,
    256 size_t argsize)
    257{
    258 struct iovec iov[2];
    259 int count = 1;
    260 if (argsize) {
    261 iov[1].iov_base = (void *) arg;
    262 iov[1].iov_len = argsize;
    263 count++;
    264 }
    265 return send_reply_iov(req, error, iov, count);
    266}
    267
    268int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
    269{
    270 int res;
    271 struct iovec *padded_iov;
    272
    273 padded_iov = malloc((count + 1) * sizeof(struct iovec));
    274 if (padded_iov == NULL)
    275 return fuse_reply_err(req, ENOMEM);
    276
    277 memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
    278 count++;
    279
    280 res = send_reply_iov(req, 0, padded_iov, count);
    281 free(padded_iov);
    282
    283 return res;
    284}
    285
    286
    287/* `buf` is allowed to be empty so that the proper size may be
    288 allocated by the caller */
    289size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
    290 const char *name, const struct stat *stbuf, off_t off)
    291{
    292 (void)req;
    293 size_t namelen;
    294 size_t entlen;
    295 size_t entlen_padded;
    296 struct fuse_dirent *dirent;
    297
    298 namelen = strlen(name);
    299 entlen = FUSE_NAME_OFFSET + namelen;
    300 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
    301
    302 if ((buf == NULL) || (entlen_padded > bufsize))
    303 return entlen_padded;
    304
    305 dirent = (struct fuse_dirent*) buf;
    306 dirent->ino = stbuf->st_ino;
    307 dirent->off = off;
    308 dirent->namelen = namelen;
    309 dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
    310 memcpy(dirent->name, name, namelen);
    311 memset(dirent->name + namelen, 0, entlen_padded - entlen);
    312
    313 return entlen_padded;
    314}
    315
    316static void convert_statfs(const struct statvfs *stbuf,
    317 struct fuse_kstatfs *kstatfs)
    318{
    319 kstatfs->bsize = stbuf->f_bsize;
    320 kstatfs->frsize = stbuf->f_frsize;
    321 kstatfs->blocks = stbuf->f_blocks;
    322 kstatfs->bfree = stbuf->f_bfree;
    323 kstatfs->bavail = stbuf->f_bavail;
    324 kstatfs->files = stbuf->f_files;
    325 kstatfs->ffree = stbuf->f_ffree;
    326 kstatfs->namelen = stbuf->f_namemax;
    327}
    328
    329static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
    330{
    331 return send_reply(req, 0, arg, argsize);
    332}
    333
    334int fuse_reply_err(fuse_req_t req, int err)
    335{
    336 return send_reply(req, -err, NULL, 0);
    337}
    338
    340{
    341 fuse_free_req(req);
    342}
    343
    344static unsigned long calc_timeout_sec(double t)
    345{
    346 if (t > (double) ULONG_MAX)
    347 return ULONG_MAX;
    348 else if (t < 0.0)
    349 return 0;
    350 else
    351 return (unsigned long) t;
    352}
    353
    354static unsigned int calc_timeout_nsec(double t)
    355{
    356 double f = t - (double) calc_timeout_sec(t);
    357 if (f < 0.0)
    358 return 0;
    359 else if (f >= 0.999999999)
    360 return 999999999;
    361 else
    362 return (unsigned int) (f * 1.0e9);
    363}
    364
    365static void fill_entry(struct fuse_entry_out *arg,
    366 const struct fuse_entry_param *e)
    367{
    368 arg->nodeid = e->ino;
    369 arg->generation = e->generation;
    370 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
    371 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
    372 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
    373 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
    374 convert_stat(&e->attr, &arg->attr);
    375}
    376
    377/* `buf` is allowed to be empty so that the proper size may be
    378 allocated by the caller */
    379size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
    380 const char *name,
    381 const struct fuse_entry_param *e, off_t off)
    382{
    383 (void)req;
    384 size_t namelen;
    385 size_t entlen;
    386 size_t entlen_padded;
    387
    388 namelen = strlen(name);
    389 entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
    390 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
    391 if ((buf == NULL) || (entlen_padded > bufsize))
    392 return entlen_padded;
    393
    394 struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
    395 memset(&dp->entry_out, 0, sizeof(dp->entry_out));
    396 fill_entry(&dp->entry_out, e);
    397
    398 struct fuse_dirent *dirent = &dp->dirent;
    399 dirent->ino = e->attr.st_ino;
    400 dirent->off = off;
    401 dirent->namelen = namelen;
    402 dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
    403 memcpy(dirent->name, name, namelen);
    404 memset(dirent->name + namelen, 0, entlen_padded - entlen);
    405
    406 return entlen_padded;
    407}
    408
    409static void fill_open(struct fuse_open_out *arg,
    410 const struct fuse_file_info *f)
    411{
    412 arg->fh = f->fh;
    413 if (f->backing_id > 0) {
    414 arg->backing_id = f->backing_id;
    415 arg->open_flags |= FOPEN_PASSTHROUGH;
    416 }
    417 if (f->direct_io)
    418 arg->open_flags |= FOPEN_DIRECT_IO;
    419 if (f->keep_cache)
    420 arg->open_flags |= FOPEN_KEEP_CACHE;
    421 if (f->cache_readdir)
    422 arg->open_flags |= FOPEN_CACHE_DIR;
    423 if (f->nonseekable)
    424 arg->open_flags |= FOPEN_NONSEEKABLE;
    425 if (f->noflush)
    426 arg->open_flags |= FOPEN_NOFLUSH;
    428 arg->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
    429}
    430
    432{
    433 struct fuse_entry_out arg;
    434 size_t size = req->se->conn.proto_minor < 9 ?
    435 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
    436
    437 /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
    438 negative entry */
    439 if (!e->ino && req->se->conn.proto_minor < 4)
    440 return fuse_reply_err(req, ENOENT);
    441
    442 memset(&arg, 0, sizeof(arg));
    443 fill_entry(&arg, e);
    444 return send_reply_ok(req, &arg, size);
    445}
    446
    448 const struct fuse_file_info *f)
    449{
    450 alignas(uint64_t) char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
    451 size_t entrysize = req->se->conn.proto_minor < 9 ?
    452 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
    453 struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
    454 struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
    455
    456 memset(buf, 0, sizeof(buf));
    457 fill_entry(earg, e);
    458 fill_open(oarg, f);
    459 return send_reply_ok(req, buf,
    460 entrysize + sizeof(struct fuse_open_out));
    461}
    462
    463int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
    464 double attr_timeout)
    465{
    466 struct fuse_attr_out arg;
    467 size_t size = req->se->conn.proto_minor < 9 ?
    468 FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
    469
    470 memset(&arg, 0, sizeof(arg));
    471 arg.attr_valid = calc_timeout_sec(attr_timeout);
    472 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
    473 convert_stat(attr, &arg.attr);
    474
    475 return send_reply_ok(req, &arg, size);
    476}
    477
    478int fuse_reply_readlink(fuse_req_t req, const char *linkname)
    479{
    480 return send_reply_ok(req, linkname, strlen(linkname));
    481}
    482
    484{
    485 struct fuse_backing_map map = { .fd = fd };
    486 int ret;
    487
    488 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map);
    489 if (ret <= 0) {
    490 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno));
    491 return 0;
    492 }
    493
    494 return ret;
    495}
    496
    497int fuse_passthrough_close(fuse_req_t req, int backing_id)
    498{
    499 int ret;
    500
    501 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id);
    502 if (ret < 0)
    503 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno));
    504
    505 return ret;
    506}
    507
    509{
    510 struct fuse_open_out arg;
    511
    512 memset(&arg, 0, sizeof(arg));
    513 fill_open(&arg, f);
    514 return send_reply_ok(req, &arg, sizeof(arg));
    515}
    516
    517int fuse_reply_write(fuse_req_t req, size_t count)
    518{
    519 struct fuse_write_out arg;
    520
    521 memset(&arg, 0, sizeof(arg));
    522 arg.size = count;
    523
    524 return send_reply_ok(req, &arg, sizeof(arg));
    525}
    526
    527int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
    528{
    529 return send_reply_ok(req, buf, size);
    530}
    531
    532static int fuse_send_data_iov_fallback(struct fuse_session *se,
    533 struct fuse_chan *ch,
    534 struct iovec *iov, int iov_count,
    535 struct fuse_bufvec *buf,
    536 size_t len)
    537{
    538 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
    539 void *mbuf;
    540 int res;
    541
    542 /* Optimize common case */
    543 if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
    544 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
    545 /* FIXME: also avoid memory copy if there are multiple buffers
    546 but none of them contain an fd */
    547
    548 iov[iov_count].iov_base = buf->buf[0].mem;
    549 iov[iov_count].iov_len = len;
    550 iov_count++;
    551 return fuse_send_msg(se, ch, iov, iov_count);
    552 }
    553
    554 res = posix_memalign(&mbuf, pagesize, len);
    555 if (res != 0)
    556 return res;
    557
    558 mem_buf.buf[0].mem = mbuf;
    559 res = fuse_buf_copy(&mem_buf, buf, 0);
    560 if (res < 0) {
    561 free(mbuf);
    562 return -res;
    563 }
    564 len = res;
    565
    566 iov[iov_count].iov_base = mbuf;
    567 iov[iov_count].iov_len = len;
    568 iov_count++;
    569 res = fuse_send_msg(se, ch, iov, iov_count);
    570 free(mbuf);
    571
    572 return res;
    573}
    574
    575struct fuse_ll_pipe {
    576 size_t size;
    577 int can_grow;
    578 int pipe[2];
    579};
    580
    581static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
    582{
    583 close(llp->pipe[0]);
    584 close(llp->pipe[1]);
    585 free(llp);
    586}
    587
    588#ifdef HAVE_SPLICE
    589#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC)
    590static int fuse_pipe(int fds[2])
    591{
    592 int rv = pipe(fds);
    593
    594 if (rv == -1)
    595 return rv;
    596
    597 if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
    598 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 ||
    599 fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
    600 fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
    601 close(fds[0]);
    602 close(fds[1]);
    603 rv = -1;
    604 }
    605 return rv;
    606}
    607#else
    608static int fuse_pipe(int fds[2])
    609{
    610 return pipe2(fds, O_CLOEXEC | O_NONBLOCK);
    611}
    612#endif
    613
    614static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
    615{
    616 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
    617 if (llp == NULL) {
    618 int res;
    619
    620 llp = malloc(sizeof(struct fuse_ll_pipe));
    621 if (llp == NULL)
    622 return NULL;
    623
    624 res = fuse_pipe(llp->pipe);
    625 if (res == -1) {
    626 free(llp);
    627 return NULL;
    628 }
    629
    630 /*
    631 *the default size is 16 pages on linux
    632 */
    633 llp->size = pagesize * 16;
    634 llp->can_grow = 1;
    635
    636 pthread_setspecific(se->pipe_key, llp);
    637 }
    638
    639 return llp;
    640}
    641#endif
    642
    643static void fuse_ll_clear_pipe(struct fuse_session *se)
    644{
    645 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
    646 if (llp) {
    647 pthread_setspecific(se->pipe_key, NULL);
    648 fuse_ll_pipe_free(llp);
    649 }
    650}
    651
    652#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
    653static int read_back(int fd, char *buf, size_t len)
    654{
    655 int res;
    656
    657 res = read(fd, buf, len);
    658 if (res == -1) {
    659 fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
    660 return -EIO;
    661 }
    662 if (res != len) {
    663 fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
    664 return -EIO;
    665 }
    666 return 0;
    667}
    668
    669static int grow_pipe_to_max(int pipefd)
    670{
    671 int res;
    672 long max;
    673 long maxfd;
    674 char buf[32];
    675
    676 maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY);
    677 if (maxfd < 0)
    678 return -errno;
    679
    680 res = read(maxfd, buf, sizeof(buf) - 1);
    681 if (res < 0) {
    682 int saved_errno;
    683
    684 saved_errno = errno;
    685 close(maxfd);
    686 return -saved_errno;
    687 }
    688 close(maxfd);
    689 buf[res] = '\0';
    690
    691 res = libfuse_strtol(buf, &max);
    692 if (res)
    693 return res;
    694 res = fcntl(pipefd, F_SETPIPE_SZ, max);
    695 if (res < 0)
    696 return -errno;
    697 return max;
    698}
    699
    700static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
    701 struct iovec *iov, int iov_count,
    702 struct fuse_bufvec *buf, unsigned int flags)
    703{
    704 int res;
    705 size_t len = fuse_buf_size(buf);
    706 struct fuse_out_header *out = iov[0].iov_base;
    707 struct fuse_ll_pipe *llp;
    708 int splice_flags;
    709 size_t pipesize;
    710 size_t total_buf_size;
    711 size_t idx;
    712 size_t headerlen;
    713 struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
    714
    715 if (se->broken_splice_nonblock)
    716 goto fallback;
    717
    718 if (flags & FUSE_BUF_NO_SPLICE)
    719 goto fallback;
    720
    721 total_buf_size = 0;
    722 for (idx = buf->idx; idx < buf->count; idx++) {
    723 total_buf_size += buf->buf[idx].size;
    724 if (idx == buf->idx)
    725 total_buf_size -= buf->off;
    726 }
    727 if (total_buf_size < 2 * pagesize)
    728 goto fallback;
    729
    730 if (se->conn.proto_minor < 14 ||
    731 !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE))
    732 goto fallback;
    733
    734 llp = fuse_ll_get_pipe(se);
    735 if (llp == NULL)
    736 goto fallback;
    737
    738
    739 headerlen = iov_length(iov, iov_count);
    740
    741 out->len = headerlen + len;
    742
    743 /*
    744 * Heuristic for the required pipe size, does not work if the
    745 * source contains less than page size fragments
    746 */
    747 pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
    748
    749 if (llp->size < pipesize) {
    750 if (llp->can_grow) {
    751 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
    752 if (res == -1) {
    753 res = grow_pipe_to_max(llp->pipe[0]);
    754 if (res > 0)
    755 llp->size = res;
    756 llp->can_grow = 0;
    757 goto fallback;
    758 }
    759 llp->size = res;
    760 }
    761 if (llp->size < pipesize)
    762 goto fallback;
    763 }
    764
    765
    766 res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
    767 if (res == -1)
    768 goto fallback;
    769
    770 if (res != headerlen) {
    771 res = -EIO;
    772 fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
    773 headerlen);
    774 goto clear_pipe;
    775 }
    776
    777 pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
    778 pipe_buf.buf[0].fd = llp->pipe[1];
    779
    780 res = fuse_buf_copy(&pipe_buf, buf,
    782 if (res < 0) {
    783 if (res == -EAGAIN || res == -EINVAL) {
    784 /*
    785 * Should only get EAGAIN on kernels with
    786 * broken SPLICE_F_NONBLOCK support (<=
    787 * 2.6.35) where this error or a short read is
    788 * returned even if the pipe itself is not
    789 * full
    790 *
    791 * EINVAL might mean that splice can't handle
    792 * this combination of input and output.
    793 */
    794 if (res == -EAGAIN)
    795 se->broken_splice_nonblock = 1;
    796
    797 pthread_setspecific(se->pipe_key, NULL);
    798 fuse_ll_pipe_free(llp);
    799 goto fallback;
    800 }
    801 res = -res;
    802 goto clear_pipe;
    803 }
    804
    805 if (res != 0 && res < len) {
    806 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
    807 void *mbuf;
    808 size_t now_len = res;
    809 /*
    810 * For regular files a short count is either
    811 * 1) due to EOF, or
    812 * 2) because of broken SPLICE_F_NONBLOCK (see above)
    813 *
    814 * For other inputs it's possible that we overflowed
    815 * the pipe because of small buffer fragments.
    816 */
    817
    818 res = posix_memalign(&mbuf, pagesize, len);
    819 if (res != 0)
    820 goto clear_pipe;
    821
    822 mem_buf.buf[0].mem = mbuf;
    823 mem_buf.off = now_len;
    824 res = fuse_buf_copy(&mem_buf, buf, 0);
    825 if (res > 0) {
    826 char *tmpbuf;
    827 size_t extra_len = res;
    828 /*
    829 * Trickiest case: got more data. Need to get
    830 * back the data from the pipe and then fall
    831 * back to regular write.
    832 */
    833 tmpbuf = malloc(headerlen);
    834 if (tmpbuf == NULL) {
    835 free(mbuf);
    836 res = ENOMEM;
    837 goto clear_pipe;
    838 }
    839 res = read_back(llp->pipe[0], tmpbuf, headerlen);
    840 free(tmpbuf);
    841 if (res != 0) {
    842 free(mbuf);
    843 goto clear_pipe;
    844 }
    845 res = read_back(llp->pipe[0], mbuf, now_len);
    846 if (res != 0) {
    847 free(mbuf);
    848 goto clear_pipe;
    849 }
    850 len = now_len + extra_len;
    851 iov[iov_count].iov_base = mbuf;
    852 iov[iov_count].iov_len = len;
    853 iov_count++;
    854 res = fuse_send_msg(se, ch, iov, iov_count);
    855 free(mbuf);
    856 return res;
    857 }
    858 free(mbuf);
    859 res = now_len;
    860 }
    861 len = res;
    862 out->len = headerlen + len;
    863
    864 if (se->debug) {
    865 fuse_log(FUSE_LOG_DEBUG,
    866 " unique: %llu, success, outsize: %i (splice)\n",
    867 (unsigned long long) out->unique, out->len);
    868 }
    869
    870 splice_flags = 0;
    871 if ((flags & FUSE_BUF_SPLICE_MOVE) &&
    872 (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE))
    873 splice_flags |= SPLICE_F_MOVE;
    874
    875 if (se->io != NULL && se->io->splice_send != NULL) {
    876 res = se->io->splice_send(llp->pipe[0], NULL,
    877 ch ? ch->fd : se->fd, NULL, out->len,
    878 splice_flags, se->userdata);
    879 } else {
    880 res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
    881 out->len, splice_flags);
    882 }
    883 if (res == -1) {
    884 res = -errno;
    885 perror("fuse: splice from pipe");
    886 goto clear_pipe;
    887 }
    888 if (res != out->len) {
    889 res = -EIO;
    890 fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
    891 res, out->len);
    892 goto clear_pipe;
    893 }
    894 return 0;
    895
    896clear_pipe:
    897 fuse_ll_clear_pipe(se);
    898 return res;
    899
    900fallback:
    901 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
    902}
    903#else
    904static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
    905 struct iovec *iov, int iov_count,
    906 struct fuse_bufvec *buf, unsigned int flags)
    907{
    908 size_t len = fuse_buf_size(buf);
    909 (void) flags;
    910
    911 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
    912}
    913#endif
    914
    916 enum fuse_buf_copy_flags flags)
    917{
    918 struct iovec iov[2];
    919 struct fuse_out_header out;
    920 int res;
    921
    922 iov[0].iov_base = &out;
    923 iov[0].iov_len = sizeof(struct fuse_out_header);
    924
    925 out.unique = req->unique;
    926 out.error = 0;
    927
    928 res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags);
    929 if (res <= 0) {
    930 fuse_free_req(req);
    931 return res;
    932 } else {
    933 return fuse_reply_err(req, res);
    934 }
    935}
    936
    937int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
    938{
    939 struct fuse_statfs_out arg;
    940 size_t size = req->se->conn.proto_minor < 4 ?
    941 FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
    942
    943 memset(&arg, 0, sizeof(arg));
    944 convert_statfs(stbuf, &arg.st);
    945
    946 return send_reply_ok(req, &arg, size);
    947}
    948
    949int fuse_reply_xattr(fuse_req_t req, size_t count)
    950{
    951 struct fuse_getxattr_out arg;
    952
    953 memset(&arg, 0, sizeof(arg));
    954 arg.size = count;
    955
    956 return send_reply_ok(req, &arg, sizeof(arg));
    957}
    958
    959int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
    960{
    961 struct fuse_lk_out arg;
    962
    963 memset(&arg, 0, sizeof(arg));
    964 arg.lk.type = lock->l_type;
    965 if (lock->l_type != F_UNLCK) {
    966 arg.lk.start = lock->l_start;
    967 if (lock->l_len == 0)
    968 arg.lk.end = OFFSET_MAX;
    969 else
    970 arg.lk.end = lock->l_start + lock->l_len - 1;
    971 }
    972 arg.lk.pid = lock->l_pid;
    973 return send_reply_ok(req, &arg, sizeof(arg));
    974}
    975
    976int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
    977{
    978 struct fuse_bmap_out arg;
    979
    980 memset(&arg, 0, sizeof(arg));
    981 arg.block = idx;
    982
    983 return send_reply_ok(req, &arg, sizeof(arg));
    984}
    985
    986static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
    987 size_t count)
    988{
    989 struct fuse_ioctl_iovec *fiov;
    990 size_t i;
    991
    992 fiov = malloc(sizeof(fiov[0]) * count);
    993 if (!fiov)
    994 return NULL;
    995
    996 for (i = 0; i < count; i++) {
    997 fiov[i].base = (uintptr_t) iov[i].iov_base;
    998 fiov[i].len = iov[i].iov_len;
    999 }
    1000
    1001 return fiov;
    1002}
    1003
    1005 const struct iovec *in_iov, size_t in_count,
    1006 const struct iovec *out_iov, size_t out_count)
    1007{
    1008 struct fuse_ioctl_out arg;
    1009 struct fuse_ioctl_iovec *in_fiov = NULL;
    1010 struct fuse_ioctl_iovec *out_fiov = NULL;
    1011 struct iovec iov[4];
    1012 size_t count = 1;
    1013 int res;
    1014
    1015 memset(&arg, 0, sizeof(arg));
    1016 arg.flags |= FUSE_IOCTL_RETRY;
    1017 arg.in_iovs = in_count;
    1018 arg.out_iovs = out_count;
    1019 iov[count].iov_base = &arg;
    1020 iov[count].iov_len = sizeof(arg);
    1021 count++;
    1022
    1023 if (req->se->conn.proto_minor < 16) {
    1024 if (in_count) {
    1025 iov[count].iov_base = (void *)in_iov;
    1026 iov[count].iov_len = sizeof(in_iov[0]) * in_count;
    1027 count++;
    1028 }
    1029
    1030 if (out_count) {
    1031 iov[count].iov_base = (void *)out_iov;
    1032 iov[count].iov_len = sizeof(out_iov[0]) * out_count;
    1033 count++;
    1034 }
    1035 } else {
    1036 /* Can't handle non-compat 64bit ioctls on 32bit */
    1037 if (sizeof(void *) == 4 && req->ioctl_64bit) {
    1038 res = fuse_reply_err(req, EINVAL);
    1039 goto out;
    1040 }
    1041
    1042 if (in_count) {
    1043 in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
    1044 if (!in_fiov)
    1045 goto enomem;
    1046
    1047 iov[count].iov_base = (void *)in_fiov;
    1048 iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
    1049 count++;
    1050 }
    1051 if (out_count) {
    1052 out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
    1053 if (!out_fiov)
    1054 goto enomem;
    1055
    1056 iov[count].iov_base = (void *)out_fiov;
    1057 iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
    1058 count++;
    1059 }
    1060 }
    1061
    1062 res = send_reply_iov(req, 0, iov, count);
    1063out:
    1064 free(in_fiov);
    1065 free(out_fiov);
    1066
    1067 return res;
    1068
    1069enomem:
    1070 res = fuse_reply_err(req, ENOMEM);
    1071 goto out;
    1072}
    1073
    1074int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
    1075{
    1076 struct fuse_ioctl_out arg;
    1077 struct iovec iov[3];
    1078 size_t count = 1;
    1079
    1080 memset(&arg, 0, sizeof(arg));
    1081 arg.result = result;
    1082 iov[count].iov_base = &arg;
    1083 iov[count].iov_len = sizeof(arg);
    1084 count++;
    1085
    1086 if (size) {
    1087 iov[count].iov_base = (char *) buf;
    1088 iov[count].iov_len = size;
    1089 count++;
    1090 }
    1091
    1092 return send_reply_iov(req, 0, iov, count);
    1093}
    1094
    1095int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
    1096 int count)
    1097{
    1098 struct iovec *padded_iov;
    1099 struct fuse_ioctl_out arg;
    1100 int res;
    1101
    1102 padded_iov = malloc((count + 2) * sizeof(struct iovec));
    1103 if (padded_iov == NULL)
    1104 return fuse_reply_err(req, ENOMEM);
    1105
    1106 memset(&arg, 0, sizeof(arg));
    1107 arg.result = result;
    1108 padded_iov[1].iov_base = &arg;
    1109 padded_iov[1].iov_len = sizeof(arg);
    1110
    1111 memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
    1112
    1113 res = send_reply_iov(req, 0, padded_iov, count + 2);
    1114 free(padded_iov);
    1115
    1116 return res;
    1117}
    1118
    1119int fuse_reply_poll(fuse_req_t req, unsigned revents)
    1120{
    1121 struct fuse_poll_out arg;
    1122
    1123 memset(&arg, 0, sizeof(arg));
    1124 arg.revents = revents;
    1125
    1126 return send_reply_ok(req, &arg, sizeof(arg));
    1127}
    1128
    1129int fuse_reply_lseek(fuse_req_t req, off_t off)
    1130{
    1131 struct fuse_lseek_out arg;
    1132
    1133 memset(&arg, 0, sizeof(arg));
    1134 arg.offset = off;
    1135
    1136 return send_reply_ok(req, &arg, sizeof(arg));
    1137}
    1138
    1139static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1140{
    1141 char *name = (char *) inarg;
    1142
    1143 if (req->se->op.lookup)
    1144 req->se->op.lookup(req, nodeid, name);
    1145 else
    1146 fuse_reply_err(req, ENOSYS);
    1147}
    1148
    1149static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1150{
    1151 struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
    1152
    1153 if (req->se->op.forget)
    1154 req->se->op.forget(req, nodeid, arg->nlookup);
    1155 else
    1156 fuse_reply_none(req);
    1157}
    1158
    1159static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
    1160 const void *inarg)
    1161{
    1162 struct fuse_batch_forget_in *arg = (void *) inarg;
    1163 struct fuse_forget_one *param = (void *) PARAM(arg);
    1164 unsigned int i;
    1165
    1166 (void) nodeid;
    1167
    1168 if (req->se->op.forget_multi) {
    1169 req->se->op.forget_multi(req, arg->count,
    1170 (struct fuse_forget_data *) param);
    1171 } else if (req->se->op.forget) {
    1172 for (i = 0; i < arg->count; i++) {
    1173 struct fuse_forget_one *forget = &param[i];
    1174 struct fuse_req *dummy_req;
    1175
    1176 dummy_req = fuse_ll_alloc_req(req->se);
    1177 if (dummy_req == NULL)
    1178 break;
    1179
    1180 dummy_req->unique = req->unique;
    1181 dummy_req->ctx = req->ctx;
    1182 dummy_req->ch = NULL;
    1183
    1184 req->se->op.forget(dummy_req, forget->nodeid,
    1185 forget->nlookup);
    1186 }
    1187 fuse_reply_none(req);
    1188 } else {
    1189 fuse_reply_none(req);
    1190 }
    1191}
    1192
    1193static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1194{
    1195 struct fuse_file_info *fip = NULL;
    1196 struct fuse_file_info fi;
    1197
    1198 if (req->se->conn.proto_minor >= 9) {
    1199 struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
    1200
    1201 if (arg->getattr_flags & FUSE_GETATTR_FH) {
    1202 memset(&fi, 0, sizeof(fi));
    1203 fi.fh = arg->fh;
    1204 fip = &fi;
    1205 }
    1206 }
    1207
    1208 if (req->se->op.getattr)
    1209 req->se->op.getattr(req, nodeid, fip);
    1210 else
    1211 fuse_reply_err(req, ENOSYS);
    1212}
    1213
    1214static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1215{
    1216 struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
    1217
    1218 if (req->se->op.setattr) {
    1219 struct fuse_file_info *fi = NULL;
    1220 struct fuse_file_info fi_store;
    1221 struct stat stbuf;
    1222 memset(&stbuf, 0, sizeof(stbuf));
    1223 convert_attr(arg, &stbuf);
    1224 if (arg->valid & FATTR_FH) {
    1225 arg->valid &= ~FATTR_FH;
    1226 memset(&fi_store, 0, sizeof(fi_store));
    1227 fi = &fi_store;
    1228 fi->fh = arg->fh;
    1229 }
    1230 arg->valid &=
    1231 FUSE_SET_ATTR_MODE |
    1232 FUSE_SET_ATTR_UID |
    1233 FUSE_SET_ATTR_GID |
    1234 FUSE_SET_ATTR_SIZE |
    1235 FUSE_SET_ATTR_ATIME |
    1236 FUSE_SET_ATTR_MTIME |
    1237 FUSE_SET_ATTR_KILL_SUID |
    1238 FUSE_SET_ATTR_KILL_SGID |
    1239 FUSE_SET_ATTR_ATIME_NOW |
    1240 FUSE_SET_ATTR_MTIME_NOW |
    1241 FUSE_SET_ATTR_CTIME;
    1242
    1243 req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
    1244 } else
    1245 fuse_reply_err(req, ENOSYS);
    1246}
    1247
    1248static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1249{
    1250 struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
    1251
    1252 if (req->se->op.access)
    1253 req->se->op.access(req, nodeid, arg->mask);
    1254 else
    1255 fuse_reply_err(req, ENOSYS);
    1256}
    1257
    1258static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1259{
    1260 (void) inarg;
    1261
    1262 if (req->se->op.readlink)
    1263 req->se->op.readlink(req, nodeid);
    1264 else
    1265 fuse_reply_err(req, ENOSYS);
    1266}
    1267
    1268static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1269{
    1270 struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
    1271 char *name = PARAM(arg);
    1272
    1273 if (req->se->conn.proto_minor >= 12)
    1274 req->ctx.umask = arg->umask;
    1275 else
    1276 name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
    1277
    1278 if (req->se->op.mknod)
    1279 req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
    1280 else
    1281 fuse_reply_err(req, ENOSYS);
    1282}
    1283
    1284static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1285{
    1286 struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
    1287
    1288 if (req->se->conn.proto_minor >= 12)
    1289 req->ctx.umask = arg->umask;
    1290
    1291 if (req->se->op.mkdir)
    1292 req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
    1293 else
    1294 fuse_reply_err(req, ENOSYS);
    1295}
    1296
    1297static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1298{
    1299 char *name = (char *) inarg;
    1300
    1301 if (req->se->op.unlink)
    1302 req->se->op.unlink(req, nodeid, name);
    1303 else
    1304 fuse_reply_err(req, ENOSYS);
    1305}
    1306
    1307static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1308{
    1309 char *name = (char *) inarg;
    1310
    1311 if (req->se->op.rmdir)
    1312 req->se->op.rmdir(req, nodeid, name);
    1313 else
    1314 fuse_reply_err(req, ENOSYS);
    1315}
    1316
    1317static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1318{
    1319 char *name = (char *) inarg;
    1320 char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
    1321
    1322 if (req->se->op.symlink)
    1323 req->se->op.symlink(req, linkname, nodeid, name);
    1324 else
    1325 fuse_reply_err(req, ENOSYS);
    1326}
    1327
    1328static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1329{
    1330 struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
    1331 char *oldname = PARAM(arg);
    1332 char *newname = oldname + strlen(oldname) + 1;
    1333
    1334 if (req->se->op.rename)
    1335 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
    1336 0);
    1337 else
    1338 fuse_reply_err(req, ENOSYS);
    1339}
    1340
    1341static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1342{
    1343 struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg;
    1344 char *oldname = PARAM(arg);
    1345 char *newname = oldname + strlen(oldname) + 1;
    1346
    1347 if (req->se->op.rename)
    1348 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
    1349 arg->flags);
    1350 else
    1351 fuse_reply_err(req, ENOSYS);
    1352}
    1353
    1354static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1355{
    1356 struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
    1357
    1358 if (req->se->op.link)
    1359 req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
    1360 else
    1361 fuse_reply_err(req, ENOSYS);
    1362}
    1363
    1364static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1365{
    1366 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
    1367
    1368 if (req->se->op.tmpfile) {
    1369 struct fuse_file_info fi;
    1370
    1371 memset(&fi, 0, sizeof(fi));
    1372 fi.flags = arg->flags;
    1373
    1374 if (req->se->conn.proto_minor >= 12)
    1375 req->ctx.umask = arg->umask;
    1376
    1377 req->se->op.tmpfile(req, nodeid, arg->mode, &fi);
    1378 } else
    1379 fuse_reply_err(req, ENOSYS);
    1380}
    1381
    1382static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1383{
    1384 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
    1385
    1386 if (req->se->op.create) {
    1387 struct fuse_file_info fi;
    1388 char *name = PARAM(arg);
    1389
    1390 memset(&fi, 0, sizeof(fi));
    1391 fi.flags = arg->flags;
    1392
    1393 if (req->se->conn.proto_minor >= 12)
    1394 req->ctx.umask = arg->umask;
    1395 else
    1396 name = (char *) inarg + sizeof(struct fuse_open_in);
    1397
    1398 req->se->op.create(req, nodeid, name, arg->mode, &fi);
    1399 } else
    1400 fuse_reply_err(req, ENOSYS);
    1401}
    1402
    1403static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1404{
    1405 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
    1406 struct fuse_file_info fi;
    1407
    1408 memset(&fi, 0, sizeof(fi));
    1409 fi.flags = arg->flags;
    1410
    1411 if (req->se->op.open)
    1412 req->se->op.open(req, nodeid, &fi);
    1413 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT)
    1414 fuse_reply_err(req, ENOSYS);
    1415 else
    1416 fuse_reply_open(req, &fi);
    1417}
    1418
    1419static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1420{
    1421 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1422
    1423 if (req->se->op.read) {
    1424 struct fuse_file_info fi;
    1425
    1426 memset(&fi, 0, sizeof(fi));
    1427 fi.fh = arg->fh;
    1428 if (req->se->conn.proto_minor >= 9) {
    1429 fi.lock_owner = arg->lock_owner;
    1430 fi.flags = arg->flags;
    1431 }
    1432 req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
    1433 } else
    1434 fuse_reply_err(req, ENOSYS);
    1435}
    1436
    1437static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1438{
    1439 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
    1440 struct fuse_file_info fi;
    1441 char *param;
    1442
    1443 memset(&fi, 0, sizeof(fi));
    1444 fi.fh = arg->fh;
    1445 fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
    1446
    1447 if (req->se->conn.proto_minor < 9) {
    1448 param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
    1449 } else {
    1450 fi.lock_owner = arg->lock_owner;
    1451 fi.flags = arg->flags;
    1452 param = PARAM(arg);
    1453 }
    1454
    1455 if (req->se->op.write)
    1456 req->se->op.write(req, nodeid, param, arg->size,
    1457 arg->offset, &fi);
    1458 else
    1459 fuse_reply_err(req, ENOSYS);
    1460}
    1461
    1462static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
    1463 const struct fuse_buf *ibuf)
    1464{
    1465 struct fuse_session *se = req->se;
    1466 struct fuse_bufvec bufv = {
    1467 .buf[0] = *ibuf,
    1468 .count = 1,
    1469 };
    1470 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
    1471 struct fuse_file_info fi;
    1472
    1473 memset(&fi, 0, sizeof(fi));
    1474 fi.fh = arg->fh;
    1475 fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
    1476
    1477 if (se->conn.proto_minor < 9) {
    1478 bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
    1479 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    1480 FUSE_COMPAT_WRITE_IN_SIZE;
    1481 assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
    1482 } else {
    1483 fi.lock_owner = arg->lock_owner;
    1484 fi.flags = arg->flags;
    1485 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
    1486 bufv.buf[0].mem = PARAM(arg);
    1487
    1488 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    1489 sizeof(struct fuse_write_in);
    1490 }
    1491 if (bufv.buf[0].size < arg->size) {
    1492 fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n");
    1493 fuse_reply_err(req, EIO);
    1494 goto out;
    1495 }
    1496 bufv.buf[0].size = arg->size;
    1497
    1498 se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
    1499
    1500out:
    1501 /* Need to reset the pipe if ->write_buf() didn't consume all data */
    1502 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
    1503 fuse_ll_clear_pipe(se);
    1504}
    1505
    1506static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1507{
    1508 struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
    1509 struct fuse_file_info fi;
    1510
    1511 memset(&fi, 0, sizeof(fi));
    1512 fi.fh = arg->fh;
    1513 fi.flush = 1;
    1514 if (req->se->conn.proto_minor >= 7)
    1515 fi.lock_owner = arg->lock_owner;
    1516
    1517 if (req->se->op.flush)
    1518 req->se->op.flush(req, nodeid, &fi);
    1519 else
    1520 fuse_reply_err(req, ENOSYS);
    1521}
    1522
    1523static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1524{
    1525 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
    1526 struct fuse_file_info fi;
    1527
    1528 memset(&fi, 0, sizeof(fi));
    1529 fi.flags = arg->flags;
    1530 fi.fh = arg->fh;
    1531 if (req->se->conn.proto_minor >= 8) {
    1532 fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
    1533 fi.lock_owner = arg->lock_owner;
    1534 }
    1535 if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
    1536 fi.flock_release = 1;
    1537 fi.lock_owner = arg->lock_owner;
    1538 }
    1539
    1540 if (req->se->op.release)
    1541 req->se->op.release(req, nodeid, &fi);
    1542 else
    1543 fuse_reply_err(req, 0);
    1544}
    1545
    1546static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1547{
    1548 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
    1549 struct fuse_file_info fi;
    1550 int datasync = arg->fsync_flags & 1;
    1551
    1552 memset(&fi, 0, sizeof(fi));
    1553 fi.fh = arg->fh;
    1554
    1555 if (req->se->op.fsync)
    1556 req->se->op.fsync(req, nodeid, datasync, &fi);
    1557 else
    1558 fuse_reply_err(req, ENOSYS);
    1559}
    1560
    1561static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1562{
    1563 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
    1564 struct fuse_file_info fi;
    1565
    1566 memset(&fi, 0, sizeof(fi));
    1567 fi.flags = arg->flags;
    1568
    1569 if (req->se->op.opendir)
    1570 req->se->op.opendir(req, nodeid, &fi);
    1571 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT)
    1572 fuse_reply_err(req, ENOSYS);
    1573 else
    1574 fuse_reply_open(req, &fi);
    1575}
    1576
    1577static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1578{
    1579 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1580 struct fuse_file_info fi;
    1581
    1582 memset(&fi, 0, sizeof(fi));
    1583 fi.fh = arg->fh;
    1584
    1585 if (req->se->op.readdir)
    1586 req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
    1587 else
    1588 fuse_reply_err(req, ENOSYS);
    1589}
    1590
    1591static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1592{
    1593 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1594 struct fuse_file_info fi;
    1595
    1596 memset(&fi, 0, sizeof(fi));
    1597 fi.fh = arg->fh;
    1598
    1599 if (req->se->op.readdirplus)
    1600 req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
    1601 else
    1602 fuse_reply_err(req, ENOSYS);
    1603}
    1604
    1605static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1606{
    1607 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
    1608 struct fuse_file_info fi;
    1609
    1610 memset(&fi, 0, sizeof(fi));
    1611 fi.flags = arg->flags;
    1612 fi.fh = arg->fh;
    1613
    1614 if (req->se->op.releasedir)
    1615 req->se->op.releasedir(req, nodeid, &fi);
    1616 else
    1617 fuse_reply_err(req, 0);
    1618}
    1619
    1620static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1621{
    1622 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
    1623 struct fuse_file_info fi;
    1624 int datasync = arg->fsync_flags & 1;
    1625
    1626 memset(&fi, 0, sizeof(fi));
    1627 fi.fh = arg->fh;
    1628
    1629 if (req->se->op.fsyncdir)
    1630 req->se->op.fsyncdir(req, nodeid, datasync, &fi);
    1631 else
    1632 fuse_reply_err(req, ENOSYS);
    1633}
    1634
    1635static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1636{
    1637 (void) nodeid;
    1638 (void) inarg;
    1639
    1640 if (req->se->op.statfs)
    1641 req->se->op.statfs(req, nodeid);
    1642 else {
    1643 struct statvfs buf = {
    1644 .f_namemax = 255,
    1645 .f_bsize = 512,
    1646 };
    1647 fuse_reply_statfs(req, &buf);
    1648 }
    1649}
    1650
    1651static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1652{
    1653 struct fuse_session *se = req->se;
    1654 unsigned int xattr_ext = !!(se->conn.want_ext & FUSE_CAP_SETXATTR_EXT);
    1655 struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
    1656 char *name = xattr_ext ? PARAM(arg) :
    1657 (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
    1658 char *value = name + strlen(name) + 1;
    1659
    1660 /* XXX:The API should be extended to support extra_flags/setxattr_flags */
    1661 if (req->se->op.setxattr)
    1662 req->se->op.setxattr(req, nodeid, name, value, arg->size,
    1663 arg->flags);
    1664 else
    1665 fuse_reply_err(req, ENOSYS);
    1666}
    1667
    1668static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1669{
    1670 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
    1671
    1672 if (req->se->op.getxattr)
    1673 req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size);
    1674 else
    1675 fuse_reply_err(req, ENOSYS);
    1676}
    1677
    1678static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1679{
    1680 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
    1681
    1682 if (req->se->op.listxattr)
    1683 req->se->op.listxattr(req, nodeid, arg->size);
    1684 else
    1685 fuse_reply_err(req, ENOSYS);
    1686}
    1687
    1688static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1689{
    1690 char *name = (char *) inarg;
    1691
    1692 if (req->se->op.removexattr)
    1693 req->se->op.removexattr(req, nodeid, name);
    1694 else
    1695 fuse_reply_err(req, ENOSYS);
    1696}
    1697
    1698static void convert_fuse_file_lock(struct fuse_file_lock *fl,
    1699 struct flock *flock)
    1700{
    1701 memset(flock, 0, sizeof(struct flock));
    1702 flock->l_type = fl->type;
    1703 flock->l_whence = SEEK_SET;
    1704 flock->l_start = fl->start;
    1705 if (fl->end == OFFSET_MAX)
    1706 flock->l_len = 0;
    1707 else
    1708 flock->l_len = fl->end - fl->start + 1;
    1709 flock->l_pid = fl->pid;
    1710}
    1711
    1712static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1713{
    1714 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
    1715 struct fuse_file_info fi;
    1716 struct flock flock;
    1717
    1718 memset(&fi, 0, sizeof(fi));
    1719 fi.fh = arg->fh;
    1720 fi.lock_owner = arg->owner;
    1721
    1722 convert_fuse_file_lock(&arg->lk, &flock);
    1723 if (req->se->op.getlk)
    1724 req->se->op.getlk(req, nodeid, &fi, &flock);
    1725 else
    1726 fuse_reply_err(req, ENOSYS);
    1727}
    1728
    1729static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
    1730 const void *inarg, int sleep)
    1731{
    1732 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
    1733 struct fuse_file_info fi;
    1734 struct flock flock;
    1735
    1736 memset(&fi, 0, sizeof(fi));
    1737 fi.fh = arg->fh;
    1738 fi.lock_owner = arg->owner;
    1739
    1740 if (arg->lk_flags & FUSE_LK_FLOCK) {
    1741 int op = 0;
    1742
    1743 switch (arg->lk.type) {
    1744 case F_RDLCK:
    1745 op = LOCK_SH;
    1746 break;
    1747 case F_WRLCK:
    1748 op = LOCK_EX;
    1749 break;
    1750 case F_UNLCK:
    1751 op = LOCK_UN;
    1752 break;
    1753 }
    1754 if (!sleep)
    1755 op |= LOCK_NB;
    1756
    1757 if (req->se->op.flock)
    1758 req->se->op.flock(req, nodeid, &fi, op);
    1759 else
    1760 fuse_reply_err(req, ENOSYS);
    1761 } else {
    1762 convert_fuse_file_lock(&arg->lk, &flock);
    1763 if (req->se->op.setlk)
    1764 req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
    1765 else
    1766 fuse_reply_err(req, ENOSYS);
    1767 }
    1768}
    1769
    1770static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1771{
    1772 do_setlk_common(req, nodeid, inarg, 0);
    1773}
    1774
    1775static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1776{
    1777 do_setlk_common(req, nodeid, inarg, 1);
    1778}
    1779
    1780static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
    1781{
    1782 struct fuse_req *curr;
    1783
    1784 for (curr = se->list.next; curr != &se->list; curr = curr->next) {
    1785 if (curr->unique == req->u.i.unique) {
    1787 void *data;
    1788
    1789 curr->ref_cnt++;
    1790 pthread_mutex_unlock(&se->lock);
    1791
    1792 /* Ugh, ugly locking */
    1793 pthread_mutex_lock(&curr->lock);
    1794 pthread_mutex_lock(&se->lock);
    1795 curr->interrupted = 1;
    1796 func = curr->u.ni.func;
    1797 data = curr->u.ni.data;
    1798 pthread_mutex_unlock(&se->lock);
    1799 if (func)
    1800 func(curr, data);
    1801 pthread_mutex_unlock(&curr->lock);
    1802
    1803 pthread_mutex_lock(&se->lock);
    1804 curr->ref_cnt--;
    1805 if (!curr->ref_cnt) {
    1806 destroy_req(curr);
    1807 }
    1808
    1809 return 1;
    1810 }
    1811 }
    1812 for (curr = se->interrupts.next; curr != &se->interrupts;
    1813 curr = curr->next) {
    1814 if (curr->u.i.unique == req->u.i.unique)
    1815 return 1;
    1816 }
    1817 return 0;
    1818}
    1819
    1820static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1821{
    1822 struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
    1823 struct fuse_session *se = req->se;
    1824
    1825 (void) nodeid;
    1826 if (se->debug)
    1827 fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
    1828 (unsigned long long) arg->unique);
    1829
    1830 req->u.i.unique = arg->unique;
    1831
    1832 pthread_mutex_lock(&se->lock);
    1833 if (find_interrupted(se, req)) {
    1834 fuse_chan_put(req->ch);
    1835 req->ch = NULL;
    1836 destroy_req(req);
    1837 } else
    1838 list_add_req(req, &se->interrupts);
    1839 pthread_mutex_unlock(&se->lock);
    1840}
    1841
    1842static struct fuse_req *check_interrupt(struct fuse_session *se,
    1843 struct fuse_req *req)
    1844{
    1845 struct fuse_req *curr;
    1846
    1847 for (curr = se->interrupts.next; curr != &se->interrupts;
    1848 curr = curr->next) {
    1849 if (curr->u.i.unique == req->unique) {
    1850 req->interrupted = 1;
    1851 list_del_req(curr);
    1852 fuse_chan_put(curr->ch);
    1853 curr->ch = NULL;
    1854 destroy_req(curr);
    1855 return NULL;
    1856 }
    1857 }
    1858 curr = se->interrupts.next;
    1859 if (curr != &se->interrupts) {
    1860 list_del_req(curr);
    1861 list_init_req(curr);
    1862 return curr;
    1863 } else
    1864 return NULL;
    1865}
    1866
    1867static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1868{
    1869 struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
    1870
    1871 if (req->se->op.bmap)
    1872 req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
    1873 else
    1874 fuse_reply_err(req, ENOSYS);
    1875}
    1876
    1877static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1878{
    1879 struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
    1880 unsigned int flags = arg->flags;
    1881 void *in_buf = arg->in_size ? PARAM(arg) : NULL;
    1882 struct fuse_file_info fi;
    1883
    1884 if (flags & FUSE_IOCTL_DIR &&
    1885 !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) {
    1886 fuse_reply_err(req, ENOTTY);
    1887 return;
    1888 }
    1889
    1890 memset(&fi, 0, sizeof(fi));
    1891 fi.fh = arg->fh;
    1892
    1893 if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
    1894 !(flags & FUSE_IOCTL_32BIT)) {
    1895 req->ioctl_64bit = 1;
    1896 }
    1897
    1898 if (req->se->op.ioctl)
    1899 req->se->op.ioctl(req, nodeid, arg->cmd,
    1900 (void *)(uintptr_t)arg->arg, &fi, flags,
    1901 in_buf, arg->in_size, arg->out_size);
    1902 else
    1903 fuse_reply_err(req, ENOSYS);
    1904}
    1905
    1906void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
    1907{
    1908 free(ph);
    1909}
    1910
    1911static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1912{
    1913 struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
    1914 struct fuse_file_info fi;
    1915
    1916 memset(&fi, 0, sizeof(fi));
    1917 fi.fh = arg->fh;
    1918 fi.poll_events = arg->events;
    1919
    1920 if (req->se->op.poll) {
    1921 struct fuse_pollhandle *ph = NULL;
    1922
    1923 if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
    1924 ph = malloc(sizeof(struct fuse_pollhandle));
    1925 if (ph == NULL) {
    1926 fuse_reply_err(req, ENOMEM);
    1927 return;
    1928 }
    1929 ph->kh = arg->kh;
    1930 ph->se = req->se;
    1931 }
    1932
    1933 req->se->op.poll(req, nodeid, &fi, ph);
    1934 } else {
    1935 fuse_reply_err(req, ENOSYS);
    1936 }
    1937}
    1938
    1939static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1940{
    1941 struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
    1942 struct fuse_file_info fi;
    1943
    1944 memset(&fi, 0, sizeof(fi));
    1945 fi.fh = arg->fh;
    1946
    1947 if (req->se->op.fallocate)
    1948 req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
    1949 else
    1950 fuse_reply_err(req, ENOSYS);
    1951}
    1952
    1953static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg)
    1954{
    1955 struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg;
    1956 struct fuse_file_info fi_in, fi_out;
    1957
    1958 memset(&fi_in, 0, sizeof(fi_in));
    1959 fi_in.fh = arg->fh_in;
    1960
    1961 memset(&fi_out, 0, sizeof(fi_out));
    1962 fi_out.fh = arg->fh_out;
    1963
    1964
    1965 if (req->se->op.copy_file_range)
    1966 req->se->op.copy_file_range(req, nodeid_in, arg->off_in,
    1967 &fi_in, arg->nodeid_out,
    1968 arg->off_out, &fi_out, arg->len,
    1969 arg->flags);
    1970 else
    1971 fuse_reply_err(req, ENOSYS);
    1972}
    1973
    1974static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1975{
    1976 struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg;
    1977 struct fuse_file_info fi;
    1978
    1979 memset(&fi, 0, sizeof(fi));
    1980 fi.fh = arg->fh;
    1981
    1982 if (req->se->op.lseek)
    1983 req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
    1984 else
    1985 fuse_reply_err(req, ENOSYS);
    1986}
    1987
    1988static bool want_flags_valid(uint64_t capable, uint64_t want)
    1989{
    1990 uint64_t unknown_flags = want & (~capable);
    1991 if (unknown_flags != 0) {
    1992 fuse_log(FUSE_LOG_ERR,
    1993 "fuse: unknown connection 'want' flags: 0x%08lx\n",
    1994 unknown_flags);
    1995 return false;
    1996 }
    1997 return true;
    1998}
    1999
    2003static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn,
    2004 uint64_t want_ext_default)
    2005{
    2006 /* Convert want to want_ext if necessary */
    2007 if (conn->want != 0) {
    2008 if (conn->want_ext != want_ext_default) {
    2009 fuse_log(FUSE_LOG_ERR,
    2010 "fuse: both 'want' and 'want_ext' are set\n");
    2011 return -EINVAL;
    2012 }
    2013 conn->want_ext |= conn->want;
    2014 }
    2015
    2016 return 0;
    2017}
    2018
    2019/* Prevent bogus data races (bogus since "init" is called before
    2020 * multi-threading becomes relevant */
    2021static __attribute__((no_sanitize("thread")))
    2022void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    2023{
    2024 struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
    2025 struct fuse_init_out outarg;
    2026 struct fuse_session *se = req->se;
    2027 size_t bufsize = se->bufsize;
    2028 size_t outargsize = sizeof(outarg);
    2029 uint64_t inargflags = 0;
    2030 uint64_t outargflags = 0;
    2031 bool buf_reallocable = se->buf_reallocable;
    2032 (void) nodeid;
    2033 if (se->debug) {
    2034 fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
    2035 if (arg->major == 7 && arg->minor >= 6) {
    2036 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
    2037 fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
    2038 arg->max_readahead);
    2039 }
    2040 }
    2041 se->conn.proto_major = arg->major;
    2042 se->conn.proto_minor = arg->minor;
    2043 se->conn.capable_ext = 0;
    2044 se->conn.want_ext = 0;
    2045
    2046 memset(&outarg, 0, sizeof(outarg));
    2047 outarg.major = FUSE_KERNEL_VERSION;
    2048 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
    2049
    2050 if (arg->major < 7) {
    2051 fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
    2052 arg->major, arg->minor);
    2053 fuse_reply_err(req, EPROTO);
    2054 return;
    2055 }
    2056
    2057 if (arg->major > 7) {
    2058 /* Wait for a second INIT request with a 7.X version */
    2059 send_reply_ok(req, &outarg, sizeof(outarg));
    2060 return;
    2061 }
    2062
    2063 if (arg->minor >= 6) {
    2064 if (arg->max_readahead < se->conn.max_readahead)
    2065 se->conn.max_readahead = arg->max_readahead;
    2066 inargflags = arg->flags;
    2067 if (inargflags & FUSE_INIT_EXT)
    2068 inargflags = inargflags | (uint64_t) arg->flags2 << 32;
    2069 if (inargflags & FUSE_ASYNC_READ)
    2070 se->conn.capable_ext |= FUSE_CAP_ASYNC_READ;
    2071 if (inargflags & FUSE_POSIX_LOCKS)
    2072 se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS;
    2073 if (inargflags & FUSE_ATOMIC_O_TRUNC)
    2074 se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC;
    2075 if (inargflags & FUSE_EXPORT_SUPPORT)
    2076 se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT;
    2077 if (inargflags & FUSE_DONT_MASK)
    2078 se->conn.capable_ext |= FUSE_CAP_DONT_MASK;
    2079 if (inargflags & FUSE_FLOCK_LOCKS)
    2080 se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS;
    2081 if (inargflags & FUSE_AUTO_INVAL_DATA)
    2082 se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA;
    2083 if (inargflags & FUSE_DO_READDIRPLUS)
    2084 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS;
    2085 if (inargflags & FUSE_READDIRPLUS_AUTO)
    2086 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO;
    2087 if (inargflags & FUSE_ASYNC_DIO)
    2088 se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO;
    2089 if (inargflags & FUSE_WRITEBACK_CACHE)
    2090 se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE;
    2091 if (inargflags & FUSE_NO_OPEN_SUPPORT)
    2092 se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT;
    2093 if (inargflags & FUSE_PARALLEL_DIROPS)
    2094 se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS;
    2095 if (inargflags & FUSE_POSIX_ACL)
    2096 se->conn.capable_ext |= FUSE_CAP_POSIX_ACL;
    2097 if (inargflags & FUSE_HANDLE_KILLPRIV)
    2098 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV;
    2099 if (inargflags & FUSE_HANDLE_KILLPRIV_V2)
    2100 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2;
    2101 if (inargflags & FUSE_CACHE_SYMLINKS)
    2102 se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS;
    2103 if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
    2104 se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT;
    2105 if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
    2106 se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA;
    2107 if (inargflags & FUSE_SETXATTR_EXT)
    2108 se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT;
    2109 if (!(inargflags & FUSE_MAX_PAGES)) {
    2110 size_t max_bufsize =
    2111 FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
    2112 + FUSE_BUFFER_HEADER_SIZE;
    2113 if (bufsize > max_bufsize) {
    2114 bufsize = max_bufsize;
    2115 }
    2116 buf_reallocable = false;
    2117 }
    2118 if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP)
    2119 se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
    2120 if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY))
    2121 se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY;
    2122 if (inargflags & FUSE_PASSTHROUGH)
    2123 se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH;
    2124 if (inargflags & FUSE_NO_EXPORT_SUPPORT)
    2125 se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
    2126 } else {
    2127 se->conn.max_readahead = 0;
    2128 }
    2129
    2130 if (se->conn.proto_minor >= 14) {
    2131#ifdef HAVE_SPLICE
    2132#ifdef HAVE_VMSPLICE
    2133 if ((se->io == NULL) || (se->io->splice_send != NULL)) {
    2134 se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE |
    2136 }
    2137#endif
    2138 if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
    2139 se->conn.capable_ext |= FUSE_CAP_SPLICE_READ;
    2140 }
    2141#endif
    2142 }
    2143 if (se->conn.proto_minor >= 18)
    2144 se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR;
    2145
    2146 /* Default settings for modern filesystems.
    2147 *
    2148 * Most of these capabilities were disabled by default in
    2149 * libfuse2 for backwards compatibility reasons. In libfuse3,
    2150 * we can finally enable them by default (as long as they're
    2151 * supported by the kernel).
    2152 */
    2153#define LL_SET_DEFAULT(cond, cap) \
    2154 if ((cond)) \
    2155 fuse_set_feature_flag(&se->conn, cap)
    2156
    2157 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
    2158 LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
    2159 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
    2160 LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
    2161 LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
    2162 LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
    2163 LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
    2165 LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
    2166 LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
    2167 LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
    2169
    2170 /* This could safely become default, but libfuse needs an API extension
    2171 * to support it
    2172 * LL_SET_DEFAULT(1, FUSE_CAP_SETXATTR_EXT);
    2173 */
    2174
    2175 se->conn.time_gran = 1;
    2176
    2177 se->got_init = 1;
    2178 if (se->op.init) {
    2179 uint64_t want_ext_default = se->conn.want_ext;
    2180 int rc;
    2181
    2182 // Apply the first 32 bits of capable_ext to capable
    2183 se->conn.capable =
    2184 (uint32_t)(se->conn.capable_ext & 0xFFFFFFFF);
    2185
    2186 se->op.init(se->userdata, &se->conn);
    2187
    2188 /*
    2189 * se->conn.want is 32-bit value and deprecated in favour of
    2190 * se->conn.want_ext
    2191 * Userspace might still use conn.want - we need to convert it
    2192 */
    2193 rc = convert_to_conn_want_ext(&se->conn, want_ext_default);
    2194 if (rc != 0) {
    2195 fuse_reply_err(req, EPROTO);
    2196 se->error = -EPROTO;
    2198 return;
    2199 }
    2200 }
    2201
    2202 if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
    2203 fuse_reply_err(req, EPROTO);
    2204 se->error = -EPROTO;
    2206 return;
    2207 }
    2208
    2209 unsigned max_read_mo = get_max_read(se->mo);
    2210 if (se->conn.max_read != max_read_mo) {
    2211 fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() "
    2212 "requested different maximum read size (%u vs %u)\n",
    2213 se->conn.max_read, max_read_mo);
    2214 fuse_reply_err(req, EPROTO);
    2215 se->error = -EPROTO;
    2217 return;
    2218 }
    2219
    2220 if (bufsize < FUSE_MIN_READ_BUFFER) {
    2221 fuse_log(FUSE_LOG_ERR,
    2222 "fuse: warning: buffer size too small: %zu\n",
    2223 bufsize);
    2224 bufsize = FUSE_MIN_READ_BUFFER;
    2225 }
    2226
    2227 if (buf_reallocable)
    2228 bufsize = UINT_MAX;
    2229 se->conn.max_write = MIN(se->conn.max_write, bufsize - FUSE_BUFFER_HEADER_SIZE);
    2230 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
    2231
    2232 if (arg->flags & FUSE_MAX_PAGES) {
    2233 outarg.flags |= FUSE_MAX_PAGES;
    2234 outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
    2235 }
    2236 outargflags = outarg.flags;
    2237 /* Always enable big writes, this is superseded
    2238 by the max_write option */
    2239 outargflags |= FUSE_BIG_WRITES;
    2240
    2241 if (se->conn.want_ext & FUSE_CAP_ASYNC_READ)
    2242 outargflags |= FUSE_ASYNC_READ;
    2243 if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS)
    2244 outargflags |= FUSE_POSIX_LOCKS;
    2245 if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC)
    2246 outargflags |= FUSE_ATOMIC_O_TRUNC;
    2247 if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT)
    2248 outargflags |= FUSE_EXPORT_SUPPORT;
    2249 if (se->conn.want_ext & FUSE_CAP_DONT_MASK)
    2250 outargflags |= FUSE_DONT_MASK;
    2251 if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS)
    2252 outargflags |= FUSE_FLOCK_LOCKS;
    2253 if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA)
    2254 outargflags |= FUSE_AUTO_INVAL_DATA;
    2255 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS)
    2256 outargflags |= FUSE_DO_READDIRPLUS;
    2257 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO)
    2258 outargflags |= FUSE_READDIRPLUS_AUTO;
    2259 if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO)
    2260 outargflags |= FUSE_ASYNC_DIO;
    2261 if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE)
    2262 outargflags |= FUSE_WRITEBACK_CACHE;
    2263 if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS)
    2264 outargflags |= FUSE_PARALLEL_DIROPS;
    2265 if (se->conn.want_ext & FUSE_CAP_POSIX_ACL)
    2266 outargflags |= FUSE_POSIX_ACL;
    2267 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV)
    2268 outargflags |= FUSE_HANDLE_KILLPRIV;
    2269 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2)
    2270 outargflags |= FUSE_HANDLE_KILLPRIV_V2;
    2271 if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS)
    2272 outargflags |= FUSE_CACHE_SYMLINKS;
    2273 if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA)
    2274 outargflags |= FUSE_EXPLICIT_INVAL_DATA;
    2275 if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT)
    2276 outargflags |= FUSE_SETXATTR_EXT;
    2277 if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
    2278 outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
    2279 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) {
    2280 outargflags |= FUSE_PASSTHROUGH;
    2281 /*
    2282 * outarg.max_stack_depth includes the fuse stack layer,
    2283 * so it is one more than max_backing_stack_depth.
    2284 */
    2285 outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1;
    2286 }
    2287 if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT)
    2288 outargflags |= FUSE_NO_EXPORT_SUPPORT;
    2289
    2290 if (inargflags & FUSE_INIT_EXT) {
    2291 outargflags |= FUSE_INIT_EXT;
    2292 outarg.flags2 = outargflags >> 32;
    2293 }
    2294
    2295 outarg.flags = outargflags;
    2296
    2297 outarg.max_readahead = se->conn.max_readahead;
    2298 outarg.max_write = se->conn.max_write;
    2299 if (se->conn.proto_minor >= 13) {
    2300 if (se->conn.max_background >= (1 << 16))
    2301 se->conn.max_background = (1 << 16) - 1;
    2302 if (se->conn.congestion_threshold > se->conn.max_background)
    2303 se->conn.congestion_threshold = se->conn.max_background;
    2304 if (!se->conn.congestion_threshold) {
    2305 se->conn.congestion_threshold =
    2306 se->conn.max_background * 3 / 4;
    2307 }
    2308
    2309 outarg.max_background = se->conn.max_background;
    2310 outarg.congestion_threshold = se->conn.congestion_threshold;
    2311 }
    2312 if (se->conn.proto_minor >= 23)
    2313 outarg.time_gran = se->conn.time_gran;
    2314
    2315 if (se->debug) {
    2316 fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
    2317 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
    2318 fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
    2319 outarg.max_readahead);
    2320 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
    2321 fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
    2322 outarg.max_background);
    2323 fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
    2324 outarg.congestion_threshold);
    2325 fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
    2326 outarg.time_gran);
    2327 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH)
    2328 fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n",
    2329 outarg.max_stack_depth);
    2330 }
    2331 if (arg->minor < 5)
    2332 outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
    2333 else if (arg->minor < 23)
    2334 outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
    2335
    2336 send_reply_ok(req, &outarg, outargsize);
    2337}
    2338
    2339static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    2340{
    2341 struct fuse_session *se = req->se;
    2342
    2343 (void) nodeid;
    2344 (void) inarg;
    2345
    2346 se->got_destroy = 1;
    2347 se->got_init = 0;
    2348 if (se->op.destroy)
    2349 se->op.destroy(se->userdata);
    2350
    2351 send_reply_ok(req, NULL, 0);
    2352}
    2353
    2354static void list_del_nreq(struct fuse_notify_req *nreq)
    2355{
    2356 struct fuse_notify_req *prev = nreq->prev;
    2357 struct fuse_notify_req *next = nreq->next;
    2358 prev->next = next;
    2359 next->prev = prev;
    2360}
    2361
    2362static void list_add_nreq(struct fuse_notify_req *nreq,
    2363 struct fuse_notify_req *next)
    2364{
    2365 struct fuse_notify_req *prev = next->prev;
    2366 nreq->next = next;
    2367 nreq->prev = prev;
    2368 prev->next = nreq;
    2369 next->prev = nreq;
    2370}
    2371
    2372static void list_init_nreq(struct fuse_notify_req *nreq)
    2373{
    2374 nreq->next = nreq;
    2375 nreq->prev = nreq;
    2376}
    2377
    2378static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
    2379 const void *inarg, const struct fuse_buf *buf)
    2380{
    2381 struct fuse_session *se = req->se;
    2382 struct fuse_notify_req *nreq;
    2383 struct fuse_notify_req *head;
    2384
    2385 pthread_mutex_lock(&se->lock);
    2386 head = &se->notify_list;
    2387 for (nreq = head->next; nreq != head; nreq = nreq->next) {
    2388 if (nreq->unique == req->unique) {
    2389 list_del_nreq(nreq);
    2390 break;
    2391 }
    2392 }
    2393 pthread_mutex_unlock(&se->lock);
    2394
    2395 if (nreq != head)
    2396 nreq->reply(nreq, req, nodeid, inarg, buf);
    2397}
    2398
    2399static int send_notify_iov(struct fuse_session *se, int notify_code,
    2400 struct iovec *iov, int count)
    2401{
    2402 struct fuse_out_header out;
    2403
    2404 if (!se->got_init)
    2405 return -ENOTCONN;
    2406
    2407 out.unique = 0;
    2408 out.error = notify_code;
    2409 iov[0].iov_base = &out;
    2410 iov[0].iov_len = sizeof(struct fuse_out_header);
    2411
    2412 return fuse_send_msg(se, NULL, iov, count);
    2413}
    2414
    2415int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
    2416{
    2417 if (ph != NULL) {
    2418 struct fuse_notify_poll_wakeup_out outarg;
    2419 struct iovec iov[2];
    2420
    2421 outarg.kh = ph->kh;
    2422
    2423 iov[1].iov_base = &outarg;
    2424 iov[1].iov_len = sizeof(outarg);
    2425
    2426 return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
    2427 } else {
    2428 return 0;
    2429 }
    2430}
    2431
    2432int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
    2433 off_t off, off_t len)
    2434{
    2435 struct fuse_notify_inval_inode_out outarg;
    2436 struct iovec iov[2];
    2437
    2438 if (!se)
    2439 return -EINVAL;
    2440
    2441 if (se->conn.proto_minor < 12)
    2442 return -ENOSYS;
    2443
    2444 outarg.ino = ino;
    2445 outarg.off = off;
    2446 outarg.len = len;
    2447
    2448 iov[1].iov_base = &outarg;
    2449 iov[1].iov_len = sizeof(outarg);
    2450
    2451 return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
    2452}
    2453
    2473static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
    2474 const char *name, size_t namelen,
    2475 enum fuse_notify_entry_flags flags)
    2476{
    2477 struct fuse_notify_inval_entry_out outarg;
    2478 struct iovec iov[3];
    2479
    2480 if (!se)
    2481 return -EINVAL;
    2482
    2483 if (se->conn.proto_minor < 12)
    2484 return -ENOSYS;
    2485
    2486 outarg.parent = parent;
    2487 outarg.namelen = namelen;
    2488 outarg.flags = 0;
    2489 if (flags & FUSE_LL_EXPIRE_ONLY)
    2490 outarg.flags |= FUSE_EXPIRE_ONLY;
    2491
    2492 iov[1].iov_base = &outarg;
    2493 iov[1].iov_len = sizeof(outarg);
    2494 iov[2].iov_base = (void *)name;
    2495 iov[2].iov_len = namelen + 1;
    2496
    2497 return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
    2498}
    2499
    2500int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
    2501 const char *name, size_t namelen)
    2502{
    2503 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
    2504}
    2505
    2506int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
    2507 const char *name, size_t namelen)
    2508{
    2509 if (!se)
    2510 return -EINVAL;
    2511
    2512 if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY))
    2513 return -ENOSYS;
    2514
    2515 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
    2516}
    2517
    2518
    2519int fuse_lowlevel_notify_delete(struct fuse_session *se,
    2520 fuse_ino_t parent, fuse_ino_t child,
    2521 const char *name, size_t namelen)
    2522{
    2523 struct fuse_notify_delete_out outarg;
    2524 struct iovec iov[3];
    2525
    2526 if (!se)
    2527 return -EINVAL;
    2528
    2529 if (se->conn.proto_minor < 18)
    2530 return -ENOSYS;
    2531
    2532 outarg.parent = parent;
    2533 outarg.child = child;
    2534 outarg.namelen = namelen;
    2535 outarg.padding = 0;
    2536
    2537 iov[1].iov_base = &outarg;
    2538 iov[1].iov_len = sizeof(outarg);
    2539 iov[2].iov_base = (void *)name;
    2540 iov[2].iov_len = namelen + 1;
    2541
    2542 return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
    2543}
    2544
    2545int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
    2546 off_t offset, struct fuse_bufvec *bufv,
    2547 enum fuse_buf_copy_flags flags)
    2548{
    2549 struct fuse_out_header out;
    2550 struct fuse_notify_store_out outarg;
    2551 struct iovec iov[3];
    2552 size_t size = fuse_buf_size(bufv);
    2553 int res;
    2554
    2555 if (!se)
    2556 return -EINVAL;
    2557
    2558 if (se->conn.proto_minor < 15)
    2559 return -ENOSYS;
    2560
    2561 out.unique = 0;
    2562 out.error = FUSE_NOTIFY_STORE;
    2563
    2564 outarg.nodeid = ino;
    2565 outarg.offset = offset;
    2566 outarg.size = size;
    2567 outarg.padding = 0;
    2568
    2569 iov[0].iov_base = &out;
    2570 iov[0].iov_len = sizeof(out);
    2571 iov[1].iov_base = &outarg;
    2572 iov[1].iov_len = sizeof(outarg);
    2573
    2574 res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags);
    2575 if (res > 0)
    2576 res = -res;
    2577
    2578 return res;
    2579}
    2580
    2581struct fuse_retrieve_req {
    2582 struct fuse_notify_req nreq;
    2583 void *cookie;
    2584};
    2585
    2586static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
    2587 fuse_req_t req, fuse_ino_t ino,
    2588 const void *inarg,
    2589 const struct fuse_buf *ibuf)
    2590{
    2591 struct fuse_session *se = req->se;
    2592 struct fuse_retrieve_req *rreq =
    2593 container_of(nreq, struct fuse_retrieve_req, nreq);
    2594 const struct fuse_notify_retrieve_in *arg = inarg;
    2595 struct fuse_bufvec bufv = {
    2596 .buf[0] = *ibuf,
    2597 .count = 1,
    2598 };
    2599
    2600 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
    2601 bufv.buf[0].mem = PARAM(arg);
    2602
    2603 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    2604 sizeof(struct fuse_notify_retrieve_in);
    2605
    2606 if (bufv.buf[0].size < arg->size) {
    2607 fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
    2608 fuse_reply_none(req);
    2609 goto out;
    2610 }
    2611 bufv.buf[0].size = arg->size;
    2612
    2613 if (se->op.retrieve_reply) {
    2614 se->op.retrieve_reply(req, rreq->cookie, ino,
    2615 arg->offset, &bufv);
    2616 } else {
    2617 fuse_reply_none(req);
    2618 }
    2619out:
    2620 free(rreq);
    2621 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
    2622 fuse_ll_clear_pipe(se);
    2623}
    2624
    2625int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
    2626 size_t size, off_t offset, void *cookie)
    2627{
    2628 struct fuse_notify_retrieve_out outarg;
    2629 struct iovec iov[2];
    2630 struct fuse_retrieve_req *rreq;
    2631 int err;
    2632
    2633 if (!se)
    2634 return -EINVAL;
    2635
    2636 if (se->conn.proto_minor < 15)
    2637 return -ENOSYS;
    2638
    2639 rreq = malloc(sizeof(*rreq));
    2640 if (rreq == NULL)
    2641 return -ENOMEM;
    2642
    2643 pthread_mutex_lock(&se->lock);
    2644 rreq->cookie = cookie;
    2645 rreq->nreq.unique = se->notify_ctr++;
    2646 rreq->nreq.reply = fuse_ll_retrieve_reply;
    2647 list_add_nreq(&rreq->nreq, &se->notify_list);
    2648 pthread_mutex_unlock(&se->lock);
    2649
    2650 outarg.notify_unique = rreq->nreq.unique;
    2651 outarg.nodeid = ino;
    2652 outarg.offset = offset;
    2653 outarg.size = size;
    2654 outarg.padding = 0;
    2655
    2656 iov[1].iov_base = &outarg;
    2657 iov[1].iov_len = sizeof(outarg);
    2658
    2659 err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
    2660 if (err) {
    2661 pthread_mutex_lock(&se->lock);
    2662 list_del_nreq(&rreq->nreq);
    2663 pthread_mutex_unlock(&se->lock);
    2664 free(rreq);
    2665 }
    2666
    2667 return err;
    2668}
    2669
    2671{
    2672 return req->se->userdata;
    2673}
    2674
    2676{
    2677 return &req->ctx;
    2678}
    2679
    2681 void *data)
    2682{
    2683 pthread_mutex_lock(&req->lock);
    2684 pthread_mutex_lock(&req->se->lock);
    2685 req->u.ni.func = func;
    2686 req->u.ni.data = data;
    2687 pthread_mutex_unlock(&req->se->lock);
    2688 if (req->interrupted && func)
    2689 func(req, data);
    2690 pthread_mutex_unlock(&req->lock);
    2691}
    2692
    2694{
    2695 int interrupted;
    2696
    2697 pthread_mutex_lock(&req->se->lock);
    2698 interrupted = req->interrupted;
    2699 pthread_mutex_unlock(&req->se->lock);
    2700
    2701 return interrupted;
    2702}
    2703
    2704static struct {
    2705 void (*func)(fuse_req_t, fuse_ino_t, const void *);
    2706 const char *name;
    2707} fuse_ll_ops[] = {
    2708 [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
    2709 [FUSE_FORGET] = { do_forget, "FORGET" },
    2710 [FUSE_GETATTR] = { do_getattr, "GETATTR" },
    2711 [FUSE_SETATTR] = { do_setattr, "SETATTR" },
    2712 [FUSE_READLINK] = { do_readlink, "READLINK" },
    2713 [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
    2714 [FUSE_MKNOD] = { do_mknod, "MKNOD" },
    2715 [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
    2716 [FUSE_UNLINK] = { do_unlink, "UNLINK" },
    2717 [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
    2718 [FUSE_RENAME] = { do_rename, "RENAME" },
    2719 [FUSE_LINK] = { do_link, "LINK" },
    2720 [FUSE_OPEN] = { do_open, "OPEN" },
    2721 [FUSE_READ] = { do_read, "READ" },
    2722 [FUSE_WRITE] = { do_write, "WRITE" },
    2723 [FUSE_STATFS] = { do_statfs, "STATFS" },
    2724 [FUSE_RELEASE] = { do_release, "RELEASE" },
    2725 [FUSE_FSYNC] = { do_fsync, "FSYNC" },
    2726 [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
    2727 [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
    2728 [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
    2729 [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
    2730 [FUSE_FLUSH] = { do_flush, "FLUSH" },
    2731 [FUSE_INIT] = { do_init, "INIT" },
    2732 [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
    2733 [FUSE_READDIR] = { do_readdir, "READDIR" },
    2734 [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
    2735 [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
    2736 [FUSE_GETLK] = { do_getlk, "GETLK" },
    2737 [FUSE_SETLK] = { do_setlk, "SETLK" },
    2738 [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
    2739 [FUSE_ACCESS] = { do_access, "ACCESS" },
    2740 [FUSE_CREATE] = { do_create, "CREATE" },
    2741 [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" },
    2742 [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
    2743 [FUSE_BMAP] = { do_bmap, "BMAP" },
    2744 [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
    2745 [FUSE_POLL] = { do_poll, "POLL" },
    2746 [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
    2747 [FUSE_DESTROY] = { do_destroy, "DESTROY" },
    2748 [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
    2749 [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
    2750 [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
    2751 [FUSE_RENAME2] = { do_rename2, "RENAME2" },
    2752 [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
    2753 [FUSE_LSEEK] = { do_lseek, "LSEEK" },
    2754 [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
    2755};
    2756
    2757/*
    2758 * For ABI compatibility we cannot allow higher values than CUSE_INIT.
    2759 * Without ABI compatibility we could use the size of the array.
    2760 * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
    2761 */
    2762#define FUSE_MAXOP (CUSE_INIT + 1)
    2763
    2764static const char *opname(enum fuse_opcode opcode)
    2765{
    2766 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
    2767 return "???";
    2768 else
    2769 return fuse_ll_ops[opcode].name;
    2770}
    2771
    2772static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
    2773 struct fuse_bufvec *src)
    2774{
    2775 ssize_t res = fuse_buf_copy(dst, src, 0);
    2776 if (res < 0) {
    2777 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res));
    2778 return res;
    2779 }
    2780 if ((size_t)res < fuse_buf_size(dst)) {
    2781 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
    2782 return -1;
    2783 }
    2784 return 0;
    2785}
    2786
    2787void fuse_session_process_buf(struct fuse_session *se,
    2788 const struct fuse_buf *buf)
    2789{
    2790 fuse_session_process_buf_internal(se, buf, NULL);
    2791}
    2792
    2793/* libfuse internal handler */
    2794void fuse_session_process_buf_internal(struct fuse_session *se,
    2795 const struct fuse_buf *buf, struct fuse_chan *ch)
    2796{
    2797 const size_t write_header_size = sizeof(struct fuse_in_header) +
    2798 sizeof(struct fuse_write_in);
    2799 struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
    2800 struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
    2801 struct fuse_in_header *in;
    2802 const void *inarg;
    2803 struct fuse_req *req;
    2804 void *mbuf = NULL;
    2805 int err;
    2806 int res;
    2807
    2808 if (buf->flags & FUSE_BUF_IS_FD) {
    2809 if (buf->size < tmpbuf.buf[0].size)
    2810 tmpbuf.buf[0].size = buf->size;
    2811
    2812 mbuf = malloc(tmpbuf.buf[0].size);
    2813 if (mbuf == NULL) {
    2814 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n");
    2815 goto clear_pipe;
    2816 }
    2817 tmpbuf.buf[0].mem = mbuf;
    2818
    2819 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
    2820 if (res < 0)
    2821 goto clear_pipe;
    2822
    2823 in = mbuf;
    2824 } else {
    2825 in = buf->mem;
    2826 }
    2827
    2828 if (se->debug) {
    2829 fuse_log(FUSE_LOG_DEBUG,
    2830 "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
    2831 (unsigned long long) in->unique,
    2832 opname((enum fuse_opcode) in->opcode), in->opcode,
    2833 (unsigned long long) in->nodeid, buf->size, in->pid);
    2834 }
    2835
    2836 req = fuse_ll_alloc_req(se);
    2837 if (req == NULL) {
    2838 struct fuse_out_header out = {
    2839 .unique = in->unique,
    2840 .error = -ENOMEM,
    2841 };
    2842 struct iovec iov = {
    2843 .iov_base = &out,
    2844 .iov_len = sizeof(struct fuse_out_header),
    2845 };
    2846
    2847 fuse_send_msg(se, ch, &iov, 1);
    2848 goto clear_pipe;
    2849 }
    2850
    2851 req->unique = in->unique;
    2852 req->ctx.uid = in->uid;
    2853 req->ctx.gid = in->gid;
    2854 req->ctx.pid = in->pid;
    2855 req->ch = ch ? fuse_chan_get(ch) : NULL;
    2856
    2857 err = EIO;
    2858 if (!se->got_init) {
    2859 enum fuse_opcode expected;
    2860
    2861 expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
    2862 if (in->opcode != expected)
    2863 goto reply_err;
    2864 } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
    2865 goto reply_err;
    2866
    2867 err = EACCES;
    2868 /* Implement -o allow_root */
    2869 if (se->deny_others && in->uid != se->owner && in->uid != 0 &&
    2870 in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
    2871 in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
    2872 in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
    2873 in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
    2874 in->opcode != FUSE_NOTIFY_REPLY &&
    2875 in->opcode != FUSE_READDIRPLUS)
    2876 goto reply_err;
    2877
    2878 err = ENOSYS;
    2879 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
    2880 goto reply_err;
    2881 /* Do not process interrupt request */
    2882 if (se->conn.no_interrupt && in->opcode == FUSE_INTERRUPT) {
    2883 if (se->debug)
    2884 fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n");
    2885 goto reply_err;
    2886 }
    2887 if (!se->conn.no_interrupt && in->opcode != FUSE_INTERRUPT) {
    2888 struct fuse_req *intr;
    2889 pthread_mutex_lock(&se->lock);
    2890 intr = check_interrupt(se, req);
    2891 list_add_req(req, &se->list);
    2892 pthread_mutex_unlock(&se->lock);
    2893 if (intr)
    2894 fuse_reply_err(intr, EAGAIN);
    2895 }
    2896
    2897 if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
    2898 (in->opcode != FUSE_WRITE || !se->op.write_buf) &&
    2899 in->opcode != FUSE_NOTIFY_REPLY) {
    2900 void *newmbuf;
    2901
    2902 err = ENOMEM;
    2903 newmbuf = realloc(mbuf, buf->size);
    2904 if (newmbuf == NULL)
    2905 goto reply_err;
    2906 mbuf = newmbuf;
    2907
    2908 tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
    2909 tmpbuf.buf[0].mem = (char *)mbuf + write_header_size;
    2910
    2911 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
    2912 err = -res;
    2913 if (res < 0)
    2914 goto reply_err;
    2915
    2916 in = mbuf;
    2917 }
    2918
    2919 inarg = (void *) &in[1];
    2920 if (in->opcode == FUSE_WRITE && se->op.write_buf)
    2921 do_write_buf(req, in->nodeid, inarg, buf);
    2922 else if (in->opcode == FUSE_NOTIFY_REPLY)
    2923 do_notify_reply(req, in->nodeid, inarg, buf);
    2924 else
    2925 fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
    2926
    2927out_free:
    2928 free(mbuf);
    2929 return;
    2930
    2931reply_err:
    2932 fuse_reply_err(req, err);
    2933clear_pipe:
    2934 if (buf->flags & FUSE_BUF_IS_FD)
    2935 fuse_ll_clear_pipe(se);
    2936 goto out_free;
    2937}
    2938
    2939#define LL_OPTION(n,o,v) \
    2940 { n, offsetof(struct fuse_session, o), v }
    2941
    2942static const struct fuse_opt fuse_ll_opts[] = {
    2943 LL_OPTION("debug", debug, 1),
    2944 LL_OPTION("-d", debug, 1),
    2945 LL_OPTION("--debug", debug, 1),
    2946 LL_OPTION("allow_root", deny_others, 1),
    2948};
    2949
    2951{
    2952 printf("using FUSE kernel interface version %i.%i\n",
    2953 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
    2954 fuse_mount_version();
    2955}
    2956
    2958{
    2959 /* These are not all options, but the ones that are
    2960 potentially of interest to an end-user */
    2961 printf(
    2962" -o allow_other allow access by all users\n"
    2963" -o allow_root allow access by root\n"
    2964" -o auto_unmount auto unmount on process termination\n");
    2965}
    2966
    2967void fuse_session_destroy(struct fuse_session *se)
    2968{
    2969 struct fuse_ll_pipe *llp;
    2970
    2971 if (se->got_init && !se->got_destroy) {
    2972 if (se->op.destroy)
    2973 se->op.destroy(se->userdata);
    2974 }
    2975 llp = pthread_getspecific(se->pipe_key);
    2976 if (llp != NULL)
    2977 fuse_ll_pipe_free(llp);
    2978 pthread_key_delete(se->pipe_key);
    2979 pthread_mutex_destroy(&se->lock);
    2980 free(se->cuse_data);
    2981 if (se->fd != -1)
    2982 close(se->fd);
    2983 if (se->io != NULL)
    2984 free(se->io);
    2985 destroy_mount_opts(se->mo);
    2986 free(se);
    2987}
    2988
    2989
    2990static void fuse_ll_pipe_destructor(void *data)
    2991{
    2992 struct fuse_ll_pipe *llp = data;
    2993 fuse_ll_pipe_free(llp);
    2994}
    2995
    2996void fuse_buf_free(struct fuse_buf *buf)
    2997{
    2998 if (buf->mem == NULL)
    2999 return;
    3000
    3001 size_t write_header_sz =
    3002 sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in);
    3003
    3004 char *ptr = (char *)buf->mem - pagesize + write_header_sz;
    3005 free(ptr);
    3006 buf->mem = NULL;
    3007}
    3008
    3009/*
    3010 * This is used to allocate buffers that hold fuse requests
    3011 */
    3012static void *buf_alloc(size_t size, bool internal)
    3013{
    3014 /*
    3015 * For libfuse internal caller add in alignment. That cannot be done
    3016 * for an external caller, as it is not guaranteed that the external
    3017 * caller frees the raw pointer.
    3018 */
    3019 if (internal) {
    3020 size_t write_header_sz = sizeof(struct fuse_in_header) +
    3021 sizeof(struct fuse_write_in);
    3022 size_t new_size = ROUND_UP(size + write_header_sz, pagesize);
    3023
    3024 char *buf = aligned_alloc(pagesize, new_size);
    3025 if (buf == NULL)
    3026 return NULL;
    3027
    3028 buf += pagesize - write_header_sz;
    3029
    3030 return buf;
    3031 } else {
    3032 return malloc(size);
    3033 }
    3034}
    3035
    3036/*
    3037 *@param internal true if called from libfuse internal code
    3038 */
    3039static int _fuse_session_receive_buf(struct fuse_session *se,
    3040 struct fuse_buf *buf, struct fuse_chan *ch,
    3041 bool internal)
    3042{
    3043 int err;
    3044 ssize_t res;
    3045 size_t bufsize = se->bufsize;
    3046#ifdef HAVE_SPLICE
    3047 struct fuse_ll_pipe *llp;
    3048 struct fuse_buf tmpbuf;
    3049
    3050 if (se->conn.proto_minor < 14 ||
    3051 !(se->conn.want_ext & FUSE_CAP_SPLICE_READ))
    3052 goto fallback;
    3053
    3054 llp = fuse_ll_get_pipe(se);
    3055 if (llp == NULL)
    3056 goto fallback;
    3057
    3058 if (llp->size < bufsize) {
    3059 if (llp->can_grow) {
    3060 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
    3061 if (res == -1) {
    3062 llp->can_grow = 0;
    3063 res = grow_pipe_to_max(llp->pipe[0]);
    3064 if (res > 0)
    3065 llp->size = res;
    3066 goto fallback;
    3067 }
    3068 llp->size = res;
    3069 }
    3070 if (llp->size < bufsize)
    3071 goto fallback;
    3072 }
    3073
    3074 if (se->io != NULL && se->io->splice_receive != NULL) {
    3075 res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
    3076 llp->pipe[1], NULL, bufsize, 0,
    3077 se->userdata);
    3078 } else {
    3079 res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
    3080 bufsize, 0);
    3081 }
    3082 err = errno;
    3083
    3084 if (fuse_session_exited(se))
    3085 return 0;
    3086
    3087 if (res == -1) {
    3088 if (err == ENODEV) {
    3089 /* Filesystem was unmounted, or connection was aborted
    3090 via /sys/fs/fuse/connections */
    3092 return 0;
    3093 }
    3094 if (err != EINTR && err != EAGAIN)
    3095 perror("fuse: splice from device");
    3096 return -err;
    3097 }
    3098
    3099 if (res < sizeof(struct fuse_in_header)) {
    3100 fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n");
    3101 return -EIO;
    3102 }
    3103
    3104 tmpbuf = (struct fuse_buf){
    3105 .size = res,
    3106 .flags = FUSE_BUF_IS_FD,
    3107 .fd = llp->pipe[0],
    3108 };
    3109
    3110 /*
    3111 * Don't bother with zero copy for small requests.
    3112 * fuse_loop_mt() needs to check for FORGET so this more than
    3113 * just an optimization.
    3114 */
    3115 if (res < sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) +
    3116 pagesize) {
    3117 struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
    3118 struct fuse_bufvec dst = { .count = 1 };
    3119
    3120 if (!buf->mem) {
    3121 buf->mem = buf_alloc(se->bufsize, internal);
    3122 if (!buf->mem) {
    3123 fuse_log(
    3124 FUSE_LOG_ERR,
    3125 "fuse: failed to allocate read buffer\n");
    3126 return -ENOMEM;
    3127 }
    3128 buf->mem_size = se->bufsize;
    3129 if (internal)
    3130 se->buf_reallocable = true;
    3131 }
    3132 buf->size = se->bufsize;
    3133 buf->flags = 0;
    3134 dst.buf[0] = *buf;
    3135
    3136 res = fuse_buf_copy(&dst, &src, 0);
    3137 if (res < 0) {
    3138 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n",
    3139 strerror(-res));
    3140 fuse_ll_clear_pipe(se);
    3141 return res;
    3142 }
    3143 if (res < tmpbuf.size) {
    3144 fuse_log(FUSE_LOG_ERR,
    3145 "fuse: copy from pipe: short read\n");
    3146 fuse_ll_clear_pipe(se);
    3147 return -EIO;
    3148 }
    3149 assert(res == tmpbuf.size);
    3150
    3151 } else {
    3152 /* Don't overwrite buf->mem, as that would cause a leak */
    3153 buf->fd = tmpbuf.fd;
    3154 buf->flags = tmpbuf.flags;
    3155 }
    3156 buf->size = tmpbuf.size;
    3157
    3158 return res;
    3159
    3160fallback:
    3161#endif
    3162 if (!buf->mem) {
    3163 buf->mem = buf_alloc(se->bufsize, internal);
    3164 if (!buf->mem) {
    3165 fuse_log(FUSE_LOG_ERR,
    3166 "fuse: failed to allocate read buffer\n");
    3167 return -ENOMEM;
    3168 }
    3169 buf->mem_size = se->bufsize;
    3170 if (internal)
    3171 se->buf_reallocable = true;
    3172 }
    3173
    3174restart:
    3175 if (se->buf_reallocable)
    3176 bufsize = buf->mem_size;
    3177 if (se->io != NULL) {
    3178 /* se->io->read is never NULL if se->io is not NULL as
    3179 specified by fuse_session_custom_io()*/
    3180 res = se->io->read(ch ? ch->fd : se->fd, buf->mem, bufsize,
    3181 se->userdata);
    3182 } else {
    3183 res = read(ch ? ch->fd : se->fd, buf->mem, bufsize);
    3184 }
    3185 err = errno;
    3186
    3187 if (fuse_session_exited(se))
    3188 return 0;
    3189 if (res == -1) {
    3190 if (err == EINVAL && se->buf_reallocable &&
    3191 se->bufsize > buf->mem_size) {
    3192 void *newbuf = buf_alloc(se->bufsize, internal);
    3193 if (!newbuf) {
    3194 fuse_log(
    3195 FUSE_LOG_ERR,
    3196 "fuse: failed to (re)allocate read buffer\n");
    3197 return -ENOMEM;
    3198 }
    3199 fuse_buf_free(buf);
    3200 buf->mem = newbuf;
    3201 buf->mem_size = se->bufsize;
    3202 se->buf_reallocable = true;
    3203 goto restart;
    3204 }
    3205
    3206 /* ENOENT means the operation was interrupted, it's safe
    3207 to restart */
    3208 if (err == ENOENT)
    3209 goto restart;
    3210
    3211 if (err == ENODEV) {
    3212 /* Filesystem was unmounted, or connection was aborted
    3213 via /sys/fs/fuse/connections */
    3215 return 0;
    3216 }
    3217 /* Errors occurring during normal operation: EINTR (read
    3218 interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
    3219 umounted) */
    3220 if (err != EINTR && err != EAGAIN)
    3221 perror("fuse: reading device");
    3222 return -err;
    3223 }
    3224 if ((size_t)res < sizeof(struct fuse_in_header)) {
    3225 fuse_log(FUSE_LOG_ERR, "short read on fuse device\n");
    3226 return -EIO;
    3227 }
    3228
    3229 buf->size = res;
    3230
    3231 return res;
    3232}
    3233
    3234int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
    3235{
    3236 return _fuse_session_receive_buf(se, buf, NULL, false);
    3237}
    3238
    3239/* libfuse internal handler */
    3240int fuse_session_receive_buf_internal(struct fuse_session *se,
    3241 struct fuse_buf *buf,
    3242 struct fuse_chan *ch)
    3243{
    3244 return _fuse_session_receive_buf(se, buf, ch, true);
    3245}
    3246
    3247struct fuse_session *
    3248fuse_session_new_versioned(struct fuse_args *args,
    3249 const struct fuse_lowlevel_ops *op, size_t op_size,
    3250 struct libfuse_version *version, void *userdata);
    3251struct fuse_session *
    3252fuse_session_new_versioned(struct fuse_args *args,
    3253 const struct fuse_lowlevel_ops *op, size_t op_size,
    3254 struct libfuse_version *version, void *userdata)
    3255{
    3256 int err;
    3257 struct fuse_session *se;
    3258 struct mount_opts *mo;
    3259
    3260 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
    3261 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
    3262 op_size = sizeof(struct fuse_lowlevel_ops);
    3263 }
    3264
    3265 if (args->argc == 0) {
    3266 fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
    3267 return NULL;
    3268 }
    3269
    3270 se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
    3271 if (se == NULL) {
    3272 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
    3273 goto out1;
    3274 }
    3275 se->fd = -1;
    3276 se->conn.max_write = FUSE_DEFAULT_MAX_PAGES_LIMIT * getpagesize();
    3277 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
    3278 se->conn.max_readahead = UINT_MAX;
    3279
    3280 /* Parse options */
    3281 if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
    3282 goto out2;
    3283 if(se->deny_others) {
    3284 /* Allowing access only by root is done by instructing
    3285 * kernel to allow access by everyone, and then restricting
    3286 * access to root and mountpoint owner in libfuse.
    3287 */
    3288 // We may be adding the option a second time, but
    3289 // that doesn't hurt.
    3290 if(fuse_opt_add_arg(args, "-oallow_other") == -1)
    3291 goto out2;
    3292 }
    3293 mo = parse_mount_opts(args);
    3294 if (mo == NULL)
    3295 goto out3;
    3296
    3297 if(args->argc == 1 &&
    3298 args->argv[0][0] == '-') {
    3299 fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
    3300 "will be ignored\n");
    3301 } else if (args->argc != 1) {
    3302 int i;
    3303 fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
    3304 for(i = 1; i < args->argc-1; i++)
    3305 fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
    3306 fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
    3307 goto out4;
    3308 }
    3309
    3310 if (se->debug)
    3311 fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION);
    3312
    3313 list_init_req(&se->list);
    3314 list_init_req(&se->interrupts);
    3315 list_init_nreq(&se->notify_list);
    3316 se->notify_ctr = 1;
    3317 pthread_mutex_init(&se->lock, NULL);
    3318
    3319 err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
    3320 if (err) {
    3321 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
    3322 strerror(err));
    3323 goto out5;
    3324 }
    3325
    3326 memcpy(&se->op, op, op_size);
    3327 se->owner = getuid();
    3328 se->userdata = userdata;
    3329
    3330 se->mo = mo;
    3331
    3332 /* Fuse server application should pass the version it was compiled
    3333 * against and pass it. If a libfuse version accidentally introduces an
    3334 * ABI incompatibility, it might be possible to 'fix' that at run time,
    3335 * by checking the version numbers.
    3336 */
    3337 se->version = *version;
    3338
    3339 return se;
    3340
    3341out5:
    3342 pthread_mutex_destroy(&se->lock);
    3343out4:
    3344 fuse_opt_free_args(args);
    3345out3:
    3346 if (mo != NULL)
    3347 destroy_mount_opts(mo);
    3348out2:
    3349 free(se);
    3350out1:
    3351 return NULL;
    3352}
    3353
    3354struct fuse_session *fuse_session_new_30(struct fuse_args *args,
    3355 const struct fuse_lowlevel_ops *op,
    3356 size_t op_size, void *userdata);
    3357struct fuse_session *fuse_session_new_30(struct fuse_args *args,
    3358 const struct fuse_lowlevel_ops *op,
    3359 size_t op_size,
    3360 void *userdata)
    3361{
    3362 /* unknown version */
    3363 struct libfuse_version version = { 0 };
    3364
    3365 return fuse_session_new_versioned(args, op, op_size, &version,
    3366 userdata);
    3367}
    3368
    3369FUSE_SYMVER("fuse_session_custom_io_317", "fuse_session_custom_io@@FUSE_3.17")
    3370int fuse_session_custom_io_317(struct fuse_session *se,
    3371 const struct fuse_custom_io *io, size_t op_size, int fd)
    3372{
    3373 if (sizeof(struct fuse_custom_io) < op_size) {
    3374 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
    3375 op_size = sizeof(struct fuse_custom_io);
    3376 }
    3377
    3378 if (fd < 0) {
    3379 fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
    3380 "fuse_session_custom_io()\n", fd);
    3381 return -EBADF;
    3382 }
    3383 if (io == NULL) {
    3384 fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
    3385 "fuse_session_custom_io()\n");
    3386 return -EINVAL;
    3387 } else if (io->read == NULL || io->writev == NULL) {
    3388 /* If the user provides their own file descriptor, we can't
    3389 guarantee that the default behavior of the io operations made
    3390 in libfuse will function properly. Therefore, we enforce the
    3391 user to implement these io operations when using custom io. */
    3392 fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
    3393 "implement both io->read() and io->writev\n");
    3394 return -EINVAL;
    3395 }
    3396
    3397 se->io = calloc(1, sizeof(struct fuse_custom_io));
    3398 if (se->io == NULL) {
    3399 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
    3400 "Error: %s\n", strerror(errno));
    3401 return -errno;
    3402 }
    3403
    3404 se->fd = fd;
    3405 memcpy(se->io, io, op_size);
    3406 return 0;
    3407}
    3408
    3409int fuse_session_custom_io_30(struct fuse_session *se,
    3410 const struct fuse_custom_io *io, int fd);
    3411FUSE_SYMVER("fuse_session_custom_io_30", "fuse_session_custom_io@FUSE_3.0")
    3412int fuse_session_custom_io_30(struct fuse_session *se,
    3413 const struct fuse_custom_io *io, int fd)
    3414{
    3415 return fuse_session_custom_io_317(se, io,
    3416 offsetof(struct fuse_custom_io, clone_fd), fd);
    3417}
    3418
    3419int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
    3420{
    3421 int fd;
    3422
    3423 if (mountpoint == NULL) {
    3424 fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n");
    3425 return -1;
    3426 }
    3427
    3428 /*
    3429 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
    3430 * would ensue.
    3431 */
    3432 do {
    3433 fd = open("/dev/null", O_RDWR);
    3434 if (fd > 2)
    3435 close(fd);
    3436 } while (fd >= 0 && fd <= 2);
    3437
    3438 /*
    3439 * To allow FUSE daemons to run without privileges, the caller may open
    3440 * /dev/fuse before launching the file system and pass on the file
    3441 * descriptor by specifying /dev/fd/N as the mount point. Note that the
    3442 * parent process takes care of performing the mount in this case.
    3443 */
    3444 fd = fuse_mnt_parse_fuse_fd(mountpoint);
    3445 if (fd != -1) {
    3446 if (fcntl(fd, F_GETFD) == -1) {
    3447 fuse_log(FUSE_LOG_ERR,
    3448 "fuse: Invalid file descriptor /dev/fd/%u\n",
    3449 fd);
    3450 return -1;
    3451 }
    3452 se->fd = fd;
    3453 return 0;
    3454 }
    3455
    3456 /* Open channel */
    3457 fd = fuse_kern_mount(mountpoint, se->mo);
    3458 if (fd == -1)
    3459 return -1;
    3460 se->fd = fd;
    3461
    3462 /* Save mountpoint */
    3463 se->mountpoint = strdup(mountpoint);
    3464 if (se->mountpoint == NULL)
    3465 goto error_out;
    3466
    3467 return 0;
    3468
    3469error_out:
    3470 fuse_kern_unmount(mountpoint, fd);
    3471 return -1;
    3472}
    3473
    3474int fuse_session_fd(struct fuse_session *se)
    3475{
    3476 return se->fd;
    3477}
    3478
    3479void fuse_session_unmount(struct fuse_session *se)
    3480{
    3481 if (se->mountpoint != NULL) {
    3482 fuse_kern_unmount(se->mountpoint, se->fd);
    3483 se->fd = -1;
    3484 free(se->mountpoint);
    3485 se->mountpoint = NULL;
    3486 }
    3487}
    3488
    3489#ifdef linux
    3490int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
    3491{
    3492 char *buf;
    3493 size_t bufsize = 1024;
    3494 char path[128];
    3495 int ret;
    3496 int fd;
    3497 unsigned long pid = req->ctx.pid;
    3498 char *s;
    3499
    3500 sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
    3501
    3502retry:
    3503 buf = malloc(bufsize);
    3504 if (buf == NULL)
    3505 return -ENOMEM;
    3506
    3507 ret = -EIO;
    3508 fd = open(path, O_RDONLY);
    3509 if (fd == -1)
    3510 goto out_free;
    3511
    3512 ret = read(fd, buf, bufsize);
    3513 close(fd);
    3514 if (ret < 0) {
    3515 ret = -EIO;
    3516 goto out_free;
    3517 }
    3518
    3519 if ((size_t)ret == bufsize) {
    3520 free(buf);
    3521 bufsize *= 4;
    3522 goto retry;
    3523 }
    3524
    3525 buf[ret] = '\0';
    3526 ret = -EIO;
    3527 s = strstr(buf, "\nGroups:");
    3528 if (s == NULL)
    3529 goto out_free;
    3530
    3531 s += 8;
    3532 ret = 0;
    3533 while (1) {
    3534 char *end;
    3535 unsigned long val = strtoul(s, &end, 0);
    3536 if (end == s)
    3537 break;
    3538
    3539 s = end;
    3540 if (ret < size)
    3541 list[ret] = val;
    3542 ret++;
    3543 }
    3544
    3545out_free:
    3546 free(buf);
    3547 return ret;
    3548}
    3549#else /* linux */
    3550/*
    3551 * This is currently not implemented on other than Linux...
    3552 */
    3553int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
    3554{
    3555 (void) req; (void) size; (void) list;
    3556 return -ENOSYS;
    3557}
    3558#endif
    3559
    3560/* Prevent spurious data race warning - we don't care
    3561 * about races for this flag */
    3562__attribute__((no_sanitize_thread))
    3563void fuse_session_exit(struct fuse_session *se)
    3564{
    3565 se->exited = 1;
    3566}
    3567
    3568__attribute__((no_sanitize_thread))
    3569void fuse_session_reset(struct fuse_session *se)
    3570{
    3571 se->exited = 0;
    3572 se->error = 0;
    3573}
    3574
    3575__attribute__((no_sanitize_thread))
    3576int fuse_session_exited(struct fuse_session *se)
    3577{
    3578 return se->exited;
    3579}
    size_t fuse_buf_size(const struct fuse_bufvec *bufv)
    Definition buffer.c:22
    @ 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_copy_flags
    @ FUSE_BUF_SPLICE_NONBLOCK
    @ FUSE_BUF_FORCE_SPLICE
    @ FUSE_BUF_NO_SPLICE
    @ FUSE_BUF_SPLICE_MOVE
    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_IOCTL_DIR
    #define FUSE_CAP_DONT_MASK
    #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
    #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
    #define FUSE_CAP_NO_OPENDIR_SUPPORT
    #define FUSE_CAP_ASYNC_DIO
    #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)
    #define FUSE_CAP_SETXATTR_EXT
    #define FUSE_CAP_SPLICE_MOVE
    #define FUSE_CAP_NO_EXPORT_SUPPORT
    #define FUSE_CAP_FLOCK_LOCKS
    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 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:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t nonseekable
    Definition fuse_common.h:86
    int32_t backing_id
    uint32_t parallel_direct_writes
    uint32_t noflush
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    fuse-3.17.2/doc/html/lib_2fuse__lowlevel_8c_source.html0000644000175000017500000237062615002273247022031 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 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
    47#define container_of(ptr, type, member) ({ \
    48 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    49 (type *)( (char *)__mptr - offsetof(type,member) );})
    50
    51struct fuse_pollhandle {
    52 uint64_t kh;
    53 struct fuse_session *se;
    54};
    55
    56static size_t pagesize;
    57
    58static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
    59{
    60 pagesize = getpagesize();
    61}
    62
    63static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
    64{
    65 attr->ino = stbuf->st_ino;
    66 attr->mode = stbuf->st_mode;
    67 attr->nlink = stbuf->st_nlink;
    68 attr->uid = stbuf->st_uid;
    69 attr->gid = stbuf->st_gid;
    70 attr->rdev = stbuf->st_rdev;
    71 attr->size = stbuf->st_size;
    72 attr->blksize = stbuf->st_blksize;
    73 attr->blocks = stbuf->st_blocks;
    74 attr->atime = stbuf->st_atime;
    75 attr->mtime = stbuf->st_mtime;
    76 attr->ctime = stbuf->st_ctime;
    77 attr->atimensec = ST_ATIM_NSEC(stbuf);
    78 attr->mtimensec = ST_MTIM_NSEC(stbuf);
    79 attr->ctimensec = ST_CTIM_NSEC(stbuf);
    80}
    81
    82static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
    83{
    84 stbuf->st_mode = attr->mode;
    85 stbuf->st_uid = attr->uid;
    86 stbuf->st_gid = attr->gid;
    87 stbuf->st_size = attr->size;
    88 stbuf->st_atime = attr->atime;
    89 stbuf->st_mtime = attr->mtime;
    90 stbuf->st_ctime = attr->ctime;
    91 ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
    92 ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
    93 ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
    94}
    95
    96static size_t iov_length(const struct iovec *iov, size_t count)
    97{
    98 size_t seg;
    99 size_t ret = 0;
    100
    101 for (seg = 0; seg < count; seg++)
    102 ret += iov[seg].iov_len;
    103 return ret;
    104}
    105
    106static void list_init_req(struct fuse_req *req)
    107{
    108 req->next = req;
    109 req->prev = req;
    110}
    111
    112static void list_del_req(struct fuse_req *req)
    113{
    114 struct fuse_req *prev = req->prev;
    115 struct fuse_req *next = req->next;
    116 prev->next = next;
    117 next->prev = prev;
    118}
    119
    120static void list_add_req(struct fuse_req *req, struct fuse_req *next)
    121{
    122 struct fuse_req *prev = next->prev;
    123 req->next = next;
    124 req->prev = prev;
    125 prev->next = req;
    126 next->prev = req;
    127}
    128
    129static void destroy_req(fuse_req_t req)
    130{
    131 assert(req->ch == NULL);
    132 pthread_mutex_destroy(&req->lock);
    133 free(req);
    134}
    135
    136void fuse_free_req(fuse_req_t req)
    137{
    138 int ctr;
    139 struct fuse_session *se = req->se;
    140
    141 if (se->conn.no_interrupt) {
    142 ctr = --req->ref_cnt;
    143 fuse_chan_put(req->ch);
    144 req->ch = NULL;
    145 } else {
    146 pthread_mutex_lock(&se->lock);
    147 req->u.ni.func = NULL;
    148 req->u.ni.data = NULL;
    149 list_del_req(req);
    150 ctr = --req->ref_cnt;
    151 fuse_chan_put(req->ch);
    152 req->ch = NULL;
    153 pthread_mutex_unlock(&se->lock);
    154 }
    155 if (!ctr)
    156 destroy_req(req);
    157}
    158
    159static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
    160{
    161 struct fuse_req *req;
    162
    163 req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
    164 if (req == NULL) {
    165 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
    166 } else {
    167 req->se = se;
    168 req->ref_cnt = 1;
    169 list_init_req(req);
    170 pthread_mutex_init(&req->lock, NULL);
    171 }
    172
    173 return req;
    174}
    175
    176/* Send data. If *ch* is NULL, send via session master fd */
    177static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
    178 struct iovec *iov, int count)
    179{
    180 struct fuse_out_header *out = iov[0].iov_base;
    181
    182 assert(se != NULL);
    183 out->len = iov_length(iov, count);
    184 if (se->debug) {
    185 if (out->unique == 0) {
    186 fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
    187 out->error, out->len);
    188 } else if (out->error) {
    189 fuse_log(FUSE_LOG_DEBUG,
    190 " unique: %llu, error: %i (%s), outsize: %i\n",
    191 (unsigned long long) out->unique, out->error,
    192 strerror(-out->error), out->len);
    193 } else {
    194 fuse_log(FUSE_LOG_DEBUG,
    195 " unique: %llu, success, outsize: %i\n",
    196 (unsigned long long) out->unique, out->len);
    197 }
    198 }
    199
    200 ssize_t res;
    201 if (se->io != NULL)
    202 /* se->io->writev is never NULL if se->io is not NULL as
    203 specified by fuse_session_custom_io()*/
    204 res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
    205 se->userdata);
    206 else
    207 res = writev(ch ? ch->fd : se->fd, iov, count);
    208
    209 int err = errno;
    210
    211 if (res == -1) {
    212 /* ENOENT means the operation was interrupted */
    213 if (!fuse_session_exited(se) && err != ENOENT)
    214 perror("fuse: writing device");
    215 return -err;
    216 }
    217
    218 return 0;
    219}
    220
    221
    222int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
    223 int count)
    224{
    225 struct fuse_out_header out;
    226
    227#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
    228 const char *str = strerrordesc_np(error * -1);
    229 if ((str == NULL && error != 0) || error > 0) {
    230#else
    231 if (error <= -1000 || error > 0) {
    232#endif
    233 fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
    234 error = -ERANGE;
    235 }
    236
    237 out.unique = req->unique;
    238 out.error = error;
    239
    240 iov[0].iov_base = &out;
    241 iov[0].iov_len = sizeof(struct fuse_out_header);
    242
    243 return fuse_send_msg(req->se, req->ch, iov, count);
    244}
    245
    246static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
    247 int count)
    248{
    249 int res;
    250
    251 res = fuse_send_reply_iov_nofree(req, error, iov, count);
    252 fuse_free_req(req);
    253 return res;
    254}
    255
    256static int send_reply(fuse_req_t req, int error, const void *arg,
    257 size_t argsize)
    258{
    259 struct iovec iov[2];
    260 int count = 1;
    261 if (argsize) {
    262 iov[1].iov_base = (void *) arg;
    263 iov[1].iov_len = argsize;
    264 count++;
    265 }
    266 return send_reply_iov(req, error, iov, count);
    267}
    268
    269int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
    270{
    271 int res;
    272 struct iovec *padded_iov;
    273
    274 padded_iov = malloc((count + 1) * sizeof(struct iovec));
    275 if (padded_iov == NULL)
    276 return fuse_reply_err(req, ENOMEM);
    277
    278 memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
    279 count++;
    280
    281 res = send_reply_iov(req, 0, padded_iov, count);
    282 free(padded_iov);
    283
    284 return res;
    285}
    286
    287
    288/* `buf` is allowed to be empty so that the proper size may be
    289 allocated by the caller */
    290size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
    291 const char *name, const struct stat *stbuf, off_t off)
    292{
    293 (void)req;
    294 size_t namelen;
    295 size_t entlen;
    296 size_t entlen_padded;
    297 struct fuse_dirent *dirent;
    298
    299 namelen = strlen(name);
    300 entlen = FUSE_NAME_OFFSET + namelen;
    301 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
    302
    303 if ((buf == NULL) || (entlen_padded > bufsize))
    304 return entlen_padded;
    305
    306 dirent = (struct fuse_dirent*) buf;
    307 dirent->ino = stbuf->st_ino;
    308 dirent->off = off;
    309 dirent->namelen = namelen;
    310 dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
    311 memcpy(dirent->name, name, namelen);
    312 memset(dirent->name + namelen, 0, entlen_padded - entlen);
    313
    314 return entlen_padded;
    315}
    316
    317static void convert_statfs(const struct statvfs *stbuf,
    318 struct fuse_kstatfs *kstatfs)
    319{
    320 kstatfs->bsize = stbuf->f_bsize;
    321 kstatfs->frsize = stbuf->f_frsize;
    322 kstatfs->blocks = stbuf->f_blocks;
    323 kstatfs->bfree = stbuf->f_bfree;
    324 kstatfs->bavail = stbuf->f_bavail;
    325 kstatfs->files = stbuf->f_files;
    326 kstatfs->ffree = stbuf->f_ffree;
    327 kstatfs->namelen = stbuf->f_namemax;
    328}
    329
    330static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
    331{
    332 return send_reply(req, 0, arg, argsize);
    333}
    334
    335int fuse_reply_err(fuse_req_t req, int err)
    336{
    337 return send_reply(req, -err, NULL, 0);
    338}
    339
    341{
    342 fuse_free_req(req);
    343}
    344
    345static unsigned long calc_timeout_sec(double t)
    346{
    347 if (t > (double) ULONG_MAX)
    348 return ULONG_MAX;
    349 else if (t < 0.0)
    350 return 0;
    351 else
    352 return (unsigned long) t;
    353}
    354
    355static unsigned int calc_timeout_nsec(double t)
    356{
    357 double f = t - (double) calc_timeout_sec(t);
    358 if (f < 0.0)
    359 return 0;
    360 else if (f >= 0.999999999)
    361 return 999999999;
    362 else
    363 return (unsigned int) (f * 1.0e9);
    364}
    365
    366static void fill_entry(struct fuse_entry_out *arg,
    367 const struct fuse_entry_param *e)
    368{
    369 arg->nodeid = e->ino;
    370 arg->generation = e->generation;
    371 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
    372 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
    373 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
    374 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
    375 convert_stat(&e->attr, &arg->attr);
    376}
    377
    378/* `buf` is allowed to be empty so that the proper size may be
    379 allocated by the caller */
    380size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
    381 const char *name,
    382 const struct fuse_entry_param *e, off_t off)
    383{
    384 (void)req;
    385 size_t namelen;
    386 size_t entlen;
    387 size_t entlen_padded;
    388
    389 namelen = strlen(name);
    390 entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
    391 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
    392 if ((buf == NULL) || (entlen_padded > bufsize))
    393 return entlen_padded;
    394
    395 struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
    396 memset(&dp->entry_out, 0, sizeof(dp->entry_out));
    397 fill_entry(&dp->entry_out, e);
    398
    399 struct fuse_dirent *dirent = &dp->dirent;
    400 dirent->ino = e->attr.st_ino;
    401 dirent->off = off;
    402 dirent->namelen = namelen;
    403 dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
    404 memcpy(dirent->name, name, namelen);
    405 memset(dirent->name + namelen, 0, entlen_padded - entlen);
    406
    407 return entlen_padded;
    408}
    409
    410static void fill_open(struct fuse_open_out *arg,
    411 const struct fuse_file_info *f)
    412{
    413 arg->fh = f->fh;
    414 if (f->backing_id > 0) {
    415 arg->backing_id = f->backing_id;
    416 arg->open_flags |= FOPEN_PASSTHROUGH;
    417 }
    418 if (f->direct_io)
    419 arg->open_flags |= FOPEN_DIRECT_IO;
    420 if (f->keep_cache)
    421 arg->open_flags |= FOPEN_KEEP_CACHE;
    422 if (f->cache_readdir)
    423 arg->open_flags |= FOPEN_CACHE_DIR;
    424 if (f->nonseekable)
    425 arg->open_flags |= FOPEN_NONSEEKABLE;
    426 if (f->noflush)
    427 arg->open_flags |= FOPEN_NOFLUSH;
    429 arg->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
    430}
    431
    432int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
    433{
    434 struct fuse_entry_out arg;
    435 size_t size = req->se->conn.proto_minor < 9 ?
    436 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
    437
    438 /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
    439 negative entry */
    440 if (!e->ino && req->se->conn.proto_minor < 4)
    441 return fuse_reply_err(req, ENOENT);
    442
    443 memset(&arg, 0, sizeof(arg));
    444 fill_entry(&arg, e);
    445 return send_reply_ok(req, &arg, size);
    446}
    447
    448int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
    449 const struct fuse_file_info *f)
    450{
    451 alignas(uint64_t) char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
    452 size_t entrysize = req->se->conn.proto_minor < 9 ?
    453 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
    454 struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
    455 struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
    456
    457 memset(buf, 0, sizeof(buf));
    458 fill_entry(earg, e);
    459 fill_open(oarg, f);
    460 return send_reply_ok(req, buf,
    461 entrysize + sizeof(struct fuse_open_out));
    462}
    463
    464int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
    465 double attr_timeout)
    466{
    467 struct fuse_attr_out arg;
    468 size_t size = req->se->conn.proto_minor < 9 ?
    469 FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
    470
    471 memset(&arg, 0, sizeof(arg));
    472 arg.attr_valid = calc_timeout_sec(attr_timeout);
    473 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
    474 convert_stat(attr, &arg.attr);
    475
    476 return send_reply_ok(req, &arg, size);
    477}
    478
    479int fuse_reply_readlink(fuse_req_t req, const char *linkname)
    480{
    481 return send_reply_ok(req, linkname, strlen(linkname));
    482}
    483
    484int fuse_passthrough_open(fuse_req_t req, int fd)
    485{
    486 struct fuse_backing_map map = { .fd = fd };
    487 int ret;
    488
    489 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map);
    490 if (ret <= 0) {
    491 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno));
    492 return 0;
    493 }
    494
    495 return ret;
    496}
    497
    498int fuse_passthrough_close(fuse_req_t req, int backing_id)
    499{
    500 int ret;
    501
    502 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id);
    503 if (ret < 0)
    504 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno));
    505
    506 return ret;
    507}
    508
    509int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
    510{
    511 struct fuse_open_out arg;
    512
    513 memset(&arg, 0, sizeof(arg));
    514 fill_open(&arg, f);
    515 return send_reply_ok(req, &arg, sizeof(arg));
    516}
    517
    518int fuse_reply_write(fuse_req_t req, size_t count)
    519{
    520 struct fuse_write_out arg;
    521
    522 memset(&arg, 0, sizeof(arg));
    523 arg.size = count;
    524
    525 return send_reply_ok(req, &arg, sizeof(arg));
    526}
    527
    528int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
    529{
    530 return send_reply_ok(req, buf, size);
    531}
    532
    533static int fuse_send_data_iov_fallback(struct fuse_session *se,
    534 struct fuse_chan *ch,
    535 struct iovec *iov, int iov_count,
    536 struct fuse_bufvec *buf,
    537 size_t len)
    538{
    539 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
    540 void *mbuf;
    541 int res;
    542
    543 /* Optimize common case */
    544 if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
    545 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
    546 /* FIXME: also avoid memory copy if there are multiple buffers
    547 but none of them contain an fd */
    548
    549 iov[iov_count].iov_base = buf->buf[0].mem;
    550 iov[iov_count].iov_len = len;
    551 iov_count++;
    552 return fuse_send_msg(se, ch, iov, iov_count);
    553 }
    554
    555 res = posix_memalign(&mbuf, pagesize, len);
    556 if (res != 0)
    557 return res;
    558
    559 mem_buf.buf[0].mem = mbuf;
    560 res = fuse_buf_copy(&mem_buf, buf, 0);
    561 if (res < 0) {
    562 free(mbuf);
    563 return -res;
    564 }
    565 len = res;
    566
    567 iov[iov_count].iov_base = mbuf;
    568 iov[iov_count].iov_len = len;
    569 iov_count++;
    570 res = fuse_send_msg(se, ch, iov, iov_count);
    571 free(mbuf);
    572
    573 return res;
    574}
    575
    576struct fuse_ll_pipe {
    577 size_t size;
    578 int can_grow;
    579 int pipe[2];
    580};
    581
    582static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
    583{
    584 close(llp->pipe[0]);
    585 close(llp->pipe[1]);
    586 free(llp);
    587}
    588
    589#ifdef HAVE_SPLICE
    590#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC)
    591static int fuse_pipe(int fds[2])
    592{
    593 int rv = pipe(fds);
    594
    595 if (rv == -1)
    596 return rv;
    597
    598 if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
    599 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 ||
    600 fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
    601 fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
    602 close(fds[0]);
    603 close(fds[1]);
    604 rv = -1;
    605 }
    606 return rv;
    607}
    608#else
    609static int fuse_pipe(int fds[2])
    610{
    611 return pipe2(fds, O_CLOEXEC | O_NONBLOCK);
    612}
    613#endif
    614
    615static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
    616{
    617 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
    618 if (llp == NULL) {
    619 int res;
    620
    621 llp = malloc(sizeof(struct fuse_ll_pipe));
    622 if (llp == NULL)
    623 return NULL;
    624
    625 res = fuse_pipe(llp->pipe);
    626 if (res == -1) {
    627 free(llp);
    628 return NULL;
    629 }
    630
    631 /*
    632 *the default size is 16 pages on linux
    633 */
    634 llp->size = pagesize * 16;
    635 llp->can_grow = 1;
    636
    637 pthread_setspecific(se->pipe_key, llp);
    638 }
    639
    640 return llp;
    641}
    642#endif
    643
    644static void fuse_ll_clear_pipe(struct fuse_session *se)
    645{
    646 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
    647 if (llp) {
    648 pthread_setspecific(se->pipe_key, NULL);
    649 fuse_ll_pipe_free(llp);
    650 }
    651}
    652
    653#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
    654static int read_back(int fd, char *buf, size_t len)
    655{
    656 int res;
    657
    658 res = read(fd, buf, len);
    659 if (res == -1) {
    660 fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
    661 return -EIO;
    662 }
    663 if (res != len) {
    664 fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
    665 return -EIO;
    666 }
    667 return 0;
    668}
    669
    670static int grow_pipe_to_max(int pipefd)
    671{
    672 int res;
    673 long max;
    674 long maxfd;
    675 char buf[32];
    676
    677 maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY);
    678 if (maxfd < 0)
    679 return -errno;
    680
    681 res = read(maxfd, buf, sizeof(buf) - 1);
    682 if (res < 0) {
    683 int saved_errno;
    684
    685 saved_errno = errno;
    686 close(maxfd);
    687 return -saved_errno;
    688 }
    689 close(maxfd);
    690 buf[res] = '\0';
    691
    692 res = libfuse_strtol(buf, &max);
    693 if (res)
    694 return res;
    695 res = fcntl(pipefd, F_SETPIPE_SZ, max);
    696 if (res < 0)
    697 return -errno;
    698 return max;
    699}
    700
    701static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
    702 struct iovec *iov, int iov_count,
    703 struct fuse_bufvec *buf, unsigned int flags)
    704{
    705 int res;
    706 size_t len = fuse_buf_size(buf);
    707 struct fuse_out_header *out = iov[0].iov_base;
    708 struct fuse_ll_pipe *llp;
    709 int splice_flags;
    710 size_t pipesize;
    711 size_t total_buf_size;
    712 size_t idx;
    713 size_t headerlen;
    714 struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
    715
    716 if (se->broken_splice_nonblock)
    717 goto fallback;
    718
    719 if (flags & FUSE_BUF_NO_SPLICE)
    720 goto fallback;
    721
    722 total_buf_size = 0;
    723 for (idx = buf->idx; idx < buf->count; idx++) {
    724 total_buf_size += buf->buf[idx].size;
    725 if (idx == buf->idx)
    726 total_buf_size -= buf->off;
    727 }
    728 if (total_buf_size < 2 * pagesize)
    729 goto fallback;
    730
    731 if (se->conn.proto_minor < 14 ||
    732 !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE))
    733 goto fallback;
    734
    735 llp = fuse_ll_get_pipe(se);
    736 if (llp == NULL)
    737 goto fallback;
    738
    739
    740 headerlen = iov_length(iov, iov_count);
    741
    742 out->len = headerlen + len;
    743
    744 /*
    745 * Heuristic for the required pipe size, does not work if the
    746 * source contains less than page size fragments
    747 */
    748 pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
    749
    750 if (llp->size < pipesize) {
    751 if (llp->can_grow) {
    752 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
    753 if (res == -1) {
    754 res = grow_pipe_to_max(llp->pipe[0]);
    755 if (res > 0)
    756 llp->size = res;
    757 llp->can_grow = 0;
    758 goto fallback;
    759 }
    760 llp->size = res;
    761 }
    762 if (llp->size < pipesize)
    763 goto fallback;
    764 }
    765
    766
    767 res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
    768 if (res == -1)
    769 goto fallback;
    770
    771 if (res != headerlen) {
    772 res = -EIO;
    773 fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
    774 headerlen);
    775 goto clear_pipe;
    776 }
    777
    778 pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
    779 pipe_buf.buf[0].fd = llp->pipe[1];
    780
    781 res = fuse_buf_copy(&pipe_buf, buf,
    783 if (res < 0) {
    784 if (res == -EAGAIN || res == -EINVAL) {
    785 /*
    786 * Should only get EAGAIN on kernels with
    787 * broken SPLICE_F_NONBLOCK support (<=
    788 * 2.6.35) where this error or a short read is
    789 * returned even if the pipe itself is not
    790 * full
    791 *
    792 * EINVAL might mean that splice can't handle
    793 * this combination of input and output.
    794 */
    795 if (res == -EAGAIN)
    796 se->broken_splice_nonblock = 1;
    797
    798 pthread_setspecific(se->pipe_key, NULL);
    799 fuse_ll_pipe_free(llp);
    800 goto fallback;
    801 }
    802 res = -res;
    803 goto clear_pipe;
    804 }
    805
    806 if (res != 0 && res < len) {
    807 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
    808 void *mbuf;
    809 size_t now_len = res;
    810 /*
    811 * For regular files a short count is either
    812 * 1) due to EOF, or
    813 * 2) because of broken SPLICE_F_NONBLOCK (see above)
    814 *
    815 * For other inputs it's possible that we overflowed
    816 * the pipe because of small buffer fragments.
    817 */
    818
    819 res = posix_memalign(&mbuf, pagesize, len);
    820 if (res != 0)
    821 goto clear_pipe;
    822
    823 mem_buf.buf[0].mem = mbuf;
    824 mem_buf.off = now_len;
    825 res = fuse_buf_copy(&mem_buf, buf, 0);
    826 if (res > 0) {
    827 char *tmpbuf;
    828 size_t extra_len = res;
    829 /*
    830 * Trickiest case: got more data. Need to get
    831 * back the data from the pipe and then fall
    832 * back to regular write.
    833 */
    834 tmpbuf = malloc(headerlen);
    835 if (tmpbuf == NULL) {
    836 free(mbuf);
    837 res = ENOMEM;
    838 goto clear_pipe;
    839 }
    840 res = read_back(llp->pipe[0], tmpbuf, headerlen);
    841 free(tmpbuf);
    842 if (res != 0) {
    843 free(mbuf);
    844 goto clear_pipe;
    845 }
    846 res = read_back(llp->pipe[0], mbuf, now_len);
    847 if (res != 0) {
    848 free(mbuf);
    849 goto clear_pipe;
    850 }
    851 len = now_len + extra_len;
    852 iov[iov_count].iov_base = mbuf;
    853 iov[iov_count].iov_len = len;
    854 iov_count++;
    855 res = fuse_send_msg(se, ch, iov, iov_count);
    856 free(mbuf);
    857 return res;
    858 }
    859 free(mbuf);
    860 res = now_len;
    861 }
    862 len = res;
    863 out->len = headerlen + len;
    864
    865 if (se->debug) {
    866 fuse_log(FUSE_LOG_DEBUG,
    867 " unique: %llu, success, outsize: %i (splice)\n",
    868 (unsigned long long) out->unique, out->len);
    869 }
    870
    871 splice_flags = 0;
    872 if ((flags & FUSE_BUF_SPLICE_MOVE) &&
    873 (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE))
    874 splice_flags |= SPLICE_F_MOVE;
    875
    876 if (se->io != NULL && se->io->splice_send != NULL) {
    877 res = se->io->splice_send(llp->pipe[0], NULL,
    878 ch ? ch->fd : se->fd, NULL, out->len,
    879 splice_flags, se->userdata);
    880 } else {
    881 res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
    882 out->len, splice_flags);
    883 }
    884 if (res == -1) {
    885 res = -errno;
    886 perror("fuse: splice from pipe");
    887 goto clear_pipe;
    888 }
    889 if (res != out->len) {
    890 res = -EIO;
    891 fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
    892 res, out->len);
    893 goto clear_pipe;
    894 }
    895 return 0;
    896
    897clear_pipe:
    898 fuse_ll_clear_pipe(se);
    899 return res;
    900
    901fallback:
    902 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
    903}
    904#else
    905static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
    906 struct iovec *iov, int iov_count,
    907 struct fuse_bufvec *buf, unsigned int flags)
    908{
    909 size_t len = fuse_buf_size(buf);
    910 (void) flags;
    911
    912 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
    913}
    914#endif
    915
    916int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
    917 enum fuse_buf_copy_flags flags)
    918{
    919 struct iovec iov[2];
    920 struct fuse_out_header out;
    921 int res;
    922
    923 iov[0].iov_base = &out;
    924 iov[0].iov_len = sizeof(struct fuse_out_header);
    925
    926 out.unique = req->unique;
    927 out.error = 0;
    928
    929 res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags);
    930 if (res <= 0) {
    931 fuse_free_req(req);
    932 return res;
    933 } else {
    934 return fuse_reply_err(req, res);
    935 }
    936}
    937
    938int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
    939{
    940 struct fuse_statfs_out arg;
    941 size_t size = req->se->conn.proto_minor < 4 ?
    942 FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
    943
    944 memset(&arg, 0, sizeof(arg));
    945 convert_statfs(stbuf, &arg.st);
    946
    947 return send_reply_ok(req, &arg, size);
    948}
    949
    950int fuse_reply_xattr(fuse_req_t req, size_t count)
    951{
    952 struct fuse_getxattr_out arg;
    953
    954 memset(&arg, 0, sizeof(arg));
    955 arg.size = count;
    956
    957 return send_reply_ok(req, &arg, sizeof(arg));
    958}
    959
    960int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
    961{
    962 struct fuse_lk_out arg;
    963
    964 memset(&arg, 0, sizeof(arg));
    965 arg.lk.type = lock->l_type;
    966 if (lock->l_type != F_UNLCK) {
    967 arg.lk.start = lock->l_start;
    968 if (lock->l_len == 0)
    969 arg.lk.end = OFFSET_MAX;
    970 else
    971 arg.lk.end = lock->l_start + lock->l_len - 1;
    972 }
    973 arg.lk.pid = lock->l_pid;
    974 return send_reply_ok(req, &arg, sizeof(arg));
    975}
    976
    977int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
    978{
    979 struct fuse_bmap_out arg;
    980
    981 memset(&arg, 0, sizeof(arg));
    982 arg.block = idx;
    983
    984 return send_reply_ok(req, &arg, sizeof(arg));
    985}
    986
    987static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
    988 size_t count)
    989{
    990 struct fuse_ioctl_iovec *fiov;
    991 size_t i;
    992
    993 fiov = malloc(sizeof(fiov[0]) * count);
    994 if (!fiov)
    995 return NULL;
    996
    997 for (i = 0; i < count; i++) {
    998 fiov[i].base = (uintptr_t) iov[i].iov_base;
    999 fiov[i].len = iov[i].iov_len;
    1000 }
    1001
    1002 return fiov;
    1003}
    1004
    1006 const struct iovec *in_iov, size_t in_count,
    1007 const struct iovec *out_iov, size_t out_count)
    1008{
    1009 struct fuse_ioctl_out arg;
    1010 struct fuse_ioctl_iovec *in_fiov = NULL;
    1011 struct fuse_ioctl_iovec *out_fiov = NULL;
    1012 struct iovec iov[4];
    1013 size_t count = 1;
    1014 int res;
    1015
    1016 memset(&arg, 0, sizeof(arg));
    1017 arg.flags |= FUSE_IOCTL_RETRY;
    1018 arg.in_iovs = in_count;
    1019 arg.out_iovs = out_count;
    1020 iov[count].iov_base = &arg;
    1021 iov[count].iov_len = sizeof(arg);
    1022 count++;
    1023
    1024 if (req->se->conn.proto_minor < 16) {
    1025 if (in_count) {
    1026 iov[count].iov_base = (void *)in_iov;
    1027 iov[count].iov_len = sizeof(in_iov[0]) * in_count;
    1028 count++;
    1029 }
    1030
    1031 if (out_count) {
    1032 iov[count].iov_base = (void *)out_iov;
    1033 iov[count].iov_len = sizeof(out_iov[0]) * out_count;
    1034 count++;
    1035 }
    1036 } else {
    1037 /* Can't handle non-compat 64bit ioctls on 32bit */
    1038 if (sizeof(void *) == 4 && req->ioctl_64bit) {
    1039 res = fuse_reply_err(req, EINVAL);
    1040 goto out;
    1041 }
    1042
    1043 if (in_count) {
    1044 in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
    1045 if (!in_fiov)
    1046 goto enomem;
    1047
    1048 iov[count].iov_base = (void *)in_fiov;
    1049 iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
    1050 count++;
    1051 }
    1052 if (out_count) {
    1053 out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
    1054 if (!out_fiov)
    1055 goto enomem;
    1056
    1057 iov[count].iov_base = (void *)out_fiov;
    1058 iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
    1059 count++;
    1060 }
    1061 }
    1062
    1063 res = send_reply_iov(req, 0, iov, count);
    1064out:
    1065 free(in_fiov);
    1066 free(out_fiov);
    1067
    1068 return res;
    1069
    1070enomem:
    1071 res = fuse_reply_err(req, ENOMEM);
    1072 goto out;
    1073}
    1074
    1075int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
    1076{
    1077 struct fuse_ioctl_out arg;
    1078 struct iovec iov[3];
    1079 size_t count = 1;
    1080
    1081 memset(&arg, 0, sizeof(arg));
    1082 arg.result = result;
    1083 iov[count].iov_base = &arg;
    1084 iov[count].iov_len = sizeof(arg);
    1085 count++;
    1086
    1087 if (size) {
    1088 iov[count].iov_base = (char *) buf;
    1089 iov[count].iov_len = size;
    1090 count++;
    1091 }
    1092
    1093 return send_reply_iov(req, 0, iov, count);
    1094}
    1095
    1096int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
    1097 int count)
    1098{
    1099 struct iovec *padded_iov;
    1100 struct fuse_ioctl_out arg;
    1101 int res;
    1102
    1103 padded_iov = malloc((count + 2) * sizeof(struct iovec));
    1104 if (padded_iov == NULL)
    1105 return fuse_reply_err(req, ENOMEM);
    1106
    1107 memset(&arg, 0, sizeof(arg));
    1108 arg.result = result;
    1109 padded_iov[1].iov_base = &arg;
    1110 padded_iov[1].iov_len = sizeof(arg);
    1111
    1112 memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
    1113
    1114 res = send_reply_iov(req, 0, padded_iov, count + 2);
    1115 free(padded_iov);
    1116
    1117 return res;
    1118}
    1119
    1120int fuse_reply_poll(fuse_req_t req, unsigned revents)
    1121{
    1122 struct fuse_poll_out arg;
    1123
    1124 memset(&arg, 0, sizeof(arg));
    1125 arg.revents = revents;
    1126
    1127 return send_reply_ok(req, &arg, sizeof(arg));
    1128}
    1129
    1130int fuse_reply_lseek(fuse_req_t req, off_t off)
    1131{
    1132 struct fuse_lseek_out arg;
    1133
    1134 memset(&arg, 0, sizeof(arg));
    1135 arg.offset = off;
    1136
    1137 return send_reply_ok(req, &arg, sizeof(arg));
    1138}
    1139
    1140static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1141{
    1142 char *name = (char *) inarg;
    1143
    1144 if (req->se->op.lookup)
    1145 req->se->op.lookup(req, nodeid, name);
    1146 else
    1147 fuse_reply_err(req, ENOSYS);
    1148}
    1149
    1150static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1151{
    1152 struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
    1153
    1154 if (req->se->op.forget)
    1155 req->se->op.forget(req, nodeid, arg->nlookup);
    1156 else
    1157 fuse_reply_none(req);
    1158}
    1159
    1160static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
    1161 const void *inarg)
    1162{
    1163 struct fuse_batch_forget_in *arg = (void *) inarg;
    1164 struct fuse_forget_one *param = (void *) PARAM(arg);
    1165 unsigned int i;
    1166
    1167 (void) nodeid;
    1168
    1169 if (req->se->op.forget_multi) {
    1170 req->se->op.forget_multi(req, arg->count,
    1171 (struct fuse_forget_data *) param);
    1172 } else if (req->se->op.forget) {
    1173 for (i = 0; i < arg->count; i++) {
    1174 struct fuse_forget_one *forget = &param[i];
    1175 struct fuse_req *dummy_req;
    1176
    1177 dummy_req = fuse_ll_alloc_req(req->se);
    1178 if (dummy_req == NULL)
    1179 break;
    1180
    1181 dummy_req->unique = req->unique;
    1182 dummy_req->ctx = req->ctx;
    1183 dummy_req->ch = NULL;
    1184
    1185 req->se->op.forget(dummy_req, forget->nodeid,
    1186 forget->nlookup);
    1187 }
    1188 fuse_reply_none(req);
    1189 } else {
    1190 fuse_reply_none(req);
    1191 }
    1192}
    1193
    1194static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1195{
    1196 struct fuse_file_info *fip = NULL;
    1197 struct fuse_file_info fi;
    1198
    1199 if (req->se->conn.proto_minor >= 9) {
    1200 struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
    1201
    1202 if (arg->getattr_flags & FUSE_GETATTR_FH) {
    1203 memset(&fi, 0, sizeof(fi));
    1204 fi.fh = arg->fh;
    1205 fip = &fi;
    1206 }
    1207 }
    1208
    1209 if (req->se->op.getattr)
    1210 req->se->op.getattr(req, nodeid, fip);
    1211 else
    1212 fuse_reply_err(req, ENOSYS);
    1213}
    1214
    1215static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1216{
    1217 struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
    1218
    1219 if (req->se->op.setattr) {
    1220 struct fuse_file_info *fi = NULL;
    1221 struct fuse_file_info fi_store;
    1222 struct stat stbuf;
    1223 memset(&stbuf, 0, sizeof(stbuf));
    1224 convert_attr(arg, &stbuf);
    1225 if (arg->valid & FATTR_FH) {
    1226 arg->valid &= ~FATTR_FH;
    1227 memset(&fi_store, 0, sizeof(fi_store));
    1228 fi = &fi_store;
    1229 fi->fh = arg->fh;
    1230 }
    1231 arg->valid &=
    1232 FUSE_SET_ATTR_MODE |
    1233 FUSE_SET_ATTR_UID |
    1234 FUSE_SET_ATTR_GID |
    1235 FUSE_SET_ATTR_SIZE |
    1236 FUSE_SET_ATTR_ATIME |
    1237 FUSE_SET_ATTR_MTIME |
    1238 FUSE_SET_ATTR_KILL_SUID |
    1239 FUSE_SET_ATTR_KILL_SGID |
    1240 FUSE_SET_ATTR_ATIME_NOW |
    1241 FUSE_SET_ATTR_MTIME_NOW |
    1242 FUSE_SET_ATTR_CTIME;
    1243
    1244 req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
    1245 } else
    1246 fuse_reply_err(req, ENOSYS);
    1247}
    1248
    1249static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1250{
    1251 struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
    1252
    1253 if (req->se->op.access)
    1254 req->se->op.access(req, nodeid, arg->mask);
    1255 else
    1256 fuse_reply_err(req, ENOSYS);
    1257}
    1258
    1259static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1260{
    1261 (void) inarg;
    1262
    1263 if (req->se->op.readlink)
    1264 req->se->op.readlink(req, nodeid);
    1265 else
    1266 fuse_reply_err(req, ENOSYS);
    1267}
    1268
    1269static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1270{
    1271 struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
    1272 char *name = PARAM(arg);
    1273
    1274 if (req->se->conn.proto_minor >= 12)
    1275 req->ctx.umask = arg->umask;
    1276 else
    1277 name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
    1278
    1279 if (req->se->op.mknod)
    1280 req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
    1281 else
    1282 fuse_reply_err(req, ENOSYS);
    1283}
    1284
    1285static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1286{
    1287 struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
    1288
    1289 if (req->se->conn.proto_minor >= 12)
    1290 req->ctx.umask = arg->umask;
    1291
    1292 if (req->se->op.mkdir)
    1293 req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
    1294 else
    1295 fuse_reply_err(req, ENOSYS);
    1296}
    1297
    1298static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1299{
    1300 char *name = (char *) inarg;
    1301
    1302 if (req->se->op.unlink)
    1303 req->se->op.unlink(req, nodeid, name);
    1304 else
    1305 fuse_reply_err(req, ENOSYS);
    1306}
    1307
    1308static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1309{
    1310 char *name = (char *) inarg;
    1311
    1312 if (req->se->op.rmdir)
    1313 req->se->op.rmdir(req, nodeid, name);
    1314 else
    1315 fuse_reply_err(req, ENOSYS);
    1316}
    1317
    1318static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1319{
    1320 char *name = (char *) inarg;
    1321 char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
    1322
    1323 if (req->se->op.symlink)
    1324 req->se->op.symlink(req, linkname, nodeid, name);
    1325 else
    1326 fuse_reply_err(req, ENOSYS);
    1327}
    1328
    1329static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1330{
    1331 struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
    1332 char *oldname = PARAM(arg);
    1333 char *newname = oldname + strlen(oldname) + 1;
    1334
    1335 if (req->se->op.rename)
    1336 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
    1337 0);
    1338 else
    1339 fuse_reply_err(req, ENOSYS);
    1340}
    1341
    1342static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1343{
    1344 struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg;
    1345 char *oldname = PARAM(arg);
    1346 char *newname = oldname + strlen(oldname) + 1;
    1347
    1348 if (req->se->op.rename)
    1349 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
    1350 arg->flags);
    1351 else
    1352 fuse_reply_err(req, ENOSYS);
    1353}
    1354
    1355static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1356{
    1357 struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
    1358
    1359 if (req->se->op.link)
    1360 req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
    1361 else
    1362 fuse_reply_err(req, ENOSYS);
    1363}
    1364
    1365static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1366{
    1367 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
    1368
    1369 if (req->se->op.tmpfile) {
    1370 struct fuse_file_info fi;
    1371
    1372 memset(&fi, 0, sizeof(fi));
    1373 fi.flags = arg->flags;
    1374
    1375 if (req->se->conn.proto_minor >= 12)
    1376 req->ctx.umask = arg->umask;
    1377
    1378 req->se->op.tmpfile(req, nodeid, arg->mode, &fi);
    1379 } else
    1380 fuse_reply_err(req, ENOSYS);
    1381}
    1382
    1383static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1384{
    1385 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
    1386
    1387 if (req->se->op.create) {
    1388 struct fuse_file_info fi;
    1389 char *name = PARAM(arg);
    1390
    1391 memset(&fi, 0, sizeof(fi));
    1392 fi.flags = arg->flags;
    1393
    1394 if (req->se->conn.proto_minor >= 12)
    1395 req->ctx.umask = arg->umask;
    1396 else
    1397 name = (char *) inarg + sizeof(struct fuse_open_in);
    1398
    1399 req->se->op.create(req, nodeid, name, arg->mode, &fi);
    1400 } else
    1401 fuse_reply_err(req, ENOSYS);
    1402}
    1403
    1404static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1405{
    1406 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
    1407 struct fuse_file_info fi;
    1408
    1409 memset(&fi, 0, sizeof(fi));
    1410 fi.flags = arg->flags;
    1411
    1412 if (req->se->op.open)
    1413 req->se->op.open(req, nodeid, &fi);
    1414 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT)
    1415 fuse_reply_err(req, ENOSYS);
    1416 else
    1417 fuse_reply_open(req, &fi);
    1418}
    1419
    1420static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1421{
    1422 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1423
    1424 if (req->se->op.read) {
    1425 struct fuse_file_info fi;
    1426
    1427 memset(&fi, 0, sizeof(fi));
    1428 fi.fh = arg->fh;
    1429 if (req->se->conn.proto_minor >= 9) {
    1430 fi.lock_owner = arg->lock_owner;
    1431 fi.flags = arg->flags;
    1432 }
    1433 req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
    1434 } else
    1435 fuse_reply_err(req, ENOSYS);
    1436}
    1437
    1438static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1439{
    1440 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
    1441 struct fuse_file_info fi;
    1442 char *param;
    1443
    1444 memset(&fi, 0, sizeof(fi));
    1445 fi.fh = arg->fh;
    1446 fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
    1447
    1448 if (req->se->conn.proto_minor < 9) {
    1449 param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
    1450 } else {
    1451 fi.lock_owner = arg->lock_owner;
    1452 fi.flags = arg->flags;
    1453 param = PARAM(arg);
    1454 }
    1455
    1456 if (req->se->op.write)
    1457 req->se->op.write(req, nodeid, param, arg->size,
    1458 arg->offset, &fi);
    1459 else
    1460 fuse_reply_err(req, ENOSYS);
    1461}
    1462
    1463static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
    1464 const struct fuse_buf *ibuf)
    1465{
    1466 struct fuse_session *se = req->se;
    1467 struct fuse_bufvec bufv = {
    1468 .buf[0] = *ibuf,
    1469 .count = 1,
    1470 };
    1471 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
    1472 struct fuse_file_info fi;
    1473
    1474 memset(&fi, 0, sizeof(fi));
    1475 fi.fh = arg->fh;
    1476 fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
    1477
    1478 if (se->conn.proto_minor < 9) {
    1479 bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
    1480 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    1481 FUSE_COMPAT_WRITE_IN_SIZE;
    1482 assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
    1483 } else {
    1484 fi.lock_owner = arg->lock_owner;
    1485 fi.flags = arg->flags;
    1486 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
    1487 bufv.buf[0].mem = PARAM(arg);
    1488
    1489 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    1490 sizeof(struct fuse_write_in);
    1491 }
    1492 if (bufv.buf[0].size < arg->size) {
    1493 fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n");
    1494 fuse_reply_err(req, EIO);
    1495 goto out;
    1496 }
    1497 bufv.buf[0].size = arg->size;
    1498
    1499 se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
    1500
    1501out:
    1502 /* Need to reset the pipe if ->write_buf() didn't consume all data */
    1503 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
    1504 fuse_ll_clear_pipe(se);
    1505}
    1506
    1507static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1508{
    1509 struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
    1510 struct fuse_file_info fi;
    1511
    1512 memset(&fi, 0, sizeof(fi));
    1513 fi.fh = arg->fh;
    1514 fi.flush = 1;
    1515 if (req->se->conn.proto_minor >= 7)
    1516 fi.lock_owner = arg->lock_owner;
    1517
    1518 if (req->se->op.flush)
    1519 req->se->op.flush(req, nodeid, &fi);
    1520 else
    1521 fuse_reply_err(req, ENOSYS);
    1522}
    1523
    1524static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1525{
    1526 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
    1527 struct fuse_file_info fi;
    1528
    1529 memset(&fi, 0, sizeof(fi));
    1530 fi.flags = arg->flags;
    1531 fi.fh = arg->fh;
    1532 if (req->se->conn.proto_minor >= 8) {
    1533 fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
    1534 fi.lock_owner = arg->lock_owner;
    1535 }
    1536 if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
    1537 fi.flock_release = 1;
    1538 fi.lock_owner = arg->lock_owner;
    1539 }
    1540
    1541 if (req->se->op.release)
    1542 req->se->op.release(req, nodeid, &fi);
    1543 else
    1544 fuse_reply_err(req, 0);
    1545}
    1546
    1547static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1548{
    1549 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
    1550 struct fuse_file_info fi;
    1551 int datasync = arg->fsync_flags & 1;
    1552
    1553 memset(&fi, 0, sizeof(fi));
    1554 fi.fh = arg->fh;
    1555
    1556 if (req->se->op.fsync)
    1557 req->se->op.fsync(req, nodeid, datasync, &fi);
    1558 else
    1559 fuse_reply_err(req, ENOSYS);
    1560}
    1561
    1562static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1563{
    1564 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
    1565 struct fuse_file_info fi;
    1566
    1567 memset(&fi, 0, sizeof(fi));
    1568 fi.flags = arg->flags;
    1569
    1570 if (req->se->op.opendir)
    1571 req->se->op.opendir(req, nodeid, &fi);
    1572 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT)
    1573 fuse_reply_err(req, ENOSYS);
    1574 else
    1575 fuse_reply_open(req, &fi);
    1576}
    1577
    1578static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1579{
    1580 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1581 struct fuse_file_info fi;
    1582
    1583 memset(&fi, 0, sizeof(fi));
    1584 fi.fh = arg->fh;
    1585
    1586 if (req->se->op.readdir)
    1587 req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
    1588 else
    1589 fuse_reply_err(req, ENOSYS);
    1590}
    1591
    1592static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1593{
    1594 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1595 struct fuse_file_info fi;
    1596
    1597 memset(&fi, 0, sizeof(fi));
    1598 fi.fh = arg->fh;
    1599
    1600 if (req->se->op.readdirplus)
    1601 req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
    1602 else
    1603 fuse_reply_err(req, ENOSYS);
    1604}
    1605
    1606static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1607{
    1608 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
    1609 struct fuse_file_info fi;
    1610
    1611 memset(&fi, 0, sizeof(fi));
    1612 fi.flags = arg->flags;
    1613 fi.fh = arg->fh;
    1614
    1615 if (req->se->op.releasedir)
    1616 req->se->op.releasedir(req, nodeid, &fi);
    1617 else
    1618 fuse_reply_err(req, 0);
    1619}
    1620
    1621static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1622{
    1623 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
    1624 struct fuse_file_info fi;
    1625 int datasync = arg->fsync_flags & 1;
    1626
    1627 memset(&fi, 0, sizeof(fi));
    1628 fi.fh = arg->fh;
    1629
    1630 if (req->se->op.fsyncdir)
    1631 req->se->op.fsyncdir(req, nodeid, datasync, &fi);
    1632 else
    1633 fuse_reply_err(req, ENOSYS);
    1634}
    1635
    1636static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1637{
    1638 (void) nodeid;
    1639 (void) inarg;
    1640
    1641 if (req->se->op.statfs)
    1642 req->se->op.statfs(req, nodeid);
    1643 else {
    1644 struct statvfs buf = {
    1645 .f_namemax = 255,
    1646 .f_bsize = 512,
    1647 };
    1648 fuse_reply_statfs(req, &buf);
    1649 }
    1650}
    1651
    1652static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1653{
    1654 struct fuse_session *se = req->se;
    1655 unsigned int xattr_ext = !!(se->conn.want_ext & FUSE_CAP_SETXATTR_EXT);
    1656 struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
    1657 char *name = xattr_ext ? PARAM(arg) :
    1658 (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
    1659 char *value = name + strlen(name) + 1;
    1660
    1661 /* XXX:The API should be extended to support extra_flags/setxattr_flags */
    1662 if (req->se->op.setxattr)
    1663 req->se->op.setxattr(req, nodeid, name, value, arg->size,
    1664 arg->flags);
    1665 else
    1666 fuse_reply_err(req, ENOSYS);
    1667}
    1668
    1669static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1670{
    1671 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
    1672
    1673 if (req->se->op.getxattr)
    1674 req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size);
    1675 else
    1676 fuse_reply_err(req, ENOSYS);
    1677}
    1678
    1679static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1680{
    1681 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
    1682
    1683 if (req->se->op.listxattr)
    1684 req->se->op.listxattr(req, nodeid, arg->size);
    1685 else
    1686 fuse_reply_err(req, ENOSYS);
    1687}
    1688
    1689static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1690{
    1691 char *name = (char *) inarg;
    1692
    1693 if (req->se->op.removexattr)
    1694 req->se->op.removexattr(req, nodeid, name);
    1695 else
    1696 fuse_reply_err(req, ENOSYS);
    1697}
    1698
    1699static void convert_fuse_file_lock(struct fuse_file_lock *fl,
    1700 struct flock *flock)
    1701{
    1702 memset(flock, 0, sizeof(struct flock));
    1703 flock->l_type = fl->type;
    1704 flock->l_whence = SEEK_SET;
    1705 flock->l_start = fl->start;
    1706 if (fl->end == OFFSET_MAX)
    1707 flock->l_len = 0;
    1708 else
    1709 flock->l_len = fl->end - fl->start + 1;
    1710 flock->l_pid = fl->pid;
    1711}
    1712
    1713static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1714{
    1715 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
    1716 struct fuse_file_info fi;
    1717 struct flock flock;
    1718
    1719 memset(&fi, 0, sizeof(fi));
    1720 fi.fh = arg->fh;
    1721 fi.lock_owner = arg->owner;
    1722
    1723 convert_fuse_file_lock(&arg->lk, &flock);
    1724 if (req->se->op.getlk)
    1725 req->se->op.getlk(req, nodeid, &fi, &flock);
    1726 else
    1727 fuse_reply_err(req, ENOSYS);
    1728}
    1729
    1730static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
    1731 const void *inarg, int sleep)
    1732{
    1733 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
    1734 struct fuse_file_info fi;
    1735 struct flock flock;
    1736
    1737 memset(&fi, 0, sizeof(fi));
    1738 fi.fh = arg->fh;
    1739 fi.lock_owner = arg->owner;
    1740
    1741 if (arg->lk_flags & FUSE_LK_FLOCK) {
    1742 int op = 0;
    1743
    1744 switch (arg->lk.type) {
    1745 case F_RDLCK:
    1746 op = LOCK_SH;
    1747 break;
    1748 case F_WRLCK:
    1749 op = LOCK_EX;
    1750 break;
    1751 case F_UNLCK:
    1752 op = LOCK_UN;
    1753 break;
    1754 }
    1755 if (!sleep)
    1756 op |= LOCK_NB;
    1757
    1758 if (req->se->op.flock)
    1759 req->se->op.flock(req, nodeid, &fi, op);
    1760 else
    1761 fuse_reply_err(req, ENOSYS);
    1762 } else {
    1763 convert_fuse_file_lock(&arg->lk, &flock);
    1764 if (req->se->op.setlk)
    1765 req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
    1766 else
    1767 fuse_reply_err(req, ENOSYS);
    1768 }
    1769}
    1770
    1771static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1772{
    1773 do_setlk_common(req, nodeid, inarg, 0);
    1774}
    1775
    1776static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1777{
    1778 do_setlk_common(req, nodeid, inarg, 1);
    1779}
    1780
    1781static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
    1782{
    1783 struct fuse_req *curr;
    1784
    1785 for (curr = se->list.next; curr != &se->list; curr = curr->next) {
    1786 if (curr->unique == req->u.i.unique) {
    1788 void *data;
    1789
    1790 curr->ref_cnt++;
    1791 pthread_mutex_unlock(&se->lock);
    1792
    1793 /* Ugh, ugly locking */
    1794 pthread_mutex_lock(&curr->lock);
    1795 pthread_mutex_lock(&se->lock);
    1796 curr->interrupted = 1;
    1797 func = curr->u.ni.func;
    1798 data = curr->u.ni.data;
    1799 pthread_mutex_unlock(&se->lock);
    1800 if (func)
    1801 func(curr, data);
    1802 pthread_mutex_unlock(&curr->lock);
    1803
    1804 pthread_mutex_lock(&se->lock);
    1805 curr->ref_cnt--;
    1806 if (!curr->ref_cnt) {
    1807 destroy_req(curr);
    1808 }
    1809
    1810 return 1;
    1811 }
    1812 }
    1813 for (curr = se->interrupts.next; curr != &se->interrupts;
    1814 curr = curr->next) {
    1815 if (curr->u.i.unique == req->u.i.unique)
    1816 return 1;
    1817 }
    1818 return 0;
    1819}
    1820
    1821static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1822{
    1823 struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
    1824 struct fuse_session *se = req->se;
    1825
    1826 (void) nodeid;
    1827 if (se->debug)
    1828 fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
    1829 (unsigned long long) arg->unique);
    1830
    1831 req->u.i.unique = arg->unique;
    1832
    1833 pthread_mutex_lock(&se->lock);
    1834 if (find_interrupted(se, req)) {
    1835 fuse_chan_put(req->ch);
    1836 req->ch = NULL;
    1837 destroy_req(req);
    1838 } else
    1839 list_add_req(req, &se->interrupts);
    1840 pthread_mutex_unlock(&se->lock);
    1841}
    1842
    1843static struct fuse_req *check_interrupt(struct fuse_session *se,
    1844 struct fuse_req *req)
    1845{
    1846 struct fuse_req *curr;
    1847
    1848 for (curr = se->interrupts.next; curr != &se->interrupts;
    1849 curr = curr->next) {
    1850 if (curr->u.i.unique == req->unique) {
    1851 req->interrupted = 1;
    1852 list_del_req(curr);
    1853 fuse_chan_put(curr->ch);
    1854 curr->ch = NULL;
    1855 destroy_req(curr);
    1856 return NULL;
    1857 }
    1858 }
    1859 curr = se->interrupts.next;
    1860 if (curr != &se->interrupts) {
    1861 list_del_req(curr);
    1862 list_init_req(curr);
    1863 return curr;
    1864 } else
    1865 return NULL;
    1866}
    1867
    1868static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1869{
    1870 struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
    1871
    1872 if (req->se->op.bmap)
    1873 req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
    1874 else
    1875 fuse_reply_err(req, ENOSYS);
    1876}
    1877
    1878static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1879{
    1880 struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
    1881 unsigned int flags = arg->flags;
    1882 void *in_buf = arg->in_size ? PARAM(arg) : NULL;
    1883 struct fuse_file_info fi;
    1884
    1885 if (flags & FUSE_IOCTL_DIR &&
    1886 !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) {
    1887 fuse_reply_err(req, ENOTTY);
    1888 return;
    1889 }
    1890
    1891 memset(&fi, 0, sizeof(fi));
    1892 fi.fh = arg->fh;
    1893
    1894 if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
    1895 !(flags & FUSE_IOCTL_32BIT)) {
    1896 req->ioctl_64bit = 1;
    1897 }
    1898
    1899 if (req->se->op.ioctl)
    1900 req->se->op.ioctl(req, nodeid, arg->cmd,
    1901 (void *)(uintptr_t)arg->arg, &fi, flags,
    1902 in_buf, arg->in_size, arg->out_size);
    1903 else
    1904 fuse_reply_err(req, ENOSYS);
    1905}
    1906
    1907void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
    1908{
    1909 free(ph);
    1910}
    1911
    1912static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1913{
    1914 struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
    1915 struct fuse_file_info fi;
    1916
    1917 memset(&fi, 0, sizeof(fi));
    1918 fi.fh = arg->fh;
    1919 fi.poll_events = arg->events;
    1920
    1921 if (req->se->op.poll) {
    1922 struct fuse_pollhandle *ph = NULL;
    1923
    1924 if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
    1925 ph = malloc(sizeof(struct fuse_pollhandle));
    1926 if (ph == NULL) {
    1927 fuse_reply_err(req, ENOMEM);
    1928 return;
    1929 }
    1930 ph->kh = arg->kh;
    1931 ph->se = req->se;
    1932 }
    1933
    1934 req->se->op.poll(req, nodeid, &fi, ph);
    1935 } else {
    1936 fuse_reply_err(req, ENOSYS);
    1937 }
    1938}
    1939
    1940static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1941{
    1942 struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
    1943 struct fuse_file_info fi;
    1944
    1945 memset(&fi, 0, sizeof(fi));
    1946 fi.fh = arg->fh;
    1947
    1948 if (req->se->op.fallocate)
    1949 req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
    1950 else
    1951 fuse_reply_err(req, ENOSYS);
    1952}
    1953
    1954static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg)
    1955{
    1956 struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg;
    1957 struct fuse_file_info fi_in, fi_out;
    1958
    1959 memset(&fi_in, 0, sizeof(fi_in));
    1960 fi_in.fh = arg->fh_in;
    1961
    1962 memset(&fi_out, 0, sizeof(fi_out));
    1963 fi_out.fh = arg->fh_out;
    1964
    1965
    1966 if (req->se->op.copy_file_range)
    1967 req->se->op.copy_file_range(req, nodeid_in, arg->off_in,
    1968 &fi_in, arg->nodeid_out,
    1969 arg->off_out, &fi_out, arg->len,
    1970 arg->flags);
    1971 else
    1972 fuse_reply_err(req, ENOSYS);
    1973}
    1974
    1975static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1976{
    1977 struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg;
    1978 struct fuse_file_info fi;
    1979
    1980 memset(&fi, 0, sizeof(fi));
    1981 fi.fh = arg->fh;
    1982
    1983 if (req->se->op.lseek)
    1984 req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
    1985 else
    1986 fuse_reply_err(req, ENOSYS);
    1987}
    1988
    1989static bool want_flags_valid(uint64_t capable, uint64_t want)
    1990{
    1991 uint64_t unknown_flags = want & (~capable);
    1992 if (unknown_flags != 0) {
    1993 fuse_log(FUSE_LOG_ERR,
    1994 "fuse: unknown connection 'want' flags: 0x%08lx\n",
    1995 unknown_flags);
    1996 return false;
    1997 }
    1998 return true;
    1999}
    2000
    2001/* Prevent bogus data races (bogus since "init" is called before
    2002 * multi-threading becomes relevant */
    2003static __attribute__((no_sanitize("thread")))
    2004void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    2005{
    2006 struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
    2007 struct fuse_init_out outarg;
    2008 struct fuse_session *se = req->se;
    2009 size_t bufsize = se->bufsize;
    2010 size_t outargsize = sizeof(outarg);
    2011 uint64_t inargflags = 0;
    2012 uint64_t outargflags = 0;
    2013 bool buf_reallocable = se->buf_reallocable;
    2014 (void) nodeid;
    2015 if (se->debug) {
    2016 fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
    2017 if (arg->major == 7 && arg->minor >= 6) {
    2018 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
    2019 fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
    2020 arg->max_readahead);
    2021 }
    2022 }
    2023 se->conn.proto_major = arg->major;
    2024 se->conn.proto_minor = arg->minor;
    2025 se->conn.capable_ext = 0;
    2026 se->conn.want_ext = 0;
    2027
    2028 memset(&outarg, 0, sizeof(outarg));
    2029 outarg.major = FUSE_KERNEL_VERSION;
    2030 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
    2031
    2032 if (arg->major < 7) {
    2033 fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
    2034 arg->major, arg->minor);
    2035 fuse_reply_err(req, EPROTO);
    2036 return;
    2037 }
    2038
    2039 if (arg->major > 7) {
    2040 /* Wait for a second INIT request with a 7.X version */
    2041 send_reply_ok(req, &outarg, sizeof(outarg));
    2042 return;
    2043 }
    2044
    2045 if (arg->minor >= 6) {
    2046 if (arg->max_readahead < se->conn.max_readahead)
    2047 se->conn.max_readahead = arg->max_readahead;
    2048 inargflags = arg->flags;
    2049 if (inargflags & FUSE_INIT_EXT)
    2050 inargflags = inargflags | (uint64_t) arg->flags2 << 32;
    2051 if (inargflags & FUSE_ASYNC_READ)
    2052 se->conn.capable_ext |= FUSE_CAP_ASYNC_READ;
    2053 if (inargflags & FUSE_POSIX_LOCKS)
    2054 se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS;
    2055 if (inargflags & FUSE_ATOMIC_O_TRUNC)
    2056 se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC;
    2057 if (inargflags & FUSE_EXPORT_SUPPORT)
    2058 se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT;
    2059 if (inargflags & FUSE_DONT_MASK)
    2060 se->conn.capable_ext |= FUSE_CAP_DONT_MASK;
    2061 if (inargflags & FUSE_FLOCK_LOCKS)
    2062 se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS;
    2063 if (inargflags & FUSE_AUTO_INVAL_DATA)
    2064 se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA;
    2065 if (inargflags & FUSE_DO_READDIRPLUS)
    2066 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS;
    2067 if (inargflags & FUSE_READDIRPLUS_AUTO)
    2068 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO;
    2069 if (inargflags & FUSE_ASYNC_DIO)
    2070 se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO;
    2071 if (inargflags & FUSE_WRITEBACK_CACHE)
    2072 se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE;
    2073 if (inargflags & FUSE_NO_OPEN_SUPPORT)
    2074 se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT;
    2075 if (inargflags & FUSE_PARALLEL_DIROPS)
    2076 se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS;
    2077 if (inargflags & FUSE_POSIX_ACL)
    2078 se->conn.capable_ext |= FUSE_CAP_POSIX_ACL;
    2079 if (inargflags & FUSE_HANDLE_KILLPRIV)
    2080 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV;
    2081 if (inargflags & FUSE_HANDLE_KILLPRIV_V2)
    2082 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2;
    2083 if (inargflags & FUSE_CACHE_SYMLINKS)
    2084 se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS;
    2085 if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
    2086 se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT;
    2087 if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
    2088 se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA;
    2089 if (inargflags & FUSE_SETXATTR_EXT)
    2090 se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT;
    2091 if (!(inargflags & FUSE_MAX_PAGES)) {
    2092 size_t max_bufsize =
    2093 FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
    2094 + FUSE_BUFFER_HEADER_SIZE;
    2095 if (bufsize > max_bufsize) {
    2096 bufsize = max_bufsize;
    2097 }
    2098 buf_reallocable = false;
    2099 }
    2100 if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP)
    2101 se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
    2102 if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY))
    2103 se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY;
    2104 if (inargflags & FUSE_PASSTHROUGH)
    2105 se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH;
    2106 if (inargflags & FUSE_NO_EXPORT_SUPPORT)
    2107 se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
    2108 } else {
    2109 se->conn.max_readahead = 0;
    2110 }
    2111
    2112 if (se->conn.proto_minor >= 14) {
    2113#ifdef HAVE_SPLICE
    2114#ifdef HAVE_VMSPLICE
    2115 if ((se->io == NULL) || (se->io->splice_send != NULL)) {
    2116 se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE |
    2118 }
    2119#endif
    2120 if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
    2121 se->conn.capable_ext |= FUSE_CAP_SPLICE_READ;
    2122 }
    2123#endif
    2124 }
    2125 if (se->conn.proto_minor >= 18)
    2126 se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR;
    2127
    2128 /* Default settings for modern filesystems.
    2129 *
    2130 * Most of these capabilities were disabled by default in
    2131 * libfuse2 for backwards compatibility reasons. In libfuse3,
    2132 * we can finally enable them by default (as long as they're
    2133 * supported by the kernel).
    2134 */
    2135#define LL_SET_DEFAULT(cond, cap) \
    2136 if ((cond)) \
    2137 fuse_set_feature_flag(&se->conn, cap)
    2138
    2139 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
    2140 LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
    2141 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
    2142 LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
    2143 LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
    2144 LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
    2145 LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
    2147 LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
    2148 LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
    2149 LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
    2151
    2152 /* This could safely become default, but libfuse needs an API extension
    2153 * to support it
    2154 * LL_SET_DEFAULT(1, FUSE_CAP_SETXATTR_EXT);
    2155 */
    2156
    2157 se->conn.time_gran = 1;
    2158
    2159 se->got_init = 1;
    2160 if (se->op.init) {
    2161 uint64_t want_ext_default = se->conn.want_ext;
    2162 uint32_t want_default = fuse_lower_32_bits(se->conn.want_ext);
    2163 int rc;
    2164
    2165 // Apply the first 32 bits of capable_ext to capable
    2166 se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext);
    2167 se->conn.want = want_default;
    2168
    2169 se->op.init(se->userdata, &se->conn);
    2170
    2171 /*
    2172 * se->conn.want is 32-bit value and deprecated in favour of
    2173 * se->conn.want_ext
    2174 * Userspace might still use conn.want - we need to convert it
    2175 */
    2176 rc = convert_to_conn_want_ext(&se->conn, want_ext_default,
    2177 want_default);
    2178 if (rc != 0) {
    2179 fuse_reply_err(req, EPROTO);
    2180 se->error = -EPROTO;
    2182 return;
    2183 }
    2184 }
    2185
    2186 if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
    2187 fuse_reply_err(req, EPROTO);
    2188 se->error = -EPROTO;
    2190 return;
    2191 }
    2192
    2193 unsigned max_read_mo = get_max_read(se->mo);
    2194 if (se->conn.max_read != max_read_mo) {
    2195 fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() "
    2196 "requested different maximum read size (%u vs %u)\n",
    2197 se->conn.max_read, max_read_mo);
    2198 fuse_reply_err(req, EPROTO);
    2199 se->error = -EPROTO;
    2201 return;
    2202 }
    2203
    2204 if (bufsize < FUSE_MIN_READ_BUFFER) {
    2205 fuse_log(FUSE_LOG_ERR,
    2206 "fuse: warning: buffer size too small: %zu\n",
    2207 bufsize);
    2208 bufsize = FUSE_MIN_READ_BUFFER;
    2209 }
    2210
    2211 if (buf_reallocable)
    2212 bufsize = UINT_MAX;
    2213 se->conn.max_write = MIN(se->conn.max_write, bufsize - FUSE_BUFFER_HEADER_SIZE);
    2214 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
    2215
    2216 if (arg->flags & FUSE_MAX_PAGES) {
    2217 outarg.flags |= FUSE_MAX_PAGES;
    2218 outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
    2219 }
    2220 outargflags = outarg.flags;
    2221 /* Always enable big writes, this is superseded
    2222 by the max_write option */
    2223 outargflags |= FUSE_BIG_WRITES;
    2224
    2225 if (se->conn.want_ext & FUSE_CAP_ASYNC_READ)
    2226 outargflags |= FUSE_ASYNC_READ;
    2227 if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS)
    2228 outargflags |= FUSE_POSIX_LOCKS;
    2229 if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC)
    2230 outargflags |= FUSE_ATOMIC_O_TRUNC;
    2231 if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT)
    2232 outargflags |= FUSE_EXPORT_SUPPORT;
    2233 if (se->conn.want_ext & FUSE_CAP_DONT_MASK)
    2234 outargflags |= FUSE_DONT_MASK;
    2235 if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS)
    2236 outargflags |= FUSE_FLOCK_LOCKS;
    2237 if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA)
    2238 outargflags |= FUSE_AUTO_INVAL_DATA;
    2239 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS)
    2240 outargflags |= FUSE_DO_READDIRPLUS;
    2241 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO)
    2242 outargflags |= FUSE_READDIRPLUS_AUTO;
    2243 if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO)
    2244 outargflags |= FUSE_ASYNC_DIO;
    2245 if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE)
    2246 outargflags |= FUSE_WRITEBACK_CACHE;
    2247 if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS)
    2248 outargflags |= FUSE_PARALLEL_DIROPS;
    2249 if (se->conn.want_ext & FUSE_CAP_POSIX_ACL)
    2250 outargflags |= FUSE_POSIX_ACL;
    2251 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV)
    2252 outargflags |= FUSE_HANDLE_KILLPRIV;
    2253 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2)
    2254 outargflags |= FUSE_HANDLE_KILLPRIV_V2;
    2255 if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS)
    2256 outargflags |= FUSE_CACHE_SYMLINKS;
    2257 if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA)
    2258 outargflags |= FUSE_EXPLICIT_INVAL_DATA;
    2259 if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT)
    2260 outargflags |= FUSE_SETXATTR_EXT;
    2261 if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
    2262 outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
    2263 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) {
    2264 outargflags |= FUSE_PASSTHROUGH;
    2265 /*
    2266 * outarg.max_stack_depth includes the fuse stack layer,
    2267 * so it is one more than max_backing_stack_depth.
    2268 */
    2269 outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1;
    2270 }
    2271 if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT)
    2272 outargflags |= FUSE_NO_EXPORT_SUPPORT;
    2273
    2274 if (inargflags & FUSE_INIT_EXT) {
    2275 outargflags |= FUSE_INIT_EXT;
    2276 outarg.flags2 = outargflags >> 32;
    2277 }
    2278
    2279 outarg.flags = outargflags;
    2280
    2281 outarg.max_readahead = se->conn.max_readahead;
    2282 outarg.max_write = se->conn.max_write;
    2283 if (se->conn.proto_minor >= 13) {
    2284 if (se->conn.max_background >= (1 << 16))
    2285 se->conn.max_background = (1 << 16) - 1;
    2286 if (se->conn.congestion_threshold > se->conn.max_background)
    2287 se->conn.congestion_threshold = se->conn.max_background;
    2288 if (!se->conn.congestion_threshold) {
    2289 se->conn.congestion_threshold =
    2290 se->conn.max_background * 3 / 4;
    2291 }
    2292
    2293 outarg.max_background = se->conn.max_background;
    2294 outarg.congestion_threshold = se->conn.congestion_threshold;
    2295 }
    2296 if (se->conn.proto_minor >= 23)
    2297 outarg.time_gran = se->conn.time_gran;
    2298
    2299 if (se->debug) {
    2300 fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
    2301 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
    2302 fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
    2303 outarg.max_readahead);
    2304 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
    2305 fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
    2306 outarg.max_background);
    2307 fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
    2308 outarg.congestion_threshold);
    2309 fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
    2310 outarg.time_gran);
    2311 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH)
    2312 fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n",
    2313 outarg.max_stack_depth);
    2314 }
    2315 if (arg->minor < 5)
    2316 outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
    2317 else if (arg->minor < 23)
    2318 outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
    2319
    2320 send_reply_ok(req, &outarg, outargsize);
    2321}
    2322
    2323static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    2324{
    2325 struct fuse_session *se = req->se;
    2326
    2327 (void) nodeid;
    2328 (void) inarg;
    2329
    2330 se->got_destroy = 1;
    2331 se->got_init = 0;
    2332 if (se->op.destroy)
    2333 se->op.destroy(se->userdata);
    2334
    2335 send_reply_ok(req, NULL, 0);
    2336}
    2337
    2338static void list_del_nreq(struct fuse_notify_req *nreq)
    2339{
    2340 struct fuse_notify_req *prev = nreq->prev;
    2341 struct fuse_notify_req *next = nreq->next;
    2342 prev->next = next;
    2343 next->prev = prev;
    2344}
    2345
    2346static void list_add_nreq(struct fuse_notify_req *nreq,
    2347 struct fuse_notify_req *next)
    2348{
    2349 struct fuse_notify_req *prev = next->prev;
    2350 nreq->next = next;
    2351 nreq->prev = prev;
    2352 prev->next = nreq;
    2353 next->prev = nreq;
    2354}
    2355
    2356static void list_init_nreq(struct fuse_notify_req *nreq)
    2357{
    2358 nreq->next = nreq;
    2359 nreq->prev = nreq;
    2360}
    2361
    2362static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
    2363 const void *inarg, const struct fuse_buf *buf)
    2364{
    2365 struct fuse_session *se = req->se;
    2366 struct fuse_notify_req *nreq;
    2367 struct fuse_notify_req *head;
    2368
    2369 pthread_mutex_lock(&se->lock);
    2370 head = &se->notify_list;
    2371 for (nreq = head->next; nreq != head; nreq = nreq->next) {
    2372 if (nreq->unique == req->unique) {
    2373 list_del_nreq(nreq);
    2374 break;
    2375 }
    2376 }
    2377 pthread_mutex_unlock(&se->lock);
    2378
    2379 if (nreq != head)
    2380 nreq->reply(nreq, req, nodeid, inarg, buf);
    2381}
    2382
    2383static int send_notify_iov(struct fuse_session *se, int notify_code,
    2384 struct iovec *iov, int count)
    2385{
    2386 struct fuse_out_header out;
    2387
    2388 if (!se->got_init)
    2389 return -ENOTCONN;
    2390
    2391 out.unique = 0;
    2392 out.error = notify_code;
    2393 iov[0].iov_base = &out;
    2394 iov[0].iov_len = sizeof(struct fuse_out_header);
    2395
    2396 return fuse_send_msg(se, NULL, iov, count);
    2397}
    2398
    2399int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
    2400{
    2401 if (ph != NULL) {
    2402 struct fuse_notify_poll_wakeup_out outarg;
    2403 struct iovec iov[2];
    2404
    2405 outarg.kh = ph->kh;
    2406
    2407 iov[1].iov_base = &outarg;
    2408 iov[1].iov_len = sizeof(outarg);
    2409
    2410 return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
    2411 } else {
    2412 return 0;
    2413 }
    2414}
    2415
    2416int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
    2417 off_t off, off_t len)
    2418{
    2419 struct fuse_notify_inval_inode_out outarg;
    2420 struct iovec iov[2];
    2421
    2422 if (!se)
    2423 return -EINVAL;
    2424
    2425 if (se->conn.proto_minor < 12)
    2426 return -ENOSYS;
    2427
    2428 outarg.ino = ino;
    2429 outarg.off = off;
    2430 outarg.len = len;
    2431
    2432 iov[1].iov_base = &outarg;
    2433 iov[1].iov_len = sizeof(outarg);
    2434
    2435 return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
    2436}
    2437
    2457static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
    2458 const char *name, size_t namelen,
    2459 enum fuse_notify_entry_flags flags)
    2460{
    2461 struct fuse_notify_inval_entry_out outarg;
    2462 struct iovec iov[3];
    2463
    2464 if (!se)
    2465 return -EINVAL;
    2466
    2467 if (se->conn.proto_minor < 12)
    2468 return -ENOSYS;
    2469
    2470 outarg.parent = parent;
    2471 outarg.namelen = namelen;
    2472 outarg.flags = 0;
    2473 if (flags & FUSE_LL_EXPIRE_ONLY)
    2474 outarg.flags |= FUSE_EXPIRE_ONLY;
    2475
    2476 iov[1].iov_base = &outarg;
    2477 iov[1].iov_len = sizeof(outarg);
    2478 iov[2].iov_base = (void *)name;
    2479 iov[2].iov_len = namelen + 1;
    2480
    2481 return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
    2482}
    2483
    2484int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
    2485 const char *name, size_t namelen)
    2486{
    2487 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
    2488}
    2489
    2490int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
    2491 const char *name, size_t namelen)
    2492{
    2493 if (!se)
    2494 return -EINVAL;
    2495
    2496 if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY))
    2497 return -ENOSYS;
    2498
    2499 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
    2500}
    2501
    2502
    2503int fuse_lowlevel_notify_delete(struct fuse_session *se,
    2504 fuse_ino_t parent, fuse_ino_t child,
    2505 const char *name, size_t namelen)
    2506{
    2507 struct fuse_notify_delete_out outarg;
    2508 struct iovec iov[3];
    2509
    2510 if (!se)
    2511 return -EINVAL;
    2512
    2513 if (se->conn.proto_minor < 18)
    2514 return -ENOSYS;
    2515
    2516 outarg.parent = parent;
    2517 outarg.child = child;
    2518 outarg.namelen = namelen;
    2519 outarg.padding = 0;
    2520
    2521 iov[1].iov_base = &outarg;
    2522 iov[1].iov_len = sizeof(outarg);
    2523 iov[2].iov_base = (void *)name;
    2524 iov[2].iov_len = namelen + 1;
    2525
    2526 return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
    2527}
    2528
    2529int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
    2530 off_t offset, struct fuse_bufvec *bufv,
    2531 enum fuse_buf_copy_flags flags)
    2532{
    2533 struct fuse_out_header out;
    2534 struct fuse_notify_store_out outarg;
    2535 struct iovec iov[3];
    2536 size_t size = fuse_buf_size(bufv);
    2537 int res;
    2538
    2539 if (!se)
    2540 return -EINVAL;
    2541
    2542 if (se->conn.proto_minor < 15)
    2543 return -ENOSYS;
    2544
    2545 out.unique = 0;
    2546 out.error = FUSE_NOTIFY_STORE;
    2547
    2548 outarg.nodeid = ino;
    2549 outarg.offset = offset;
    2550 outarg.size = size;
    2551 outarg.padding = 0;
    2552
    2553 iov[0].iov_base = &out;
    2554 iov[0].iov_len = sizeof(out);
    2555 iov[1].iov_base = &outarg;
    2556 iov[1].iov_len = sizeof(outarg);
    2557
    2558 res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags);
    2559 if (res > 0)
    2560 res = -res;
    2561
    2562 return res;
    2563}
    2564
    2565struct fuse_retrieve_req {
    2566 struct fuse_notify_req nreq;
    2567 void *cookie;
    2568};
    2569
    2570static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
    2571 fuse_req_t req, fuse_ino_t ino,
    2572 const void *inarg,
    2573 const struct fuse_buf *ibuf)
    2574{
    2575 struct fuse_session *se = req->se;
    2576 struct fuse_retrieve_req *rreq =
    2577 container_of(nreq, struct fuse_retrieve_req, nreq);
    2578 const struct fuse_notify_retrieve_in *arg = inarg;
    2579 struct fuse_bufvec bufv = {
    2580 .buf[0] = *ibuf,
    2581 .count = 1,
    2582 };
    2583
    2584 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
    2585 bufv.buf[0].mem = PARAM(arg);
    2586
    2587 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    2588 sizeof(struct fuse_notify_retrieve_in);
    2589
    2590 if (bufv.buf[0].size < arg->size) {
    2591 fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
    2592 fuse_reply_none(req);
    2593 goto out;
    2594 }
    2595 bufv.buf[0].size = arg->size;
    2596
    2597 if (se->op.retrieve_reply) {
    2598 se->op.retrieve_reply(req, rreq->cookie, ino,
    2599 arg->offset, &bufv);
    2600 } else {
    2601 fuse_reply_none(req);
    2602 }
    2603out:
    2604 free(rreq);
    2605 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
    2606 fuse_ll_clear_pipe(se);
    2607}
    2608
    2609int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
    2610 size_t size, off_t offset, void *cookie)
    2611{
    2612 struct fuse_notify_retrieve_out outarg;
    2613 struct iovec iov[2];
    2614 struct fuse_retrieve_req *rreq;
    2615 int err;
    2616
    2617 if (!se)
    2618 return -EINVAL;
    2619
    2620 if (se->conn.proto_minor < 15)
    2621 return -ENOSYS;
    2622
    2623 rreq = malloc(sizeof(*rreq));
    2624 if (rreq == NULL)
    2625 return -ENOMEM;
    2626
    2627 pthread_mutex_lock(&se->lock);
    2628 rreq->cookie = cookie;
    2629 rreq->nreq.unique = se->notify_ctr++;
    2630 rreq->nreq.reply = fuse_ll_retrieve_reply;
    2631 list_add_nreq(&rreq->nreq, &se->notify_list);
    2632 pthread_mutex_unlock(&se->lock);
    2633
    2634 outarg.notify_unique = rreq->nreq.unique;
    2635 outarg.nodeid = ino;
    2636 outarg.offset = offset;
    2637 outarg.size = size;
    2638 outarg.padding = 0;
    2639
    2640 iov[1].iov_base = &outarg;
    2641 iov[1].iov_len = sizeof(outarg);
    2642
    2643 err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
    2644 if (err) {
    2645 pthread_mutex_lock(&se->lock);
    2646 list_del_nreq(&rreq->nreq);
    2647 pthread_mutex_unlock(&se->lock);
    2648 free(rreq);
    2649 }
    2650
    2651 return err;
    2652}
    2653
    2655{
    2656 return req->se->userdata;
    2657}
    2658
    2659const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
    2660{
    2661 return &req->ctx;
    2662}
    2663
    2665 void *data)
    2666{
    2667 pthread_mutex_lock(&req->lock);
    2668 pthread_mutex_lock(&req->se->lock);
    2669 req->u.ni.func = func;
    2670 req->u.ni.data = data;
    2671 pthread_mutex_unlock(&req->se->lock);
    2672 if (req->interrupted && func)
    2673 func(req, data);
    2674 pthread_mutex_unlock(&req->lock);
    2675}
    2676
    2678{
    2679 int interrupted;
    2680
    2681 pthread_mutex_lock(&req->se->lock);
    2682 interrupted = req->interrupted;
    2683 pthread_mutex_unlock(&req->se->lock);
    2684
    2685 return interrupted;
    2686}
    2687
    2688static struct {
    2689 void (*func)(fuse_req_t, fuse_ino_t, const void *);
    2690 const char *name;
    2691} fuse_ll_ops[] = {
    2692 [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
    2693 [FUSE_FORGET] = { do_forget, "FORGET" },
    2694 [FUSE_GETATTR] = { do_getattr, "GETATTR" },
    2695 [FUSE_SETATTR] = { do_setattr, "SETATTR" },
    2696 [FUSE_READLINK] = { do_readlink, "READLINK" },
    2697 [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
    2698 [FUSE_MKNOD] = { do_mknod, "MKNOD" },
    2699 [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
    2700 [FUSE_UNLINK] = { do_unlink, "UNLINK" },
    2701 [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
    2702 [FUSE_RENAME] = { do_rename, "RENAME" },
    2703 [FUSE_LINK] = { do_link, "LINK" },
    2704 [FUSE_OPEN] = { do_open, "OPEN" },
    2705 [FUSE_READ] = { do_read, "READ" },
    2706 [FUSE_WRITE] = { do_write, "WRITE" },
    2707 [FUSE_STATFS] = { do_statfs, "STATFS" },
    2708 [FUSE_RELEASE] = { do_release, "RELEASE" },
    2709 [FUSE_FSYNC] = { do_fsync, "FSYNC" },
    2710 [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
    2711 [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
    2712 [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
    2713 [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
    2714 [FUSE_FLUSH] = { do_flush, "FLUSH" },
    2715 [FUSE_INIT] = { do_init, "INIT" },
    2716 [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
    2717 [FUSE_READDIR] = { do_readdir, "READDIR" },
    2718 [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
    2719 [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
    2720 [FUSE_GETLK] = { do_getlk, "GETLK" },
    2721 [FUSE_SETLK] = { do_setlk, "SETLK" },
    2722 [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
    2723 [FUSE_ACCESS] = { do_access, "ACCESS" },
    2724 [FUSE_CREATE] = { do_create, "CREATE" },
    2725 [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" },
    2726 [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
    2727 [FUSE_BMAP] = { do_bmap, "BMAP" },
    2728 [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
    2729 [FUSE_POLL] = { do_poll, "POLL" },
    2730 [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
    2731 [FUSE_DESTROY] = { do_destroy, "DESTROY" },
    2732 [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
    2733 [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
    2734 [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
    2735 [FUSE_RENAME2] = { do_rename2, "RENAME2" },
    2736 [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
    2737 [FUSE_LSEEK] = { do_lseek, "LSEEK" },
    2738 [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
    2739};
    2740
    2741/*
    2742 * For ABI compatibility we cannot allow higher values than CUSE_INIT.
    2743 * Without ABI compatibility we could use the size of the array.
    2744 * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
    2745 */
    2746#define FUSE_MAXOP (CUSE_INIT + 1)
    2747
    2748static const char *opname(enum fuse_opcode opcode)
    2749{
    2750 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
    2751 return "???";
    2752 else
    2753 return fuse_ll_ops[opcode].name;
    2754}
    2755
    2756static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
    2757 struct fuse_bufvec *src)
    2758{
    2759 ssize_t res = fuse_buf_copy(dst, src, 0);
    2760 if (res < 0) {
    2761 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res));
    2762 return res;
    2763 }
    2764 if ((size_t)res < fuse_buf_size(dst)) {
    2765 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
    2766 return -1;
    2767 }
    2768 return 0;
    2769}
    2770
    2771void fuse_session_process_buf(struct fuse_session *se,
    2772 const struct fuse_buf *buf)
    2773{
    2774 fuse_session_process_buf_internal(se, buf, NULL);
    2775}
    2776
    2777/* libfuse internal handler */
    2778void fuse_session_process_buf_internal(struct fuse_session *se,
    2779 const struct fuse_buf *buf, struct fuse_chan *ch)
    2780{
    2781 const size_t write_header_size = sizeof(struct fuse_in_header) +
    2782 sizeof(struct fuse_write_in);
    2783 struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
    2784 struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
    2785 struct fuse_in_header *in;
    2786 const void *inarg;
    2787 struct fuse_req *req;
    2788 void *mbuf = NULL;
    2789 int err;
    2790 int res;
    2791
    2792 if (buf->flags & FUSE_BUF_IS_FD) {
    2793 if (buf->size < tmpbuf.buf[0].size)
    2794 tmpbuf.buf[0].size = buf->size;
    2795
    2796 mbuf = malloc(tmpbuf.buf[0].size);
    2797 if (mbuf == NULL) {
    2798 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n");
    2799 goto clear_pipe;
    2800 }
    2801 tmpbuf.buf[0].mem = mbuf;
    2802
    2803 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
    2804 if (res < 0)
    2805 goto clear_pipe;
    2806
    2807 in = mbuf;
    2808 } else {
    2809 in = buf->mem;
    2810 }
    2811
    2812 if (se->debug) {
    2813 fuse_log(FUSE_LOG_DEBUG,
    2814 "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
    2815 (unsigned long long) in->unique,
    2816 opname((enum fuse_opcode) in->opcode), in->opcode,
    2817 (unsigned long long) in->nodeid, buf->size, in->pid);
    2818 }
    2819
    2820 req = fuse_ll_alloc_req(se);
    2821 if (req == NULL) {
    2822 struct fuse_out_header out = {
    2823 .unique = in->unique,
    2824 .error = -ENOMEM,
    2825 };
    2826 struct iovec iov = {
    2827 .iov_base = &out,
    2828 .iov_len = sizeof(struct fuse_out_header),
    2829 };
    2830
    2831 fuse_send_msg(se, ch, &iov, 1);
    2832 goto clear_pipe;
    2833 }
    2834
    2835 req->unique = in->unique;
    2836 req->ctx.uid = in->uid;
    2837 req->ctx.gid = in->gid;
    2838 req->ctx.pid = in->pid;
    2839 req->ch = ch ? fuse_chan_get(ch) : NULL;
    2840
    2841 err = EIO;
    2842 if (!se->got_init) {
    2843 enum fuse_opcode expected;
    2844
    2845 expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
    2846 if (in->opcode != expected)
    2847 goto reply_err;
    2848 } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
    2849 goto reply_err;
    2850
    2851 err = EACCES;
    2852 /* Implement -o allow_root */
    2853 if (se->deny_others && in->uid != se->owner && in->uid != 0 &&
    2854 in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
    2855 in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
    2856 in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
    2857 in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
    2858 in->opcode != FUSE_NOTIFY_REPLY &&
    2859 in->opcode != FUSE_READDIRPLUS)
    2860 goto reply_err;
    2861
    2862 err = ENOSYS;
    2863 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
    2864 goto reply_err;
    2865 /* Do not process interrupt request */
    2866 if (se->conn.no_interrupt && in->opcode == FUSE_INTERRUPT) {
    2867 if (se->debug)
    2868 fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n");
    2869 goto reply_err;
    2870 }
    2871 if (!se->conn.no_interrupt && in->opcode != FUSE_INTERRUPT) {
    2872 struct fuse_req *intr;
    2873 pthread_mutex_lock(&se->lock);
    2874 intr = check_interrupt(se, req);
    2875 list_add_req(req, &se->list);
    2876 pthread_mutex_unlock(&se->lock);
    2877 if (intr)
    2878 fuse_reply_err(intr, EAGAIN);
    2879 }
    2880
    2881 if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
    2882 (in->opcode != FUSE_WRITE || !se->op.write_buf) &&
    2883 in->opcode != FUSE_NOTIFY_REPLY) {
    2884 void *newmbuf;
    2885
    2886 err = ENOMEM;
    2887 newmbuf = realloc(mbuf, buf->size);
    2888 if (newmbuf == NULL)
    2889 goto reply_err;
    2890 mbuf = newmbuf;
    2891
    2892 tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
    2893 tmpbuf.buf[0].mem = (char *)mbuf + write_header_size;
    2894
    2895 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
    2896 err = -res;
    2897 if (res < 0)
    2898 goto reply_err;
    2899
    2900 in = mbuf;
    2901 }
    2902
    2903 inarg = (void *) &in[1];
    2904 if (in->opcode == FUSE_WRITE && se->op.write_buf)
    2905 do_write_buf(req, in->nodeid, inarg, buf);
    2906 else if (in->opcode == FUSE_NOTIFY_REPLY)
    2907 do_notify_reply(req, in->nodeid, inarg, buf);
    2908 else
    2909 fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
    2910
    2911out_free:
    2912 free(mbuf);
    2913 return;
    2914
    2915reply_err:
    2916 fuse_reply_err(req, err);
    2917clear_pipe:
    2918 if (buf->flags & FUSE_BUF_IS_FD)
    2919 fuse_ll_clear_pipe(se);
    2920 goto out_free;
    2921}
    2922
    2923#define LL_OPTION(n,o,v) \
    2924 { n, offsetof(struct fuse_session, o), v }
    2925
    2926static const struct fuse_opt fuse_ll_opts[] = {
    2927 LL_OPTION("debug", debug, 1),
    2928 LL_OPTION("-d", debug, 1),
    2929 LL_OPTION("--debug", debug, 1),
    2930 LL_OPTION("allow_root", deny_others, 1),
    2932};
    2933
    2934void fuse_lowlevel_version(void)
    2935{
    2936 printf("using FUSE kernel interface version %i.%i\n",
    2937 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
    2938 fuse_mount_version();
    2939}
    2940
    2941void fuse_lowlevel_help(void)
    2942{
    2943 /* These are not all options, but the ones that are
    2944 potentially of interest to an end-user */
    2945 printf(
    2946" -o allow_other allow access by all users\n"
    2947" -o allow_root allow access by root\n"
    2948" -o auto_unmount auto unmount on process termination\n");
    2949}
    2950
    2951void fuse_session_destroy(struct fuse_session *se)
    2952{
    2953 struct fuse_ll_pipe *llp;
    2954
    2955 if (se->got_init && !se->got_destroy) {
    2956 if (se->op.destroy)
    2957 se->op.destroy(se->userdata);
    2958 }
    2959 llp = pthread_getspecific(se->pipe_key);
    2960 if (llp != NULL)
    2961 fuse_ll_pipe_free(llp);
    2962 pthread_key_delete(se->pipe_key);
    2963 pthread_mutex_destroy(&se->lock);
    2964 free(se->cuse_data);
    2965 if (se->fd != -1)
    2966 close(se->fd);
    2967 if (se->io != NULL)
    2968 free(se->io);
    2969 destroy_mount_opts(se->mo);
    2970 free(se);
    2971}
    2972
    2973
    2974static void fuse_ll_pipe_destructor(void *data)
    2975{
    2976 struct fuse_ll_pipe *llp = data;
    2977 fuse_ll_pipe_free(llp);
    2978}
    2979
    2980void fuse_buf_free(struct fuse_buf *buf)
    2981{
    2982 if (buf->mem == NULL)
    2983 return;
    2984
    2985 size_t write_header_sz =
    2986 sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in);
    2987
    2988 char *ptr = (char *)buf->mem - pagesize + write_header_sz;
    2989 free(ptr);
    2990 buf->mem = NULL;
    2991}
    2992
    2993/*
    2994 * This is used to allocate buffers that hold fuse requests
    2995 */
    2996static void *buf_alloc(size_t size, bool internal)
    2997{
    2998 /*
    2999 * For libfuse internal caller add in alignment. That cannot be done
    3000 * for an external caller, as it is not guaranteed that the external
    3001 * caller frees the raw pointer.
    3002 */
    3003 if (internal) {
    3004 size_t write_header_sz = sizeof(struct fuse_in_header) +
    3005 sizeof(struct fuse_write_in);
    3006 size_t new_size = ROUND_UP(size + write_header_sz, pagesize);
    3007
    3008 char *buf = aligned_alloc(pagesize, new_size);
    3009 if (buf == NULL)
    3010 return NULL;
    3011
    3012 buf += pagesize - write_header_sz;
    3013
    3014 return buf;
    3015 } else {
    3016 return malloc(size);
    3017 }
    3018}
    3019
    3020/*
    3021 *@param internal true if called from libfuse internal code
    3022 */
    3023static int _fuse_session_receive_buf(struct fuse_session *se,
    3024 struct fuse_buf *buf, struct fuse_chan *ch,
    3025 bool internal)
    3026{
    3027 int err;
    3028 ssize_t res;
    3029 size_t bufsize;
    3030#ifdef HAVE_SPLICE
    3031 struct fuse_ll_pipe *llp;
    3032 struct fuse_buf tmpbuf;
    3033
    3034pipe_retry:
    3035 bufsize = se->bufsize;
    3036
    3037 if (se->conn.proto_minor < 14 ||
    3038 !(se->conn.want_ext & FUSE_CAP_SPLICE_READ))
    3039 goto fallback;
    3040
    3041 llp = fuse_ll_get_pipe(se);
    3042 if (llp == NULL)
    3043 goto fallback;
    3044
    3045 if (llp->size < bufsize) {
    3046 if (llp->can_grow) {
    3047 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
    3048 if (res == -1) {
    3049 llp->can_grow = 0;
    3050 res = grow_pipe_to_max(llp->pipe[0]);
    3051 if (res > 0)
    3052 llp->size = res;
    3053 goto fallback;
    3054 }
    3055 llp->size = res;
    3056 }
    3057 if (llp->size < bufsize)
    3058 goto fallback;
    3059 }
    3060
    3061 if (se->io != NULL && se->io->splice_receive != NULL) {
    3062 res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
    3063 llp->pipe[1], NULL, bufsize, 0,
    3064 se->userdata);
    3065 } else {
    3066 res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
    3067 bufsize, 0);
    3068 }
    3069 err = errno;
    3070
    3071 if (fuse_session_exited(se))
    3072 return 0;
    3073
    3074 if (res == -1) {
    3075 if (err == ENODEV) {
    3076 /* Filesystem was unmounted, or connection was aborted
    3077 via /sys/fs/fuse/connections */
    3079 return 0;
    3080 }
    3081
    3082 /* FUSE_INIT might have increased the required bufsize */
    3083 if (err == EINVAL && bufsize < se->bufsize) {
    3084 fuse_ll_clear_pipe(se);
    3085 goto pipe_retry;
    3086 }
    3087
    3088 if (err != EINTR && err != EAGAIN)
    3089 perror("fuse: splice from device");
    3090 return -err;
    3091 }
    3092
    3093 if (res < sizeof(struct fuse_in_header)) {
    3094 fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n");
    3095 return -EIO;
    3096 }
    3097
    3098 tmpbuf = (struct fuse_buf){
    3099 .size = res,
    3100 .flags = FUSE_BUF_IS_FD,
    3101 .fd = llp->pipe[0],
    3102 };
    3103
    3104 /*
    3105 * Don't bother with zero copy for small requests.
    3106 * fuse_loop_mt() needs to check for FORGET so this more than
    3107 * just an optimization.
    3108 */
    3109 if (res < sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) +
    3110 pagesize) {
    3111 struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
    3112 struct fuse_bufvec dst = { .count = 1 };
    3113
    3114 if (!buf->mem) {
    3115 buf->mem = buf_alloc(bufsize, internal);
    3116 if (!buf->mem) {
    3117 fuse_log(
    3118 FUSE_LOG_ERR,
    3119 "fuse: failed to allocate read buffer\n");
    3120 return -ENOMEM;
    3121 }
    3122 buf->mem_size = bufsize;
    3123 }
    3124 buf->size = bufsize;
    3125 buf->flags = 0;
    3126 dst.buf[0] = *buf;
    3127
    3128 res = fuse_buf_copy(&dst, &src, 0);
    3129 if (res < 0) {
    3130 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n",
    3131 strerror(-res));
    3132 fuse_ll_clear_pipe(se);
    3133 return res;
    3134 }
    3135 if (res < tmpbuf.size) {
    3136 fuse_log(FUSE_LOG_ERR,
    3137 "fuse: copy from pipe: short read\n");
    3138 fuse_ll_clear_pipe(se);
    3139 return -EIO;
    3140 }
    3141 assert(res == tmpbuf.size);
    3142
    3143 } else {
    3144 /* Don't overwrite buf->mem, as that would cause a leak */
    3145 buf->fd = tmpbuf.fd;
    3146 buf->flags = tmpbuf.flags;
    3147 }
    3148 buf->size = tmpbuf.size;
    3149
    3150 return res;
    3151
    3152fallback:
    3153#endif
    3154 bufsize = internal ? buf->mem_size : se->bufsize;
    3155 if (!buf->mem) {
    3156 bufsize = se->bufsize; /* might have changed */
    3157 buf->mem = buf_alloc(bufsize, internal);
    3158 if (!buf->mem) {
    3159 fuse_log(FUSE_LOG_ERR,
    3160 "fuse: failed to allocate read buffer\n");
    3161 return -ENOMEM;
    3162 }
    3163
    3164 if (internal)
    3165 buf->mem_size = bufsize;
    3166 }
    3167
    3168restart:
    3169 if (se->io != NULL) {
    3170 /* se->io->read is never NULL if se->io is not NULL as
    3171 specified by fuse_session_custom_io()*/
    3172 res = se->io->read(ch ? ch->fd : se->fd, buf->mem, bufsize,
    3173 se->userdata);
    3174 } else {
    3175 res = read(ch ? ch->fd : se->fd, buf->mem, bufsize);
    3176 }
    3177 err = errno;
    3178
    3179 if (fuse_session_exited(se))
    3180 return 0;
    3181 if (res == -1) {
    3182 if (err == EINVAL && internal && se->bufsize > bufsize) {
    3183 /* FUSE_INIT might have increased the required bufsize */
    3184 bufsize = se->bufsize;
    3185 void *newbuf = buf_alloc(bufsize, internal);
    3186 if (!newbuf) {
    3187 fuse_log(
    3188 FUSE_LOG_ERR,
    3189 "fuse: failed to (re)allocate read buffer\n");
    3190 return -ENOMEM;
    3191 }
    3192 fuse_buf_free(buf);
    3193 buf->mem = newbuf;
    3194 buf->mem_size = bufsize;
    3195 goto restart;
    3196 }
    3197
    3198 /* ENOENT means the operation was interrupted, it's safe
    3199 to restart */
    3200 if (err == ENOENT)
    3201 goto restart;
    3202
    3203 if (err == ENODEV) {
    3204 /* Filesystem was unmounted, or connection was aborted
    3205 via /sys/fs/fuse/connections */
    3207 return 0;
    3208 }
    3209 /* Errors occurring during normal operation: EINTR (read
    3210 interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
    3211 umounted) */
    3212 if (err != EINTR && err != EAGAIN)
    3213 perror("fuse: reading device");
    3214 return -err;
    3215 }
    3216 if ((size_t)res < sizeof(struct fuse_in_header)) {
    3217 fuse_log(FUSE_LOG_ERR, "short read on fuse device\n");
    3218 return -EIO;
    3219 }
    3220
    3221 buf->size = res;
    3222
    3223 return res;
    3224}
    3225
    3226int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
    3227{
    3228 return _fuse_session_receive_buf(se, buf, NULL, false);
    3229}
    3230
    3231/* libfuse internal handler */
    3232int fuse_session_receive_buf_internal(struct fuse_session *se,
    3233 struct fuse_buf *buf,
    3234 struct fuse_chan *ch)
    3235{
    3236 /*
    3237 * if run internally thread buffers are from libfuse - we can
    3238 * reallocate them
    3239 */
    3240 if (unlikely(!se->got_init) && !se->buf_reallocable)
    3241 se->buf_reallocable = true;
    3242
    3243 return _fuse_session_receive_buf(se, buf, ch, true);
    3244}
    3245
    3246struct fuse_session *
    3247fuse_session_new_versioned(struct fuse_args *args,
    3248 const struct fuse_lowlevel_ops *op, size_t op_size,
    3249 struct libfuse_version *version, void *userdata)
    3250{
    3251 int err;
    3252 struct fuse_session *se;
    3253 struct mount_opts *mo;
    3254
    3255 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
    3256 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
    3257 op_size = sizeof(struct fuse_lowlevel_ops);
    3258 }
    3259
    3260 if (args->argc == 0) {
    3261 fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
    3262 return NULL;
    3263 }
    3264
    3265 se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
    3266 if (se == NULL) {
    3267 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
    3268 goto out1;
    3269 }
    3270 se->fd = -1;
    3271 se->conn.max_write = FUSE_DEFAULT_MAX_PAGES_LIMIT * getpagesize();
    3272 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
    3273 se->conn.max_readahead = UINT_MAX;
    3274
    3275 /* Parse options */
    3276 if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
    3277 goto out2;
    3278 if(se->deny_others) {
    3279 /* Allowing access only by root is done by instructing
    3280 * kernel to allow access by everyone, and then restricting
    3281 * access to root and mountpoint owner in libfuse.
    3282 */
    3283 // We may be adding the option a second time, but
    3284 // that doesn't hurt.
    3285 if(fuse_opt_add_arg(args, "-oallow_other") == -1)
    3286 goto out2;
    3287 }
    3288 mo = parse_mount_opts(args);
    3289 if (mo == NULL)
    3290 goto out3;
    3291
    3292 if(args->argc == 1 &&
    3293 args->argv[0][0] == '-') {
    3294 fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
    3295 "will be ignored\n");
    3296 } else if (args->argc != 1) {
    3297 int i;
    3298 fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
    3299 for(i = 1; i < args->argc-1; i++)
    3300 fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
    3301 fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
    3302 goto out4;
    3303 }
    3304
    3305 if (se->debug)
    3306 fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION);
    3307
    3308 list_init_req(&se->list);
    3309 list_init_req(&se->interrupts);
    3310 list_init_nreq(&se->notify_list);
    3311 se->notify_ctr = 1;
    3312 pthread_mutex_init(&se->lock, NULL);
    3313
    3314 err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
    3315 if (err) {
    3316 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
    3317 strerror(err));
    3318 goto out5;
    3319 }
    3320
    3321 memcpy(&se->op, op, op_size);
    3322 se->owner = getuid();
    3323 se->userdata = userdata;
    3324
    3325 se->mo = mo;
    3326
    3327 /* Fuse server application should pass the version it was compiled
    3328 * against and pass it. If a libfuse version accidentally introduces an
    3329 * ABI incompatibility, it might be possible to 'fix' that at run time,
    3330 * by checking the version numbers.
    3331 */
    3332 se->version = *version;
    3333
    3334 return se;
    3335
    3336out5:
    3337 pthread_mutex_destroy(&se->lock);
    3338out4:
    3339 fuse_opt_free_args(args);
    3340out3:
    3341 if (mo != NULL)
    3342 destroy_mount_opts(mo);
    3343out2:
    3344 free(se);
    3345out1:
    3346 return NULL;
    3347}
    3348
    3349struct fuse_session *fuse_session_new_30(struct fuse_args *args,
    3350 const struct fuse_lowlevel_ops *op,
    3351 size_t op_size, void *userdata);
    3352struct fuse_session *fuse_session_new_30(struct fuse_args *args,
    3353 const struct fuse_lowlevel_ops *op,
    3354 size_t op_size,
    3355 void *userdata)
    3356{
    3357 /* unknown version */
    3358 struct libfuse_version version = { 0 };
    3359
    3360 return fuse_session_new_versioned(args, op, op_size, &version,
    3361 userdata);
    3362}
    3363
    3364FUSE_SYMVER("fuse_session_custom_io_317", "fuse_session_custom_io@@FUSE_3.17")
    3365int fuse_session_custom_io_317(struct fuse_session *se,
    3366 const struct fuse_custom_io *io, size_t op_size, int fd)
    3367{
    3368 if (sizeof(struct fuse_custom_io) < op_size) {
    3369 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
    3370 op_size = sizeof(struct fuse_custom_io);
    3371 }
    3372
    3373 if (fd < 0) {
    3374 fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
    3375 "fuse_session_custom_io()\n", fd);
    3376 return -EBADF;
    3377 }
    3378 if (io == NULL) {
    3379 fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
    3380 "fuse_session_custom_io()\n");
    3381 return -EINVAL;
    3382 } else if (io->read == NULL || io->writev == NULL) {
    3383 /* If the user provides their own file descriptor, we can't
    3384 guarantee that the default behavior of the io operations made
    3385 in libfuse will function properly. Therefore, we enforce the
    3386 user to implement these io operations when using custom io. */
    3387 fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
    3388 "implement both io->read() and io->writev\n");
    3389 return -EINVAL;
    3390 }
    3391
    3392 se->io = calloc(1, sizeof(struct fuse_custom_io));
    3393 if (se->io == NULL) {
    3394 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
    3395 "Error: %s\n", strerror(errno));
    3396 return -errno;
    3397 }
    3398
    3399 se->fd = fd;
    3400 memcpy(se->io, io, op_size);
    3401 return 0;
    3402}
    3403
    3404int fuse_session_custom_io_30(struct fuse_session *se,
    3405 const struct fuse_custom_io *io, int fd);
    3406FUSE_SYMVER("fuse_session_custom_io_30", "fuse_session_custom_io@FUSE_3.0")
    3407int fuse_session_custom_io_30(struct fuse_session *se,
    3408 const struct fuse_custom_io *io, int fd)
    3409{
    3410 return fuse_session_custom_io_317(se, io,
    3411 offsetof(struct fuse_custom_io, clone_fd), fd);
    3412}
    3413
    3414int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
    3415{
    3416 int fd;
    3417
    3418 if (mountpoint == NULL) {
    3419 fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n");
    3420 return -1;
    3421 }
    3422
    3423 /*
    3424 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
    3425 * would ensue.
    3426 */
    3427 do {
    3428 fd = open("/dev/null", O_RDWR);
    3429 if (fd > 2)
    3430 close(fd);
    3431 } while (fd >= 0 && fd <= 2);
    3432
    3433 /*
    3434 * To allow FUSE daemons to run without privileges, the caller may open
    3435 * /dev/fuse before launching the file system and pass on the file
    3436 * descriptor by specifying /dev/fd/N as the mount point. Note that the
    3437 * parent process takes care of performing the mount in this case.
    3438 */
    3439 fd = fuse_mnt_parse_fuse_fd(mountpoint);
    3440 if (fd != -1) {
    3441 if (fcntl(fd, F_GETFD) == -1) {
    3442 fuse_log(FUSE_LOG_ERR,
    3443 "fuse: Invalid file descriptor /dev/fd/%u\n",
    3444 fd);
    3445 return -1;
    3446 }
    3447 se->fd = fd;
    3448 return 0;
    3449 }
    3450
    3451 /* Open channel */
    3452 fd = fuse_kern_mount(mountpoint, se->mo);
    3453 if (fd == -1)
    3454 return -1;
    3455 se->fd = fd;
    3456
    3457 /* Save mountpoint */
    3458 se->mountpoint = strdup(mountpoint);
    3459 if (se->mountpoint == NULL)
    3460 goto error_out;
    3461
    3462 return 0;
    3463
    3464error_out:
    3465 fuse_kern_unmount(mountpoint, fd);
    3466 return -1;
    3467}
    3468
    3469int fuse_session_fd(struct fuse_session *se)
    3470{
    3471 return se->fd;
    3472}
    3473
    3474void fuse_session_unmount(struct fuse_session *se)
    3475{
    3476 if (se->mountpoint != NULL) {
    3477 fuse_kern_unmount(se->mountpoint, se->fd);
    3478 se->fd = -1;
    3479 free(se->mountpoint);
    3480 se->mountpoint = NULL;
    3481 }
    3482}
    3483
    3484#ifdef linux
    3485int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
    3486{
    3487 char *buf;
    3488 size_t bufsize = 1024;
    3489 char path[128];
    3490 int ret;
    3491 int fd;
    3492 unsigned long pid = req->ctx.pid;
    3493 char *s;
    3494
    3495 sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
    3496
    3497retry:
    3498 buf = malloc(bufsize);
    3499 if (buf == NULL)
    3500 return -ENOMEM;
    3501
    3502 ret = -EIO;
    3503 fd = open(path, O_RDONLY);
    3504 if (fd == -1)
    3505 goto out_free;
    3506
    3507 ret = read(fd, buf, bufsize);
    3508 close(fd);
    3509 if (ret < 0) {
    3510 ret = -EIO;
    3511 goto out_free;
    3512 }
    3513
    3514 if ((size_t)ret == bufsize) {
    3515 free(buf);
    3516 bufsize *= 4;
    3517 goto retry;
    3518 }
    3519
    3520 buf[ret] = '\0';
    3521 ret = -EIO;
    3522 s = strstr(buf, "\nGroups:");
    3523 if (s == NULL)
    3524 goto out_free;
    3525
    3526 s += 8;
    3527 ret = 0;
    3528 while (1) {
    3529 char *end;
    3530 unsigned long val = strtoul(s, &end, 0);
    3531 if (end == s)
    3532 break;
    3533
    3534 s = end;
    3535 if (ret < size)
    3536 list[ret] = val;
    3537 ret++;
    3538 }
    3539
    3540out_free:
    3541 free(buf);
    3542 return ret;
    3543}
    3544#else /* linux */
    3545/*
    3546 * This is currently not implemented on other than Linux...
    3547 */
    3548int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
    3549{
    3550 (void) req; (void) size; (void) list;
    3551 return -ENOSYS;
    3552}
    3553#endif
    3554
    3555/* Prevent spurious data race warning - we don't care
    3556 * about races for this flag */
    3557__attribute__((no_sanitize_thread))
    3558void fuse_session_exit(struct fuse_session *se)
    3559{
    3560 se->exited = 1;
    3561}
    3562
    3563__attribute__((no_sanitize_thread))
    3564void fuse_session_reset(struct fuse_session *se)
    3565{
    3566 se->exited = 0;
    3567 se->error = 0;
    3568}
    3569
    3570__attribute__((no_sanitize_thread))
    3571int fuse_session_exited(struct fuse_session *se)
    3572{
    3573 return se->exited;
    3574}
    #define FUSE_CAP_IOCTL_DIR
    #define FUSE_CAP_DONT_MASK
    #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
    #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
    @ 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]
    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:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t nonseekable
    Definition fuse_common.h:86
    int32_t backing_id
    uint32_t parallel_direct_writes
    uint32_t noflush
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2lib_2fuse__misc_8h_source.html0000644000175000017500000002435414770250311023763 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/lib_2fuse__misc_8h_source.html0000644000175000017500000002416115002273247021124 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 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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2fuse__opt_8c_source.html0000644000175000017500000024002314770250311023616 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/lib_2fuse__opt_8c_source.html0000644000175000017500000023556215002273247020777 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 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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2fuse__signals_8c_source.html0000644000175000017500000010405714770250311024462 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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#define BT_STACK_SZ (1024 * 1024)
    31static void *backtrace_buffer[BT_STACK_SZ];
    32
    33static void dump_stack(void)
    34{
    35#ifdef HAVE_BACKTRACE
    36 char **strings;
    37
    38 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
    39 strings = backtrace_symbols(backtrace_buffer, nptrs);
    40
    41 if (strings == NULL) {
    42 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
    43 strerror(errno));
    44 return;
    45 }
    46
    47 for (int idx = 0; idx < nptrs; idx++)
    48 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
    49
    50 free(strings);
    51#endif
    52}
    53
    54static void exit_handler(int sig)
    55{
    56 if (fuse_instance == NULL)
    57 return;
    58
    59 fuse_session_exit(fuse_instance);
    60
    61 if (sig < 0) {
    62 fuse_log(FUSE_LOG_ERR,
    63 "assertion error: signal value <= 0\n");
    64 dump_stack();
    65 abort();
    66 fuse_instance->error = sig;
    67 }
    68
    69 fuse_instance->error = sig;
    70}
    71
    72static void exit_backtrace(int sig)
    73{
    74 if (fuse_instance == NULL)
    75 return;
    76
    77 fuse_session_exit(fuse_instance);
    78
    79 fuse_remove_signal_handlers(fuse_instance);
    80 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
    81 dump_stack();
    82 abort();
    83}
    84
    85
    86static void do_nothing(int sig)
    87{
    88 (void) sig;
    89}
    90
    91static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
    92{
    93 struct sigaction sa;
    94 struct sigaction old_sa;
    95
    96 memset(&sa, 0, sizeof(struct sigaction));
    97 sa.sa_handler = remove ? SIG_DFL : handler;
    98 sigemptyset(&(sa.sa_mask));
    99 sa.sa_flags = 0;
    100
    101 if (sigaction(sig, NULL, &old_sa) == -1) {
    102 perror("fuse: cannot get old signal handler");
    103 return -1;
    104 }
    105
    106 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
    107 sigaction(sig, &sa, NULL) == -1) {
    108 perror("fuse: cannot set signal handler");
    109 return -1;
    110 }
    111 return 0;
    112}
    113
    114static int _fuse_set_signal_handlers(int signals[], int nr_signals,
    115 void (*handler)(int))
    116{
    117 for (int idx = 0; idx < nr_signals; idx++) {
    118 int signal = signals[idx];
    119
    120 /*
    121 * If we used SIG_IGN instead of the do_nothing function,
    122 * then we would be unable to tell if we set SIG_IGN (and
    123 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
    124 * or if it was already set to SIG_IGN (and should be left
    125 * untouched.
    126 */
    127 if (set_one_signal_handler(signal, handler, 0) == -1) {
    128 fuse_log(FUSE_LOG_ERR,
    129 "Failed to install signal handler for sig %d\n",
    130 signal);
    131 return -1;
    132 }
    133 }
    134
    135 return 0;
    136}
    137
    138int fuse_set_signal_handlers(struct fuse_session *se)
    139{
    140 size_t nr_signals;
    141 int rc;
    142
    143 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
    144 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
    145 if (rc < 0)
    146 return rc;
    147
    148 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
    149 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
    150 if (rc < 0)
    151 return rc;
    152
    153 if (fuse_instance == NULL)
    154 fuse_instance = se;
    155 return 0;
    156}
    157
    158int fuse_set_fail_signal_handlers(struct fuse_session *se)
    159{
    160 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
    161
    162 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
    163 exit_backtrace);
    164 if (rc < 0)
    165 return rc;
    166
    167 if (fuse_instance == NULL)
    168 fuse_instance = se;
    169
    170 return 0;
    171}
    172
    173static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
    174 void (*handler)(int))
    175{
    176 for (int idx = 0; idx < nr_signals; idx++)
    177 set_one_signal_handler(signals[idx], handler, 1);
    178}
    179
    180void fuse_remove_signal_handlers(struct fuse_session *se)
    181{
    182 size_t nr_signals;
    183
    184 if (fuse_instance != se)
    185 fuse_log(FUSE_LOG_ERR,
    186 "fuse: fuse_remove_signal_handlers: unknown session\n");
    187 else
    188 fuse_instance = NULL;
    189
    190 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
    191 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
    192
    193 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
    194 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
    195
    196 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
    197 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
    198}
    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.17.2/doc/html/lib_2fuse__signals_8c_source.html0000644000175000017500000010423015002273247021620 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 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#define BT_STACK_SZ (1024 * 1024)
    31static void *backtrace_buffer[BT_STACK_SZ];
    32
    33static void dump_stack(void)
    34{
    35#ifdef HAVE_BACKTRACE
    36 char **strings;
    37
    38 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
    39 strings = backtrace_symbols(backtrace_buffer, nptrs);
    40
    41 if (strings == NULL) {
    42 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
    43 strerror(errno));
    44 return;
    45 }
    46
    47 for (int idx = 0; idx < nptrs; idx++)
    48 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
    49
    50 free(strings);
    51#endif
    52}
    53
    54static void exit_handler(int sig)
    55{
    56 if (fuse_instance == NULL)
    57 return;
    58
    59 fuse_session_exit(fuse_instance);
    60
    61 if (sig < 0) {
    62 fuse_log(FUSE_LOG_ERR,
    63 "assertion error: signal value <= 0\n");
    64 dump_stack();
    65 abort();
    66 fuse_instance->error = sig;
    67 }
    68
    69 fuse_instance->error = sig;
    70}
    71
    72static void exit_backtrace(int sig)
    73{
    74 if (fuse_instance == NULL)
    75 return;
    76
    77 fuse_session_exit(fuse_instance);
    78
    79 fuse_remove_signal_handlers(fuse_instance);
    80 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
    81 dump_stack();
    82 abort();
    83}
    84
    85
    86static void do_nothing(int sig)
    87{
    88 (void) sig;
    89}
    90
    91static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
    92{
    93 struct sigaction sa;
    94 struct sigaction old_sa;
    95
    96 memset(&sa, 0, sizeof(struct sigaction));
    97 sa.sa_handler = remove ? SIG_DFL : handler;
    98 sigemptyset(&(sa.sa_mask));
    99 sa.sa_flags = 0;
    100
    101 if (sigaction(sig, NULL, &old_sa) == -1) {
    102 perror("fuse: cannot get old signal handler");
    103 return -1;
    104 }
    105
    106 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
    107 sigaction(sig, &sa, NULL) == -1) {
    108 perror("fuse: cannot set signal handler");
    109 return -1;
    110 }
    111 return 0;
    112}
    113
    114static int _fuse_set_signal_handlers(int signals[], int nr_signals,
    115 void (*handler)(int))
    116{
    117 for (int idx = 0; idx < nr_signals; idx++) {
    118 int signal = signals[idx];
    119
    120 /*
    121 * If we used SIG_IGN instead of the do_nothing function,
    122 * then we would be unable to tell if we set SIG_IGN (and
    123 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
    124 * or if it was already set to SIG_IGN (and should be left
    125 * untouched.
    126 */
    127 if (set_one_signal_handler(signal, handler, 0) == -1) {
    128 fuse_log(FUSE_LOG_ERR,
    129 "Failed to install signal handler for sig %d\n",
    130 signal);
    131 return -1;
    132 }
    133 }
    134
    135 return 0;
    136}
    137
    138int fuse_set_signal_handlers(struct fuse_session *se)
    139{
    140 size_t nr_signals;
    141 int rc;
    142
    143 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
    144 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
    145 if (rc < 0)
    146 return rc;
    147
    148 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
    149 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
    150 if (rc < 0)
    151 return rc;
    152
    153 /*
    154 * needs to be set independently if already set, as some applications
    155 * may have multiple sessions and might rely on traditional behavior
    156 * that the last session is used.
    157 */
    158 fuse_instance = se;
    159
    160 return 0;
    161}
    162
    163int fuse_set_fail_signal_handlers(struct fuse_session *se)
    164{
    165 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
    166
    167 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
    168 exit_backtrace);
    169 if (rc < 0)
    170 return rc;
    171
    172 /* See fuse_set_signal_handlers, why set unconditionally */
    173 fuse_instance = se;
    174
    175 return 0;
    176}
    177
    178static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
    179 void (*handler)(int))
    180{
    181 for (int idx = 0; idx < nr_signals; idx++)
    182 set_one_signal_handler(signals[idx], handler, 1);
    183}
    184
    185void fuse_remove_signal_handlers(struct fuse_session *se)
    186{
    187 size_t nr_signals;
    188
    189 if (fuse_instance != se)
    190 fuse_log(FUSE_LOG_ERR,
    191 "fuse: fuse_remove_signal_handlers: unknown session\n");
    192 else
    193 fuse_instance = NULL;
    194
    195 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
    196 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
    197
    198 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
    199 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
    200
    201 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
    202 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
    203}
    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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2helper_8c_source.html0000644000175000017500000032462714770250311023127 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    307/* Not symboled, as not part of the official API */
    308int fuse_main_real_versioned(int argc, char *argv[],
    309 const struct fuse_operations *op, size_t op_size,
    310 struct libfuse_version *version, void *user_data);
    311int fuse_main_real_versioned(int argc, char *argv[],
    312 const struct fuse_operations *op, size_t op_size,
    313 struct libfuse_version *version, void *user_data)
    314{
    315 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    316 struct fuse *fuse;
    317 struct fuse_cmdline_opts opts;
    318 int res;
    319 struct fuse_loop_config *loop_config = NULL;
    320
    321 if (fuse_parse_cmdline(&args, &opts) != 0)
    322 return 1;
    323
    324 if (opts.show_version) {
    325 printf("FUSE library version %s\n", PACKAGE_VERSION);
    327 res = 0;
    328 goto out1;
    329 }
    330
    331 if (opts.show_help) {
    332 if(args.argv[0][0] != '\0')
    333 printf("usage: %s [options] <mountpoint>\n\n",
    334 args.argv[0]);
    335 printf("FUSE options:\n");
    337 fuse_lib_help(&args);
    338 res = 0;
    339 goto out1;
    340 }
    341
    342 if (!opts.show_help &&
    343 !opts.mountpoint) {
    344 fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n");
    345 res = 2;
    346 goto out1;
    347 }
    348
    349 struct fuse *_fuse_new_31(struct fuse_args *args,
    350 const struct fuse_operations *op, size_t op_size,
    351 struct libfuse_version *version,
    352 void *user_data);
    353 fuse = _fuse_new_31(&args, op, op_size, version, user_data);
    354 if (fuse == NULL) {
    355 res = 3;
    356 goto out1;
    357 }
    358
    359 if (fuse_mount(fuse,opts.mountpoint) != 0) {
    360 res = 4;
    361 goto out2;
    362 }
    363
    364 if (fuse_daemonize(opts.foreground) != 0) {
    365 res = 5;
    366 goto out3;
    367 }
    368
    369 struct fuse_session *se = fuse_get_session(fuse);
    370 if (fuse_set_signal_handlers(se) != 0) {
    371 res = 6;
    372 goto out3;
    373 }
    374
    375 if (opts.singlethread)
    376 res = fuse_loop(fuse);
    377 else {
    378 loop_config = fuse_loop_cfg_create();
    379 if (loop_config == NULL) {
    380 res = 7;
    381 goto out3;
    382 }
    383
    384 fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd);
    385
    386 fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads);
    387 fuse_loop_cfg_set_max_threads(loop_config, opts.max_threads);
    388 res = fuse_loop_mt(fuse, loop_config);
    389 }
    390 if (res)
    391 res = 8;
    392
    394out3:
    395 fuse_unmount(fuse);
    396out2:
    397 fuse_destroy(fuse);
    398out1:
    399 fuse_loop_cfg_destroy(loop_config);
    400 free(opts.mountpoint);
    401 fuse_opt_free_args(&args);
    402 return res;
    403}
    404
    405/* Not symboled, as not part of the official API */
    406int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
    407 size_t op_size, void *user_data);
    408int fuse_main_real_30(int argc, char *argv[], const struct fuse_operations *op,
    409 size_t op_size, void *user_data)
    410{
    411 struct libfuse_version version = { 0 };
    412 return fuse_main_real_versioned(argc, argv, op, op_size, &version,
    413 user_data);
    414}
    415
    416void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
    417 struct fuse_conn_info *conn)
    418{
    419 if(opts->set_max_write)
    420 conn->max_write = opts->max_write;
    421 if(opts->set_max_background)
    422 conn->max_background = opts->max_background;
    423 if(opts->set_congestion_threshold)
    424 conn->congestion_threshold = opts->congestion_threshold;
    425 if(opts->set_time_gran)
    426 conn->time_gran = opts->time_gran;
    427 if(opts->set_max_readahead)
    428 conn->max_readahead = opts->max_readahead;
    429
    430#define LL_ENABLE(cond,cap) \
    431 if (cond) conn->want |= (cap)
    432#define LL_DISABLE(cond,cap) \
    433 if (cond) conn->want &= ~(cap)
    434
    435 LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
    436 LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
    437
    438 LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
    439 LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
    440
    441 LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
    442 LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
    443
    444 LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
    445 LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
    446
    447 LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
    448 LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
    449
    450 LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
    451 LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
    452
    453 LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
    454 LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
    455
    456 LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
    457 LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
    458
    459 LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
    460 LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
    461}
    462
    463struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
    464{
    465 struct fuse_conn_info_opts *opts;
    466
    467 opts = calloc(1, sizeof(struct fuse_conn_info_opts));
    468 if(opts == NULL) {
    469 fuse_log(FUSE_LOG_ERR, "calloc failed\n");
    470 return NULL;
    471 }
    472 if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
    473 free(opts);
    474 return NULL;
    475 }
    476 return opts;
    477}
    478
    479int fuse_open_channel(const char *mountpoint, const char* options)
    480{
    481 struct mount_opts *opts = NULL;
    482 int fd = -1;
    483 const char *argv[] = { "", "-o", options };
    484 int argc = sizeof(argv) / sizeof(argv[0]);
    485 struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv);
    486
    487 opts = parse_mount_opts(&args);
    488 if (opts == NULL)
    489 return -1;
    490
    491 fd = fuse_kern_mount(mountpoint, opts);
    492 destroy_mount_opts(opts);
    493
    494 return fd;
    495}
    int fuse_mount(struct fuse *f, const char *mountpoint)
    Definition fuse.c:5204
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5153
    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:479
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4520
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5209
    int fuse_set_signal_handlers(struct fuse_session *se)
    struct fuse_conn_info_opts * fuse_parse_conn_info_opts(struct fuse_args *args)
    Definition helper.c:463
    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_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
    #define FUSE_CAP_AUTO_INVAL_DATA
    #define FUSE_CAP_SPLICE_READ
    #define FUSE_CAP_WRITEBACK_CACHE
    #define FUSE_CAP_ASYNC_READ
    #define FUSE_CAP_SPLICE_WRITE
    void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, struct fuse_conn_info *conn)
    Definition helper.c:416
    #define FUSE_CAP_POSIX_LOCKS
    #define FUSE_CAP_READDIRPLUS_AUTO
    #define FUSE_CAP_ASYNC_DIO
    #define FUSE_CAP_READDIRPLUS
    #define FUSE_CAP_SPLICE_MOVE
    #define FUSE_CAP_FLOCK_LOCKS
    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.17.2/doc/html/lib_2helper_8c_source.html0000644000175000017500000032363415002273247020271 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 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
    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 if (cond) conn->want_ext |= (cap)
    428#define LL_DISABLE(cond,cap) \
    429 if (cond) conn->want_ext &= ~(cap)
    430
    431 LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
    432 LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
    433
    434 LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
    435 LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
    436
    437 LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
    438 LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
    439
    440 LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
    441 LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
    442
    443 LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
    444 LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
    445
    446 LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
    447 LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
    448
    449 LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
    450 LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
    451
    452 LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
    453 LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
    454
    455 LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
    456 LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
    457}
    458
    459struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
    460{
    461 struct fuse_conn_info_opts *opts;
    462
    463 opts = calloc(1, sizeof(struct fuse_conn_info_opts));
    464 if(opts == NULL) {
    465 fuse_log(FUSE_LOG_ERR, "calloc failed\n");
    466 return NULL;
    467 }
    468 if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
    469 free(opts);
    470 return NULL;
    471 }
    472 return opts;
    473}
    474
    475int fuse_open_channel(const char *mountpoint, const char* options)
    476{
    477 struct mount_opts *opts = NULL;
    478 int fd = -1;
    479 const char *argv[] = { "", "-o", options };
    480 int argc = sizeof(argv) / sizeof(argv[0]);
    481 struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv);
    482
    483 opts = parse_mount_opts(&args);
    484 if (opts == NULL)
    485 return -1;
    486
    487 fd = fuse_kern_mount(mountpoint, opts);
    488 destroy_mount_opts(opts);
    489
    490 return fd;
    491}
    int fuse_mount(struct fuse *f, const char *mountpoint)
    Definition fuse.c:5220
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5169
    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:4602
    void fuse_lib_help(struct fuse_args *args)
    Definition fuse.c:4769
    int fuse_open_channel(const char *mountpoint, const char *options)
    Definition helper.c:475
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4545
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5225
    #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:459
    #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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2modules_2iconv_8c_source.html0000644000175000017500000035030414770250311024567 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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:1415
    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.17.2/doc/html/lib_2modules_2iconv_8c_source.html0000644000175000017500000035017715002273247021744 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 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:4669
    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:4879
    fuse_fill_dir_flags
    Definition fuse.h:58
    fuse_readdir_flags
    Definition fuse.h:42
    #define FUSE_REGISTER_MODULE(name_, factory_)
    Definition fuse.h:1398
    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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2modules_2subdir_8c_source.html0000644000175000017500000033367614770250311024756 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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:1415
    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.17.2/doc/html/lib_2modules_2subdir_8c_source.html0000644000175000017500000033356415002273247022117 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 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:4669
    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:4879
    fuse_readdir_flags
    Definition fuse.h:42
    #define FUSE_REGISTER_MODULE(name_, factory_)
    Definition fuse.h:1398
    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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2mount_8c_source.html0000644000175000017500000035300714770250311023004 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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);
    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, "Spawaning %s to unumount failed",
    345 FUSERMOUNT_PROG);
    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_addclose(&action, 1);
    394 posix_spawn_file_actions_addclose(&action, 2);
    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");
    410 return -1;
    411 }
    412 // passed to child now, so can close here.
    413 close(fds[0]);
    414
    415 // Now fusermount3 will only exit when fds[1] closes automatically when our
    416 // process exits.
    417 return 0;
    418 // Note: fds[1] is leakend and doesn't get FD_CLOEXEC
    419}
    420
    421static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
    422 const char *opts, int quiet)
    423{
    424 int fds[2];
    425 pid_t pid;
    426 int res;
    427
    428 if (!mountpoint) {
    429 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
    430 return -1;
    431 }
    432
    433 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
    434 if(res == -1) {
    435 fuse_log(FUSE_LOG_ERR, "Running %s: socketpair() failed: %s\n",
    436 FUSERMOUNT_PROG, strerror(errno));
    437 return -1;
    438 }
    439
    440 char arg_fd_entry[30];
    441 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]);
    442 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
    443 /*
    444 * This helps to identify the FD hold by parent process.
    445 * In auto-unmount case, parent process can close this FD explicitly to do unmount.
    446 * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV).
    447 * One potential use case is to satisfy FD-Leak checks.
    448 */
    449 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]);
    450 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
    451
    452 char const *const argv[] = {
    453 FUSERMOUNT_PROG,
    454 "-o", opts ? opts : "",
    455 "--",
    456 mountpoint,
    457 NULL,
    458 };
    459
    460
    461 posix_spawn_file_actions_t action;
    462 posix_spawn_file_actions_init(&action);
    463
    464 if (quiet) {
    465 posix_spawn_file_actions_addclose(&action, 1);
    466 posix_spawn_file_actions_addclose(&action, 2);
    467 }
    468 posix_spawn_file_actions_addclose(&action, fds[1]);
    469
    470 int status = fusermount_posix_spawn(&action, argv, &pid);
    471
    472 posix_spawn_file_actions_destroy(&action);
    473
    474 if(status != 0) {
    475 close(fds[0]);
    476 close(fds[1]);
    477 fuse_log(FUSE_LOG_ERR, "posix_spawnp() for %s failed",
    478 FUSERMOUNT_PROG, strerror(errno));
    479 return -1;
    480 }
    481
    482 // passed to child now, so can close here.
    483 close(fds[0]);
    484
    485 int fd = receive_fd(fds[1]);
    486
    487 if (!mo->auto_unmount) {
    488 /* with auto_unmount option fusermount3 will not exit until
    489 this socket is closed */
    490 close(fds[1]);
    491 waitpid(pid, NULL, 0); /* bury zombie */
    492 }
    493
    494 if (fd >= 0)
    495 fcntl(fd, F_SETFD, FD_CLOEXEC);
    496
    497 return fd;
    498}
    499
    500#ifndef O_CLOEXEC
    501#define O_CLOEXEC 0
    502#endif
    503
    504static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
    505 const char *mnt_opts)
    506{
    507 char tmp[128];
    508 const char *devname = "/dev/fuse";
    509 char *source = NULL;
    510 char *type = NULL;
    511 struct stat stbuf;
    512 int fd;
    513 int res;
    514
    515 if (!mnt) {
    516 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
    517 return -1;
    518 }
    519
    520 res = stat(mnt, &stbuf);
    521 if (res == -1) {
    522 fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n",
    523 mnt, strerror(errno));
    524 return -1;
    525 }
    526
    527 fd = open(devname, O_RDWR | O_CLOEXEC);
    528 if (fd == -1) {
    529 if (errno == ENODEV || errno == ENOENT)
    530 fuse_log(FUSE_LOG_ERR, "fuse: device not found, try 'modprobe fuse' first\n");
    531 else
    532 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n",
    533 devname, strerror(errno));
    534 return -1;
    535 }
    536 if (!O_CLOEXEC)
    537 fcntl(fd, F_SETFD, FD_CLOEXEC);
    538
    539 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
    540 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
    541
    542 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
    543 if (res == -1)
    544 goto out_close;
    545
    546 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
    547 (mo->subtype ? strlen(mo->subtype) : 0) +
    548 strlen(devname) + 32);
    549
    550 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
    551 if (!type || !source) {
    552 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n");
    553 goto out_close;
    554 }
    555
    556 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
    557 if (mo->subtype) {
    558 strcat(type, ".");
    559 strcat(type, mo->subtype);
    560 }
    561 strcpy(source,
    562 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
    563
    564 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
    565 if (res == -1 && errno == ENODEV && mo->subtype) {
    566 /* Probably missing subtype support */
    567 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
    568 if (mo->fsname) {
    569 if (!mo->blkdev)
    570 sprintf(source, "%s#%s", mo->subtype,
    571 mo->fsname);
    572 } else {
    573 strcpy(source, type);
    574 }
    575 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
    576 }
    577 if (res == -1) {
    578 /*
    579 * Maybe kernel doesn't support unprivileged mounts, in this
    580 * case try falling back to fusermount3
    581 */
    582 if (errno == EPERM) {
    583 res = -2;
    584 } else {
    585 int errno_save = errno;
    586 if (mo->blkdev && errno == ENODEV &&
    587 !fuse_mnt_check_fuseblk())
    588 fuse_log(FUSE_LOG_ERR,
    589 "fuse: 'fuseblk' support missing\n");
    590 else
    591 fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n",
    592 strerror(errno_save));
    593 }
    594
    595 goto out_close;
    596 }
    597
    598#ifndef IGNORE_MTAB
    599 if (geteuid() == 0) {
    600 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
    601 res = -1;
    602 if (!newmnt)
    603 goto out_umount;
    604
    605 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
    606 mnt_opts);
    607 free(newmnt);
    608 if (res == -1)
    609 goto out_umount;
    610 }
    611#endif /* IGNORE_MTAB */
    612 free(type);
    613 free(source);
    614
    615 return fd;
    616
    617out_umount:
    618 umount2(mnt, 2); /* lazy umount */
    619out_close:
    620 free(type);
    621 free(source);
    622 close(fd);
    623 return res;
    624}
    625
    626static int get_mnt_flag_opts(char **mnt_optsp, int flags)
    627{
    628 int i;
    629
    630 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
    631 return -1;
    632
    633 for (i = 0; mount_flags[i].opt != NULL; i++) {
    634 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
    635 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
    636 return -1;
    637 }
    638 return 0;
    639}
    640
    641struct mount_opts *parse_mount_opts(struct fuse_args *args)
    642{
    643 struct mount_opts *mo;
    644
    645 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
    646 if (mo == NULL)
    647 return NULL;
    648
    649 memset(mo, 0, sizeof(struct mount_opts));
    650 mo->flags = MS_NOSUID | MS_NODEV;
    651
    652 if (args &&
    653 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
    654 goto err_out;
    655
    656 return mo;
    657
    658err_out:
    659 destroy_mount_opts(mo);
    660 return NULL;
    661}
    662
    663void destroy_mount_opts(struct mount_opts *mo)
    664{
    665 free(mo->fsname);
    666 free(mo->subtype);
    667 free(mo->fusermount_opts);
    668 free(mo->subtype_opt);
    669 free(mo->kernel_opts);
    670 free(mo->mtab_opts);
    671 free(mo);
    672}
    673
    674
    675int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
    676{
    677 int res = -1;
    678 char *mnt_opts = NULL;
    679
    680 res = -1;
    681 if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1)
    682 goto out;
    683 if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1)
    684 goto out;
    685 if (mo->mtab_opts && fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1)
    686 goto out;
    687
    688 res = fuse_mount_sys(mountpoint, mo, mnt_opts);
    689 if (res >= 0 && mo->auto_unmount) {
    690 if(0 > setup_auto_unmount(mountpoint, 0)) {
    691 // Something went wrong, let's umount like in fuse_mount_sys.
    692 umount2(mountpoint, MNT_DETACH); /* lazy umount */
    693 res = -1;
    694 }
    695 } else if (res == -2) {
    696 if (mo->fusermount_opts &&
    697 fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
    698 goto out;
    699
    700 if (mo->subtype) {
    701 char *tmp_opts = NULL;
    702
    703 res = -1;
    704 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
    705 fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) {
    706 free(tmp_opts);
    707 goto out;
    708 }
    709
    710 res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1);
    711 free(tmp_opts);
    712 if (res == -1)
    713 res = fuse_mount_fusermount(mountpoint, mo,
    714 mnt_opts, 0);
    715 } else {
    716 res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
    717 }
    718 }
    719out:
    720 free(mnt_opts);
    721 return res;
    722}
    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.17.2/doc/html/lib_2mount_8c_source.html0000644000175000017500000035406215002273247020153 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 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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2mount__bsd_8c_source.html0000644000175000017500000013737014770250311023776 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    16#include <sys/param.h>
    17#include "fuse_mount_compat.h"
    18
    19#include <sys/stat.h>
    20#include <sys/wait.h>
    21#include <sys/sysctl.h>
    22#include <sys/user.h>
    23#include <stdio.h>
    24#include <stdlib.h>
    25#include <unistd.h>
    26#include <stddef.h>
    27#include <fcntl.h>
    28#include <errno.h>
    29#include <string.h>
    30#include <paths.h>
    31#include <limits.h>
    32
    33#define FUSERMOUNT_PROG "mount_fusefs"
    34#define FUSE_DEV_TRUNK "/dev/fuse"
    35
    36enum {
    37 KEY_RO,
    38 KEY_KERN
    39};
    40
    41struct mount_opts {
    42 int allow_other;
    43 char *kernel_opts;
    44 unsigned max_read;
    45};
    46
    47#define FUSE_DUAL_OPT_KEY(templ, key) \
    48 FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
    49
    50static const struct fuse_opt fuse_mount_opts[] = {
    51 { "allow_other", offsetof(struct mount_opts, allow_other), 1 },
    52 { "max_read=%u", offsetof(struct mount_opts, max_read), 1 },
    53 FUSE_OPT_KEY("-r", KEY_RO),
    54 /* standard FreeBSD mount options */
    55 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
    56 FUSE_DUAL_OPT_KEY("async", KEY_KERN),
    57 FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
    58 FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
    59 FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
    60 FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
    61 FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
    62 FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
    63 FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
    64 FUSE_DUAL_OPT_KEY("union", KEY_KERN),
    65 FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
    66 FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
    67 FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
    68 FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
    69 FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
    70 FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
    71 FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
    72 FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
    73 FUSE_DUAL_OPT_KEY("force", KEY_KERN),
    74 FUSE_DUAL_OPT_KEY("update", KEY_KERN),
    75 FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
    76 FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
    77 FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
    78 FUSE_DUAL_OPT_KEY("automounted", KEY_KERN),
    79 /* options supported under both Linux and FBSD */
    80 FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
    81 FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
    82 FUSE_OPT_KEY("max_read=", KEY_KERN),
    83 FUSE_OPT_KEY("subtype=", KEY_KERN),
    84 /* FBSD FUSE specific mount options */
    85 FUSE_DUAL_OPT_KEY("private", KEY_KERN),
    86 FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
    87 FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
    88 FUSE_OPT_KEY("nosync_unmount", KEY_KERN),
    89#if __FreeBSD_version >= 1200519
    90 FUSE_DUAL_OPT_KEY("intr", KEY_KERN),
    91#endif
    92 /* stock FBSD mountopt parsing routine lets anything be negated... */
    93 /*
    94 * Linux specific mount options, but let just the mount util
    95 * handle them
    96 */
    97 FUSE_OPT_KEY("fsname=", KEY_KERN),
    99};
    100
    101void fuse_mount_version(void)
    102{
    103 system(FUSERMOUNT_PROG " --version");
    104}
    105
    106unsigned get_max_read(struct mount_opts *o)
    107{
    108 return o->max_read;
    109}
    110
    111static int fuse_mount_opt_proc(void *data, const char *arg, int key,
    112 struct fuse_args *outargs)
    113{
    114 (void) outargs;
    115 struct mount_opts *mo = data;
    116
    117 switch (key) {
    118 case KEY_RO:
    119 arg = "ro";
    120 /* fall through */
    121
    122 case KEY_KERN:
    123 return fuse_opt_add_opt(&mo->kernel_opts, arg);
    124 }
    125
    126 /* Pass through unknown options */
    127 return 1;
    128}
    129
    130void fuse_kern_unmount(const char *mountpoint, int fd)
    131{
    132 close(fd);
    133 unmount(mountpoint, MNT_FORCE);
    134}
    135
    136/* Check if kernel is doing init in background */
    137static int init_backgrounded(void)
    138{
    139 unsigned ibg;
    140 size_t len;
    141
    142 len = sizeof(ibg);
    143
    144 if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
    145 return 0;
    146
    147 return ibg;
    148}
    149
    150
    151static int fuse_mount_core(const char *mountpoint, const char *opts)
    152{
    153 const char *mountprog = FUSERMOUNT_PROG;
    154 int fd;
    155 char *fdnam, *dev;
    156 pid_t pid, cpid;
    157 int status;
    158 int err;
    159
    160 fdnam = getenv("FUSE_DEV_FD");
    161
    162 if (fdnam) {
    163 err = libfuse_strtol(fdnam, &fd);
    164 if (err) {
    165 fuse_log(FUSE_LOG_ERR, "invalid value given in FUSE_DEV_FD\n");
    166 return -1;
    167 }
    168
    169 goto mount;
    170 }
    171
    172 dev = getenv("FUSE_DEV_NAME");
    173
    174 if (! dev)
    175 dev = (char *)FUSE_DEV_TRUNK;
    176
    177 if ((fd = open(dev, O_RDWR)) < 0) {
    178 perror("fuse: failed to open fuse device");
    179 return -1;
    180 }
    181
    182mount:
    183 if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
    184 goto out;
    185
    186 pid = fork();
    187 cpid = pid;
    188
    189 if (pid == -1) {
    190 perror("fuse: fork() failed");
    191 close(fd);
    192 return -1;
    193 }
    194
    195 if (pid == 0) {
    196 if (! init_backgrounded()) {
    197 /*
    198 * If init is not backgrounded, we have to
    199 * call the mount util backgrounded, to avoid
    200 * deadlock.
    201 */
    202
    203 pid = fork();
    204
    205 if (pid == -1) {
    206 perror("fuse: fork() failed");
    207 close(fd);
    208 exit(1);
    209 }
    210 }
    211
    212 if (pid == 0) {
    213 const char *argv[32];
    214 int a = 0;
    215 int ret = -1;
    216
    217 if (! fdnam)
    218 {
    219 ret = asprintf(&fdnam, "%d", fd);
    220 if(ret == -1)
    221 {
    222 perror("fuse: failed to assemble mount arguments");
    223 close(fd);
    224 exit(1);
    225 }
    226 }
    227
    228 argv[a++] = mountprog;
    229 if (opts) {
    230 argv[a++] = "-o";
    231 argv[a++] = opts;
    232 }
    233 argv[a++] = fdnam;
    234 argv[a++] = mountpoint;
    235 argv[a++] = NULL;
    236 execvp(mountprog, (char **) argv);
    237 perror("fuse: failed to exec mount program");
    238 free(fdnam);
    239 exit(1);
    240 }
    241
    242 exit(0);
    243 }
    244
    245 if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
    246 perror("fuse: failed to mount file system");
    247 close(fd);
    248 return -1;
    249 }
    250
    251out:
    252 return fd;
    253}
    254
    255struct mount_opts *parse_mount_opts(struct fuse_args *args)
    256{
    257 struct mount_opts *mo;
    258
    259 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
    260 if (mo == NULL)
    261 return NULL;
    262
    263 memset(mo, 0, sizeof(struct mount_opts));
    264
    265 if (args &&
    266 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
    267 goto err_out;
    268
    269 return mo;
    270
    271err_out:
    272 destroy_mount_opts(mo);
    273 return NULL;
    274}
    275
    276void destroy_mount_opts(struct mount_opts *mo)
    277{
    278 free(mo->kernel_opts);
    279 free(mo);
    280}
    281
    282int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
    283{
    284 /* mount util should not try to spawn the daemon */
    285 setenv("MOUNT_FUSEFS_SAFE", "1", 1);
    286 /* to notify the mount util it's called from lib */
    287 setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
    288
    289 return fuse_mount_core(mountpoint, mo->kernel_opts);
    290}
    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.17.2/doc/html/lib_2mount__bsd_8c_source.html0000644000175000017500000013133615002273247021137 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 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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2mount__util_8c_source.html0000644000175000017500000015714214770250311024202 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    80 return 1;
    81}
    82#endif /* IGNORE_MTAB */
    83
    84static int add_mount(const char *progname, const char *fsname,
    85 const char *mnt, const char *type, const char *opts)
    86{
    87 int res;
    88 int status;
    89 sigset_t blockmask;
    90 sigset_t oldmask;
    91
    92 sigemptyset(&blockmask);
    93 sigaddset(&blockmask, SIGCHLD);
    94 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    95 if (res == -1) {
    96 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    97 return -1;
    98 }
    99
    100 res = fork();
    101 if (res == -1) {
    102 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    103 goto out_restore;
    104 }
    105 if (res == 0) {
    106 char *env = NULL;
    107
    108 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    109
    110 if(setuid(geteuid()) == -1) {
    111 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    112 res = -1;
    113 goto out_restore;
    114 }
    115
    116 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
    117 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
    118 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
    119 progname, strerror(errno));
    120 exit(1);
    121 }
    122 res = waitpid(res, &status, 0);
    123 if (res == -1)
    124 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    125
    126 if (status != 0)
    127 res = -1;
    128
    129 out_restore:
    130 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    131
    132 return res;
    133}
    134
    135int fuse_mnt_add_mount(const char *progname, const char *fsname,
    136 const char *mnt, const char *type, const char *opts)
    137{
    138 if (!mtab_needs_update(mnt))
    139 return 0;
    140
    141 return add_mount(progname, fsname, mnt, type, opts);
    142}
    143
    144static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
    145{
    146 int res;
    147 int status;
    148 sigset_t blockmask;
    149 sigset_t oldmask;
    150
    151 sigemptyset(&blockmask);
    152 sigaddset(&blockmask, SIGCHLD);
    153 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    154 if (res == -1) {
    155 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    156 return -1;
    157 }
    158
    159 res = fork();
    160 if (res == -1) {
    161 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    162 goto out_restore;
    163 }
    164 if (res == 0) {
    165 char *env = NULL;
    166
    167 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    168
    169 if(setuid(geteuid()) == -1) {
    170 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    171 res = -1;
    172 goto out_restore;
    173 }
    174
    175 if (lazy) {
    176 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
    177 "-l", NULL, &env);
    178 } else {
    179 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
    180 NULL, &env);
    181 }
    182 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
    183 progname, strerror(errno));
    184 exit(1);
    185 }
    186 res = waitpid(res, &status, 0);
    187 if (res == -1)
    188 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    189
    190 if (status != 0) {
    191 res = -1;
    192 }
    193
    194 out_restore:
    195 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    196 return res;
    197
    198}
    199
    200int fuse_mnt_umount(const char *progname, const char *abs_mnt,
    201 const char *rel_mnt, int lazy)
    202{
    203 int res;
    204
    205 if (!mtab_needs_update(abs_mnt)) {
    206 res = umount2(rel_mnt, lazy ? 2 : 0);
    207 if (res == -1)
    208 fprintf(stderr, "%s: failed to unmount %s: %s\n",
    209 progname, abs_mnt, strerror(errno));
    210 return res;
    211 }
    212
    213 return exec_umount(progname, rel_mnt, lazy);
    214}
    215
    216static int remove_mount(const char *progname, const char *mnt)
    217{
    218 int res;
    219 int status;
    220 sigset_t blockmask;
    221 sigset_t oldmask;
    222
    223 sigemptyset(&blockmask);
    224 sigaddset(&blockmask, SIGCHLD);
    225 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    226 if (res == -1) {
    227 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    228 return -1;
    229 }
    230
    231 res = fork();
    232 if (res == -1) {
    233 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    234 goto out_restore;
    235 }
    236 if (res == 0) {
    237 char *env = NULL;
    238
    239 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    240
    241 if(setuid(geteuid()) == -1) {
    242 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    243 res = -1;
    244 goto out_restore;
    245 }
    246
    247 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
    248 "--fake", mnt, NULL, &env);
    249 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
    250 progname, strerror(errno));
    251 exit(1);
    252 }
    253 res = waitpid(res, &status, 0);
    254 if (res == -1)
    255 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    256
    257 if (status != 0)
    258 res = -1;
    259
    260 out_restore:
    261 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    262 return res;
    263}
    264
    265int fuse_mnt_remove_mount(const char *progname, const char *mnt)
    266{
    267 if (!mtab_needs_update(mnt))
    268 return 0;
    269
    270 return remove_mount(progname, mnt);
    271}
    272
    273char *fuse_mnt_resolve_path(const char *progname, const char *orig)
    274{
    275 char buf[PATH_MAX];
    276 char *copy;
    277 char *dst;
    278 char *end;
    279 char *lastcomp;
    280 const char *toresolv;
    281
    282 if (!orig[0]) {
    283 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
    284 orig);
    285 return NULL;
    286 }
    287
    288 copy = strdup(orig);
    289 if (copy == NULL) {
    290 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    291 return NULL;
    292 }
    293
    294 toresolv = copy;
    295 lastcomp = NULL;
    296 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
    297 if (end[0] != '/') {
    298 char *tmp;
    299 end[1] = '\0';
    300 tmp = strrchr(copy, '/');
    301 if (tmp == NULL) {
    302 lastcomp = copy;
    303 toresolv = ".";
    304 } else {
    305 lastcomp = tmp + 1;
    306 if (tmp == copy)
    307 toresolv = "/";
    308 }
    309 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
    310 lastcomp = NULL;
    311 toresolv = copy;
    312 }
    313 else if (tmp)
    314 tmp[0] = '\0';
    315 }
    316 if (realpath(toresolv, buf) == NULL) {
    317 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
    318 strerror(errno));
    319 free(copy);
    320 return NULL;
    321 }
    322 if (lastcomp == NULL)
    323 dst = strdup(buf);
    324 else {
    325 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
    326 if (dst) {
    327 unsigned buflen = strlen(buf);
    328 if (buflen && buf[buflen-1] == '/')
    329 sprintf(dst, "%s%s", buf, lastcomp);
    330 else
    331 sprintf(dst, "%s/%s", buf, lastcomp);
    332 }
    333 }
    334 free(copy);
    335 if (dst == NULL)
    336 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    337 return dst;
    338}
    339
    340int fuse_mnt_check_fuseblk(void)
    341{
    342 char buf[256];
    343 FILE *f = fopen("/proc/filesystems", "r");
    344 if (!f)
    345 return 1;
    346
    347 while (fgets(buf, sizeof(buf), f))
    348 if (strstr(buf, "fuseblk\n")) {
    349 fclose(f);
    350 return 1;
    351 }
    352
    353 fclose(f);
    354 return 0;
    355}
    356
    357int fuse_mnt_parse_fuse_fd(const char *mountpoint)
    358{
    359 int fd = -1;
    360 int len = 0;
    361
    362 if (mountpoint == NULL) {
    363 fprintf(stderr, "Invalid null-ptr mount-point!\n");
    364 return -1;
    365 }
    366
    367 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
    368 len == strlen(mountpoint)) {
    369 return fd;
    370 }
    371
    372 return -1;
    373}
    fuse-3.17.2/doc/html/lib_2mount__util_8c_source.html0000644000175000017500000015674715002273247021361 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 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
    80 return 1;
    81}
    82#endif /* IGNORE_MTAB */
    83
    84static int add_mount(const char *progname, const char *fsname,
    85 const char *mnt, const char *type, const char *opts)
    86{
    87 int res;
    88 int status;
    89 sigset_t blockmask;
    90 sigset_t oldmask;
    91
    92 sigemptyset(&blockmask);
    93 sigaddset(&blockmask, SIGCHLD);
    94 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    95 if (res == -1) {
    96 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    97 return -1;
    98 }
    99
    100 res = fork();
    101 if (res == -1) {
    102 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    103 goto out_restore;
    104 }
    105 if (res == 0) {
    106 char *env = NULL;
    107
    108 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    109
    110 if(setuid(geteuid()) == -1) {
    111 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    112 res = -1;
    113 goto out_restore;
    114 }
    115
    116 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
    117 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
    118 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
    119 progname, strerror(errno));
    120 exit(1);
    121 }
    122 res = waitpid(res, &status, 0);
    123 if (res == -1)
    124 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    125
    126 if (status != 0)
    127 res = -1;
    128
    129 out_restore:
    130 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    131
    132 return res;
    133}
    134
    135int fuse_mnt_add_mount(const char *progname, const char *fsname,
    136 const char *mnt, const char *type, const char *opts)
    137{
    138 if (!mtab_needs_update(mnt))
    139 return 0;
    140
    141 return add_mount(progname, fsname, mnt, type, opts);
    142}
    143
    144static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
    145{
    146 int res;
    147 int status;
    148 sigset_t blockmask;
    149 sigset_t oldmask;
    150
    151 sigemptyset(&blockmask);
    152 sigaddset(&blockmask, SIGCHLD);
    153 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    154 if (res == -1) {
    155 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    156 return -1;
    157 }
    158
    159 res = fork();
    160 if (res == -1) {
    161 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    162 goto out_restore;
    163 }
    164 if (res == 0) {
    165 char *env = NULL;
    166
    167 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    168
    169 if(setuid(geteuid()) == -1) {
    170 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    171 res = -1;
    172 goto out_restore;
    173 }
    174
    175 if (lazy) {
    176 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
    177 "-l", NULL, &env);
    178 } else {
    179 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
    180 NULL, &env);
    181 }
    182 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
    183 progname, strerror(errno));
    184 exit(1);
    185 }
    186 res = waitpid(res, &status, 0);
    187 if (res == -1)
    188 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    189
    190 if (status != 0) {
    191 res = -1;
    192 }
    193
    194 out_restore:
    195 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    196 return res;
    197
    198}
    199
    200int fuse_mnt_umount(const char *progname, const char *abs_mnt,
    201 const char *rel_mnt, int lazy)
    202{
    203 int res;
    204
    205 if (!mtab_needs_update(abs_mnt)) {
    206 res = umount2(rel_mnt, lazy ? 2 : 0);
    207 if (res == -1)
    208 fprintf(stderr, "%s: failed to unmount %s: %s\n",
    209 progname, abs_mnt, strerror(errno));
    210 return res;
    211 }
    212
    213 return exec_umount(progname, rel_mnt, lazy);
    214}
    215
    216static int remove_mount(const char *progname, const char *mnt)
    217{
    218 int res;
    219 int status;
    220 sigset_t blockmask;
    221 sigset_t oldmask;
    222
    223 sigemptyset(&blockmask);
    224 sigaddset(&blockmask, SIGCHLD);
    225 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    226 if (res == -1) {
    227 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    228 return -1;
    229 }
    230
    231 res = fork();
    232 if (res == -1) {
    233 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    234 goto out_restore;
    235 }
    236 if (res == 0) {
    237 char *env = NULL;
    238
    239 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    240
    241 if(setuid(geteuid()) == -1) {
    242 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    243 res = -1;
    244 goto out_restore;
    245 }
    246
    247 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
    248 "--fake", mnt, NULL, &env);
    249 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
    250 progname, strerror(errno));
    251 exit(1);
    252 }
    253 res = waitpid(res, &status, 0);
    254 if (res == -1)
    255 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    256
    257 if (status != 0)
    258 res = -1;
    259
    260 out_restore:
    261 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    262 return res;
    263}
    264
    265int fuse_mnt_remove_mount(const char *progname, const char *mnt)
    266{
    267 if (!mtab_needs_update(mnt))
    268 return 0;
    269
    270 return remove_mount(progname, mnt);
    271}
    272
    273char *fuse_mnt_resolve_path(const char *progname, const char *orig)
    274{
    275 char buf[PATH_MAX];
    276 char *copy;
    277 char *dst;
    278 char *end;
    279 char *lastcomp;
    280 const char *toresolv;
    281
    282 if (!orig[0]) {
    283 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
    284 orig);
    285 return NULL;
    286 }
    287
    288 copy = strdup(orig);
    289 if (copy == NULL) {
    290 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    291 return NULL;
    292 }
    293
    294 toresolv = copy;
    295 lastcomp = NULL;
    296 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
    297 if (end[0] != '/') {
    298 char *tmp;
    299 end[1] = '\0';
    300 tmp = strrchr(copy, '/');
    301 if (tmp == NULL) {
    302 lastcomp = copy;
    303 toresolv = ".";
    304 } else {
    305 lastcomp = tmp + 1;
    306 if (tmp == copy)
    307 toresolv = "/";
    308 }
    309 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
    310 lastcomp = NULL;
    311 toresolv = copy;
    312 }
    313 else if (tmp)
    314 tmp[0] = '\0';
    315 }
    316 if (realpath(toresolv, buf) == NULL) {
    317 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
    318 strerror(errno));
    319 free(copy);
    320 return NULL;
    321 }
    322 if (lastcomp == NULL)
    323 dst = strdup(buf);
    324 else {
    325 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
    326 if (dst) {
    327 unsigned buflen = strlen(buf);
    328 if (buflen && buf[buflen-1] == '/')
    329 sprintf(dst, "%s%s", buf, lastcomp);
    330 else
    331 sprintf(dst, "%s/%s", buf, lastcomp);
    332 }
    333 }
    334 free(copy);
    335 if (dst == NULL)
    336 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    337 return dst;
    338}
    339
    340int fuse_mnt_check_fuseblk(void)
    341{
    342 char buf[256];
    343 FILE *f = fopen("/proc/filesystems", "r");
    344 if (!f)
    345 return 1;
    346
    347 while (fgets(buf, sizeof(buf), f))
    348 if (strstr(buf, "fuseblk\n")) {
    349 fclose(f);
    350 return 1;
    351 }
    352
    353 fclose(f);
    354 return 0;
    355}
    356
    357int fuse_mnt_parse_fuse_fd(const char *mountpoint)
    358{
    359 int fd = -1;
    360 int len = 0;
    361
    362 if (mountpoint == NULL) {
    363 fprintf(stderr, "Invalid null-ptr mount-point!\n");
    364 return -1;
    365 }
    366
    367 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
    368 len == strlen(mountpoint)) {
    369 return fd;
    370 }
    371
    372 return -1;
    373}
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2lib_2mount__util_8h_source.html0000644000175000017500000001364414770250311024205 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/lib_2mount__util_8h_source.html0000644000175000017500000001345115002273247021346 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 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.17.2/doc/html/fuse-3_817_81-rc0_2lib_2util_8c_source.html0000644000175000017500000001343514770250311022615 0ustar berndbernd libfuse: fuse-3.17.1-rc0/lib/util.c Source File
    libfuse
    util.c
    1#include <stdlib.h>
    2#include <errno.h>
    3
    4#include "util.h"
    5
    6int libfuse_strtol(const char *str, long *res)
    7{
    8 char *endptr;
    9 int base = 10;
    10 long val;
    11
    12 errno = 0;
    13
    14 if (!str)
    15 return -EINVAL;
    16
    17 val = strtol(str, &endptr, base);
    18
    19 if (errno)
    20 return -errno;
    21
    22 if (endptr == str || *endptr != '\0')
    23 return -EINVAL;
    24
    25 *res = val;
    26 return 0;
    27}
    fuse-3.17.2/doc/html/lib_2util_8c_source.html0000644000175000017500000001324215002273247017756 0ustar berndbernd libfuse: lib/util.c Source File
    libfuse
    util.c
    1#include <stdlib.h>
    2#include <errno.h>
    3
    4#include "util.h"
    5
    6int libfuse_strtol(const char *str, long *res)
    7{
    8 char *endptr;
    9 int base = 10;
    10 long val;
    11
    12 errno = 0;
    13
    14 if (!str)
    15 return -EINVAL;
    16
    17 val = strtol(str, &endptr, base);
    18
    19 if (errno)
    20 return -errno;
    21
    22 if (endptr == str || *endptr != '\0')
    23 return -EINVAL;
    24
    25 *res = val;
    26 return 0;
    27}
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2lib_2util_8h_source.html0000644000175000017500000000562014770250311022617 0ustar berndbernd libfuse: fuse-3.17.1-rc0/lib/util.h Source File
    libfuse
    util.h
    1#define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
    2
    3int libfuse_strtol(const char *str, long *res);
    fuse-3.17.2/doc/html/lib_2util_8h_source.html0000644000175000017500000001351315002273247017764 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
    6#define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
    7
    8#define likely(x) __builtin_expect(!!(x), 1)
    9#define unlikely(x) __builtin_expect(!!(x), 0)
    10
    11int libfuse_strtol(const char *str, long *res);
    12
    16static inline uint32_t fuse_lower_32_bits(uint64_t nr)
    17{
    18 return (uint32_t)(nr & 0xffffffff);
    19}
    20
    24static inline uint64_t fuse_higher_32_bits(uint64_t nr)
    25{
    26 return nr & ~0xffffffffULL;
    27}
    28
    29#ifndef FUSE_VAR_UNUSED
    30#define FUSE_VAR_UNUSED(var) (__attribute__((unused)) var)
    31#endif
    32
    33#endif
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2test_2readdir__inode_8c_source.html0000644000175000017500000002532414770250311025000 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/test_2readdir__inode_8c_source.html0000644000175000017500000002513115002273247022141 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.17.2/doc/html/fuse-3_817_81-rc0_2test_2release__unlink__race_8c_source.html0000644000175000017500000005426414770250311026346 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/test_2release__unlink__race_8c_source.html0000644000175000017500000005410515002273247023505 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 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.17.2/doc/html/fuse-3_817_81-rc0_2test_2stracedecode_8c_source.html0000644000175000017500000010623214770250311024474 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/test_2stracedecode_8c_source.html0000644000175000017500000010603715002273247021644 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.17.2/doc/html/fuse-3_817_81-rc0_2test_2test__setattr_8c_source.html0000644000175000017500000012165514770250311024741 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/test_2test__setattr_8c_source.html0000644000175000017500000012157615002273247022110 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 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.17.2/doc/html/fuse-3_817_81-rc0_2test_2test__syscalls_8c_source.html0000644000175000017500000114337214770250311025111 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    1068#ifndef __FreeBSD__
    1069static int test_mknod(void)
    1070{
    1071 int err = 0;
    1072 int res;
    1073
    1074 start_test("mknod");
    1075 unlink(testfile);
    1076 res = mknod(testfile, 0644, 0);
    1077 if (res == -1) {
    1078 PERROR("mknod");
    1079 return -1;
    1080 }
    1081 res = check_type(testfile, S_IFREG);
    1082 if (res == -1)
    1083 return -1;
    1084 err += check_mode(testfile, 0644);
    1085 err += check_nlink(testfile, 1);
    1086 err += check_size(testfile, 0);
    1087 res = unlink(testfile);
    1088 if (res == -1) {
    1089 PERROR("unlink");
    1090 return -1;
    1091 }
    1092 res = check_nonexist(testfile);
    1093 if (res == -1)
    1094 return -1;
    1095 if (err)
    1096 return -1;
    1097
    1098 success();
    1099 return 0;
    1100}
    1101#endif
    1102
    1103#define test_open(exist, flags, mode) do_test_open(exist, flags, #flags, mode)
    1104
    1105static int do_test_open(int exist, int flags, const char *flags_str, int mode)
    1106{
    1107 char buf[4096];
    1108 const char *data = testdata;
    1109 int datalen = testdatalen;
    1110 unsigned currlen = 0;
    1111 int err = 0;
    1112 int res;
    1113 int fd;
    1114 off_t off;
    1115
    1116 start_test("open(%s, %s, 0%03o)", exist ? "+" : "-", flags_str, mode);
    1117 unlink(testfile);
    1118 if (exist) {
    1119 res = create_file(testfile_r, testdata2, testdata2len);
    1120 if (res == -1)
    1121 return -1;
    1122
    1123 currlen = testdata2len;
    1124 }
    1125
    1126 fd = open(testfile, flags, mode);
    1127 if ((flags & O_CREAT) && (flags & O_EXCL) && exist) {
    1128 if (fd != -1) {
    1129 ERROR("open should have failed");
    1130 close(fd);
    1131 return -1;
    1132 } else if (errno == EEXIST)
    1133 goto succ;
    1134 }
    1135 if (!(flags & O_CREAT) && !exist) {
    1136 if (fd != -1) {
    1137 ERROR("open should have failed");
    1138 close(fd);
    1139 return -1;
    1140 } else if (errno == ENOENT)
    1141 goto succ;
    1142 }
    1143 if (fd == -1) {
    1144 PERROR("open");
    1145 return -1;
    1146 }
    1147
    1148 if (flags & O_TRUNC)
    1149 currlen = 0;
    1150
    1151 err += check_type(testfile, S_IFREG);
    1152 if (exist)
    1153 err += check_mode(testfile, 0644);
    1154 else
    1155 err += check_mode(testfile, mode);
    1156 err += check_nlink(testfile, 1);
    1157 err += check_size(testfile, currlen);
    1158 if (exist && !(flags & O_TRUNC) && (mode & S_IRUSR))
    1159 err += check_data(testfile, testdata2, 0, testdata2len);
    1160
    1161 res = write(fd, data, datalen);
    1162 if ((flags & O_ACCMODE) != O_RDONLY) {
    1163 if (res == -1) {
    1164 PERROR("write");
    1165 err --;
    1166 } else if (res != datalen) {
    1167 ERROR("write is short: %u instead of %u", res, datalen);
    1168 err --;
    1169 } else {
    1170 if (datalen > (int) currlen)
    1171 currlen = datalen;
    1172
    1173 err += check_size(testfile, currlen);
    1174
    1175 if (mode & S_IRUSR) {
    1176 err += check_data(testfile, data, 0, datalen);
    1177 if (exist && !(flags & O_TRUNC) &&
    1178 testdata2len > datalen)
    1179 err += check_data(testfile,
    1180 testdata2 + datalen,
    1181 datalen,
    1182 testdata2len - datalen);
    1183 }
    1184 }
    1185 } else {
    1186 if (res != -1) {
    1187 ERROR("write should have failed");
    1188 err --;
    1189 } else if (errno != EBADF) {
    1190 PERROR("write");
    1191 err --;
    1192 }
    1193 }
    1194 off = lseek(fd, SEEK_SET, 0);
    1195 if (off == (off_t) -1) {
    1196 PERROR("lseek");
    1197 err--;
    1198 } else if (off != 0) {
    1199 ERROR("offset should have returned 0");
    1200 err --;
    1201 }
    1202 res = read(fd, buf, sizeof(buf));
    1203 if ((flags & O_ACCMODE) != O_WRONLY) {
    1204 if (res == -1) {
    1205 PERROR("read");
    1206 err--;
    1207 } else {
    1208 int readsize =
    1209 currlen < sizeof(buf) ? currlen : sizeof(buf);
    1210 if (res != readsize) {
    1211 ERROR("read is short: %i instead of %u",
    1212 res, readsize);
    1213 err--;
    1214 } else {
    1215 if ((flags & O_ACCMODE) != O_RDONLY) {
    1216 err += check_buffer(buf, data, datalen);
    1217 if (exist && !(flags & O_TRUNC) &&
    1218 testdata2len > datalen)
    1219 err += check_buffer(buf + datalen,
    1220 testdata2 + datalen,
    1221 testdata2len - datalen);
    1222 } else if (exist)
    1223 err += check_buffer(buf, testdata2,
    1224 testdata2len);
    1225 }
    1226 }
    1227 } else {
    1228 if (res != -1) {
    1229 ERROR("read should have failed");
    1230 err --;
    1231 } else if (errno != EBADF) {
    1232 PERROR("read");
    1233 err --;
    1234 }
    1235 }
    1236
    1237 res = close(fd);
    1238 if (res == -1) {
    1239 PERROR("close");
    1240 return -1;
    1241 }
    1242 res = unlink(testfile);
    1243 if (res == -1) {
    1244 PERROR("unlink");
    1245 return -1;
    1246 }
    1247 res = check_nonexist(testfile);
    1248 if (res == -1)
    1249 return -1;
    1250 res = check_nonexist(testfile_r);
    1251 if (res == -1)
    1252 return -1;
    1253 if (err)
    1254 return -1;
    1255
    1256succ:
    1257 success();
    1258 return 0;
    1259}
    1260
    1261#define test_open_acc(flags, mode, err) \
    1262 do_test_open_acc(flags, #flags, mode, err)
    1263
    1264static int do_test_open_acc(int flags, const char *flags_str, int mode, int err)
    1265{
    1266 const char *data = testdata;
    1267 int datalen = testdatalen;
    1268 int res;
    1269 int fd;
    1270
    1271 start_test("open_acc(%s) mode: 0%03o message: '%s'", flags_str, mode,
    1272 strerror(err));
    1273 unlink(testfile);
    1274 res = create_testfile(testfile, data, datalen);
    1275 if (res == -1)
    1276 return -1;
    1277
    1278 res = chmod(testfile, mode);
    1279 if (res == -1) {
    1280 PERROR("chmod");
    1281 return -1;
    1282 }
    1283
    1284 res = check_testfile_mode(testfile, mode);
    1285 if (res == -1)
    1286 return -1;
    1287
    1288 fd = open(testfile, flags);
    1289 if (fd == -1) {
    1290 if (err != errno) {
    1291 PERROR("open");
    1292 return -1;
    1293 }
    1294 } else {
    1295 if (err) {
    1296 ERROR("open should have failed");
    1297 close(fd);
    1298 return -1;
    1299 }
    1300 close(fd);
    1301 }
    1302
    1303 res = unlink(testfile);
    1304 if (res == -1) {
    1305 PERROR("unlink");
    1306 return -1;
    1307 }
    1308 res = check_nonexist(testfile);
    1309 if (res == -1)
    1310 return -1;
    1311 res = check_nonexist(testfile_r);
    1312 if (res == -1)
    1313 return -1;
    1314
    1315 success();
    1316 return 0;
    1317}
    1318
    1319static int test_symlink(void)
    1320{
    1321 char buf[1024];
    1322 const char *data = testdata;
    1323 int datalen = testdatalen;
    1324 int linklen = strlen(testfile);
    1325 int err = 0;
    1326 int res;
    1327
    1328 start_test("symlink");
    1329 res = create_testfile(testfile, data, datalen);
    1330 if (res == -1)
    1331 return -1;
    1332
    1333 unlink(testfile2);
    1334 res = symlink(testfile, testfile2);
    1335 if (res == -1) {
    1336 PERROR("symlink");
    1337 return -1;
    1338 }
    1339 res = check_type(testfile2, S_IFLNK);
    1340 if (res == -1)
    1341 return -1;
    1342 err += check_mode(testfile2, 0777);
    1343 err += check_nlink(testfile2, 1);
    1344 res = readlink(testfile2, buf, sizeof(buf));
    1345 if (res == -1) {
    1346 PERROR("readlink");
    1347 err--;
    1348 }
    1349 if (res != linklen) {
    1350 ERROR("short readlink: %u instead of %u", res, linklen);
    1351 err--;
    1352 }
    1353 if (memcmp(buf, testfile, linklen) != 0) {
    1354 ERROR("link mismatch");
    1355 err--;
    1356 }
    1357 err += check_size(testfile2, datalen);
    1358 err += check_data(testfile2, data, 0, datalen);
    1359 res = unlink(testfile2);
    1360 if (res == -1) {
    1361 PERROR("unlink");
    1362 return -1;
    1363 }
    1364 res = check_nonexist(testfile2);
    1365 if (res == -1)
    1366 return -1;
    1367 if (err)
    1368 return -1;
    1369
    1370 res = unlink(testfile);
    1371 if (res == -1) {
    1372 PERROR("unlink");
    1373 return -1;
    1374 }
    1375 res = check_nonexist(testfile);
    1376 if (res == -1)
    1377 return -1;
    1378
    1379 success();
    1380 return 0;
    1381}
    1382
    1383static int test_link(void)
    1384{
    1385 const char *data = testdata;
    1386 int datalen = testdatalen;
    1387 int err = 0;
    1388 int res;
    1389
    1390 start_test("link");
    1391 res = create_testfile(testfile, data, datalen);
    1392 if (res == -1)
    1393 return -1;
    1394
    1395 unlink(testfile2);
    1396 res = link(testfile, testfile2);
    1397 if (res == -1) {
    1398 PERROR("link");
    1399 return -1;
    1400 }
    1401 res = check_type(testfile2, S_IFREG);
    1402 if (res == -1)
    1403 return -1;
    1404 err += check_mode(testfile2, 0644);
    1405 err += check_nlink(testfile2, 2);
    1406 err += check_size(testfile2, datalen);
    1407 err += check_data(testfile2, data, 0, datalen);
    1408 res = unlink(testfile);
    1409 if (res == -1) {
    1410 PERROR("unlink");
    1411 return -1;
    1412 }
    1413 res = check_nonexist(testfile);
    1414 if (res == -1)
    1415 return -1;
    1416
    1417 err += check_nlink(testfile2, 1);
    1418 res = unlink(testfile2);
    1419 if (res == -1) {
    1420 PERROR("unlink");
    1421 return -1;
    1422 }
    1423 res = check_nonexist(testfile2);
    1424 if (res == -1)
    1425 return -1;
    1426 if (err)
    1427 return -1;
    1428
    1429 success();
    1430 return 0;
    1431}
    1432
    1433static int test_link2(void)
    1434{
    1435 const char *data = testdata;
    1436 int datalen = testdatalen;
    1437 int err = 0;
    1438 int res;
    1439
    1440 start_test("link-unlink-link");
    1441 res = create_testfile(testfile, data, datalen);
    1442 if (res == -1)
    1443 return -1;
    1444
    1445 unlink(testfile2);
    1446 res = link(testfile, testfile2);
    1447 if (res == -1) {
    1448 PERROR("link");
    1449 return -1;
    1450 }
    1451 res = unlink(testfile);
    1452 if (res == -1) {
    1453 PERROR("unlink");
    1454 return -1;
    1455 }
    1456 res = check_nonexist(testfile);
    1457 if (res == -1)
    1458 return -1;
    1459 res = link(testfile2, testfile);
    1460 if (res == -1) {
    1461 PERROR("link");
    1462 }
    1463 res = check_type(testfile, S_IFREG);
    1464 if (res == -1)
    1465 return -1;
    1466 err += check_mode(testfile, 0644);
    1467 err += check_nlink(testfile, 2);
    1468 err += check_size(testfile, datalen);
    1469 err += check_data(testfile, data, 0, datalen);
    1470
    1471 res = unlink(testfile2);
    1472 if (res == -1) {
    1473 PERROR("unlink");
    1474 return -1;
    1475 }
    1476 err += check_nlink(testfile, 1);
    1477 res = unlink(testfile);
    1478 if (res == -1) {
    1479 PERROR("unlink");
    1480 return -1;
    1481 }
    1482 res = check_nonexist(testfile);
    1483 if (res == -1)
    1484 return -1;
    1485 if (err)
    1486 return -1;
    1487
    1488 success();
    1489 return 0;
    1490}
    1491
    1492static int test_rename_file(void)
    1493{
    1494 const char *data = testdata;
    1495 int datalen = testdatalen;
    1496 int err = 0;
    1497 int res;
    1498
    1499 start_test("rename file");
    1500 res = create_testfile(testfile, data, datalen);
    1501 if (res == -1)
    1502 return -1;
    1503
    1504 unlink(testfile2);
    1505 res = rename(testfile, testfile2);
    1506 if (res == -1) {
    1507 PERROR("rename");
    1508 return -1;
    1509 }
    1510 res = check_nonexist(testfile);
    1511 if (res == -1)
    1512 return -1;
    1513 res = check_type(testfile2, S_IFREG);
    1514 if (res == -1)
    1515 return -1;
    1516 err += check_mode(testfile2, 0644);
    1517 err += check_nlink(testfile2, 1);
    1518 err += check_size(testfile2, datalen);
    1519 err += check_data(testfile2, data, 0, datalen);
    1520 res = unlink(testfile2);
    1521 if (res == -1) {
    1522 PERROR("unlink");
    1523 return -1;
    1524 }
    1525 res = check_nonexist(testfile2);
    1526 if (res == -1)
    1527 return -1;
    1528 if (err)
    1529 return -1;
    1530
    1531 success();
    1532 return 0;
    1533}
    1534
    1535static int test_rename_dir(void)
    1536{
    1537 int err = 0;
    1538 int res;
    1539
    1540 start_test("rename dir");
    1541 res = create_dir(testdir, testdir_files);
    1542 if (res == -1)
    1543 return -1;
    1544
    1545 rmdir(testdir2);
    1546 res = rename(testdir, testdir2);
    1547 if (res == -1) {
    1548 PERROR("rename");
    1549 cleanup_dir(testdir, testdir_files, 1);
    1550 return -1;
    1551 }
    1552 res = check_nonexist(testdir);
    1553 if (res == -1) {
    1554 cleanup_dir(testdir, testdir_files, 1);
    1555 return -1;
    1556 }
    1557 res = check_type(testdir2, S_IFDIR);
    1558 if (res == -1) {
    1559 cleanup_dir(testdir2, testdir_files, 1);
    1560 return -1;
    1561 }
    1562 err += check_mode(testdir2, 0755);
    1563 err += check_dir_contents(testdir2, testdir_files);
    1564 err += cleanup_dir(testdir2, testdir_files, 0);
    1565 res = rmdir(testdir2);
    1566 if (res == -1) {
    1567 PERROR("rmdir");
    1568 return -1;
    1569 }
    1570 res = check_nonexist(testdir2);
    1571 if (res == -1)
    1572 return -1;
    1573 if (err)
    1574 return -1;
    1575
    1576 success();
    1577 return 0;
    1578}
    1579
    1580static int test_rename_dir_loop(void)
    1581{
    1582#define PATH(p) (snprintf(path, sizeof path, "%s/%s", testdir, p), path)
    1583#define PATH2(p) (snprintf(path2, sizeof path2, "%s/%s", testdir, p), path2)
    1584
    1585 char path[1280], path2[1280];
    1586 int err = 0;
    1587 int res;
    1588
    1589 start_test("rename dir loop");
    1590
    1591 res = create_dir(testdir, testdir_files);
    1592 if (res == -1)
    1593 return -1;
    1594
    1595 res = mkdir(PATH("a"), 0755);
    1596 if (res == -1) {
    1597 PERROR("mkdir");
    1598 goto fail;
    1599 }
    1600
    1601 res = rename(PATH("a"), PATH2("a"));
    1602 if (res == -1) {
    1603 PERROR("rename");
    1604 goto fail;
    1605 }
    1606
    1607 errno = 0;
    1608 res = rename(PATH("a"), PATH2("a/b"));
    1609 if (res == 0 || errno != EINVAL) {
    1610 PERROR("rename");
    1611 goto fail;
    1612 }
    1613
    1614 res = mkdir(PATH("a/b"), 0755);
    1615 if (res == -1) {
    1616 PERROR("mkdir");
    1617 goto fail;
    1618 }
    1619
    1620 res = mkdir(PATH("a/b/c"), 0755);
    1621 if (res == -1) {
    1622 PERROR("mkdir");
    1623 goto fail;
    1624 }
    1625
    1626 errno = 0;
    1627 res = rename(PATH("a"), PATH2("a/b/c"));
    1628 if (res == 0 || errno != EINVAL) {
    1629 PERROR("rename");
    1630 goto fail;
    1631 }
    1632
    1633 errno = 0;
    1634 res = rename(PATH("a"), PATH2("a/b/c/a"));
    1635 if (res == 0 || errno != EINVAL) {
    1636 PERROR("rename");
    1637 goto fail;
    1638 }
    1639
    1640 errno = 0;
    1641 res = rename(PATH("a/b/c"), PATH2("a"));
    1642 if (res == 0 || errno != ENOTEMPTY) {
    1643 PERROR("rename");
    1644 goto fail;
    1645 }
    1646
    1647 res = open(PATH("a/foo"), O_CREAT, 0644);
    1648 if (res == -1) {
    1649 PERROR("open");
    1650 goto fail;
    1651 }
    1652 close(res);
    1653
    1654 res = rename(PATH("a/foo"), PATH2("a/bar"));
    1655 if (res == -1) {
    1656 PERROR("rename");
    1657 goto fail;
    1658 }
    1659
    1660 res = rename(PATH("a/bar"), PATH2("a/foo"));
    1661 if (res == -1) {
    1662 PERROR("rename");
    1663 goto fail;
    1664 }
    1665
    1666 res = rename(PATH("a/foo"), PATH2("a/b/bar"));
    1667 if (res == -1) {
    1668 PERROR("rename");
    1669 goto fail;
    1670 }
    1671
    1672 res = rename(PATH("a/b/bar"), PATH2("a/foo"));
    1673 if (res == -1) {
    1674 PERROR("rename");
    1675 goto fail;
    1676 }
    1677
    1678 res = rename(PATH("a/foo"), PATH2("a/b/c/bar"));
    1679 if (res == -1) {
    1680 PERROR("rename");
    1681 goto fail;
    1682 }
    1683
    1684 res = rename(PATH("a/b/c/bar"), PATH2("a/foo"));
    1685 if (res == -1) {
    1686 PERROR("rename");
    1687 goto fail;
    1688 }
    1689
    1690 res = open(PATH("a/bar"), O_CREAT, 0644);
    1691 if (res == -1) {
    1692 PERROR("open");
    1693 goto fail;
    1694 }
    1695 close(res);
    1696
    1697 res = rename(PATH("a/foo"), PATH2("a/bar"));
    1698 if (res == -1) {
    1699 PERROR("rename");
    1700 goto fail;
    1701 }
    1702
    1703 unlink(PATH("a/bar"));
    1704
    1705 res = rename(PATH("a/b"), PATH2("a/d"));
    1706 if (res == -1) {
    1707 PERROR("rename");
    1708 goto fail;
    1709 }
    1710
    1711 res = rename(PATH("a/d"), PATH2("a/b"));
    1712 if (res == -1) {
    1713 PERROR("rename");
    1714 goto fail;
    1715 }
    1716
    1717 res = mkdir(PATH("a/d"), 0755);
    1718 if (res == -1) {
    1719 PERROR("mkdir");
    1720 goto fail;
    1721 }
    1722
    1723 res = rename(PATH("a/b"), PATH2("a/d"));
    1724 if (res == -1) {
    1725 PERROR("rename");
    1726 goto fail;
    1727 }
    1728
    1729 res = rename(PATH("a/d"), PATH2("a/b"));
    1730 if (res == -1) {
    1731 PERROR("rename");
    1732 goto fail;
    1733 }
    1734
    1735 res = mkdir(PATH("a/d"), 0755);
    1736 if (res == -1) {
    1737 PERROR("mkdir");
    1738 goto fail;
    1739 }
    1740
    1741 res = mkdir(PATH("a/d/e"), 0755);
    1742 if (res == -1) {
    1743 PERROR("mkdir");
    1744 goto fail;
    1745 }
    1746
    1747 errno = 0;
    1748 res = rename(PATH("a/b"), PATH2("a/d"));
    1749 if (res == 0 || (errno != ENOTEMPTY && errno != EEXIST)) {
    1750 PERROR("rename");
    1751 goto fail;
    1752 }
    1753
    1754 rmdir(PATH("a/d/e"));
    1755 rmdir(PATH("a/d"));
    1756
    1757 rmdir(PATH("a/b/c"));
    1758 rmdir(PATH("a/b"));
    1759 rmdir(PATH("a"));
    1760
    1761 err += cleanup_dir(testdir, testdir_files, 0);
    1762 res = rmdir(testdir);
    1763 if (res == -1) {
    1764 PERROR("rmdir");
    1765 goto fail;
    1766 }
    1767 res = check_nonexist(testdir);
    1768 if (res == -1)
    1769 return -1;
    1770 if (err)
    1771 return -1;
    1772
    1773 success();
    1774 return 0;
    1775
    1776fail:
    1777 unlink(PATH("a/bar"));
    1778
    1779 rmdir(PATH("a/d/e"));
    1780 rmdir(PATH("a/d"));
    1781
    1782 rmdir(PATH("a/b/c"));
    1783 rmdir(PATH("a/b"));
    1784 rmdir(PATH("a"));
    1785
    1786 cleanup_dir(testdir, testdir_files, 1);
    1787 rmdir(testdir);
    1788
    1789 return -1;
    1790
    1791#undef PATH2
    1792#undef PATH
    1793}
    1794
    1795#ifndef __FreeBSD__
    1796static int test_mkfifo(void)
    1797{
    1798 int res;
    1799 int err = 0;
    1800
    1801 start_test("mkfifo");
    1802 unlink(testfile);
    1803 res = mkfifo(testfile, 0644);
    1804 if (res == -1) {
    1805 PERROR("mkfifo");
    1806 return -1;
    1807 }
    1808 res = check_type(testfile, S_IFIFO);
    1809 if (res == -1)
    1810 return -1;
    1811 err += check_mode(testfile, 0644);
    1812 err += check_nlink(testfile, 1);
    1813 res = unlink(testfile);
    1814 if (res == -1) {
    1815 PERROR("unlink");
    1816 return -1;
    1817 }
    1818 res = check_nonexist(testfile);
    1819 if (res == -1)
    1820 return -1;
    1821 if (err)
    1822 return -1;
    1823
    1824 success();
    1825 return 0;
    1826}
    1827#endif
    1828
    1829static int test_mkdir(void)
    1830{
    1831 int res;
    1832 int err = 0;
    1833 const char *dir_contents[] = {NULL};
    1834
    1835 start_test("mkdir");
    1836 rmdir(testdir);
    1837 res = mkdir(testdir, 0755);
    1838 if (res == -1) {
    1839 PERROR("mkdir");
    1840 return -1;
    1841 }
    1842 res = check_type(testdir, S_IFDIR);
    1843 if (res == -1)
    1844 return -1;
    1845 err += check_mode(testdir, 0755);
    1846 /* Some file systems (like btrfs) don't track link
    1847 count for directories */
    1848 //err += check_nlink(testdir, 2);
    1849 err += check_dir_contents(testdir, dir_contents);
    1850 res = rmdir(testdir);
    1851 if (res == -1) {
    1852 PERROR("rmdir");
    1853 return -1;
    1854 }
    1855 res = check_nonexist(testdir);
    1856 if (res == -1)
    1857 return -1;
    1858 if (err)
    1859 return -1;
    1860
    1861 success();
    1862 return 0;
    1863}
    1864
    1865static int test_socket(void)
    1866{
    1867 struct sockaddr_un su;
    1868 int fd;
    1869 int res;
    1870 int err = 0;
    1871 const size_t test_sock_len = strlen(testsock) + 1;
    1872
    1873 start_test("socket");
    1874 if (test_sock_len > sizeof(su.sun_path)) {
    1875 fprintf(stderr, "Need to shorten mount point by %zu chars\n",
    1876 strlen(testsock) + 1 - sizeof(su.sun_path));
    1877 return -1;
    1878 }
    1879 unlink(testsock);
    1880 fd = socket(AF_UNIX, SOCK_STREAM, 0);
    1881 if (fd < 0) {
    1882 PERROR("socket");
    1883 return -1;
    1884 }
    1885 su.sun_family = AF_UNIX;
    1886
    1887 strncpy(su.sun_path, testsock, test_sock_len);
    1888 su.sun_path[sizeof(su.sun_path) - 1] = '\0';
    1889 res = bind(fd, (struct sockaddr*)&su, sizeof(su));
    1890 if (res == -1) {
    1891 PERROR("bind");
    1892 return -1;
    1893 }
    1894
    1895 res = check_type(testsock, S_IFSOCK);
    1896 if (res == -1) {
    1897 close(fd);
    1898 return -1;
    1899 }
    1900 err += check_nlink(testsock, 1);
    1901 close(fd);
    1902 res = unlink(testsock);
    1903 if (res == -1) {
    1904 PERROR("unlink");
    1905 return -1;
    1906 }
    1907 res = check_nonexist(testsock);
    1908 if (res == -1)
    1909 return -1;
    1910 if (err)
    1911 return -1;
    1912
    1913 success();
    1914 return 0;
    1915}
    1916
    1917#define test_create_ro_dir(flags) \
    1918 do_test_create_ro_dir(flags, #flags)
    1919
    1920static int do_test_create_ro_dir(int flags, const char *flags_str)
    1921{
    1922 int res;
    1923 int err = 0;
    1924 int fd;
    1925
    1926 start_test("open(%s) in read-only directory", flags_str);
    1927 rmdir(testdir);
    1928 res = mkdir(testdir, 0555);
    1929 if (res == -1) {
    1930 PERROR("mkdir");
    1931 return -1;
    1932 }
    1933 fd = open(subfile, flags, 0644);
    1934 if (fd != -1) {
    1935 close(fd);
    1936 unlink(subfile);
    1937 ERROR("open should have failed");
    1938 err--;
    1939 } else {
    1940 res = check_nonexist(subfile);
    1941 if (res == -1)
    1942 err--;
    1943 }
    1944 unlink(subfile);
    1945 res = rmdir(testdir);
    1946 if (res == -1) {
    1947 PERROR("rmdir");
    1948 return -1;
    1949 }
    1950 res = check_nonexist(testdir);
    1951 if (res == -1)
    1952 return -1;
    1953 if (err)
    1954 return -1;
    1955
    1956 success();
    1957 return 0;
    1958}
    1959
    1960/* this tests open with O_TMPFILE
    1961 note that this will only work with the fuse low level api
    1962 you will get ENOTSUP with the high level api */
    1963static int test_create_tmpfile(void)
    1964{
    1965 rmdir(testdir);
    1966 int res = mkdir(testdir, 0777);
    1967 if (res)
    1968 return -1;
    1969
    1970 start_test("create tmpfile");
    1971
    1972 int fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
    1973 if(fd == -1) {
    1974 if (errno == ENOTSUP) {
    1975 /* don't bother if we're working on an old kernel
    1976 or on the high level API */
    1977 return 0;
    1978 }
    1979
    1980 PERROR("open O_TMPFILE | O_RDWR");
    1981 return -1;
    1982 }
    1983 close(fd);
    1984
    1985 fd = open(testdir, O_TMPFILE | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
    1986 if(fd == -1){
    1987 PERROR("open with O_TMPFILE | O_WRONLY | O_EXCL");
    1988 return -1;
    1989 };
    1990 close(fd);
    1991
    1992 fd = open(testdir, O_TMPFILE | O_RDONLY, S_IRUSR);
    1993 if (fd != -1) {
    1994 ERROR("open with O_TMPFILE | O_RDONLY succeeded");
    1995 return -1;
    1996 }
    1997
    1998 success();
    1999 return 0;
    2000}
    2001
    2002static int test_create_and_link_tmpfile(void)
    2003{
    2004 /* skip this test for now since the github runner will fail in the linkat call below */
    2005 return 0;
    2006
    2007 rmdir(testdir);
    2008 unlink(testfile);
    2009
    2010 int res = mkdir(testdir, 0777);
    2011 if (res)
    2012 return -1;
    2013
    2014 start_test("create and link tmpfile");
    2015
    2016 int fd = open(testdir, O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
    2017 if(fd == -1) {
    2018 if (errno == ENOTSUP) {
    2019 /* don't bother if we're working on an old kernel
    2020 or on the high level API */
    2021 return 0;
    2022 }
    2023 PERROR("open with O_TMPFILE | O_RDWR | O_EXCL");
    2024 return -1;
    2025 }
    2026
    2027 if (!linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
    2028 ERROR("linkat succeeded on a tmpfile opened with O_EXCL");
    2029 return -1;
    2030 }
    2031 close(fd);
    2032
    2033 fd = open(testdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
    2034 if(fd == -1) {
    2035 PERROR("open O_TMPFILE");
    2036 return -1;
    2037 }
    2038
    2039 if (check_nonexist(testfile)) {
    2040 return -1;
    2041 }
    2042
    2043 if (linkat(fd, "", AT_FDCWD, testfile, AT_EMPTY_PATH)) {
    2044 PERROR("linkat tempfile");
    2045 return -1;
    2046 }
    2047 close(fd);
    2048
    2049 if (check_nlink(testfile, 1)) {
    2050 return -1;
    2051 }
    2052 unlink(testfile);
    2053
    2054 success();
    2055 return 0;
    2056}
    2057
    2058int main(int argc, char *argv[])
    2059{
    2060 int err = 0;
    2061 int a;
    2062 int is_root;
    2063
    2064 umask(0);
    2065 if (argc < 2 || argc > 4) {
    2066 fprintf(stderr, "usage: %s testdir [:realdir] [[-]test#] [-u]\n", argv[0]);
    2067 return 1;
    2068 }
    2069 basepath = argv[1];
    2070 basepath_r = basepath;
    2071 for (a = 2; a < argc; a++) {
    2072 char *endptr;
    2073 char *arg = argv[a];
    2074 if (arg[0] == ':') {
    2075 basepath_r = arg + 1;
    2076 } else {
    2077 if (arg[0] == '-') {
    2078 arg++;
    2079 if (arg[0] == 'u') {
    2080 unlinked_test = 1;
    2081 endptr = arg + 1;
    2082 } else {
    2083 skip_test = strtoul(arg, &endptr, 10);
    2084 }
    2085 } else {
    2086 select_test = strtoul(arg, &endptr, 10);
    2087 }
    2088 if (arg[0] == '\0' || *endptr != '\0') {
    2089 fprintf(stderr, "invalid option: '%s'\n", argv[a]);
    2090 return 1;
    2091 }
    2092 }
    2093 }
    2094 assert(strlen(basepath) < 512);
    2095 assert(strlen(basepath_r) < 512);
    2096 if (basepath[0] != '/') {
    2097 fprintf(stderr, "testdir must be an absolute path\n");
    2098 return 1;
    2099 }
    2100
    2101 sprintf(testfile, "%s/testfile", basepath);
    2102 sprintf(testfile2, "%s/testfile2", basepath);
    2103 sprintf(testdir, "%s/testdir", basepath);
    2104 sprintf(testdir2, "%s/testdir2", basepath);
    2105 sprintf(subfile, "%s/subfile", testdir2);
    2106 sprintf(testsock, "%s/testsock", basepath);
    2107
    2108 sprintf(testfile_r, "%s/testfile", basepath_r);
    2109 sprintf(testfile2_r, "%s/testfile2", basepath_r);
    2110 sprintf(testdir_r, "%s/testdir", basepath_r);
    2111 sprintf(testdir2_r, "%s/testdir2", basepath_r);
    2112 sprintf(subfile_r, "%s/subfile", testdir2_r);
    2113
    2114 is_root = (geteuid() == 0);
    2115
    2116 err += test_create();
    2117 err += test_create_unlink();
    2118 err += test_symlink();
    2119 err += test_link();
    2120 err += test_link2();
    2121#ifndef __FreeBSD__
    2122 err += test_mknod();
    2123 err += test_mkfifo();
    2124#endif
    2125 err += test_mkdir();
    2126 err += test_rename_file();
    2127 err += test_rename_dir();
    2128 err += test_rename_dir_loop();
    2129 err += test_seekdir();
    2130 err += test_socket();
    2131 err += test_utime();
    2132 err += test_truncate(0);
    2133 err += test_truncate(testdatalen / 2);
    2134 err += test_truncate(testdatalen);
    2135 err += test_truncate(testdatalen + 100);
    2136 err += test_ftruncate(0, 0600);
    2137 err += test_ftruncate(testdatalen / 2, 0600);
    2138 err += test_ftruncate(testdatalen, 0600);
    2139 err += test_ftruncate(testdatalen + 100, 0600);
    2140 err += test_ftruncate(0, 0400);
    2141 err += test_ftruncate(0, 0200);
    2142 err += test_ftruncate(0, 0000);
    2143 err += test_open(0, O_RDONLY, 0);
    2144 err += test_open(1, O_RDONLY, 0);
    2145 err += test_open(1, O_RDWR, 0);
    2146 err += test_open(1, O_WRONLY, 0);
    2147 err += test_open(0, O_RDWR | O_CREAT, 0600);
    2148 err += test_open(1, O_RDWR | O_CREAT, 0600);
    2149 err += test_open(0, O_RDWR | O_CREAT | O_TRUNC, 0600);
    2150 err += test_open(1, O_RDWR | O_CREAT | O_TRUNC, 0600);
    2151 err += test_open(0, O_RDONLY | O_CREAT, 0600);
    2152 err += test_open(0, O_RDONLY | O_CREAT, 0400);
    2153 err += test_open(0, O_RDONLY | O_CREAT, 0200);
    2154 err += test_open(0, O_RDONLY | O_CREAT, 0000);
    2155 err += test_open(0, O_WRONLY | O_CREAT, 0600);
    2156 err += test_open(0, O_WRONLY | O_CREAT, 0400);
    2157 err += test_open(0, O_WRONLY | O_CREAT, 0200);
    2158 err += test_open(0, O_WRONLY | O_CREAT, 0000);
    2159 err += test_open(0, O_RDWR | O_CREAT, 0400);
    2160 err += test_open(0, O_RDWR | O_CREAT, 0200);
    2161 err += test_open(0, O_RDWR | O_CREAT, 0000);
    2162 err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0600);
    2163 err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600);
    2164 err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000);
    2165 err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000);
    2166 err += test_open_acc(O_RDONLY, 0600, 0);
    2167 err += test_open_acc(O_WRONLY, 0600, 0);
    2168 err += test_open_acc(O_RDWR, 0600, 0);
    2169 err += test_open_acc(O_RDONLY, 0400, 0);
    2170 err += test_open_acc(O_WRONLY, 0200, 0);
    2171 if(!is_root) {
    2172 err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES);
    2173 err += test_open_acc(O_WRONLY, 0400, EACCES);
    2174 err += test_open_acc(O_RDWR, 0400, EACCES);
    2175 err += test_open_acc(O_RDONLY, 0200, EACCES);
    2176 err += test_open_acc(O_RDWR, 0200, EACCES);
    2177 err += test_open_acc(O_RDONLY, 0000, EACCES);
    2178 err += test_open_acc(O_WRONLY, 0000, EACCES);
    2179 err += test_open_acc(O_RDWR, 0000, EACCES);
    2180 }
    2181 err += test_create_ro_dir(O_CREAT);
    2182 err += test_create_ro_dir(O_CREAT | O_EXCL);
    2183 err += test_create_ro_dir(O_CREAT | O_WRONLY);
    2184 err += test_create_ro_dir(O_CREAT | O_TRUNC);
    2185 err += test_copy_file_range();
    2186 err += test_create_tmpfile();
    2187 err += test_create_and_link_tmpfile();
    2188
    2189 unlink(testfile2);
    2190 unlink(testsock);
    2191 rmdir(testdir);
    2192 rmdir(testdir2);
    2193
    2194 if (err) {
    2195 fprintf(stderr, "%i tests failed\n", -err);
    2196 return 1;
    2197 }
    2198
    2199 return check_unlinked_testfiles();
    2200}
    fuse-3.17.2/doc/html/test_2test__syscalls_8c_source.html0000644000175000017500000114254715002273247022261 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 <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.17.2/doc/html/fuse-3_817_81-rc0_2test_2test__write__cache_8c_source.html0000644000175000017500000017361314770250311025670 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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) {
    71 assert(fuse_get_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE));
    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
    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)
    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_CAP_WRITEBACK_CACHE
    #define FUSE_ROOT_ID
    char ** argv
    Definition fuse_opt.h:114
    fuse_ino_t ino
    uint32_t noflush
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/test_2test__write__cache_8c_source.html0000644000175000017500000017276615002273247023045 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 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) {
    71 assert(fuse_get_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE));
    72 fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
    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
    int fuse_set_signal_handlers(struct fuse_session *se)
    #define FUSE_CAP_WRITEBACK_CACHE
    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
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2test_2wrong__command_8c_source.html0000644000175000017500000001204214770250311025033 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/test_2wrong__command_8c_source.html0000644000175000017500000001164715002273247022212 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.17.2/doc/html/fuse-3_817_81-rc0_2util_2fusermount_8c_source.html0000644000175000017500000076661214770250311024272 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 and strchrnul */
    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#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
    40
    41#define FUSE_DEV "/dev/fuse"
    42
    43static const char *progname;
    44
    45static int user_allow_other = 0;
    46static int mount_max = 1000;
    47
    48static int auto_unmount = 0;
    49
    50#ifdef GETMNTENT_NEEDS_UNESCAPING
    51// Older versions of musl libc don't unescape entries in /etc/mtab
    52
    53// unescapes octal sequences like \040 in-place
    54// That's ok, because unescaping can not extend the length of the string.
    55static void unescape(char *buf) {
    56 char *src = buf;
    57 char *dest = buf;
    58 while (1) {
    59 char *next_src = strchrnul(src, '\\');
    60 int offset = next_src - src;
    61 memmove(dest, src, offset);
    62 src = next_src;
    63 dest += offset;
    64
    65 if(*src == '\0') {
    66 *dest = *src;
    67 return;
    68 }
    69 src++;
    70
    71 if('0' <= src[0] && src[0] < '2' &&
    72 '0' <= src[1] && src[1] < '8' &&
    73 '0' <= src[2] && src[2] < '8') {
    74 *dest++ = (src[0] - '0') << 6
    75 | (src[1] - '0') << 3
    76 | (src[2] - '0') << 0;
    77 src += 3;
    78 } else if (src[0] == '\\') {
    79 *dest++ = '\\';
    80 src += 1;
    81 } else {
    82 *dest++ = '\\';
    83 }
    84 }
    85}
    86
    87static struct mntent *GETMNTENT(FILE *stream)
    88{
    89 struct mntent *entp = getmntent(stream);
    90 if(entp != NULL) {
    91 unescape(entp->mnt_fsname);
    92 unescape(entp->mnt_dir);
    93 unescape(entp->mnt_type);
    94 unescape(entp->mnt_opts);
    95 }
    96 return entp;
    97}
    98#else
    99#define GETMNTENT getmntent
    100#endif // GETMNTENT_NEEDS_UNESCAPING
    101
    102/*
    103 * Take a ',' separated option string and extract "x-" options
    104 */
    105static int extract_x_options(const char *original, char **non_x_opts,
    106 char **x_opts)
    107{
    108 size_t orig_len;
    109 const char *opt, *opt_end;
    110
    111 orig_len = strlen(original) + 1;
    112
    113 *non_x_opts = calloc(1, orig_len);
    114 *x_opts = calloc(1, orig_len);
    115
    116 size_t non_x_opts_len = orig_len;
    117 size_t x_opts_len = orig_len;
    118
    119 if (*non_x_opts == NULL || *x_opts == NULL) {
    120 fprintf(stderr, "%s: Failed to allocate %zuB.\n",
    121 __func__, orig_len);
    122 return -ENOMEM;
    123 }
    124
    125 for (opt = original; opt < original + orig_len; opt = opt_end + 1) {
    126 char *opt_buf;
    127
    128 opt_end = strchr(opt, ',');
    129 if (opt_end == NULL)
    130 opt_end = original + orig_len;
    131
    132 size_t opt_len = opt_end - opt;
    133 size_t opt_len_left = orig_len - (opt - original);
    134 size_t buf_len;
    135 bool is_x_opts;
    136
    137 if (strncmp(opt, "x-", MIN(2, opt_len_left)) == 0) {
    138 buf_len = x_opts_len;
    139 is_x_opts = true;
    140 opt_buf = *x_opts;
    141 } else {
    142 buf_len = non_x_opts_len;
    143 is_x_opts = false;
    144 opt_buf = *non_x_opts;
    145 }
    146
    147 if (buf_len < orig_len) {
    148 strncat(opt_buf, ",", 2);
    149 buf_len -= 1;
    150 }
    151
    152 /* omits ',' */
    153 if ((ssize_t)(buf_len - opt_len) < 0) {
    154 /* This would be a bug */
    155 fprintf(stderr, "%s: no buf space left in copy, orig='%s'\n",
    156 __func__, original);
    157 return -EIO;
    158 }
    159
    160 strncat(opt_buf, opt, opt_end - opt);
    161 buf_len -= opt_len;
    162
    163 if (is_x_opts)
    164 x_opts_len = buf_len;
    165 else
    166 non_x_opts_len = buf_len;
    167 }
    168
    169 return 0;
    170}
    171
    172static const char *get_user_name(void)
    173{
    174 struct passwd *pw = getpwuid(getuid());
    175 if (pw != NULL && pw->pw_name != NULL)
    176 return pw->pw_name;
    177 else {
    178 fprintf(stderr, "%s: could not determine username\n", progname);
    179 return NULL;
    180 }
    181}
    182
    183static uid_t oldfsuid;
    184static gid_t oldfsgid;
    185
    186static void drop_privs(void)
    187{
    188 if (getuid() != 0) {
    189 oldfsuid = setfsuid(getuid());
    190 oldfsgid = setfsgid(getgid());
    191 }
    192}
    193
    194static void restore_privs(void)
    195{
    196 if (getuid() != 0) {
    197 setfsuid(oldfsuid);
    198 setfsgid(oldfsgid);
    199 }
    200}
    201
    202#ifndef IGNORE_MTAB
    203/*
    204 * Make sure that /etc/mtab is checked and updated atomically
    205 */
    206static int lock_umount(void)
    207{
    208 const char *mtab_lock = _PATH_MOUNTED ".fuselock";
    209 int mtablock;
    210 int res;
    211 struct stat mtab_stat;
    212
    213 /* /etc/mtab could be a symlink to /proc/mounts */
    214 if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
    215 return -1;
    216
    217 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
    218 if (mtablock == -1) {
    219 fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
    220 progname, strerror(errno));
    221 return -1;
    222 }
    223 res = lockf(mtablock, F_LOCK, 0);
    224 if (res < 0) {
    225 fprintf(stderr, "%s: error getting lock: %s\n", progname,
    226 strerror(errno));
    227 close(mtablock);
    228 return -1;
    229 }
    230
    231 return mtablock;
    232}
    233
    234static void unlock_umount(int mtablock)
    235{
    236 if (mtablock >= 0) {
    237 int res;
    238
    239 res = lockf(mtablock, F_ULOCK, 0);
    240 if (res < 0) {
    241 fprintf(stderr, "%s: error releasing lock: %s\n",
    242 progname, strerror(errno));
    243 }
    244 close(mtablock);
    245 }
    246}
    247
    248static int add_mount(const char *source, const char *mnt, const char *type,
    249 const char *opts)
    250{
    251 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
    252}
    253
    254static int may_unmount(const char *mnt, int quiet)
    255{
    256 struct mntent *entp;
    257 FILE *fp;
    258 const char *user = NULL;
    259 char uidstr[32];
    260 unsigned uidlen = 0;
    261 int found;
    262 const char *mtab = _PATH_MOUNTED;
    263
    264 user = get_user_name();
    265 if (user == NULL)
    266 return -1;
    267
    268 fp = setmntent(mtab, "r");
    269 if (fp == NULL) {
    270 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
    271 strerror(errno));
    272 return -1;
    273 }
    274
    275 uidlen = sprintf(uidstr, "%u", getuid());
    276
    277 found = 0;
    278 while ((entp = GETMNTENT(fp)) != NULL) {
    279 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
    280 (strcmp(entp->mnt_type, "fuse") == 0 ||
    281 strcmp(entp->mnt_type, "fuseblk") == 0 ||
    282 strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
    283 strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
    284 char *p = strstr(entp->mnt_opts, "user=");
    285 if (p &&
    286 (p == entp->mnt_opts || *(p-1) == ',') &&
    287 strcmp(p + 5, user) == 0) {
    288 found = 1;
    289 break;
    290 }
    291 /* /etc/mtab is a link pointing to
    292 /proc/mounts: */
    293 else if ((p =
    294 strstr(entp->mnt_opts, "user_id=")) &&
    295 (p == entp->mnt_opts ||
    296 *(p-1) == ',') &&
    297 strncmp(p + 8, uidstr, uidlen) == 0 &&
    298 (*(p+8+uidlen) == ',' ||
    299 *(p+8+uidlen) == '\0')) {
    300 found = 1;
    301 break;
    302 }
    303 }
    304 }
    305 endmntent(fp);
    306
    307 if (!found) {
    308 if (!quiet)
    309 fprintf(stderr,
    310 "%s: entry for %s not found in %s\n",
    311 progname, mnt, mtab);
    312 return -1;
    313 }
    314
    315 return 0;
    316}
    317#endif
    318
    319/*
    320 * Check whether the file specified in "fusermount3 -u" is really a
    321 * mountpoint and not a symlink. This is necessary otherwise the user
    322 * could move the mountpoint away and replace it with a symlink
    323 * pointing to an arbitrary mount, thereby tricking fusermount3 into
    324 * unmounting that (umount(2) will follow symlinks).
    325 *
    326 * This is the child process running in a separate mount namespace, so
    327 * we don't mess with the global namespace and if the process is
    328 * killed for any reason, mounts are automatically cleaned up.
    329 *
    330 * First make sure nothing is propagated back into the parent
    331 * namespace by marking all mounts "private".
    332 *
    333 * Then bind mount parent onto a stable base where the user can't move
    334 * it around.
    335 *
    336 * Finally check /proc/mounts for an entry matching the requested
    337 * mountpoint. If it's found then we are OK, and the user can't move
    338 * it around within the parent directory as rename() will return
    339 * EBUSY. Be careful to ignore any mounts that existed before the
    340 * bind.
    341 */
    342static int check_is_mount_child(void *p)
    343{
    344 const char **a = p;
    345 const char *last = a[0];
    346 const char *mnt = a[1];
    347 const char *type = a[2];
    348 int res;
    349 const char *procmounts = "/proc/mounts";
    350 int found;
    351 FILE *fp;
    352 struct mntent *entp;
    353 int count;
    354
    355 res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
    356 if (res == -1) {
    357 fprintf(stderr, "%s: failed to mark mounts private: %s\n",
    358 progname, strerror(errno));
    359 return 1;
    360 }
    361
    362 fp = setmntent(procmounts, "r");
    363 if (fp == NULL) {
    364 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
    365 procmounts, strerror(errno));
    366 return 1;
    367 }
    368
    369 count = 0;
    370 while (GETMNTENT(fp) != NULL)
    371 count++;
    372 endmntent(fp);
    373
    374 fp = setmntent(procmounts, "r");
    375 if (fp == NULL) {
    376 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
    377 procmounts, strerror(errno));
    378 return 1;
    379 }
    380
    381 res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
    382 if (res == -1) {
    383 fprintf(stderr, "%s: failed to bind parent to /: %s\n",
    384 progname, strerror(errno));
    385 return 1;
    386 }
    387
    388 found = 0;
    389 while ((entp = GETMNTENT(fp)) != NULL) {
    390 if (count > 0) {
    391 count--;
    392 continue;
    393 }
    394 if (entp->mnt_dir[0] == '/' &&
    395 strcmp(entp->mnt_dir + 1, last) == 0 &&
    396 (!type || strcmp(entp->mnt_type, type) == 0)) {
    397 found = 1;
    398 break;
    399 }
    400 }
    401 endmntent(fp);
    402
    403 if (!found) {
    404 fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
    405 return 1;
    406 }
    407
    408 return 0;
    409}
    410
    411static pid_t clone_newns(void *a)
    412{
    413 char buf[131072];
    414 char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
    415
    416#ifdef __ia64__
    417 extern int __clone2(int (*fn)(void *),
    418 void *child_stack_base, size_t stack_size,
    419 int flags, void *arg, pid_t *ptid,
    420 void *tls, pid_t *ctid);
    421
    422 return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
    423 CLONE_NEWNS, a, NULL, NULL, NULL);
    424#else
    425 return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
    426#endif
    427}
    428
    429static int check_is_mount(const char *last, const char *mnt, const char *type)
    430{
    431 pid_t pid, p;
    432 int status;
    433 const char *a[3] = { last, mnt, type };
    434
    435 pid = clone_newns((void *) a);
    436 if (pid == (pid_t) -1) {
    437 fprintf(stderr, "%s: failed to clone namespace: %s\n",
    438 progname, strerror(errno));
    439 return -1;
    440 }
    441 p = waitpid(pid, &status, __WCLONE);
    442 if (p == (pid_t) -1) {
    443 fprintf(stderr, "%s: waitpid failed: %s\n",
    444 progname, strerror(errno));
    445 return -1;
    446 }
    447 if (!WIFEXITED(status)) {
    448 fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
    449 progname, status);
    450 return -1;
    451 }
    452 if (WEXITSTATUS(status) != 0)
    453 return -1;
    454
    455 return 0;
    456}
    457
    458static int chdir_to_parent(char *copy, const char **lastp)
    459{
    460 char *tmp;
    461 const char *parent;
    462 char buf[65536];
    463 int res;
    464
    465 tmp = strrchr(copy, '/');
    466 if (tmp == NULL || tmp[1] == '\0') {
    467 fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
    468 progname, copy);
    469 return -1;
    470 }
    471 if (tmp != copy) {
    472 *tmp = '\0';
    473 parent = copy;
    474 *lastp = tmp + 1;
    475 } else if (tmp[1] != '\0') {
    476 *lastp = tmp + 1;
    477 parent = "/";
    478 } else {
    479 *lastp = ".";
    480 parent = "/";
    481 }
    482
    483 res = chdir(parent);
    484 if (res == -1) {
    485 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
    486 progname, parent, strerror(errno));
    487 return -1;
    488 }
    489
    490 if (getcwd(buf, sizeof(buf)) == NULL) {
    491 fprintf(stderr, "%s: failed to obtain current directory: %s\n",
    492 progname, strerror(errno));
    493 return -1;
    494 }
    495 if (strcmp(buf, parent) != 0) {
    496 fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
    497 parent, buf);
    498 return -1;
    499
    500 }
    501
    502 return 0;
    503}
    504
    505#ifndef IGNORE_MTAB
    506static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
    507{
    508 int res;
    509 char *copy;
    510 const char *last;
    511 int umount_flags = (lazy ? UMOUNT_DETACH : 0) | UMOUNT_NOFOLLOW;
    512
    513 if (getuid() != 0) {
    514 res = may_unmount(mnt, quiet);
    515 if (res == -1)
    516 return -1;
    517 }
    518
    519 copy = strdup(mnt);
    520 if (copy == NULL) {
    521 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    522 return -1;
    523 }
    524
    525 drop_privs();
    526 res = chdir_to_parent(copy, &last);
    527 if (res == -1) {
    528 restore_privs();
    529 goto out;
    530 }
    531
    532 res = umount2(last, umount_flags);
    533 restore_privs();
    534 if (res == -1 && !quiet) {
    535 fprintf(stderr, "%s: failed to unmount %s: %s\n",
    536 progname, mnt, strerror(errno));
    537 }
    538
    539out:
    540 free(copy);
    541 if (res == -1)
    542 return -1;
    543
    544 res = chdir("/");
    545 if (res == -1) {
    546 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
    547 return -1;
    548 }
    549
    550 return fuse_mnt_remove_mount(progname, mnt);
    551}
    552
    553static int unmount_fuse(const char *mnt, int quiet, int lazy)
    554{
    555 int res;
    556 int mtablock = lock_umount();
    557
    558 res = unmount_fuse_locked(mnt, quiet, lazy);
    559 unlock_umount(mtablock);
    560
    561 return res;
    562}
    563
    564static int count_fuse_fs(void)
    565{
    566 struct mntent *entp;
    567 int count = 0;
    568 const char *mtab = _PATH_MOUNTED;
    569 FILE *fp = setmntent(mtab, "r");
    570 if (fp == NULL) {
    571 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
    572 strerror(errno));
    573 return -1;
    574 }
    575 while ((entp = GETMNTENT(fp)) != NULL) {
    576 if (strcmp(entp->mnt_type, "fuse") == 0 ||
    577 strncmp(entp->mnt_type, "fuse.", 5) == 0)
    578 count ++;
    579 }
    580 endmntent(fp);
    581 return count;
    582}
    583
    584
    585#else /* IGNORE_MTAB */
    586static int count_fuse_fs(void)
    587{
    588 return 0;
    589}
    590
    591static int add_mount(const char *source, const char *mnt, const char *type,
    592 const char *opts)
    593{
    594 (void) source;
    595 (void) mnt;
    596 (void) type;
    597 (void) opts;
    598 return 0;
    599}
    600
    601static int unmount_fuse(const char *mnt, int quiet, int lazy)
    602{
    603 (void) quiet;
    604 return fuse_mnt_umount(progname, mnt, mnt, lazy);
    605}
    606#endif /* IGNORE_MTAB */
    607
    608static void strip_line(char *line)
    609{
    610 char *s = strchr(line, '#');
    611 if (s != NULL)
    612 s[0] = '\0';
    613 for (s = line + strlen(line) - 1;
    614 s >= line && isspace((unsigned char) *s); s--);
    615 s[1] = '\0';
    616 for (s = line; isspace((unsigned char) *s); s++);
    617 if (s != line)
    618 memmove(line, s, strlen(s)+1);
    619}
    620
    621static void parse_line(char *line, int linenum)
    622{
    623 int tmp;
    624 if (strcmp(line, "user_allow_other") == 0)
    625 user_allow_other = 1;
    626 else if (sscanf(line, "mount_max = %i", &tmp) == 1)
    627 mount_max = tmp;
    628 else if(line[0])
    629 fprintf(stderr,
    630 "%s: unknown parameter in %s at line %i: '%s'\n",
    631 progname, FUSE_CONF, linenum, line);
    632}
    633
    634static void read_conf(void)
    635{
    636 FILE *fp = fopen(FUSE_CONF, "r");
    637 if (fp != NULL) {
    638 int linenum = 1;
    639 char line[256];
    640 int isnewline = 1;
    641 while (fgets(line, sizeof(line), fp) != NULL) {
    642 if (isnewline) {
    643 if (line[strlen(line)-1] == '\n') {
    644 strip_line(line);
    645 parse_line(line, linenum);
    646 } else {
    647 isnewline = 0;
    648 }
    649 } else if(line[strlen(line)-1] == '\n') {
    650 fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
    651
    652 isnewline = 1;
    653 }
    654 if (isnewline)
    655 linenum ++;
    656 }
    657 if (!isnewline) {
    658 fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
    659
    660 }
    661 if (ferror(fp)) {
    662 fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF);
    663 exit(1);
    664 }
    665 fclose(fp);
    666 } else if (errno != ENOENT) {
    667 bool fatal = (errno != EACCES && errno != ELOOP &&
    668 errno != ENAMETOOLONG && errno != ENOTDIR &&
    669 errno != EOVERFLOW);
    670 fprintf(stderr, "%s: failed to open %s: %s\n",
    671 progname, FUSE_CONF, strerror(errno));
    672 if (fatal)
    673 exit(1);
    674 }
    675}
    676
    677static int begins_with(const char *s, const char *beg)
    678{
    679 if (strncmp(s, beg, strlen(beg)) == 0)
    680 return 1;
    681 else
    682 return 0;
    683}
    684
    685struct mount_flags {
    686 const char *opt;
    687 unsigned long flag;
    688 int on;
    689 int safe;
    690};
    691
    692static struct mount_flags mount_flags[] = {
    693 {"rw", MS_RDONLY, 0, 1},
    694 {"ro", MS_RDONLY, 1, 1},
    695 {"suid", MS_NOSUID, 0, 0},
    696 {"nosuid", MS_NOSUID, 1, 1},
    697 {"dev", MS_NODEV, 0, 0},
    698 {"nodev", MS_NODEV, 1, 1},
    699 {"exec", MS_NOEXEC, 0, 1},
    700 {"noexec", MS_NOEXEC, 1, 1},
    701 {"async", MS_SYNCHRONOUS, 0, 1},
    702 {"sync", MS_SYNCHRONOUS, 1, 1},
    703 {"atime", MS_NOATIME, 0, 1},
    704 {"noatime", MS_NOATIME, 1, 1},
    705 {"diratime", MS_NODIRATIME, 0, 1},
    706 {"nodiratime", MS_NODIRATIME, 1, 1},
    707 {"lazytime", MS_LAZYTIME, 1, 1},
    708 {"nolazytime", MS_LAZYTIME, 0, 1},
    709 {"relatime", MS_RELATIME, 1, 1},
    710 {"norelatime", MS_RELATIME, 0, 1},
    711 {"strictatime", MS_STRICTATIME, 1, 1},
    712 {"nostrictatime", MS_STRICTATIME, 0, 1},
    713 {"dirsync", MS_DIRSYNC, 1, 1},
    714 {"symfollow", MS_NOSYMFOLLOW, 0, 1},
    715 {"nosymfollow", MS_NOSYMFOLLOW, 1, 1},
    716 {NULL, 0, 0, 0}
    717};
    718
    719static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
    720{
    721 int i;
    722
    723 for (i = 0; mount_flags[i].opt != NULL; i++) {
    724 const char *opt = mount_flags[i].opt;
    725 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
    726 *on = mount_flags[i].on;
    727 *flag = mount_flags[i].flag;
    728 if (!mount_flags[i].safe && getuid() != 0) {
    729 *flag = 0;
    730 fprintf(stderr,
    731 "%s: unsafe option %s ignored\n",
    732 progname, opt);
    733 }
    734 return 1;
    735 }
    736 }
    737 return 0;
    738}
    739
    740static int add_option(char **optsp, const char *opt, unsigned expand)
    741{
    742 char *newopts;
    743 if (*optsp == NULL)
    744 newopts = strdup(opt);
    745 else {
    746 unsigned oldsize = strlen(*optsp);
    747 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
    748 newopts = (char *) realloc(*optsp, newsize);
    749 if (newopts)
    750 sprintf(newopts + oldsize, ",%s", opt);
    751 }
    752 if (newopts == NULL) {
    753 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    754 return -1;
    755 }
    756 *optsp = newopts;
    757 return 0;
    758}
    759
    760static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
    761{
    762 int i;
    763 int l;
    764
    765 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
    766 return -1;
    767
    768 for (i = 0; mount_flags[i].opt != NULL; i++) {
    769 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
    770 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
    771 return -1;
    772 }
    773
    774 if (add_option(mnt_optsp, opts, 0) == -1)
    775 return -1;
    776 /* remove comma from end of opts*/
    777 l = strlen(*mnt_optsp);
    778 if ((*mnt_optsp)[l-1] == ',')
    779 (*mnt_optsp)[l-1] = '\0';
    780 if (getuid() != 0) {
    781 const char *user = get_user_name();
    782 if (user == NULL)
    783 return -1;
    784
    785 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
    786 return -1;
    787 strcat(*mnt_optsp, user);
    788 }
    789 return 0;
    790}
    791
    792static int opt_eq(const char *s, unsigned len, const char *opt)
    793{
    794 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
    795 return 1;
    796 else
    797 return 0;
    798}
    799
    800static int get_string_opt(const char *s, unsigned len, const char *opt,
    801 char **val)
    802{
    803 int i;
    804 unsigned opt_len = strlen(opt);
    805 char *d;
    806
    807 if (*val)
    808 free(*val);
    809 *val = (char *) malloc(len - opt_len + 1);
    810 if (!*val) {
    811 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    812 return 0;
    813 }
    814
    815 d = *val;
    816 s += opt_len;
    817 len -= opt_len;
    818 for (i = 0; i < len; i++) {
    819 if (s[i] == '\\' && i + 1 < len)
    820 i++;
    821 *d++ = s[i];
    822 }
    823 *d = '\0';
    824 return 1;
    825}
    826
    827/* The kernel silently truncates the "data" argument to PAGE_SIZE-1 characters.
    828 * This can be dangerous if it e.g. truncates the option "group_id=1000" to
    829 * "group_id=1".
    830 * This wrapper detects this case and bails out with an error.
    831 */
    832static int mount_notrunc(const char *source, const char *target,
    833 const char *filesystemtype, unsigned long mountflags,
    834 const char *data) {
    835 if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) {
    836 fprintf(stderr, "%s: mount options too long\n", progname);
    837 errno = EINVAL;
    838 return -1;
    839 }
    840 return mount(source, target, filesystemtype, mountflags, data);
    841}
    842
    843
    844static int do_mount(const char *mnt, const char **typep, mode_t rootmode,
    845 int fd, const char *opts, const char *dev, char **sourcep,
    846 char **mnt_optsp)
    847{
    848 int res;
    849 int flags = MS_NOSUID | MS_NODEV;
    850 char *optbuf;
    851 char *mnt_opts = NULL;
    852 const char *s;
    853 char *d;
    854 char *fsname = NULL;
    855 char *subtype = NULL;
    856 char *source = NULL;
    857 char *type = NULL;
    858 int blkdev = 0;
    859
    860 optbuf = (char *) malloc(strlen(opts) + 128);
    861 if (!optbuf) {
    862 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    863 return -1;
    864 }
    865
    866 for (s = opts, d = optbuf; *s;) {
    867 unsigned len;
    868 const char *fsname_str = "fsname=";
    869 const char *subtype_str = "subtype=";
    870 bool escape_ok = begins_with(s, fsname_str) ||
    871 begins_with(s, subtype_str);
    872 for (len = 0; s[len]; len++) {
    873 if (escape_ok && s[len] == '\\' && s[len + 1])
    874 len++;
    875 else if (s[len] == ',')
    876 break;
    877 }
    878 if (begins_with(s, fsname_str)) {
    879 if (!get_string_opt(s, len, fsname_str, &fsname))
    880 goto err;
    881 } else if (begins_with(s, subtype_str)) {
    882 if (!get_string_opt(s, len, subtype_str, &subtype))
    883 goto err;
    884 } else if (opt_eq(s, len, "blkdev")) {
    885 if (getuid() != 0) {
    886 fprintf(stderr,
    887 "%s: option blkdev is privileged\n",
    888 progname);
    889 goto err;
    890 }
    891 blkdev = 1;
    892 } else if (opt_eq(s, len, "auto_unmount")) {
    893 auto_unmount = 1;
    894 } else if (!opt_eq(s, len, "nonempty") &&
    895 !begins_with(s, "fd=") &&
    896 !begins_with(s, "rootmode=") &&
    897 !begins_with(s, "user_id=") &&
    898 !begins_with(s, "group_id=")) {
    899 int on;
    900 int flag;
    901 int skip_option = 0;
    902 if (opt_eq(s, len, "large_read")) {
    903 struct utsname utsname;
    904 unsigned kmaj, kmin;
    905 res = uname(&utsname);
    906 if (res == 0 &&
    907 sscanf(utsname.release, "%u.%u",
    908 &kmaj, &kmin) == 2 &&
    909 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
    910 fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
    911 skip_option = 1;
    912 }
    913 }
    914 if (getuid() != 0 && !user_allow_other &&
    915 (opt_eq(s, len, "allow_other") ||
    916 opt_eq(s, len, "allow_root"))) {
    917 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
    918 goto err;
    919 }
    920 if (!skip_option) {
    921 if (find_mount_flag(s, len, &on, &flag)) {
    922 if (on)
    923 flags |= flag;
    924 else
    925 flags &= ~flag;
    926 } else if (opt_eq(s, len, "default_permissions") ||
    927 opt_eq(s, len, "allow_other") ||
    928 begins_with(s, "max_read=") ||
    929 begins_with(s, "blksize=")) {
    930 memcpy(d, s, len);
    931 d += len;
    932 *d++ = ',';
    933 } else {
    934 fprintf(stderr, "%s: unknown option '%.*s'\n", progname, len, s);
    935 exit(1);
    936 }
    937 }
    938 }
    939 s += len;
    940 if (*s)
    941 s++;
    942 }
    943 *d = '\0';
    944 res = get_mnt_opts(flags, optbuf, &mnt_opts);
    945 if (res == -1)
    946 goto err;
    947
    948 sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
    949 fd, rootmode, getuid(), getgid());
    950
    951 source = malloc((fsname ? strlen(fsname) : 0) +
    952 (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
    953
    954 type = malloc((subtype ? strlen(subtype) : 0) + 32);
    955 if (!type || !source) {
    956 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    957 goto err;
    958 }
    959
    960 if (subtype)
    961 sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
    962 else
    963 strcpy(type, blkdev ? "fuseblk" : "fuse");
    964
    965 if (fsname)
    966 strcpy(source, fsname);
    967 else
    968 strcpy(source, subtype ? subtype : dev);
    969
    970 res = mount_notrunc(source, mnt, type, flags, optbuf);
    971 if (res == -1 && errno == ENODEV && subtype) {
    972 /* Probably missing subtype support */
    973 strcpy(type, blkdev ? "fuseblk" : "fuse");
    974 if (fsname) {
    975 if (!blkdev)
    976 sprintf(source, "%s#%s", subtype, fsname);
    977 } else {
    978 strcpy(source, type);
    979 }
    980
    981 res = mount_notrunc(source, mnt, type, flags, optbuf);
    982 }
    983 if (res == -1 && errno == EINVAL) {
    984 /* It could be an old version not supporting group_id */
    985 sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
    986 fd, rootmode, getuid());
    987 res = mount_notrunc(source, mnt, type, flags, optbuf);
    988 }
    989 if (res == -1) {
    990 int errno_save = errno;
    991 if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
    992 fprintf(stderr, "%s: 'fuseblk' support missing\n",
    993 progname);
    994 else
    995 fprintf(stderr, "%s: mount failed: %s\n", progname,
    996 strerror(errno_save));
    997 goto err;
    998 }
    999 *sourcep = source;
    1000 *typep = type;
    1001 *mnt_optsp = mnt_opts;
    1002 free(fsname);
    1003 free(optbuf);
    1004
    1005 return 0;
    1006
    1007err:
    1008 free(fsname);
    1009 free(subtype);
    1010 free(source);
    1011 free(type);
    1012 free(mnt_opts);
    1013 free(optbuf);
    1014 return -1;
    1015}
    1016
    1017static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
    1018{
    1019 int res;
    1020 const char *mnt = *mntp;
    1021 const char *origmnt = mnt;
    1022 struct statfs fs_buf;
    1023 size_t i;
    1024
    1025 res = lstat(mnt, stbuf);
    1026 if (res == -1) {
    1027 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
    1028 progname, mnt, strerror(errno));
    1029 return -1;
    1030 }
    1031
    1032 /* No permission checking is done for root */
    1033 if (getuid() == 0)
    1034 return 0;
    1035
    1036 if (S_ISDIR(stbuf->st_mode)) {
    1037 res = chdir(mnt);
    1038 if (res == -1) {
    1039 fprintf(stderr,
    1040 "%s: failed to chdir to mountpoint: %s\n",
    1041 progname, strerror(errno));
    1042 return -1;
    1043 }
    1044 mnt = *mntp = ".";
    1045 res = lstat(mnt, stbuf);
    1046 if (res == -1) {
    1047 fprintf(stderr,
    1048 "%s: failed to access mountpoint %s: %s\n",
    1049 progname, origmnt, strerror(errno));
    1050 return -1;
    1051 }
    1052
    1053 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
    1054 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
    1055 progname, origmnt);
    1056 return -1;
    1057 }
    1058
    1059 res = access(mnt, W_OK);
    1060 if (res == -1) {
    1061 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
    1062 progname, origmnt);
    1063 return -1;
    1064 }
    1065 } else if (S_ISREG(stbuf->st_mode)) {
    1066 static char procfile[256];
    1067 *mountpoint_fd = open(mnt, O_WRONLY);
    1068 if (*mountpoint_fd == -1) {
    1069 fprintf(stderr, "%s: failed to open %s: %s\n",
    1070 progname, mnt, strerror(errno));
    1071 return -1;
    1072 }
    1073 res = fstat(*mountpoint_fd, stbuf);
    1074 if (res == -1) {
    1075 fprintf(stderr,
    1076 "%s: failed to access mountpoint %s: %s\n",
    1077 progname, mnt, strerror(errno));
    1078 return -1;
    1079 }
    1080 if (!S_ISREG(stbuf->st_mode)) {
    1081 fprintf(stderr,
    1082 "%s: mountpoint %s is no longer a regular file\n",
    1083 progname, mnt);
    1084 return -1;
    1085 }
    1086
    1087 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
    1088 *mntp = procfile;
    1089 } else {
    1090 fprintf(stderr,
    1091 "%s: mountpoint %s is not a directory or a regular file\n",
    1092 progname, mnt);
    1093 return -1;
    1094 }
    1095
    1096 /* Do not permit mounting over anything in procfs - it has a couple
    1097 * places to which we have "write access" without being supposed to be
    1098 * able to just put anything we want there.
    1099 * Luckily, without allow_other, we can't get other users to actually
    1100 * use any fake information we try to put there anyway.
    1101 * Use a whitelist to be safe. */
    1102 if (statfs(*mntp, &fs_buf)) {
    1103 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
    1104 progname, mnt, strerror(errno));
    1105 return -1;
    1106 }
    1107
    1108 /* Define permitted filesystems for the mount target. This was
    1109 * originally the same list as used by the ecryptfs mount helper
    1110 * (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
    1111 * but got expanded as we found more filesystems that needed to be
    1112 * overlaid. */
    1113 typeof(fs_buf.f_type) f_type_whitelist[] = {
    1114 0x61756673 /* AUFS_SUPER_MAGIC */,
    1115 0x00000187 /* AUTOFS_SUPER_MAGIC */,
    1116 0xCA451A4E /* BCACHEFS_STATFS_MAGIC */,
    1117 0x9123683E /* BTRFS_SUPER_MAGIC */,
    1118 0x00C36400 /* CEPH_SUPER_MAGIC */,
    1119 0xFF534D42 /* CIFS_MAGIC_NUMBER */,
    1120 0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
    1121 0X2011BAB0 /* EXFAT_SUPER_MAGIC */,
    1122 0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
    1123 0xF2F52010 /* F2FS_SUPER_MAGIC */,
    1124 0x65735546 /* FUSE_SUPER_MAGIC */,
    1125 0x01161970 /* GFS2_MAGIC */,
    1126 0x47504653 /* GPFS_SUPER_MAGIC */,
    1127 0x0000482b /* HFSPLUS_SUPER_MAGIC */,
    1128 0x000072B6 /* JFFS2_SUPER_MAGIC */,
    1129 0x3153464A /* JFS_SUPER_MAGIC */,
    1130 0x0BD00BD0 /* LL_SUPER_MAGIC */,
    1131 0X00004D44 /* MSDOS_SUPER_MAGIC */,
    1132 0x0000564C /* NCP_SUPER_MAGIC */,
    1133 0x00006969 /* NFS_SUPER_MAGIC */,
    1134 0x00003434 /* NILFS_SUPER_MAGIC */,
    1135 0x5346544E /* NTFS_SB_MAGIC */,
    1136 0x7366746E /* NTFS3_SUPER_MAGIC */,
    1137 0x5346414f /* OPENAFS_SUPER_MAGIC */,
    1138 0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
    1139 0x52654973 /* REISERFS_SUPER_MAGIC */,
    1140 0xFE534D42 /* SMB2_SUPER_MAGIC */,
    1141 0x73717368 /* SQUASHFS_MAGIC */,
    1142 0x01021994 /* TMPFS_MAGIC */,
    1143 0x24051905 /* UBIFS_SUPER_MAGIC */,
    1144 0x736675005346544e /* UFSD */,
    1145 0x58465342 /* XFS_SB_MAGIC */,
    1146 0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
    1147 0x858458f6 /* RAMFS_MAGIC */,
    1148 };
    1149 for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
    1150 if (f_type_whitelist[i] == fs_buf.f_type)
    1151 return 0;
    1152 }
    1153
    1154 fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
    1155 progname, (unsigned long)fs_buf.f_type);
    1156 return -1;
    1157}
    1158
    1159static int try_open(const char *dev, char **devp, int silent)
    1160{
    1161 int fd = open(dev, O_RDWR);
    1162 if (fd != -1) {
    1163 *devp = strdup(dev);
    1164 if (*devp == NULL) {
    1165 fprintf(stderr, "%s: failed to allocate memory\n",
    1166 progname);
    1167 close(fd);
    1168 fd = -1;
    1169 }
    1170 } else if (errno == ENODEV ||
    1171 errno == ENOENT)/* check for ENOENT too, for the udev case */
    1172 return -2;
    1173 else if (!silent) {
    1174 fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
    1175 strerror(errno));
    1176 }
    1177 return fd;
    1178}
    1179
    1180static int try_open_fuse_device(char **devp)
    1181{
    1182 int fd;
    1183
    1184 drop_privs();
    1185 fd = try_open(FUSE_DEV, devp, 0);
    1186 restore_privs();
    1187 return fd;
    1188}
    1189
    1190static int open_fuse_device(char **devp)
    1191{
    1192 int fd = try_open_fuse_device(devp);
    1193 if (fd >= -1)
    1194 return fd;
    1195
    1196 fprintf(stderr,
    1197 "%s: fuse device not found, try 'modprobe fuse' first\n",
    1198 progname);
    1199
    1200 return -1;
    1201}
    1202
    1203
    1204static int mount_fuse(const char *mnt, const char *opts, const char **type)
    1205{
    1206 int res;
    1207 int fd;
    1208 char *dev;
    1209 struct stat stbuf;
    1210 char *source = NULL;
    1211 char *mnt_opts = NULL;
    1212 const char *real_mnt = mnt;
    1213 int mountpoint_fd = -1;
    1214 char *do_mount_opts = NULL;
    1215 char *x_opts = NULL;
    1216
    1217 fd = open_fuse_device(&dev);
    1218 if (fd == -1)
    1219 return -1;
    1220
    1221 drop_privs();
    1222 read_conf();
    1223
    1224 if (getuid() != 0 && mount_max != -1) {
    1225 int mount_count = count_fuse_fs();
    1226 if (mount_count >= mount_max) {
    1227 fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
    1228 goto fail_close_fd;
    1229 }
    1230 }
    1231
    1232 // Extract any options starting with "x-"
    1233 res= extract_x_options(opts, &do_mount_opts, &x_opts);
    1234 if (res)
    1235 goto fail_close_fd;
    1236
    1237 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
    1238 restore_privs();
    1239 if (res != -1)
    1240 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
    1241 fd, do_mount_opts, dev, &source, &mnt_opts);
    1242
    1243 if (mountpoint_fd != -1)
    1244 close(mountpoint_fd);
    1245
    1246 if (res == -1)
    1247 goto fail_close_fd;
    1248
    1249 res = chdir("/");
    1250 if (res == -1) {
    1251 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
    1252 goto fail_close_fd;
    1253 }
    1254
    1255 if (geteuid() == 0) {
    1256 if (x_opts && strlen(x_opts) > 0) {
    1257 /*
    1258 * Add back the options starting with "x-" to opts from
    1259 * do_mount. +2 for ',' and '\0'
    1260 */
    1261 size_t mnt_opts_len = strlen(mnt_opts);
    1262 size_t x_mnt_opts_len = mnt_opts_len+
    1263 strlen(x_opts) + 2;
    1264 char *x_mnt_opts = calloc(1, x_mnt_opts_len);
    1265
    1266 if (mnt_opts_len) {
    1267 strcpy(x_mnt_opts, mnt_opts);
    1268 strncat(x_mnt_opts, ",", 2);
    1269 }
    1270
    1271 strncat(x_mnt_opts, x_opts,
    1272 x_mnt_opts_len - mnt_opts_len - 2);
    1273
    1274 free(mnt_opts);
    1275 mnt_opts = x_mnt_opts;
    1276 }
    1277
    1278 res = add_mount(source, mnt, *type, mnt_opts);
    1279 if (res == -1) {
    1280 /* Can't clean up mount in a non-racy way */
    1281 goto fail_close_fd;
    1282 }
    1283 }
    1284
    1285out_free:
    1286 free(source);
    1287 free(mnt_opts);
    1288 free(dev);
    1289 free(x_opts);
    1290 free(do_mount_opts);
    1291
    1292 return fd;
    1293
    1294fail_close_fd:
    1295 close(fd);
    1296 fd = -1;
    1297 goto out_free;
    1298}
    1299
    1300static int send_fd(int sock_fd, int fd)
    1301{
    1302 int retval;
    1303 struct msghdr msg;
    1304 struct cmsghdr *p_cmsg;
    1305 struct iovec vec;
    1306 size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
    1307 int *p_fds;
    1308 char sendchar = 0;
    1309
    1310 msg.msg_control = cmsgbuf;
    1311 msg.msg_controllen = sizeof(cmsgbuf);
    1312 p_cmsg = CMSG_FIRSTHDR(&msg);
    1313 p_cmsg->cmsg_level = SOL_SOCKET;
    1314 p_cmsg->cmsg_type = SCM_RIGHTS;
    1315 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
    1316 p_fds = (int *) CMSG_DATA(p_cmsg);
    1317 *p_fds = fd;
    1318 msg.msg_controllen = p_cmsg->cmsg_len;
    1319 msg.msg_name = NULL;
    1320 msg.msg_namelen = 0;
    1321 msg.msg_iov = &vec;
    1322 msg.msg_iovlen = 1;
    1323 msg.msg_flags = 0;
    1324 /* "To pass file descriptors or credentials you need to send/read at
    1325 * least one byte" (man 7 unix) */
    1326 vec.iov_base = &sendchar;
    1327 vec.iov_len = sizeof(sendchar);
    1328 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
    1329 if (retval != 1) {
    1330 perror("sending file descriptor");
    1331 return -1;
    1332 }
    1333 return 0;
    1334}
    1335
    1336/* Helper for should_auto_unmount
    1337 *
    1338 * fusermount typically has the s-bit set - initial open of `mnt` was as root
    1339 * and got EACCESS as 'allow_other' was not specified.
    1340 * Try opening `mnt` again with uid and guid of the calling process.
    1341 */
    1342static int recheck_ENOTCONN_as_owner(const char *mnt)
    1343{
    1344 int pid = fork();
    1345 if(pid == -1) {
    1346 perror("fuse: recheck_ENOTCONN_as_owner can't fork");
    1347 _exit(EXIT_FAILURE);
    1348 } else if(pid == 0) {
    1349 uid_t uid = getuid();
    1350 gid_t gid = getgid();
    1351 if(setresgid(gid, gid, gid) == -1) {
    1352 perror("fuse: can't set resgid");
    1353 _exit(EXIT_FAILURE);
    1354 }
    1355 if(setresuid(uid, uid, uid) == -1) {
    1356 perror("fuse: can't set resuid");
    1357 _exit(EXIT_FAILURE);
    1358 }
    1359
    1360 int fd = open(mnt, O_RDONLY);
    1361 if(fd == -1 && errno == ENOTCONN)
    1362 _exit(EXIT_SUCCESS);
    1363 else
    1364 _exit(EXIT_FAILURE);
    1365 } else {
    1366 int status;
    1367 int res = waitpid(pid, &status, 0);
    1368 if (res == -1) {
    1369 perror("fuse: waiting for child failed");
    1370 _exit(EXIT_FAILURE);
    1371 }
    1372 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
    1373 }
    1374}
    1375
    1376/* The parent fuse process has died: decide whether to auto_unmount.
    1377 *
    1378 * In the normal case (umount or fusermount -u), the filesystem
    1379 * has already been unmounted. If we simply unmount again we can
    1380 * cause problems with stacked mounts (e.g. autofs).
    1381 *
    1382 * So we unmount here only in abnormal case where fuse process has
    1383 * died without unmount happening. To detect this, we first look in
    1384 * the mount table to make sure the mountpoint is still mounted and
    1385 * has proper type. If so, we then see if opening the mount dir is
    1386 * returning 'Transport endpoint is not connected'.
    1387 *
    1388 * The order of these is important, because if autofs is in use,
    1389 * opening the dir to check for ENOTCONN will cause a new mount
    1390 * in the normal case where filesystem has been unmounted cleanly.
    1391 */
    1392static int should_auto_unmount(const char *mnt, const char *type)
    1393{
    1394 char *copy;
    1395 const char *last;
    1396 int result = 0;
    1397 int fd;
    1398
    1399 copy = strdup(mnt);
    1400 if (copy == NULL) {
    1401 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    1402 return 0;
    1403 }
    1404
    1405 if (chdir_to_parent(copy, &last) == -1)
    1406 goto out;
    1407 if (check_is_mount(last, mnt, type) == -1)
    1408 goto out;
    1409
    1410 fd = open(mnt, O_RDONLY);
    1411
    1412 if (fd != -1) {
    1413 close(fd);
    1414 } else {
    1415 switch(errno) {
    1416 case ENOTCONN:
    1417 result = 1;
    1418 break;
    1419 case EACCES:
    1420 result = recheck_ENOTCONN_as_owner(mnt);
    1421 break;
    1422 default:
    1423 result = 0;
    1424 break;
    1425 }
    1426 }
    1427out:
    1428 free(copy);
    1429 return result;
    1430}
    1431
    1432static void usage(void)
    1433{
    1434 printf("%s: [options] mountpoint\n"
    1435 "Options:\n"
    1436 " -h print help\n"
    1437 " -V print version\n"
    1438 " -o opt[,opt...] mount options\n"
    1439 " -u unmount\n"
    1440 " -q quiet\n"
    1441 " -z lazy unmount\n",
    1442 progname);
    1443 exit(1);
    1444}
    1445
    1446static void show_version(void)
    1447{
    1448 printf("fusermount3 version: %s\n", PACKAGE_VERSION);
    1449 exit(0);
    1450}
    1451
    1452/*
    1453 * Close all inherited fds that are not needed
    1454 * Ideally these wouldn't come up at all, applications should better
    1455 * use FD_CLOEXEC / O_CLOEXEC
    1456 */
    1457static void close_inherited_fds(int cfd)
    1458{
    1459 int max_fd = sysconf(_SC_OPEN_MAX);
    1460 int rc;
    1461
    1462#ifdef CLOSE_RANGE_CLOEXEC
    1463 /* high range first to be able to log errors through stdout/err*/
    1464 rc = close_range(cfd + 1, ~0U, 0);
    1465 if (rc < 0) {
    1466 fprintf(stderr, "Failed to close high range of FDs: %s",
    1467 strerror(errno));
    1468 goto fallback;
    1469 }
    1470
    1471 rc = close_range(0, cfd - 1, 0);
    1472 if (rc < 0) {
    1473 fprintf(stderr, "Failed to close low range of FDs: %s",
    1474 strerror(errno));
    1475 goto fallback;
    1476 }
    1477#endif
    1478
    1479fallback:
    1480 /*
    1481 * This also needs to close stdout/stderr, as the application
    1482 * using libfuse might have closed these FDs and might be using
    1483 * it. Although issue is now that logging errors won't be possible
    1484 * after that.
    1485 */
    1486 for (int fd = 0; fd <= max_fd; fd++) {
    1487 if (fd != cfd)
    1488 close(fd);
    1489 }
    1490}
    1491
    1492int main(int argc, char *argv[])
    1493{
    1494 sigset_t sigset;
    1495 int ch;
    1496 int fd;
    1497 int res;
    1498 char *origmnt;
    1499 char *mnt;
    1500 static int unmount = 0;
    1501 static int lazy = 0;
    1502 static int quiet = 0;
    1503 char *commfd = NULL;
    1504 long cfd;
    1505 const char *opts = "";
    1506 const char *type = NULL;
    1507 int setup_auto_unmount_only = 0;
    1508
    1509 static const struct option long_opts[] = {
    1510 {"unmount", no_argument, NULL, 'u'},
    1511 {"lazy", no_argument, NULL, 'z'},
    1512 {"quiet", no_argument, NULL, 'q'},
    1513 {"help", no_argument, NULL, 'h'},
    1514 {"version", no_argument, NULL, 'V'},
    1515 {"options", required_argument, NULL, 'o'},
    1516 // Note: auto-unmount and comm-fd don't have short versions.
    1517 // They'ne meant for internal use by mount.c
    1518 {"auto-unmount", no_argument, NULL, 'U'},
    1519 {"comm-fd", required_argument, NULL, 'c'},
    1520 {0, 0, 0, 0}};
    1521
    1522 progname = strdup(argc > 0 ? argv[0] : "fusermount");
    1523 if (progname == NULL) {
    1524 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
    1525 exit(1);
    1526 }
    1527
    1528 while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
    1529 NULL)) != -1) {
    1530 switch (ch) {
    1531 case 'h':
    1532 usage();
    1533 break;
    1534
    1535 case 'V':
    1536 show_version();
    1537 break;
    1538
    1539 case 'o':
    1540 opts = optarg;
    1541 break;
    1542
    1543 case 'u':
    1544 unmount = 1;
    1545 break;
    1546 case 'U':
    1547 unmount = 1;
    1548 auto_unmount = 1;
    1549 setup_auto_unmount_only = 1;
    1550 break;
    1551 case 'c':
    1552 commfd = optarg;
    1553 break;
    1554 case 'z':
    1555 lazy = 1;
    1556 break;
    1557
    1558 case 'q':
    1559 quiet = 1;
    1560 break;
    1561
    1562 default:
    1563 exit(1);
    1564 }
    1565 }
    1566
    1567 if (lazy && !unmount) {
    1568 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
    1569 exit(1);
    1570 }
    1571
    1572 if (optind >= argc) {
    1573 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
    1574 exit(1);
    1575 } else if (argc > optind + 1) {
    1576 fprintf(stderr, "%s: extra arguments after the mountpoint\n",
    1577 progname);
    1578 exit(1);
    1579 }
    1580
    1581 origmnt = argv[optind];
    1582
    1583 drop_privs();
    1584 mnt = fuse_mnt_resolve_path(progname, origmnt);
    1585 if (mnt != NULL) {
    1586 res = chdir("/");
    1587 if (res == -1) {
    1588 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
    1589 goto err_out;
    1590 }
    1591 }
    1592 restore_privs();
    1593 if (mnt == NULL)
    1594 exit(1);
    1595
    1596 umask(033);
    1597 if (!setup_auto_unmount_only && unmount)
    1598 goto do_unmount;
    1599
    1600 if(commfd == NULL)
    1601 commfd = getenv(FUSE_COMMFD_ENV);
    1602 if (commfd == NULL) {
    1603 fprintf(stderr, "%s: old style mounting not supported\n",
    1604 progname);
    1605 goto err_out;
    1606 }
    1607
    1608 res = libfuse_strtol(commfd, &cfd);
    1609 if (res) {
    1610 fprintf(stderr,
    1611 "%s: invalid _FUSE_COMMFD: %s\n",
    1612 progname, commfd);
    1613 goto err_out;
    1614
    1615 }
    1616 {
    1617 struct stat statbuf;
    1618 fstat(cfd, &statbuf);
    1619 if(!S_ISSOCK(statbuf.st_mode)) {
    1620 fprintf(stderr,
    1621 "%s: file descriptor %li is not a socket, can't send fuse fd\n",
    1622 progname, cfd);
    1623 goto err_out;
    1624 }
    1625 }
    1626
    1627 if (setup_auto_unmount_only)
    1628 goto wait_for_auto_unmount;
    1629
    1630 fd = mount_fuse(mnt, opts, &type);
    1631 if (fd == -1)
    1632 goto err_out;
    1633
    1634 res = send_fd(cfd, fd);
    1635 if (res != 0) {
    1636 umount2(mnt, MNT_DETACH); /* lazy umount */
    1637 goto err_out;
    1638 }
    1639 close(fd);
    1640
    1641 if (!auto_unmount) {
    1642 free(mnt);
    1643 free((void*) type);
    1644 return 0;
    1645 }
    1646
    1647wait_for_auto_unmount:
    1648 /* Become a daemon and wait for the parent to exit or die.
    1649 ie For the control socket to get closed.
    1650 Btw, we don't want to use daemon() function here because
    1651 it forks and messes with the file descriptors. */
    1652
    1653 close_inherited_fds(cfd);
    1654
    1655 setsid();
    1656 res = chdir("/");
    1657 if (res == -1) {
    1658 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
    1659 goto err_out;
    1660 }
    1661
    1662 sigfillset(&sigset);
    1663 sigprocmask(SIG_BLOCK, &sigset, NULL);
    1664
    1665 lazy = 1;
    1666 quiet = 1;
    1667
    1668 while (1) {
    1669 unsigned char buf[16];
    1670 int n = recv(cfd, buf, sizeof(buf), 0);
    1671 if (!n)
    1672 break;
    1673
    1674 if (n < 0) {
    1675 if (errno == EINTR)
    1676 continue;
    1677 break;
    1678 }
    1679 }
    1680
    1681 if (!should_auto_unmount(mnt, type)) {
    1682 goto success_out;
    1683 }
    1684
    1685do_unmount:
    1686 if (geteuid() == 0)
    1687 res = unmount_fuse(mnt, quiet, lazy);
    1688 else {
    1689 res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
    1690 if (res == -1 && !quiet)
    1691 fprintf(stderr,
    1692 "%s: failed to unmount %s: %s\n",
    1693 progname, mnt, strerror(errno));
    1694 }
    1695 if (res == -1)
    1696 goto err_out;
    1697
    1698success_out:
    1699 free((void*) type);
    1700 free(mnt);
    1701 return 0;
    1702
    1703err_out:
    1704 free((void*) type);
    1705 free(mnt);
    1706 exit(1);
    1707}
    fuse-3.17.2/doc/html/util_2fusermount_8c_source.html0000644000175000017500000077610515002273247021435 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 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_LINUX_CLOSE_RANGE_H
    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_LINUX_CLOSE_RANGE_H
    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.17.2/doc/html/fuse-3_817_81-rc0_2util_2mount_8fuse_8c_source.html0000644000175000017500000021167714770250311024333 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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:479
    fuse-3.17.2/doc/html/util_2mount_8fuse_8c_source.html0000644000175000017500000021151015002273247021462 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 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:475
    fuse-3.17.2/doc/html/example_2cuse_8c.html0000644000175000017500000012032515002273247017246 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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc0_2example_2cuse_8c.html0000644000175000017500000012047114770250311022103 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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.17.2/doc/html/example_2cuse__client_8c.html0000644000175000017500000003200415002273247020737 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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc0_2example_2cuse__client_8c.html0000644000175000017500000003227114770250312023601 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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.17.2/doc/html/example_2hello_8c.html0000644000175000017500000006464415002273413017420 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 COPYING.
    */
    #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 */
    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] <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
    #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.17.2/doc/html/fuse-3_817_81-rc0_2example_2hello_8c.html0000644000175000017500000007021114770250312022244 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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 */
    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] <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
    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_CAP_ASYNC_READ
    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.17.2/doc/html/example_2hello__ll_8c.html0000644000175000017500000013177115002273247020247 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
    

    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 COPYING.
    */
    #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:5234
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2hello__ll_8c.html0000644000175000017500000013171414770250312023100 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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
    

    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 COPYING.
    */
    #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)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    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
    #define FUSE_CAP_ASYNC_READ
    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.17.2/doc/html/example_2hello__ll__uds_8c.html0000644000175000017500000013240615002273247021255 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 COPYING.
    */
    #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:5234
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2hello__ll__uds_8c.html0000644000175000017500000013233114770250312024106 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    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
    #define FUSE_CAP_ASYNC_READ
    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.17.2/doc/html/example_2invalidate__path_8c.html0000644000175000017500000012460015002273247021602 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 COPYING.
    */
    #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:5220
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5169
    int fuse_invalidate_path(struct fuse *f, const char *path)
    Definition fuse.c:4698
    int fuse_loop(struct fuse *f)
    Definition fuse.c:4602
    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:4769
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4545
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5225
    @ 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:5234
    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:77
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2invalidate__path_8c.html0000644000175000017500000012470214770250312024441 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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:5204
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5153
    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:5209
    @ 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:5218
    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:77
    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.17.2/doc/html/example_2ioctl_8c.html0000644000175000017500000005475315002273247017434 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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc0_2example_2ioctl_8c.html0000644000175000017500000005521214770250312022257 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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.17.2/doc/html/example_2ioctl_8h.html0000644000175000017500000001232215002273247017423 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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc0_2example_2ioctl_8h.html0000644000175000017500000001256314770250312022266 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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.17.2/doc/html/example_2ioctl__client_8c.html0000644000175000017500000001747415002273247021130 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 tests the ioctl.c example file systsem.
    This program can be distributed under the terms of the GNU GPLv2.
    See the file COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc0_2example_2ioctl__client_8c.html0000644000175000017500000001776014770250312023762 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 tests the ioctl.c example file systsem.
    This program can be distributed under the terms of the GNU GPLv2.
    See the file COPYING.
    */
    #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.17.2/doc/html/example_2notify__inval__entry_8c.html0000644000175000017500000015021115002273247022524 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

    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 COPYING.
    */
    #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;
    };
    static struct options options = {
    .timeout = 5,
    .no_notify = 0,
    .update_interval = 1,
    .only_expire = 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),
    };
    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;
    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");
    printf("Exiting...\n");
    // Make sure to exit now, rather than on next request from userspace
    pthread_kill(main_thread, SIGPIPE);
    break;
    }
    // 1) ret == 0: successful expire of an existing entry
    // 2) ret == -ENOENT: kernel has already expired the entry /
    // entry does not exist anymore in the kernel
    assert(ret == 0 || ret == -ENOENT);
    } else { // invalidate entry
    (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
    }
    }
    free(old_name);
    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"
    " --timeout=<secs> Timeout for kernel caches\n"
    " --update-interval=<secs> Update-rate of file system contents\n"
    " --no-notify Disable kernel notifications\n"
    " --only-expire Expire entries instead of invalidating them\n"
    "\n");
    }
    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;
    }
    /* 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:5234
    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
    #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.17.2/doc/html/fuse-3_817_81-rc0_2example_2notify__inval__entry_8c.html0000644000175000017500000015024014770250312025362 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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

    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 COPYING.
    */
    #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;
    };
    static struct options options = {
    .timeout = 5,
    .no_notify = 0,
    .update_interval = 1,
    .only_expire = 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),
    };
    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;
    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");
    printf("Exiting...\n");
    // Make sure to exit now, rather than on next request from userspace
    pthread_kill(main_thread, SIGPIPE);
    break;
    }
    // 1) ret == 0: successful expire of an existing entry
    // 2) ret == -ENOENT: kernel has already expired the entry /
    // entry does not exist anymore in the kernel
    assert(ret == 0 || ret == -ENOENT);
    } else { // invalidate entry
    (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
    }
    }
    free(old_name);
    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"
    " --timeout=<secs> Timeout for kernel caches\n"
    " --update-interval=<secs> Update-rate of file system contents\n"
    " --no-notify Disable kernel notifications\n"
    " --only-expire Expire entries instead of invalidating them\n"
    "\n");
    }
    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;
    }
    /* 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:5218
    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
    #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.17.2/doc/html/example_2notify__inval__inode_8c.html0000644000175000017500000015143115002273247022466 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 COPYING.
    */
    #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:5234
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file notify_inval_inode.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2notify__inval__inode_8c.html0000644000175000017500000015146014770250312025324 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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:5218
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file notify_inval_inode.c.

    fuse-3.17.2/doc/html/example_2notify__store__retrieve_8c.html0000644000175000017500000017140615002273247023244 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 COPYING.
    */
    #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:5234
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file notify_store_retrieve.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2notify__store__retrieve_8c.html0000644000175000017500000017141714770250312026102 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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:5218
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file notify_store_retrieve.c.

    fuse-3.17.2/doc/html/example_2null_8c.html0000644000175000017500000021624415002273247017267 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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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) */
    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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    static int xmp_fallocate(const char *path, int mode,
    off_t offset, off_t length, struct fuse_file_info *fi)
    {
    (void) path;
    if (mode)
    return -EOPNOTSUPP;
    return -posix_fallocate(fi->fh, offset, length);
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file null.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2null_8c.html0000644000175000017500000021641114770250312022117 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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) */
    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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    static int xmp_fallocate(const char *path, int mode,
    off_t offset, off_t length, struct fuse_file_info *fi)
    {
    (void) path;
    if (mode)
    return -EOPNOTSUPP;
    return -posix_fallocate(fi->fh, offset, length);
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file null.c.

    fuse-3.17.2/doc/html/example_2passthrough_8c.html0000644000175000017500000015213015002273247020655 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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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>
    #ifdef __FreeBSD__
    #include <sys/socket.h>
    #include <sys/un.h>
    #endif
    #include <sys/time.h>
    #ifdef HAVE_SETXATTR
    #include <sys/xattr.h>
    #endif
    #include "passthrough_helpers.h"
    static int fill_dir_plus = 0;
    static void *xmp_init(struct fuse_conn_info *conn,
    struct fuse_config *cfg)
    {
    (void) conn;
    cfg->use_ino = 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. */
    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 (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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    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 (mode)
    return -EOPNOTSUPP;
    if(fi == NULL)
    fd = open(path, O_WRONLY);
    else
    fd = fi->fh;
    if (fd == -1)
    return -errno;
    res = -posix_fallocate(fd, offset, length);
    if(fi == NULL)
    close(fd);
    return res;
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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 {
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file passthrough.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2passthrough_8c.html0000644000175000017500000015234214770250312023516 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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>
    #ifdef __FreeBSD__
    #include <sys/socket.h>
    #include <sys/un.h>
    #endif
    #include <sys/time.h>
    #ifdef HAVE_SETXATTR
    #include <sys/xattr.h>
    #endif
    #include "passthrough_helpers.h"
    static int fill_dir_plus = 0;
    static void *xmp_init(struct fuse_conn_info *conn,
    struct fuse_config *cfg)
    {
    (void) conn;
    cfg->use_ino = 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. */
    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 (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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    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 (mode)
    return -EOPNOTSUPP;
    if(fi == NULL)
    fd = open(path, O_WRONLY);
    else
    fd = fi->fh;
    if (fd == -1)
    return -errno;
    res = -posix_fallocate(fd, offset, length);
    if(fi == NULL)
    close(fd);
    return res;
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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 {
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file passthrough.c.

    fuse-3.17.2/doc/html/example_2passthrough__fh_8c.html0000644000175000017500000021702015002273247021471 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>

    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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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) */
    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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    static int xmp_fallocate(const char *path, int mode,
    off_t offset, off_t length, struct fuse_file_info *fi)
    {
    (void) path;
    if (mode)
    return -EOPNOTSUPP;
    return -posix_fallocate(fi->fh, offset, length);
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file passthrough_fh.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2passthrough__fh_8c.html0000644000175000017500000021716514770250312024337 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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) */
    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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    static int xmp_fallocate(const char *path, int mode,
    off_t offset, off_t length, struct fuse_file_info *fi)
    {
    (void) path;
    if (mode)
    return -EOPNOTSUPP;
    return -posix_fallocate(fi->fh, offset, length);
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file passthrough_fh.c.

    fuse-3.17.2/doc/html/example_2passthrough__ll_8c.html0000644000175000017500000052416615002273247021517 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, 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 COPYING.
    */
    #define _GNU_SOURCE
    #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    #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) {
    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/%lli -> %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=%lu)\n",
    ino, out_buf.buf[0].size, (unsigned long) 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 = EOPNOTSUPP;
    (void) ino;
    #ifdef HAVE_FALLOCATE
    err = fallocate(fi->fh, mode, offset, length);
    if (err < 0)
    err = errno;
    #elif defined(HAVE_POSIX_FALLOCATE)
    if (mode) {
    fuse_reply_err(req, EOPNOTSUPP);
    return;
    }
    err = posix_fallocate(fi->fh, offset, length);
    #endif
    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, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
    "off=%lu, ino=%" PRIu64 "/fd=%lu, "
    "off=%lu, size=%zd, flags=0x%x)\n",
    ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, 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);
    }
    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,
    };
    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;
    }
    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:5234
    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:97
    uint32_t parallel_direct_writes
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file passthrough_ll.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2passthrough__ll_8c.html0000644000175000017500000052341614770250312024350 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #define _GNU_SOURCE
    #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    #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) {
    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/%lli -> %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=%lu)\n",
    ino, out_buf.buf[0].size, (unsigned long) 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 = EOPNOTSUPP;
    (void) ino;
    #ifdef HAVE_FALLOCATE
    err = fallocate(fi->fh, mode, offset, length);
    if (err < 0)
    err = errno;
    #elif defined(HAVE_POSIX_FALLOCATE)
    if (mode) {
    fuse_reply_err(req, EOPNOTSUPP);
    return;
    }
    err = posix_fallocate(fi->fh, offset, length);
    #endif
    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, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
    "off=%lu, ino=%" PRIu64 "/fd=%lu, "
    "off=%lu, size=%zd, flags=0x%x)\n",
    ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, 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);
    }
    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,
    };
    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;
    }
    int fuse_set_signal_handlers(struct fuse_session *se)
    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
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    void fuse_remove_signal_handlers(struct fuse_session *se)
    @ FUSE_BUF_SPLICE_MOVE
    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_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
    #define FUSE_CAP_WRITEBACK_CACHE
    #define FUSE_CAP_FLOCK_LOCKS
    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:97
    uint32_t parallel_direct_writes
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file passthrough_ll.c.

    fuse-3.17.2/doc/html/example_2poll_8c.html0000644000175000017500000010000115002273247017242 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 COPYING.
    */
    #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:4669
    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:86
    uint32_t direct_io
    Definition fuse_common.h:71
    void(* destroy)(void *private_data)
    Definition fuse.h:649

    Definition in file poll.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2example_2poll_8c.html0000644000175000017500000010020414770250312022103 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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:86
    uint32_t direct_io
    Definition fuse_common.h:71
    void(* destroy)(void *private_data)
    Definition fuse.h:649

    Definition in file poll.c.

    fuse-3.17.2/doc/html/example_2poll__client_8c.html0000644000175000017500000002110315002273247020744 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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc0_2example_2poll__client_8c.html0000644000175000017500000002134414770250312023607 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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.17.2/doc/html/example_2printcap_8c.html0000644000175000017500000011504715002273247020134 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 COPYING.
    */
    #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
    #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:5234
    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.17.2/doc/html/fuse-3_817_81-rc0_2example_2printcap_8c.html0000644000175000017500000011072514770250312022766 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 COPYING.
    */
    #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;
    }
    int fuse_set_signal_handlers(struct fuse_session *se)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    void fuse_remove_signal_handlers(struct fuse_session *se)
    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
    #define FUSE_CAP_IOCTL_DIR
    #define FUSE_CAP_DONT_MASK
    #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
    #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
    #define FUSE_CAP_PASSTHROUGH
    #define FUSE_CAP_DIRECT_IO_ALLOW_MMAP
    #define FUSE_CAP_NO_OPEN_SUPPORT
    #define FUSE_CAP_READDIRPLUS
    #define FUSE_CAP_SETXATTR_EXT
    #define FUSE_CAP_SPLICE_MOVE
    #define FUSE_CAP_NO_EXPORT_SUPPORT
    #define FUSE_CAP_FLOCK_LOCKS
    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.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse_8h.html0000644000175000017500000013060714770250312022106 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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

    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 1415 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 1386 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 5153 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_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 5204 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 479 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 4904 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 4912 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 5209 of file fuse.c.

    fuse-3.17.2/doc/html/include_2fuse_8h.html0000644000175000017500000014212715002273247017252 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 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 4458 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 5169 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 4664 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 4879 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 4669 of file fuse.c.

    ◆ fuse_get_session()

    struct fuse_session * fuse_get_session ( struct fuse *  f)

    Get session from fuse object

    Definition at line 4545 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 4679 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 4688 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 4698 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 4769 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 4602 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()

    The real main function

    Do not call this directly, use fuse_main()

    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

    Parameters
    argcthe argument counter passed to the main() function
    argvthe argument vector passed to the main() function
    opthe file system operation
    private_dataInitial value for the private_data field of struct fuse_context. May be overridden by the struct fuse_operations.init handler.
    Returns
    0 on success, nonzero on failure

    Example usage, see hello.c

    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 5220 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 475 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 4929 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 4937 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 5225 of file fuse.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse__common_8h.html0000644000175000017500000017705414770250312023624 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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_IOCTL_COMPAT   (1 << 0)
     
    #define FUSE_BACKING_STACKED_UNDER   (0)
     

    Enumerations

    enum  fuse_capability {
      FUSE_CAP_ASYNC_READ = (1 << 0) , FUSE_CAP_POSIX_LOCKS = (1 << 1) , FUSE_CAP_ATOMIC_O_TRUNC = (1 << 3) , FUSE_CAP_EXPORT_SUPPORT = (1 << 4) ,
      FUSE_CAP_DONT_MASK = (1 << 6) , FUSE_CAP_SPLICE_WRITE = (1 << 7) , FUSE_CAP_SPLICE_MOVE = (1 << 8) , FUSE_CAP_SPLICE_READ = (1 << 9) ,
      FUSE_CAP_FLOCK_LOCKS = (1 << 10) , FUSE_CAP_IOCTL_DIR = (1 << 11) , FUSE_CAP_AUTO_INVAL_DATA = (1 << 12) , FUSE_CAP_READDIRPLUS = (1 << 13) ,
      FUSE_CAP_READDIRPLUS_AUTO = (1 << 14) , FUSE_CAP_ASYNC_DIO = (1 << 15) , FUSE_CAP_WRITEBACK_CACHE = (1 << 16) , FUSE_CAP_NO_OPEN_SUPPORT = (1 << 17) ,
      FUSE_CAP_PARALLEL_DIROPS = (1 << 18) , FUSE_CAP_POSIX_ACL = (1 << 19) , FUSE_CAP_HANDLE_KILLPRIV = (1 << 20) , FUSE_CAP_HANDLE_KILLPRIV_V2 = (1 << 21) ,
      FUSE_CAP_CACHE_SYMLINKS = (1 << 23) , FUSE_CAP_NO_OPENDIR_SUPPORT = (1 << 24) , FUSE_CAP_EXPLICIT_INVAL_DATA = (1 << 25) , FUSE_CAP_EXPIRE_ONLY = (1 << 26) ,
      FUSE_CAP_SETXATTR_EXT = (1 << 27) , FUSE_CAP_DIRECT_IO_ALLOW_MMAP = (1 << 28) , FUSE_CAP_PASSTHROUGH = (1 << 29) , FUSE_CAP_NO_EXPORT_SUPPORT = (1 << 30) ,
      FUSE_CAP_CURRENT_MAX
    }
     
    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)
     

    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 682 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 537 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 848 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 817 of file fuse_common.h.

    ◆ fuse_capability

    Enumerator
    FUSE_CAP_ASYNC_READ 

    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.

    FUSE_CAP_POSIX_LOCKS 

    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.

    FUSE_CAP_ATOMIC_O_TRUNC 

    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.

    FUSE_CAP_EXPORT_SUPPORT 

    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.

    FUSE_CAP_DONT_MASK 

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

    This feature is disabled by default.

    FUSE_CAP_SPLICE_WRITE 

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

    This feature is disabled by default.

    FUSE_CAP_SPLICE_MOVE 

    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.

    FUSE_CAP_SPLICE_READ 

    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.

    FUSE_CAP_FLOCK_LOCKS 

    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.

    FUSE_CAP_IOCTL_DIR 

    Indicates that the filesystem supports ioctl's on directories.

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

    FUSE_CAP_AUTO_INVAL_DATA 

    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.

    FUSE_CAP_READDIRPLUS 

    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.

    FUSE_CAP_READDIRPLUS_AUTO 

    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.

    FUSE_CAP_ASYNC_DIO 

    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.

    FUSE_CAP_WRITEBACK_CACHE 

    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.

    FUSE_CAP_NO_OPEN_SUPPORT 

    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.

    FUSE_CAP_PARALLEL_DIROPS 

    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.

    FUSE_CAP_POSIX_ACL 

    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.

    FUSE_CAP_HANDLE_KILLPRIV 

    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.

    FUSE_CAP_HANDLE_KILLPRIV_V2 

    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.

    FUSE_CAP_CACHE_SYMLINKS 

    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.

    FUSE_CAP_NO_OPENDIR_SUPPORT 

    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.

    FUSE_CAP_EXPLICIT_INVAL_DATA 

    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.

    FUSE_CAP_EXPIRE_ONLY 

    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.

    FUSE_CAP_SETXATTR_EXT 

    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.

    FUSE_CAP_DIRECT_IO_ALLOW_MMAP 

    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.

    FUSE_CAP_PASSTHROUGH 

    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.

    FUSE_CAP_NO_EXPORT_SUPPORT 

    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.

    FUSE_CAP_CURRENT_MAX 

    Current maximum capability value.

    Definition at line 177 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 416 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_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_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 463 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 5218 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 1906 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 180 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 158 of file fuse_signals.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 138 of file fuse_signals.c.

    ◆ fuse_version()

    int fuse_version ( void  )

    Get the version of the library

    Returns
    the version

    Definition at line 5213 of file fuse.c.

    fuse-3.17.2/doc/html/include_2fuse__common_8h.html0000644000175000017500000024210715002273247020760 0ustar berndbernd libfuse: 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)
     

    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 673 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 336 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 185 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 202 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 289 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 426 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 496 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 222 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 480 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 464 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 214 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 260 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 396 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 413 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 267 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 516 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 360 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 441 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 368 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 508 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 387 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 193 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 297 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 325 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 487 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 238 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 247 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 230 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 345 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 528 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 839 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 808 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_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_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 459 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 5234 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 1907 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 180 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 158 of file fuse_signals.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 138 of file fuse_signals.c.

    ◆ fuse_version()

    int fuse_version ( void  )

    Get the version of the library

    Returns
    the version

    Definition at line 5229 of file fuse.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse__log_8h.html0000644000175000017500000003445314770250312023110 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/include_2fuse__log_8h.html0000644000175000017500000003373415002273247020255 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,...)
     
    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.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse__lowlevel_8h.html0000644000175000017500000041403714770250312024160 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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 1946 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 289 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 379 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 2957 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 2519 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 2506 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 2500 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 2432 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 2415 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 2625 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 2545 of file fuse_lowlevel.c.

    ◆ fuse_lowlevel_version()

    void fuse_lowlevel_version ( void  )

    Print low-level version information to stdout.

    Definition at line 2950 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

    Possible requests: open, opendir, create

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

    Definition at line 483 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 463 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 976 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 527 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 447 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 915 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 431 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 334 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 1074 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 1095 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 1004 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 268 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 959 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 1129 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 339 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 508 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 1119 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 478 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 937 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 517 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 949 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 2675 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 3553 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 2680 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 2693 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 2670 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 2967 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 3474 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 3419 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 2787 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 3234 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 3479 of file fuse_lowlevel.c.

    fuse-3.17.2/doc/html/include_2fuse__lowlevel_8h.html0000644000175000017500000041165615002273247021330 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_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 290 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 380 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 2941 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 2503 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 2490 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 2484 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 2416 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 2399 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 2609 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 2529 of file fuse_lowlevel.c.

    ◆ fuse_lowlevel_version()

    void fuse_lowlevel_version ( void  )

    Print low-level version information to stdout.

    Definition at line 2934 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 484 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 464 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 977 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 528 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 448 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 916 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 432 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 335 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 1075 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 1096 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 1005 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 269 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 960 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 1130 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 340 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 509 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 1120 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 479 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 938 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 518 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 950 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 2659 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 3533 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 2664 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 2677 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 2654 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 2951 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 3454 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 3399 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 2771 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 3218 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 3459 of file fuse_lowlevel.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2include_2fuse__opt_8h.html0000644000175000017500000007750414770250312023135 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/include_2fuse__opt_8h.html0000644000175000017500000007614615002273247020302 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.17.2/doc/html/dir_75ac0186611fae93a0a2599fafee7b59.html0000644000175000017500000000415714770250312021430 0ustar berndbernd libfuse: fuse-3.17.1-rc0/doc Directory Reference
    libfuse
    doc Directory Reference
    fuse-3.17.2/doc/html/dir_d66497626fcb891eefdcc285752aeb54.html0000644000175000017500000002372314770250312021447 0ustar berndbernd libfuse: fuse-3.17.1-rc0/example Directory Reference
    libfuse
    example Directory Reference
    fuse-3.17.2/doc/html/dir_d0f078c01bc3ce273c081eaac57e44d3.html0000644000175000017500000001004614770250312021451 0ustar berndbernd libfuse: fuse-3.17.1-rc0 Directory Reference
    libfuse
    fuse-3.17.1-rc0 Directory Reference

    Directories

     build
     
     example
     
     include
     
     lib
     
     test
     
     util
     
    fuse-3.17.2/doc/html/dir_8e4bcc66b5040e6cd82b8756da72da10.html0000644000175000017500000001224614770250312021412 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/dir_1dfeff730c7aaf81ae73022af75d725f.html0000644000175000017500000002126414770250312021556 0ustar berndbernd libfuse: fuse-3.17.1-rc0/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.17.2/doc/html/dir_d7aafa31aa39c71b1ecb09f780e2341f.html0000644000175000017500000000577414770250312021547 0ustar berndbernd libfuse: fuse-3.17.1-rc0/lib/modules Directory Reference
    libfuse
    modules Directory Reference

    Files

     iconv.c
     
     subdir.c
     
    fuse-3.17.2/doc/html/dir_acab6aac461847b02973b7a47d1d14e5.html0000644000175000017500000001105314770250312021375 0ustar berndbernd libfuse: fuse-3.17.1-rc0/test Directory Reference
    libfuse
    test Directory Reference

    Files

     readdir_inode.c
     
     release_unlink_race.c
     
     stracedecode.c
     
     test_setattr.c
     
     test_syscalls.c
     
     test_write_cache.c
     
     wrong_command.c
     
    fuse-3.17.2/doc/html/dir_b103db43dffcdee3de8bd6a6d79f8618.html0000644000175000017500000000562314770250312021733 0ustar berndbernd libfuse: fuse-3.17.1-rc0/util Directory Reference
    libfuse
    util Directory Reference

    Files

     fusermount.c
     
     mount.fuse.c
     
    fuse-3.17.2/doc/html/build-ubuntu_2fuse__config_8h_source.html0000644000175000017500000001714215002273247023310 0ustar berndbernd libfuse: build-ubuntu/fuse_config.h Source File
    libfuse
    fuse_config.h
    1/*
    2 * Autogenerated by the Meson build system.
    3 * Do not edit, your changes will be lost.
    4 */
    5
    6#pragma once
    7
    8#define HAVE_BACKTRACE
    9
    10#define HAVE_CLOSE_RANGE
    11
    12#define HAVE_COPY_FILE_RANGE
    13
    14#define HAVE_FALLOCATE
    15
    16#define HAVE_FDATASYNC
    17
    18#define HAVE_FORK
    19
    20#define HAVE_FSTATAT
    21
    22#define HAVE_ICONV
    23
    24#define HAVE_OPENAT
    25
    26#define HAVE_PIPE2
    27
    28#define HAVE_POSIX_FALLOCATE
    29
    30#define HAVE_READLINKAT
    31
    32#define HAVE_SETXATTR
    33
    34#define HAVE_SPLICE
    35
    36#define HAVE_STRUCT_STAT_ST_ATIM
    37
    38#undef HAVE_STRUCT_STAT_ST_ATIMESPEC
    39
    40#define HAVE_UTIMENSAT
    41
    42#define HAVE_VMSPLICE
    43
    44#define PACKAGE_VERSION "3.17.1-rc1"
    45
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2build_2fuse__config_8h_source.html0000644000175000017500000001673614770250311024633 0ustar berndbernd libfuse: fuse-3.17.1-rc0/build/fuse_config.h Source File
    libfuse
    fuse_config.h
    1/*
    2 * Autogenerated by the Meson build system.
    3 * Do not edit, your changes will be lost.
    4 */
    5
    6#pragma once
    7
    8#define HAVE_BACKTRACE
    9
    10#define HAVE_COPY_FILE_RANGE
    11
    12#define HAVE_FALLOCATE
    13
    14#define HAVE_FDATASYNC
    15
    16#define HAVE_FORK
    17
    18#define HAVE_FSTATAT
    19
    20#define HAVE_ICONV
    21
    22#define HAVE_OPENAT
    23
    24#define HAVE_PIPE2
    25
    26#define HAVE_POSIX_FALLOCATE
    27
    28#define HAVE_READLINKAT
    29
    30#define HAVE_SETXATTR
    31
    32#define HAVE_SPLICE
    33
    34#define HAVE_STRUCT_STAT_ST_ATIM
    35
    36#undef HAVE_STRUCT_STAT_ST_ATIMESPEC
    37
    38#define HAVE_UTIMENSAT
    39
    40#define HAVE_VMSPLICE
    41
    42#define PACKAGE_VERSION "3.17.1-rc0"
    43
    fuse-3.17.2/doc/html/build-ubuntu_2libfuse__config_8h_source.html0000644000175000017500000001053715002273247024000 0ustar berndbernd libfuse: build-ubuntu/libfuse_config.h Source File
    libfuse
    libfuse_config.h
    1/*
    2 * Autogenerated by the Meson build system.
    3 * Do not edit, your changes will be lost.
    4 */
    5
    6#pragma once
    7
    8#define FUSE_HOTFIX_VERSION 1
    9
    10#define FUSE_MAJOR_VERSION 3
    11
    12#define FUSE_MINOR_VERSION 17
    13
    14#define FUSE_RC_VERSION rc1
    15
    16#define LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS 1
    17
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2build_2libfuse__config_8h_source.html0000644000175000017500000001071414770250311025310 0ustar berndbernd libfuse: fuse-3.17.1-rc0/build/libfuse_config.h Source File
    libfuse
    libfuse_config.h
    1/*
    2 * Autogenerated by the Meson build system.
    3 * Do not edit, your changes will be lost.
    4 */
    5
    6#pragma once
    7
    8#define FUSE_HOTFIX_VERSION 1
    9
    10#define FUSE_MAJOR_VERSION 3
    11
    12#define FUSE_MINOR_VERSION 17
    13
    14#define FUSE_RC_VERSION rc0
    15
    16#define LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS 1
    17
    fuse-3.17.2/doc/html/fuse-3_817_81-rc0_2build_2meson-private_2sanitycheckc_8c_source.html0000644000175000017500000000545614770250311027600 0ustar berndbernd libfuse: fuse-3.17.1-rc0/build/meson-private/sanitycheckc.c Source File
    libfuse
    sanitycheckc.c
    1int main(void) { int class=0; return class; }
    fuse-3.17.2/doc/html/dir_2ee0fcdc098dd79f81f9944140524a5f.html0000644000175000017500000000660714770250312021362 0ustar berndbernd libfuse: fuse-3.17.1-rc0/build Directory Reference
    libfuse
    build Directory Reference

    Directories

     meson-private
     

    Files

     fuse_config.h
     
     libfuse_config.h
     
    fuse-3.17.2/doc/html/dir_de142c35e6fa30e4531b188c66688c85.html0000644000175000017500000000534314770250312021210 0ustar berndbernd libfuse: fuse-3.17.1-rc0/build/meson-private Directory Reference
    libfuse
    meson-private Directory Reference

    Files

     sanitycheckc.c
     
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2cuse_8c_source.html0000644000175000017500000016673314770234735023513 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2cuse__client_8c_source.html0000644000175000017500000005063114770234735025175 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2hello_8c_source.html0000644000175000017500000011157514770234735023651 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    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 return NULL;
    61}
    62
    63static int hello_getattr(const char *path, struct stat *stbuf,
    64 struct fuse_file_info *fi)
    65{
    66 (void) fi;
    67 int res = 0;
    68
    69 memset(stbuf, 0, sizeof(struct stat));
    70 if (strcmp(path, "/") == 0) {
    71 stbuf->st_mode = S_IFDIR | 0755;
    72 stbuf->st_nlink = 2;
    73 } else if (strcmp(path+1, options.filename) == 0) {
    74 stbuf->st_mode = S_IFREG | 0444;
    75 stbuf->st_nlink = 1;
    76 stbuf->st_size = strlen(options.contents);
    77 } else
    78 res = -ENOENT;
    79
    80 return res;
    81}
    82
    83static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
    84 off_t offset, struct fuse_file_info *fi,
    85 enum fuse_readdir_flags flags)
    86{
    87 (void) offset;
    88 (void) fi;
    89 (void) flags;
    90
    91 if (strcmp(path, "/") != 0)
    92 return -ENOENT;
    93
    94 filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
    95 filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
    96 filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
    97
    98 return 0;
    99}
    100
    101static int hello_open(const char *path, struct fuse_file_info *fi)
    102{
    103 if (strcmp(path+1, options.filename) != 0)
    104 return -ENOENT;
    105
    106 if ((fi->flags & O_ACCMODE) != O_RDONLY)
    107 return -EACCES;
    108
    109 return 0;
    110}
    111
    112static int hello_read(const char *path, char *buf, size_t size, off_t offset,
    113 struct fuse_file_info *fi)
    114{
    115 size_t len;
    116 (void) fi;
    117 if(strcmp(path+1, options.filename) != 0)
    118 return -ENOENT;
    119
    120 len = strlen(options.contents);
    121 if (offset < len) {
    122 if (offset + size > len)
    123 size = len - offset;
    124 memcpy(buf, options.contents + offset, size);
    125 } else
    126 size = 0;
    127
    128 return size;
    129}
    130
    131static const struct fuse_operations hello_oper = {
    132 .init = hello_init,
    133 .getattr = hello_getattr,
    134 .readdir = hello_readdir,
    135 .open = hello_open,
    136 .read = hello_read,
    137};
    138
    139static void show_help(const char *progname)
    140{
    141 printf("usage: %s [options] <mountpoint>\n\n", progname);
    142 printf("File-system specific options:\n"
    143 " --name=<s> Name of the \"hello\" file\n"
    144 " (default: \"hello\")\n"
    145 " --contents=<s> Contents \"hello\" file\n"
    146 " (default \"Hello, World!\\n\")\n"
    147 "\n");
    148}
    149
    150int main(int argc, char *argv[])
    151{
    152 int ret;
    153 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    154
    155 /* Set defaults -- we have to use strdup so that
    156 fuse_opt_parse can free the defaults if other
    157 values are specified */
    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 if (options.show_help) {
    171 show_help(argv[0]);
    172 assert(fuse_opt_add_arg(&args, "--help") == 0);
    173 args.argv[0][0] = '\0';
    174 }
    175
    176 ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
    177 fuse_opt_free_args(&args);
    178 return ret;
    179}
    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
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2hello__ll_8c_source.html0000644000175000017500000017146614770234735024504 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    7*/
    8
    21#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    22
    23#include <fuse_lowlevel.h>
    24#include <stdio.h>
    25#include <stdlib.h>
    26#include <string.h>
    27#include <errno.h>
    28#include <fcntl.h>
    29#include <unistd.h>
    30#include <assert.h>
    31
    32static const char *hello_str = "Hello World!\n";
    33static const char *hello_name = "hello";
    34
    35static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
    36{
    37 stbuf->st_ino = ino;
    38 switch (ino) {
    39 case 1:
    40 stbuf->st_mode = S_IFDIR | 0755;
    41 stbuf->st_nlink = 2;
    42 break;
    43
    44 case 2:
    45 stbuf->st_mode = S_IFREG | 0444;
    46 stbuf->st_nlink = 1;
    47 stbuf->st_size = strlen(hello_str);
    48 break;
    49
    50 default:
    51 return -1;
    52 }
    53 return 0;
    54}
    55
    56static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
    57{
    58 (void)userdata;
    59
    60 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    61 conn->no_interrupt = 1;
    62}
    63
    64static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
    65 struct fuse_file_info *fi)
    66{
    67 struct stat stbuf;
    68
    69 (void) fi;
    70
    71 memset(&stbuf, 0, sizeof(stbuf));
    72 if (hello_stat(ino, &stbuf) == -1)
    73 fuse_reply_err(req, ENOENT);
    74 else
    75 fuse_reply_attr(req, &stbuf, 1.0);
    76}
    77
    78static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
    79{
    80 struct fuse_entry_param e;
    81
    82 if (parent != 1 || strcmp(name, hello_name) != 0)
    83 fuse_reply_err(req, ENOENT);
    84 else {
    85 memset(&e, 0, sizeof(e));
    86 e.ino = 2;
    87 e.attr_timeout = 1.0;
    88 e.entry_timeout = 1.0;
    89 hello_stat(e.ino, &e.attr);
    90
    91 fuse_reply_entry(req, &e);
    92 }
    93}
    94
    95struct dirbuf {
    96 char *p;
    97 size_t size;
    98};
    99
    100static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
    101 fuse_ino_t ino)
    102{
    103 struct stat stbuf;
    104 size_t oldsize = b->size;
    105 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
    106 b->p = (char *) realloc(b->p, b->size);
    107 memset(&stbuf, 0, sizeof(stbuf));
    108 stbuf.st_ino = ino;
    109 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
    110 b->size);
    111}
    112
    113#define min(x, y) ((x) < (y) ? (x) : (y))
    114
    115static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
    116 off_t off, size_t maxsize)
    117{
    118 if (off < bufsize)
    119 return fuse_reply_buf(req, buf + off,
    120 min(bufsize - off, maxsize));
    121 else
    122 return fuse_reply_buf(req, NULL, 0);
    123}
    124
    125static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    126 off_t off, struct fuse_file_info *fi)
    127{
    128 (void) fi;
    129
    130 if (ino != 1)
    131 fuse_reply_err(req, ENOTDIR);
    132 else {
    133 struct dirbuf b;
    134
    135 memset(&b, 0, sizeof(b));
    136 dirbuf_add(req, &b, ".", 1);
    137 dirbuf_add(req, &b, "..", 1);
    138 dirbuf_add(req, &b, hello_name, 2);
    139 reply_buf_limited(req, b.p, b.size, off, size);
    140 free(b.p);
    141 }
    142}
    143
    144static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
    145 struct fuse_file_info *fi)
    146{
    147 if (ino != 2)
    148 fuse_reply_err(req, EISDIR);
    149 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
    150 fuse_reply_err(req, EACCES);
    151 else
    152 fuse_reply_open(req, fi);
    153}
    154
    155static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
    156 off_t off, struct fuse_file_info *fi)
    157{
    158 (void) fi;
    159
    160 assert(ino == 2);
    161 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
    162}
    163
    164static void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    165 size_t size)
    166{
    167 (void)size;
    168 assert(ino == 1 || ino == 2);
    169 if (strcmp(name, "hello_ll_getxattr_name") == 0)
    170 {
    171 const char *buf = "hello_ll_getxattr_value";
    172 fuse_reply_buf(req, buf, strlen(buf));
    173 }
    174 else
    175 {
    176 fuse_reply_err(req, ENOTSUP);
    177 }
    178}
    179
    180static void hello_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    181 const char *value, size_t size, int flags)
    182{
    183 (void)flags;
    184 (void)size;
    185 assert(ino == 1 || ino == 2);
    186 const char* exp_val = "hello_ll_setxattr_value";
    187 if (strcmp(name, "hello_ll_setxattr_name") == 0 &&
    188 strlen(exp_val) == size &&
    189 strncmp(value, exp_val, size) == 0)
    190 {
    191 fuse_reply_err(req, 0);
    192 }
    193 else
    194 {
    195 fuse_reply_err(req, ENOTSUP);
    196 }
    197}
    198
    199static void hello_ll_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
    200{
    201 assert(ino == 1 || ino == 2);
    202 if (strcmp(name, "hello_ll_removexattr_name") == 0)
    203 {
    204 fuse_reply_err(req, 0);
    205 }
    206 else
    207 {
    208 fuse_reply_err(req, ENOTSUP);
    209 }
    210}
    211
    212static const struct fuse_lowlevel_ops hello_ll_oper = {
    213 .init = hello_ll_init,
    214 .lookup = hello_ll_lookup,
    215 .getattr = hello_ll_getattr,
    216 .readdir = hello_ll_readdir,
    217 .open = hello_ll_open,
    218 .read = hello_ll_read,
    219 .setxattr = hello_ll_setxattr,
    220 .getxattr = hello_ll_getxattr,
    221 .removexattr = hello_ll_removexattr,
    222};
    223
    224int main(int argc, char *argv[])
    225{
    226 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    227 struct fuse_session *se;
    228 struct fuse_cmdline_opts opts;
    229 struct fuse_loop_config *config;
    230 int ret = -1;
    231
    232 if (fuse_parse_cmdline(&args, &opts) != 0)
    233 return 1;
    234 if (opts.show_help) {
    235 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
    238 ret = 0;
    239 goto err_out1;
    240 } else if (opts.show_version) {
    241 printf("FUSE library version %s\n", fuse_pkgversion());
    243 ret = 0;
    244 goto err_out1;
    245 }
    246
    247 if(opts.mountpoint == NULL) {
    248 printf("usage: %s [options] <mountpoint>\n", argv[0]);
    249 printf(" %s --help\n", argv[0]);
    250 ret = 1;
    251 goto err_out1;
    252 }
    253
    254 se = fuse_session_new(&args, &hello_ll_oper,
    255 sizeof(hello_ll_oper), NULL);
    256 if (se == NULL)
    257 goto err_out1;
    258
    259 if (fuse_set_signal_handlers(se) != 0)
    260 goto err_out2;
    261
    262 if (fuse_session_mount(se, opts.mountpoint) != 0)
    263 goto err_out3;
    264
    265 fuse_daemonize(opts.foreground);
    266
    267 /* Block until ctrl+c or fusermount -u */
    268 if (opts.singlethread)
    269 ret = fuse_session_loop(se);
    270 else {
    271 config = fuse_loop_cfg_create();
    272 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
    273 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
    274 ret = fuse_session_loop_mt(se, config);
    275 fuse_loop_cfg_destroy(config);
    276 config = NULL;
    277 }
    278
    280err_out3:
    282err_out2:
    284err_out1:
    285 free(opts.mountpoint);
    286 fuse_opt_free_args(&args);
    287
    288 return ret ? 1 : 0;
    289}
    int fuse_set_signal_handlers(struct fuse_session *se)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2hello__ll__uds_8c_source.html0000644000175000017500000021032214770234735025477 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    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:5218
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2invalidate__path_8c_source.html0000644000175000017500000016566314770234735026050 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    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:5204
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5153
    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:5209
    @ 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:5218
    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:77
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2ioctl_8c_source.html0000644000175000017500000010760214770234735023654 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2ioctl_8h_source.html0000644000175000017500000001612214770234735023655 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2ioctl__client_8c_source.html0000644000175000017500000002700114770234735025343 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 tests the ioctl.c example file systsem.
    7
    8 This program can be distributed under the terms of the GNU GPLv2.
    9 See the file COPYING.
    10*/
    11
    24#include <sys/types.h>
    25#include <fcntl.h>
    26#include <sys/stat.h>
    27#include <sys/ioctl.h>
    28#include <stdio.h>
    29#include <stdlib.h>
    30#include <ctype.h>
    31#include <errno.h>
    32#include <unistd.h>
    33#include "ioctl.h"
    34
    35const char *usage =
    36"Usage: fioclient FIOC_FILE [size]\n"
    37"\n"
    38"Get size if <size> is omitted, set size otherwise\n"
    39"\n";
    40
    41int main(int argc, char **argv)
    42{
    43 size_t size;
    44 int fd;
    45 int ret = 0;
    46
    47 if (argc < 2) {
    48 fprintf(stderr, "%s", usage);
    49 return 1;
    50 }
    51
    52 fd = open(argv[1], O_RDWR);
    53 if (fd < 0) {
    54 perror("open");
    55 return 1;
    56 }
    57
    58 if (argc == 2) {
    59 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
    60 perror("ioctl");
    61 ret = 1;
    62 goto out;
    63 }
    64 printf("%zu\n", size);
    65 } else {
    66 size = strtoul(argv[2], NULL, 0);
    67 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
    68 perror("ioctl");
    69 ret = 1;
    70 goto out;
    71 }
    72 }
    73out:
    74 close(fd);
    75 return ret;
    76}
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2notify__inval__entry_8c_source.html0000644000175000017500000021244114770234735026760 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    7*/
    8
    79#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    80
    81#include <fuse_lowlevel.h>
    82#include <stdio.h>
    83#include <stdlib.h>
    84#include <string.h>
    85#include <errno.h>
    86#include <fcntl.h>
    87#include <assert.h>
    88#include <signal.h>
    89#include <stddef.h>
    90#include <sys/stat.h>
    91#include <unistd.h>
    92#include <pthread.h>
    93
    94#define MAX_STR_LEN 128
    95static char file_name[MAX_STR_LEN];
    96static fuse_ino_t file_ino = 2;
    97static int lookup_cnt = 0;
    98static pthread_t main_thread;
    99
    100/* Command line parsing */
    101struct options {
    102 int no_notify;
    103 float timeout;
    104 int update_interval;
    105 int only_expire;
    106};
    107static struct options options = {
    108 .timeout = 5,
    109 .no_notify = 0,
    110 .update_interval = 1,
    111 .only_expire = 0,
    112};
    113
    114#define OPTION(t, p) \
    115 { t, offsetof(struct options, p), 1 }
    116static const struct fuse_opt option_spec[] = {
    117 OPTION("--no-notify", no_notify),
    118 OPTION("--update-interval=%d", update_interval),
    119 OPTION("--timeout=%f", timeout),
    120 OPTION("--only-expire", only_expire),
    122};
    123
    124static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
    125 stbuf->st_ino = ino;
    126 if (ino == FUSE_ROOT_ID) {
    127 stbuf->st_mode = S_IFDIR | 0755;
    128 stbuf->st_nlink = 1;
    129 }
    130
    131 else if (ino == file_ino) {
    132 stbuf->st_mode = S_IFREG | 0000;
    133 stbuf->st_nlink = 1;
    134 stbuf->st_size = 0;
    135 }
    136
    137 else
    138 return -1;
    139
    140 return 0;
    141}
    142
    143static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
    144 (void)userdata;
    145
    146 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    147 conn->no_interrupt = 1;
    148}
    149
    150static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
    151 const char *name) {
    152 struct fuse_entry_param e;
    153 memset(&e, 0, sizeof(e));
    154
    155 if (parent != FUSE_ROOT_ID)
    156 goto err_out;
    157 else if (strcmp(name, file_name) == 0) {
    158 e.ino = file_ino;
    159 lookup_cnt++;
    160 } else
    161 goto err_out;
    162
    163 e.attr_timeout = options.timeout;
    164 e.entry_timeout = options.timeout;
    165 if (tfs_stat(e.ino, &e.attr) != 0)
    166 goto err_out;
    167 fuse_reply_entry(req, &e);
    168 return;
    169
    170err_out:
    171 fuse_reply_err(req, ENOENT);
    172}
    173
    174static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
    175 uint64_t nlookup) {
    176 (void) req;
    177 if(ino == file_ino)
    178 lookup_cnt -= nlookup;
    179 else
    180 assert(ino == FUSE_ROOT_ID);
    181 fuse_reply_none(req);
    182}
    183
    184static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
    185 struct fuse_file_info *fi) {
    186 struct stat stbuf;
    187
    188 (void) fi;
    189
    190 memset(&stbuf, 0, sizeof(stbuf));
    191 if (tfs_stat(ino, &stbuf) != 0)
    192 fuse_reply_err(req, ENOENT);
    193 else
    194 fuse_reply_attr(req, &stbuf, options.timeout);
    195}
    196
    197struct dirbuf {
    198 char *p;
    199 size_t size;
    200};
    201
    202static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
    203 fuse_ino_t ino) {
    204 struct stat stbuf;
    205 size_t oldsize = b->size;
    206 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
    207 b->p = (char *) realloc(b->p, b->size);
    208 memset(&stbuf, 0, sizeof(stbuf));
    209 stbuf.st_ino = ino;
    210 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
    211 b->size);
    212}
    213
    214#define min(x, y) ((x) < (y) ? (x) : (y))
    215
    216static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
    217 off_t off, size_t maxsize) {
    218 if (off < bufsize)
    219 return fuse_reply_buf(req, buf + off,
    220 min(bufsize - off, maxsize));
    221 else
    222 return fuse_reply_buf(req, NULL, 0);
    223}
    224
    225static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    226 off_t off, struct fuse_file_info *fi) {
    227 (void) fi;
    228
    229 if (ino != FUSE_ROOT_ID)
    230 fuse_reply_err(req, ENOTDIR);
    231 else {
    232 struct dirbuf b;
    233
    234 memset(&b, 0, sizeof(b));
    235 dirbuf_add(req, &b, file_name, file_ino);
    236 reply_buf_limited(req, b.p, b.size, off, size);
    237 free(b.p);
    238 }
    239}
    240
    241static const struct fuse_lowlevel_ops tfs_oper = {
    242 .init = tfs_init,
    243 .lookup = tfs_lookup,
    244 .getattr = tfs_getattr,
    245 .readdir = tfs_readdir,
    246 .forget = tfs_forget,
    247};
    248
    249static void update_fs(void) {
    250 time_t t;
    251 struct tm *now;
    252 ssize_t ret;
    253
    254 t = time(NULL);
    255 now = localtime(&t);
    256 assert(now != NULL);
    257
    258 ret = strftime(file_name, MAX_STR_LEN,
    259 "Time_is_%Hh_%Mm_%Ss", now);
    260 assert(ret != 0);
    261}
    262
    263static void* update_fs_loop(void *data) {
    264 struct fuse_session *se = (struct fuse_session*) data;
    265 char *old_name;
    266
    267
    268 while(!fuse_session_exited(se)) {
    269 old_name = strdup(file_name);
    270 update_fs();
    271
    272 if (!options.no_notify && lookup_cnt) {
    273 if(options.only_expire) { // expire entry
    275 (se, FUSE_ROOT_ID, old_name, strlen(old_name));
    276
    277 // no kernel support
    278 if (ret == -ENOSYS) {
    279 printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n");
    280 printf("Exiting...\n");
    281
    283 // Make sure to exit now, rather than on next request from userspace
    284 pthread_kill(main_thread, SIGPIPE);
    285
    286 break;
    287 }
    288 // 1) ret == 0: successful expire of an existing entry
    289 // 2) ret == -ENOENT: kernel has already expired the entry /
    290 // entry does not exist anymore in the kernel
    291 assert(ret == 0 || ret == -ENOENT);
    292 } else { // invalidate entry
    294 (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
    295 }
    296 }
    297 free(old_name);
    298 sleep(options.update_interval);
    299 }
    300 return NULL;
    301}
    302
    303static void show_help(const char *progname)
    304{
    305 printf("usage: %s [options] <mountpoint>\n\n", progname);
    306 printf("File-system specific options:\n"
    307 " --timeout=<secs> Timeout for kernel caches\n"
    308 " --update-interval=<secs> Update-rate of file system contents\n"
    309 " --no-notify Disable kernel notifications\n"
    310 " --only-expire Expire entries instead of invalidating them\n"
    311 "\n");
    312}
    313
    314int main(int argc, char *argv[]) {
    315 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    316 struct fuse_session *se;
    317 struct fuse_cmdline_opts opts;
    318 struct fuse_loop_config *config;
    319 pthread_t updater;
    320 int ret = -1;
    321
    322 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
    323 return 1;
    324
    325 if (fuse_parse_cmdline(&args, &opts) != 0)
    326 return 1;
    327 if (opts.show_help) {
    328 show_help(argv[0]);
    331 ret = 0;
    332 goto err_out1;
    333 } else if (opts.show_version) {
    334 printf("FUSE library version %s\n", fuse_pkgversion());
    336 ret = 0;
    337 goto err_out1;
    338 }
    339
    340 /* Initial contents */
    341 update_fs();
    342
    343 se = fuse_session_new(&args, &tfs_oper,
    344 sizeof(tfs_oper), &se);
    345 if (se == NULL)
    346 goto err_out1;
    347
    348 if (fuse_set_signal_handlers(se) != 0)
    349 goto err_out2;
    350
    351 if (fuse_session_mount(se, opts.mountpoint) != 0)
    352 goto err_out3;
    353
    354 fuse_daemonize(opts.foreground);
    355
    356 // Needed to ensure that the main thread continues/restarts processing as soon
    357 // as the fuse session ends (immediately after calling fuse_session_exit() )
    358 // and not only on the next request from userspace
    359 main_thread = pthread_self();
    360
    361 /* Start thread to update file contents */
    362 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
    363 if (ret != 0) {
    364 fprintf(stderr, "pthread_create failed with %s\n",
    365 strerror(ret));
    366 goto err_out3;
    367 }
    368
    369 /* Block until ctrl+c or fusermount -u */
    370 if (opts.singlethread) {
    371 ret = fuse_session_loop(se);
    372 } else {
    373 config = fuse_loop_cfg_create();
    374 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
    375 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
    376 ret = fuse_session_loop_mt(se, config);
    377 fuse_loop_cfg_destroy(config);
    378 config = NULL;
    379 }
    380
    382err_out3:
    384err_out2:
    386err_out1:
    387 free(opts.mountpoint);
    388 fuse_opt_free_args(&args);
    389
    390 return ret ? 1 : 0;
    391}
    392
    393
    int fuse_set_signal_handlers(struct fuse_session *se)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    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
    #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.17.2/doc/html/fuse-3_817_81-rc1_2example_2notify__inval__inode_8c_source.html0000644000175000017500000022052014770234735026712 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    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:5218
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2notify__store__retrieve_8c_source.html0000644000175000017500000025226414770234735027476 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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:5218
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2null_8c_source.html0000644000175000017500000005744714770234735023527 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2example_2passthrough_8c_source.html0000644000175000017500000026215114770234735025112 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2passthrough__fh_8c_source.html0000644000175000017500000034203414770234735025725 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2passthrough__helpers_8h_source.html0000644000175000017500000003424114770234735026775 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2example_2passthrough__ll_8c_source.html0000644000175000017500000100164614770234735025741 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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) {
    175 has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
    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) {
    181 has_flag = fuse_set_feature_flag(conn, 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}
    int fuse_set_signal_handlers(struct fuse_session *se)
    @ FUSE_CAP_WRITEBACK_CACHE
    @ FUSE_CAP_FLOCK_LOCKS
    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
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    void fuse_remove_signal_handlers(struct fuse_session *se)
    @ FUSE_BUF_SPLICE_MOVE
    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_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:97
    uint32_t parallel_direct_writes
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2poll_8c_source.html0000644000175000017500000014364114770234735023513 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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:86
    uint32_t direct_io
    Definition fuse_common.h:71
    void(* destroy)(void *private_data)
    Definition fuse.h:649
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2poll__client_8c_source.html0000644000175000017500000003105314770234735025201 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2example_2printcap_8c_source.html0000644000175000017500000014076114770234735024365 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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}
    int fuse_set_signal_handlers(struct fuse_session *se)
    @ FUSE_CAP_POSIX_ACL
    @ FUSE_CAP_READDIRPLUS
    @ FUSE_CAP_NO_OPENDIR_SUPPORT
    @ FUSE_CAP_PARALLEL_DIROPS
    @ FUSE_CAP_ASYNC_DIO
    @ FUSE_CAP_NO_EXPORT_SUPPORT
    @ FUSE_CAP_WRITEBACK_CACHE
    @ FUSE_CAP_IOCTL_DIR
    @ FUSE_CAP_AUTO_INVAL_DATA
    @ FUSE_CAP_SPLICE_READ
    @ FUSE_CAP_SPLICE_MOVE
    @ FUSE_CAP_POSIX_LOCKS
    @ FUSE_CAP_HANDLE_KILLPRIV_V2
    @ FUSE_CAP_HANDLE_KILLPRIV
    @ FUSE_CAP_DONT_MASK
    @ FUSE_CAP_ATOMIC_O_TRUNC
    @ FUSE_CAP_SPLICE_WRITE
    @ FUSE_CAP_PASSTHROUGH
    @ FUSE_CAP_FLOCK_LOCKS
    @ FUSE_CAP_EXPIRE_ONLY
    @ FUSE_CAP_EXPORT_SUPPORT
    @ FUSE_CAP_READDIRPLUS_AUTO
    @ FUSE_CAP_NO_OPEN_SUPPORT
    @ FUSE_CAP_DIRECT_IO_ALLOW_MMAP
    @ FUSE_CAP_SETXATTR_EXT
    @ FUSE_CAP_ASYNC_READ
    @ FUSE_CAP_CACHE_SYMLINKS
    @ FUSE_CAP_EXPLICIT_INVAL_DATA
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    void fuse_remove_signal_handlers(struct fuse_session *se)
    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.17.2/doc/html/fuse-3_817_81-rc1_2include_2cuse__lowlevel_8h_source.html0000644000175000017500000004611314770234735025545 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse_8h_source.html0000644000175000017500000042424514770234735023506 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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);
    101struct fuse_config {
    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
    127 double entry_timeout;
    128
    137 double negative_timeout;
    138
    143 double attr_timeout;
    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
    245 int32_t kernel_cache;
    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
    297 int32_t no_rofd_flush;
    298
    313
    314
    318 uint32_t flags;
    319
    323 uint64_t reserved[48];
    324};
    325
    326
    349struct fuse_operations {
    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
    860struct fuse_context {
    862 struct fuse *fuse;
    863
    865 uid_t uid;
    866
    868 gid_t gid;
    869
    871 pid_t pid;
    872
    874 void *private_data;
    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
    959int fuse_main_real_versioned(int argc, char *argv[],
    960 const struct fuse_operations *op, size_t op_size,
    961 struct libfuse_version *version, void *user_data);
    962static inline int fuse_main_fn(int argc, char *argv[],
    963 const struct fuse_operations *op,
    964 void *user_data)
    965{
    966 struct libfuse_version version = {
    967 .major = FUSE_MAJOR_VERSION,
    968 .minor = FUSE_MINOR_VERSION,
    969 .hotfix = FUSE_HOTFIX_VERSION,
    970 .padding = 0
    971 };
    972
    973 return fuse_main_real_versioned(argc, argv, op, sizeof(*(op)), &version,
    974 user_data);
    975}
    976#define fuse_main(argc, argv, op, user_data) \
    977 fuse_main_fn(argc, argv, op, user_data)
    978
    979/* ----------------------------------------------------------- *
    980 * More detailed API *
    981 * ----------------------------------------------------------- */
    982
    994void fuse_lib_help(struct fuse_args *args);
    995
    996/* Do not call this directly, use fuse_new() instead */
    997struct fuse *_fuse_new_30(struct fuse_args *args,
    998 const struct fuse_operations *op, size_t op_size,
    999 struct libfuse_version *version, void *user_data);
    1000struct fuse *_fuse_new_31(struct fuse_args *args,
    1001 const struct fuse_operations *op, size_t op_size,
    1002 struct libfuse_version *version, void *user_data);
    1003
    1031#if FUSE_USE_VERSION == 30
    1032static inline struct fuse *fuse_new_fn(struct fuse_args *args,
    1033 const struct fuse_operations *op,
    1034 size_t op_size, void *user_data)
    1035{
    1036 struct libfuse_version version = {
    1037 .major = FUSE_MAJOR_VERSION,
    1038 .minor = FUSE_MINOR_VERSION,
    1039 .hotfix = FUSE_HOTFIX_VERSION,
    1040 .padding = 0
    1041 };
    1042
    1043 return _fuse_new_30(args, op, op_size, &version, user_data);
    1044}
    1045#else /* FUSE_USE_VERSION */
    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_31(args, op, op_size, &version, user_data);
    1058}
    1059#endif
    1060#define fuse_new(args, op, size, data) fuse_new_fn(args, op, size, data)
    1061
    1070int fuse_mount(struct fuse *f, const char *mountpoint);
    1071
    1079void fuse_unmount(struct fuse *f);
    1080
    1089void fuse_destroy(struct fuse *f);
    1090
    1106int fuse_loop(struct fuse *f);
    1107
    1116void fuse_exit(struct fuse *f);
    1117
    1118#if FUSE_USE_VERSION < 32
    1119int fuse_loop_mt_31(struct fuse *f, int clone_fd);
    1120#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd)
    1121#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
    1122int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
    1123#define fuse_loop_mt(f, config) fuse_loop_mt_32(f, config)
    1124#else
    1156#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
    1157int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config);
    1158#else
    1159#define fuse_loop_mt(f, config) fuse_loop_mt_312(f, config)
    1160#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
    1161#endif
    1162
    1163
    1172struct fuse_context *fuse_get_context(void);
    1173
    1192int fuse_getgroups(int size, gid_t list[]);
    1193
    1199int fuse_interrupted(void);
    1200
    1212int fuse_invalidate_path(struct fuse *f, const char *path);
    1213
    1222
    1229void fuse_stop_cleanup_thread(struct fuse *fuse);
    1230
    1240int fuse_clean_cache(struct fuse *fuse);
    1241
    1242/*
    1243 * Stacking API
    1244 */
    1245
    1251struct fuse_fs;
    1252
    1253/*
    1254 * These functions call the relevant filesystem operation, and return
    1255 * the result.
    1256 *
    1257 * If the operation is not defined, they return -ENOSYS, with the
    1258 * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
    1259 * fuse_fs_releasedir and fuse_fs_statfs, which return 0.
    1260 */
    1261
    1262int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
    1263 struct fuse_file_info *fi);
    1264int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
    1265 const char *newpath, unsigned int flags);
    1266int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
    1267int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
    1268int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
    1269 const char *path);
    1270int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
    1271int fuse_fs_release(struct fuse_fs *fs, const char *path,
    1272 struct fuse_file_info *fi);
    1273int fuse_fs_open(struct fuse_fs *fs, const char *path,
    1274 struct fuse_file_info *fi);
    1275int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
    1276 off_t off, struct fuse_file_info *fi);
    1277int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
    1278 struct fuse_bufvec **bufp, size_t size, off_t off,
    1279 struct fuse_file_info *fi);
    1280int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
    1281 size_t size, off_t off, struct fuse_file_info *fi);
    1282int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
    1283 struct fuse_bufvec *buf, off_t off,
    1284 struct fuse_file_info *fi);
    1285int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
    1286 struct fuse_file_info *fi);
    1287int fuse_fs_flush(struct fuse_fs *fs, const char *path,
    1288 struct fuse_file_info *fi);
    1289int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
    1290int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
    1291 struct fuse_file_info *fi);
    1292int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
    1293 fuse_fill_dir_t filler, off_t off,
    1294 struct fuse_file_info *fi, enum fuse_readdir_flags flags);
    1295int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
    1296 struct fuse_file_info *fi);
    1297int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
    1298 struct fuse_file_info *fi);
    1299int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
    1300 struct fuse_file_info *fi);
    1301int fuse_fs_lock(struct fuse_fs *fs, const char *path,
    1302 struct fuse_file_info *fi, int cmd, struct flock *lock);
    1303int fuse_fs_flock(struct fuse_fs *fs, const char *path,
    1304 struct fuse_file_info *fi, int op);
    1305int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
    1306 struct fuse_file_info *fi);
    1307int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid,
    1308 struct fuse_file_info *fi);
    1309int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
    1310 struct fuse_file_info *fi);
    1311int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
    1312 const struct timespec tv[2], struct fuse_file_info *fi);
    1313int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
    1314int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
    1315 size_t len);
    1316int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
    1317 dev_t rdev);
    1318int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
    1319int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
    1320 const char *value, size_t size, int flags);
    1321int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
    1322 char *value, size_t size);
    1323int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
    1324 size_t size);
    1325int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
    1326 const char *name);
    1327int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
    1328 uint64_t *idx);
    1329#if FUSE_USE_VERSION < 35
    1330int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd,
    1331 void *arg, struct fuse_file_info *fi, unsigned int flags,
    1332 void *data);
    1333#else
    1334int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
    1335 void *arg, struct fuse_file_info *fi, unsigned int flags,
    1336 void *data);
    1337#endif
    1338int fuse_fs_poll(struct fuse_fs *fs, const char *path,
    1339 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
    1340 unsigned *reventsp);
    1341int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
    1342 off_t offset, off_t length, struct fuse_file_info *fi);
    1343ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
    1344 struct fuse_file_info *fi_in, off_t off_in,
    1345 const char *path_out,
    1346 struct fuse_file_info *fi_out, off_t off_out,
    1347 size_t len, int flags);
    1348off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
    1349 struct fuse_file_info *fi);
    1350void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
    1351 struct fuse_config *cfg);
    1352void fuse_fs_destroy(struct fuse_fs *fs);
    1353
    1354int fuse_notify_poll(struct fuse_pollhandle *ph);
    1355
    1369struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
    1370 void *private_data);
    1371
    1386typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args,
    1387 struct fuse_fs *fs[]);
    1398#define FUSE_REGISTER_MODULE(name_, factory_) \
    1399 fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
    1400
    1402struct fuse_session *fuse_get_session(struct fuse *f);
    1403
    1412int fuse_open_channel(const char *mountpoint, const char *options);
    1413
    1414#ifdef __cplusplus
    1415}
    1416#endif
    1417
    1418#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:5204
    int fuse_interrupted(void)
    Definition fuse.c:4663
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5153
    int fuse_start_cleanup_thread(struct fuse *fuse)
    Definition fuse.c:4904
    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:479
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4520
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5209
    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:4912
    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:87
    fuse_readdir_flags
    Definition fuse.h:42
    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.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse__common_8h_source.html0000644000175000017500000031403414770234735025207 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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#if (defined(__cplusplus) && __cplusplus >= 201103L) || \
    34 (!defined(__cplusplus) && defined(__STDC_VERSION__) && \
    35 __STDC_VERSION__ >= 201112L)
    36#define fuse_static_assert(condition, message) static_assert(condition, message)
    37#else
    38#define fuse_static_assert(condition, message)
    39#endif
    40
    41#ifdef __cplusplus
    42extern "C" {
    43#endif
    44
    58struct fuse_file_info {
    60 int32_t flags;
    61
    68 uint32_t writepage : 1;
    69
    71 uint32_t direct_io : 1;
    72
    77 uint32_t keep_cache : 1;
    78
    82 uint32_t flush : 1;
    83
    86 uint32_t nonseekable : 1;
    87
    88 /* Indicates that flock locks for this file should be
    89 released. If set, lock_owner shall contain a valid value.
    90 May only be set in ->release(). */
    91 uint32_t flock_release : 1;
    92
    97 uint32_t cache_readdir : 1;
    98
    101 uint32_t noflush : 1;
    102
    105 uint32_t parallel_direct_writes : 1;
    106
    108 uint32_t padding : 23;
    109 uint32_t padding2 : 32;
    110 uint32_t padding3 : 32;
    111
    115 uint64_t fh;
    116
    118 uint64_t lock_owner;
    119
    122 uint32_t poll_events;
    123
    127 int32_t backing_id;
    128
    130 uint64_t compat_flags;
    131
    132 uint64_t reserved[2];
    133};
    134fuse_static_assert(sizeof(struct fuse_file_info) == 64,
    135 "fuse_file_info size mismatch");
    136
    147#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
    148struct fuse_loop_config_v1; /* forward declaration */
    149struct fuse_loop_config {
    150#else
    151struct fuse_loop_config_v1 {
    152#endif
    157 int clone_fd;
    158
    169 unsigned int max_idle_threads;
    170};
    171
    172
    173/**************************************************************************
    174 * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
    175 **************************************************************************/
    176
    526
    537#define FUSE_IOCTL_COMPAT (1 << 0)
    538#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
    539#define FUSE_IOCTL_RETRY (1 << 2)
    540#define FUSE_IOCTL_DIR (1 << 4)
    541
    542#define FUSE_IOCTL_MAX_IOV 256
    543
    555struct fuse_conn_info {
    559 uint32_t proto_major;
    560
    564 uint32_t proto_minor;
    565
    569 uint32_t max_write;
    570
    583 uint32_t max_read;
    584
    588 uint32_t max_readahead;
    589
    595 uint32_t capable;
    596
    607 uint32_t want;
    608
    637 uint32_t max_background;
    638
    647 uint32_t congestion_threshold;
    648
    664 uint32_t time_gran;
    665
    682#define FUSE_BACKING_STACKED_UNDER (0)
    683#define FUSE_BACKING_STACKED_OVER (1)
    684 uint32_t max_backing_stack_depth;
    685
    694 uint32_t no_interrupt : 1;
    695
    696 /* reserved bits for future use */
    697 uint32_t padding : 31;
    698
    703 uint64_t capable_ext;
    704
    713 uint64_t want_ext;
    714
    718 uint32_t reserved[16];
    719};
    720fuse_static_assert(sizeof(struct fuse_conn_info) == 128,
    721 "Size of struct fuse_conn_info must be 128 bytes");
    722
    723struct fuse_session;
    724struct fuse_pollhandle;
    725struct fuse_conn_info_opts;
    726
    769struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
    770
    778void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
    779 struct fuse_conn_info *conn);
    780
    787int fuse_daemonize(int foreground);
    788
    794int fuse_version(void);
    795
    801const char *fuse_pkgversion(void);
    802
    808void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
    809
    810/* ----------------------------------------------------------- *
    811 * Data buffer *
    812 * ----------------------------------------------------------- */
    813
    824 FUSE_BUF_IS_FD = (1 << 1),
    825
    834
    842 FUSE_BUF_FD_RETRY = (1 << 3)
    844
    886
    893struct fuse_buf {
    897 size_t size;
    898
    902 enum fuse_buf_flags flags;
    903
    909 void *mem;
    910
    916 int fd;
    917
    923 off_t pos;
    924
    931 size_t mem_size;
    932};
    933
    942struct fuse_bufvec {
    946 size_t count;
    947
    951 size_t idx;
    952
    956 size_t off;
    957
    961 struct fuse_buf buf[1];
    962};
    963
    968struct libfuse_version
    969{
    970 uint32_t major;
    971 uint32_t minor;
    972 uint32_t hotfix;
    973 uint32_t padding;
    974};
    975
    976/* Initialize bufvec with a single buffer of given size */
    977#define FUSE_BUFVEC_INIT(size__) \
    978 ((struct fuse_bufvec) { \
    979 /* .count= */ 1, \
    980 /* .idx = */ 0, \
    981 /* .off = */ 0, \
    982 /* .buf = */ { /* [0] = */ { \
    983 /* .size = */ (size__), \
    984 /* .flags = */ (enum fuse_buf_flags) 0, \
    985 /* .mem = */ NULL, \
    986 /* .fd = */ -1, \
    987 /* .pos = */ 0, \
    988 /* .mem_size = */ 0, \
    989 } } \
    990 } )
    991
    998size_t fuse_buf_size(const struct fuse_bufvec *bufv);
    999
    1008ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
    1009 enum fuse_buf_copy_flags flags);
    1010
    1011/* ----------------------------------------------------------- *
    1012 * Signal handling *
    1013 * ----------------------------------------------------------- */
    1014
    1030int fuse_set_signal_handlers(struct fuse_session *se);
    1031
    1047int fuse_set_fail_signal_handlers(struct fuse_session *se);
    1048
    1060void fuse_remove_signal_handlers(struct fuse_session *se);
    1061
    1067#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
    1073struct fuse_loop_config *fuse_loop_cfg_create(void);
    1074
    1078void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
    1079
    1083void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
    1084 unsigned int value);
    1085
    1089void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
    1090 unsigned int value);
    1091
    1095void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
    1096 unsigned int value);
    1097
    1104void fuse_loop_cfg_convert(struct fuse_loop_config *config,
    1105 struct fuse_loop_config_v1 *v1_conf);
    1106#endif
    1107
    1108
    1109static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn,
    1110 uint64_t flag)
    1111{
    1112 if (conn->capable_ext & flag) {
    1113 conn->want_ext |= flag;
    1114 return true;
    1115 }
    1116 return false;
    1117}
    1118
    1119static inline void fuse_unset_feature_flag(struct fuse_conn_info *conn,
    1120 uint64_t flag)
    1121{
    1122 conn->want_ext &= ~flag;
    1123}
    1124
    1125static inline bool fuse_get_feature_flag(struct fuse_conn_info *conn,
    1126 uint64_t flag)
    1127{
    1128 return conn->capable_ext & flag ? true : false;
    1129}
    1130
    1131/* ----------------------------------------------------------- *
    1132 * Compatibility stuff *
    1133 * ----------------------------------------------------------- */
    1134
    1135#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30
    1136# error only API version 30 or greater is supported
    1137#endif
    1138
    1139#ifdef __cplusplus
    1140}
    1141#endif
    1142
    1143
    1144/*
    1145 * This interface uses 64 bit off_t.
    1146 *
    1147 * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags!
    1148 */
    1149
    1150#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
    1151_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit");
    1152#else
    1153struct _fuse_off_t_must_be_64bit_dummy_struct \
    1154 { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); };
    1155#endif
    1156
    1157#endif /* FUSE_COMMON_H_ */
    int fuse_set_fail_signal_handlers(struct fuse_session *se)
    int fuse_set_signal_handlers(struct fuse_session *se)
    fuse_capability
    @ FUSE_CAP_CURRENT_MAX
    @ FUSE_CAP_POSIX_ACL
    @ FUSE_CAP_READDIRPLUS
    @ FUSE_CAP_NO_OPENDIR_SUPPORT
    @ FUSE_CAP_PARALLEL_DIROPS
    @ FUSE_CAP_ASYNC_DIO
    @ FUSE_CAP_NO_EXPORT_SUPPORT
    @ FUSE_CAP_WRITEBACK_CACHE
    @ FUSE_CAP_IOCTL_DIR
    @ FUSE_CAP_AUTO_INVAL_DATA
    @ FUSE_CAP_SPLICE_READ
    @ FUSE_CAP_SPLICE_MOVE
    @ FUSE_CAP_POSIX_LOCKS
    @ FUSE_CAP_HANDLE_KILLPRIV_V2
    @ FUSE_CAP_HANDLE_KILLPRIV
    @ FUSE_CAP_DONT_MASK
    @ FUSE_CAP_ATOMIC_O_TRUNC
    @ FUSE_CAP_SPLICE_WRITE
    @ FUSE_CAP_PASSTHROUGH
    @ FUSE_CAP_FLOCK_LOCKS
    @ FUSE_CAP_EXPIRE_ONLY
    @ FUSE_CAP_EXPORT_SUPPORT
    @ FUSE_CAP_READDIRPLUS_AUTO
    @ FUSE_CAP_NO_OPEN_SUPPORT
    @ FUSE_CAP_DIRECT_IO_ALLOW_MMAP
    @ FUSE_CAP_SETXATTR_EXT
    @ FUSE_CAP_ASYNC_READ
    @ FUSE_CAP_CACHE_SYMLINKS
    @ FUSE_CAP_EXPLICIT_INVAL_DATA
    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:416
    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:463
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
    int fuse_version(void)
    Definition fuse.c:5213
    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 capable_ext
    uint64_t want_ext
    uint64_t lock_owner
    uint32_t writepage
    Definition fuse_common.h:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t nonseekable
    Definition fuse_common.h:86
    int32_t backing_id
    uint32_t parallel_direct_writes
    uint32_t padding
    uint32_t noflush
    uint64_t compat_flags
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse__kernel_8h_source.html0000644000175000017500000042304614770234735025203 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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
    1174struct fuse_ext_header {
    1175 uint32_t size;
    1176 uint32_t type;
    1177};
    1178
    1184struct fuse_supp_groups {
    1185 uint32_t nr_groups;
    1186 uint32_t groups[];
    1187};
    1188
    1189#endif /* _LINUX_FUSE_H */
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse__log_8h_source.html0000644000175000017500000003045714770234735024504 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse__lowlevel_8h_source.html0000644000175000017500000051062414770234735025553 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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
    60struct fuse_entry_param {
    69
    80 uint64_t generation;
    81
    89 struct stat attr;
    90
    95 double attr_timeout;
    96
    101 double entry_timeout;
    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
    206struct fuse_lowlevel_ops {
    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
    799 void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
    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
    1147 void (*write_buf) (fuse_req_t req, fuse_ino_t ino,
    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
    1279 void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in,
    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
    2003struct fuse_cmdline_opts {
    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)
    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.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse__mount__compat_8h_source.html0000644000175000017500000002250314770234735026560 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse__opt_8h_source.html0000644000175000017500000005254014770234735024522 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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
    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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2buffer_8c_source.html0000644000175000017500000020611114770234735023121 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2compat_8c_source.html0000644000175000017500000004540014770234735023135 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2cuse__lowlevel_8c_source.html0000644000175000017500000021333714770234735024667 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2fuse_8c_source.html0000644000175000017500000327061514770234735022627 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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
    14#include "fuse_config.h"
    15#include "fuse_i.h"
    16#include "fuse_lowlevel.h"
    17#include "fuse_opt.h"
    18#include "fuse_misc.h"
    19#include "fuse_kernel.h"
    20
    21#include <stdio.h>
    22#include <string.h>
    23#include <stdlib.h>
    24#include <stddef.h>
    25#include <stdbool.h>
    26#include <unistd.h>
    27#include <time.h>
    28#include <fcntl.h>
    29#include <limits.h>
    30#include <errno.h>
    31#include <signal.h>
    32#include <dlfcn.h>
    33#include <assert.h>
    34#include <poll.h>
    35#include <sys/param.h>
    36#include <sys/uio.h>
    37#include <sys/time.h>
    38#include <sys/mman.h>
    39#include <sys/file.h>
    40
    41#define FUSE_NODE_SLAB 1
    42
    43#ifndef MAP_ANONYMOUS
    44#undef FUSE_NODE_SLAB
    45#endif
    46
    47#ifndef RENAME_EXCHANGE
    48#define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
    49#endif
    50
    51#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
    52
    53#define FUSE_UNKNOWN_INO 0xffffffff
    54#define OFFSET_MAX 0x7fffffffffffffffLL
    55
    56#define NODE_TABLE_MIN_SIZE 8192
    57
    58struct fuse_fs {
    59 struct fuse_operations op;
    60 void *user_data;
    61 int debug;
    62};
    63
    64struct fusemod_so {
    65 void *handle;
    66 int ctr;
    67};
    68
    69struct lock_queue_element {
    70 struct lock_queue_element *next;
    71 pthread_cond_t cond;
    72 fuse_ino_t nodeid1;
    73 const char *name1;
    74 char **path1;
    75 struct node **wnode1;
    76 fuse_ino_t nodeid2;
    77 const char *name2;
    78 char **path2;
    79 struct node **wnode2;
    80 int err;
    81 bool done : 1;
    82};
    83
    84struct node_table {
    85 struct node **array;
    86 size_t use;
    87 size_t size;
    88 size_t split;
    89};
    90
    91#define container_of(ptr, type, member) ({ \
    92 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    93 (type *)( (char *)__mptr - offsetof(type,member) );})
    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)
    2609 conn->want &= ~FUSE_CAP_SPLICE_READ;
    2610 if (!fs->op.lock)
    2611 conn->want &= ~FUSE_CAP_POSIX_LOCKS;
    2612 if (!fs->op.flock)
    2613 conn->want &= ~FUSE_CAP_FLOCK_LOCKS;
    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);
    2625 fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
    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
    4644struct fuse_context *fuse_get_context(void)
    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
    4663int fuse_interrupted(void)
    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 pthread_setname_np(pthread_self(), "fuse_prune_nodes");
    4896
    4897 while(1) {
    4898 sleep_time = fuse_clean_cache(f);
    4899 sleep(sleep_time);
    4900 }
    4901 return NULL;
    4902}
    4903
    4904int fuse_start_cleanup_thread(struct fuse *f)
    4905{
    4906 if (lru_enabled(f))
    4907 return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
    4908
    4909 return 0;
    4910}
    4911
    4912void fuse_stop_cleanup_thread(struct fuse *f)
    4913{
    4914 if (lru_enabled(f)) {
    4915 pthread_mutex_lock(&f->lock);
    4916 pthread_cancel(f->prune_thread);
    4917 pthread_mutex_unlock(&f->lock);
    4918 pthread_join(f->prune_thread, NULL);
    4919 }
    4920}
    4921
    4922/*
    4923 * Not supposed to be called directly, but supposed to be called
    4924 * through the fuse_new macro
    4925 */
    4926struct fuse *_fuse_new_31(struct fuse_args *args,
    4927 const struct fuse_operations *op, size_t op_size,
    4928 struct libfuse_version *version, void *user_data)
    4929{
    4930 struct fuse *f;
    4931 struct node *root;
    4932 struct fuse_fs *fs;
    4933 struct fuse_lowlevel_ops llop = fuse_path_ops;
    4934
    4935 f = (struct fuse *) calloc(1, sizeof(struct fuse));
    4936 if (f == NULL) {
    4937 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
    4938 goto out;
    4939 }
    4940
    4941 f->conf.entry_timeout = 1.0;
    4942 f->conf.attr_timeout = 1.0;
    4943 f->conf.negative_timeout = 0.0;
    4944 f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
    4945
    4946 /* Parse options */
    4947 if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
    4948 fuse_lib_opt_proc) == -1)
    4949 goto out_free;
    4950
    4951 pthread_mutex_lock(&fuse_context_lock);
    4952 static int builtin_modules_registered = 0;
    4953 /* Have the builtin modules already been registered? */
    4954 if (builtin_modules_registered == 0) {
    4955 /* If not, register them. */
    4956 fuse_register_module("subdir", fuse_module_subdir_factory, NULL);
    4957#ifdef HAVE_ICONV
    4958 fuse_register_module("iconv", fuse_module_iconv_factory, NULL);
    4959#endif
    4960 builtin_modules_registered= 1;
    4961 }
    4962 pthread_mutex_unlock(&fuse_context_lock);
    4963
    4964 if (fuse_create_context_key() == -1)
    4965 goto out_free;
    4966
    4967 fs = fuse_fs_new(op, op_size, user_data);
    4968 if (!fs)
    4969 goto out_delete_context_key;
    4970
    4971 f->fs = fs;
    4972
    4973 /* Oh f**k, this is ugly! */
    4974 if (!fs->op.lock) {
    4975 llop.getlk = NULL;
    4976 llop.setlk = NULL;
    4977 }
    4978
    4979 f->pagesize = getpagesize();
    4980 init_list_head(&f->partial_slabs);
    4981 init_list_head(&f->full_slabs);
    4982 init_list_head(&f->lru_table);
    4983
    4984 if (f->conf.modules) {
    4985 char *module;
    4986 char *next;
    4987
    4988 for (module = f->conf.modules; module; module = next) {
    4989 char *p;
    4990 for (p = module; *p && *p != ':'; p++);
    4991 next = *p ? p + 1 : NULL;
    4992 *p = '\0';
    4993 if (module[0] &&
    4994 fuse_push_module(f, module, args) == -1)
    4995 goto out_free_fs;
    4996 }
    4997 }
    4998
    4999 if (!f->conf.ac_attr_timeout_set)
    5000 f->conf.ac_attr_timeout = f->conf.attr_timeout;
    5001
    5002#if defined(__FreeBSD__) || defined(__NetBSD__)
    5003 /*
    5004 * In FreeBSD, we always use these settings as inode numbers
    5005 * are needed to make getcwd(3) work.
    5006 */
    5007 f->conf.readdir_ino = 1;
    5008#endif
    5009
    5010 /* not declared globally, to restrict usage of this function */
    5011 struct fuse_session *fuse_session_new_versioned(
    5012 struct fuse_args *args, const struct fuse_lowlevel_ops *op,
    5013 size_t op_size, struct libfuse_version *version,
    5014 void *userdata);
    5015 f->se = fuse_session_new_versioned(args, &llop, sizeof(llop), version,
    5016 f);
    5017 if (f->se == NULL)
    5018 goto out_free_fs;
    5019
    5020 if (f->conf.debug) {
    5021 fuse_log(FUSE_LOG_DEBUG, "nullpath_ok: %i\n", f->conf.nullpath_ok);
    5022 }
    5023
    5024 /* Trace topmost layer by default */
    5025 f->fs->debug = f->conf.debug;
    5026 f->ctr = 0;
    5027 f->generation = 0;
    5028 if (node_table_init(&f->name_table) == -1)
    5029 goto out_free_session;
    5030
    5031 if (node_table_init(&f->id_table) == -1)
    5032 goto out_free_name_table;
    5033
    5034 pthread_mutex_init(&f->lock, NULL);
    5035
    5036 root = alloc_node(f);
    5037 if (root == NULL) {
    5038 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
    5039 goto out_free_id_table;
    5040 }
    5041 if (lru_enabled(f)) {
    5042 struct node_lru *lnode = node_lru(root);
    5043 init_list_head(&lnode->lru);
    5044 }
    5045
    5046 strcpy(root->inline_name, "/");
    5047 root->name = root->inline_name;
    5048 root->parent = NULL;
    5049 root->nodeid = FUSE_ROOT_ID;
    5050 inc_nlookup(root);
    5051 hash_id(f, root);
    5052
    5053 return f;
    5054
    5055out_free_id_table:
    5056 free(f->id_table.array);
    5057out_free_name_table:
    5058 free(f->name_table.array);
    5059out_free_session:
    5060 fuse_session_destroy(f->se);
    5061out_free_fs:
    5062 free(f->fs);
    5063 free(f->conf.modules);
    5064out_delete_context_key:
    5065 fuse_delete_context_key();
    5066out_free:
    5067 free(f);
    5068out:
    5069 return NULL;
    5070}
    5071
    5072/* Emulates 3.0-style fuse_new(), which processes --help */
    5073FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0")
    5074struct fuse *_fuse_new_30(struct fuse_args *args,
    5075 const struct fuse_operations *op,
    5076 size_t op_size,
    5077 struct libfuse_version *version,
    5078 void *user_data)
    5079{
    5080 struct fuse_config conf = {0};
    5081
    5082 const struct fuse_opt opts[] = {
    5083 FUSE_LIB_OPT("-h", show_help, 1),
    5084 FUSE_LIB_OPT("--help", show_help, 1),
    5086 };
    5087
    5088 if (fuse_opt_parse(args, &conf, opts,
    5089 fuse_lib_opt_proc) == -1)
    5090 return NULL;
    5091
    5092 if (conf.show_help) {
    5093 fuse_lib_help(args);
    5094 return NULL;
    5095 } else
    5096 return _fuse_new_31(args, op, op_size, version, user_data);
    5097}
    5098
    5099/* ABI compat version */
    5100struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
    5101 size_t op_size, void *user_data);
    5102FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1")
    5103struct fuse *fuse_new_31(struct fuse_args *args,
    5104 const struct fuse_operations *op,
    5105 size_t op_size, void *user_data)
    5106{
    5107 /* unknown version */
    5108 struct libfuse_version version = { 0 };
    5109
    5110 return _fuse_new_31(args, op, op_size, &version, user_data);
    5111}
    5112
    5113/*
    5114 * ABI compat version
    5115 * Emulates 3.0-style fuse_new(), which processes --help
    5116 */
    5117struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
    5118 size_t op_size, void *user_data);
    5119FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0")
    5120struct fuse *fuse_new_30(struct fuse_args *args,
    5121 const struct fuse_operations *op,
    5122 size_t op_size, void *user_data)
    5123{
    5124 struct fuse_config conf = {0};
    5125
    5126 const struct fuse_opt opts[] = {
    5127 FUSE_LIB_OPT("-h", show_help, 1),
    5128 FUSE_LIB_OPT("--help", show_help, 1),
    5130 };
    5131
    5132 if (fuse_opt_parse(args, &conf, opts,
    5133 fuse_lib_opt_proc) == -1)
    5134 return NULL;
    5135
    5136 if (conf.show_help) {
    5137 fuse_lib_help(args);
    5138 return NULL;
    5139 } else
    5140 return fuse_new_31(args, op, op_size, user_data);
    5141}
    5142
    5143
    5144void fuse_destroy(struct fuse *f)
    5145{
    5146 size_t i;
    5147
    5148 if (f->conf.intr && f->intr_installed)
    5149 fuse_restore_intr_signal(f->conf.intr_signal);
    5150
    5151 if (f->fs) {
    5152 fuse_create_context(f);
    5153
    5154 for (i = 0; i < f->id_table.size; i++) {
    5155 struct node *node;
    5156
    5157 for (node = f->id_table.array[i]; node != NULL;
    5158 node = node->id_next) {
    5159 if (node->is_hidden) {
    5160 char *path;
    5161 if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
    5162 fuse_fs_unlink(f->fs, path);
    5163 free(path);
    5164 }
    5165 }
    5166 }
    5167 }
    5168 }
    5169 for (i = 0; i < f->id_table.size; i++) {
    5170 struct node *node;
    5171 struct node *next;
    5172
    5173 for (node = f->id_table.array[i]; node != NULL; node = next) {
    5174 next = node->id_next;
    5175 free_node(f, node);
    5176 f->id_table.use--;
    5177 }
    5178 }
    5179 assert(list_empty(&f->partial_slabs));
    5180 assert(list_empty(&f->full_slabs));
    5181
    5182 while (fuse_modules) {
    5183 fuse_put_module(fuse_modules);
    5184 }
    5185 free(f->id_table.array);
    5186 free(f->name_table.array);
    5187 pthread_mutex_destroy(&f->lock);
    5188 fuse_session_destroy(f->se);
    5189 free(f->fs);
    5190 free(f->conf.modules);
    5191 free(f);
    5192 fuse_delete_context_key();
    5193}
    5194
    5195int fuse_mount(struct fuse *f, const char *mountpoint) {
    5196 return fuse_session_mount(fuse_get_session(f), mountpoint);
    5197}
    5198
    5199
    5200void fuse_unmount(struct fuse *f) {
    5202}
    5203
    5204int fuse_version(void)
    5205{
    5206 return FUSE_VERSION;
    5207}
    5208
    5209const char *fuse_pkgversion(void)
    5210{
    5211 return PACKAGE_VERSION;
    5212}
    int fuse_getgroups(int size, gid_t list[])
    Definition fuse.c:4654
    int fuse_mount(struct fuse *f, const char *mountpoint)
    Definition fuse.c:5204
    int fuse_interrupted(void)
    Definition fuse.c:4663
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5153
    int fuse_start_cleanup_thread(struct fuse *fuse)
    Definition fuse.c:4904
    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:1403
    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:5209
    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:4912
    fuse_fill_dir_flags
    Definition fuse.h:58
    fuse_readdir_flags
    Definition fuse.h:42
    @ FUSE_CAP_EXPORT_SUPPORT
    size_t fuse_buf_size(const struct fuse_bufvec *bufv)
    Definition buffer.c:22
    @ 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:5218
    int fuse_version(void)
    Definition fuse.c:5213
    @ FUSE_BUF_SPLICE_MOVE
    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:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t parallel_direct_writes
    uint32_t noflush
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2fuse__i_8h_source.html0000644000175000017500000010707614770234736023301 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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
    12#include <stdbool.h>
    13
    14#define MIN(a, b) \
    15({ \
    16 typeof(a) _a = (a); \
    17 typeof(b) _b = (b); \
    18 _a < _b ? _a : _b; \
    19})
    20
    21struct mount_opts;
    22
    23struct fuse_req {
    24 struct fuse_session *se;
    25 uint64_t unique;
    26 _Atomic int ref_cnt;
    27 pthread_mutex_t lock;
    28 struct fuse_ctx ctx;
    29 struct fuse_chan *ch;
    30 int interrupted;
    31 unsigned int ioctl_64bit : 1;
    32 union {
    33 struct {
    34 uint64_t unique;
    35 } i;
    36 struct {
    38 void *data;
    39 } ni;
    40 } u;
    41 struct fuse_req *next;
    42 struct fuse_req *prev;
    43};
    44
    45struct fuse_notify_req {
    46 uint64_t unique;
    47 void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
    48 const void *, const struct fuse_buf *);
    49 struct fuse_notify_req *next;
    50 struct fuse_notify_req *prev;
    51};
    52
    53struct fuse_session {
    54 char *mountpoint;
    55 volatile int exited;
    56 int fd;
    57 struct fuse_custom_io *io;
    58 struct mount_opts *mo;
    59 int debug;
    60 int deny_others;
    61 struct fuse_lowlevel_ops op;
    62 int got_init;
    63 struct cuse_data *cuse_data;
    64 void *userdata;
    65 uid_t owner;
    66 struct fuse_conn_info conn;
    67 struct fuse_req list;
    68 struct fuse_req interrupts;
    69 pthread_mutex_t lock;
    70 int got_destroy;
    71 pthread_key_t pipe_key;
    72 int broken_splice_nonblock;
    73 uint64_t notify_ctr;
    74 struct fuse_notify_req notify_list;
    75 size_t bufsize;
    76 int error;
    77
    78 /* This is useful if any kind of ABI incompatibility is found at
    79 * a later version, to 'fix' it at run time.
    80 */
    81 struct libfuse_version version;
    82 bool buf_reallocable;
    83};
    84
    85struct fuse_chan {
    86 pthread_mutex_t lock;
    87 int ctr;
    88 int fd;
    89};
    90
    98struct fuse_module {
    99 char *name;
    100 fuse_module_factory_t factory;
    101 struct fuse_module *next;
    102 struct fusemod_so *so;
    103 int ctr;
    104};
    105
    114#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
    115struct fuse_loop_config
    116{
    117 /* verififier that a correct struct was was passed. This is especially
    118 * needed, as versions below (3, 12) were using a public struct
    119 * (now called fuse_loop_config_v1), which was hard to extend with
    120 * additional parameters, without risking that file system implementations
    121 * would not have noticed and might either pass uninitialized members
    122 * or even too small structs.
    123 * fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
    124 * or 1. v2 or even higher version just need to set a value here
    125 * which not conflicting and very unlikely as having been set by
    126 * file system implementation.
    127 */
    128 int version_id;
    129
    134 int clone_fd;
    147
    153 unsigned int max_threads;
    154};
    155#endif
    156
    157/* ----------------------------------------------------------- *
    158 * Channel interface (when using -o clone_fd) *
    159 * ----------------------------------------------------------- */
    160
    167struct fuse_chan *fuse_chan_get(struct fuse_chan *ch);
    168
    174void fuse_chan_put(struct fuse_chan *ch);
    175
    176struct mount_opts *parse_mount_opts(struct fuse_args *args);
    177void destroy_mount_opts(struct mount_opts *mo);
    178void fuse_mount_version(void);
    179unsigned get_max_read(struct mount_opts *o);
    180void fuse_kern_unmount(const char *mountpoint, int fd);
    181int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo);
    182
    183int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
    184 int count);
    185void fuse_free_req(fuse_req_t req);
    186
    187void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
    188
    189int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
    190
    191void fuse_buf_free(struct fuse_buf *buf);
    192
    193int fuse_session_receive_buf_internal(struct fuse_session *se,
    194 struct fuse_buf *buf,
    195 struct fuse_chan *ch);
    196void fuse_session_process_buf_internal(struct fuse_session *se,
    197 const struct fuse_buf *buf,
    198 struct fuse_chan *ch);
    199
    200struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
    201 size_t op_size, void *private_data);
    202int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
    203int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
    204
    210int fuse_loop_cfg_verify(struct fuse_loop_config *config);
    211
    212
    213/*
    214 * This can be changed dynamically on recent kernels through the
    215 * /proc/sys/fs/fuse/max_pages_limit interface.
    216 *
    217 * Older kernels will always use the default value.
    218 */
    219#define FUSE_DEFAULT_MAX_PAGES_LIMIT 256
    220#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
    221
    222/* room needed in buffer to accommodate header */
    223#define FUSE_BUFFER_HEADER_SIZE 0x1000
    224
    struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
    Definition fuse.h:1403
    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:153
    unsigned int max_idle_threads
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2lib_2fuse__log_8c_source.html0000644000175000017500000004561014770234736023620 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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}
    76
    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
    93void fuse_log_close_syslog(void)
    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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2fuse__loop_8c_source.html0000644000175000017500000002650114770234736024006 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2fuse__loop__mt_8c_source.html0000644000175000017500000024321014770234736024643 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 pthread_setname_np(pthread_self(), "fuse_worker");
    136
    137 while (!fuse_session_exited(mt->se)) {
    138 int isforget = 0;
    139 int res;
    140
    141 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    142 res = fuse_session_receive_buf_internal(mt->se, &w->fbuf,
    143 w->ch);
    144 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    145 if (res == -EINTR)
    146 continue;
    147 if (res <= 0) {
    148 if (res < 0) {
    149 fuse_session_exit(mt->se);
    150 mt->error = res;
    151 }
    152 break;
    153 }
    154
    155 pthread_mutex_lock(&mt->lock);
    156 if (mt->exit) {
    157 pthread_mutex_unlock(&mt->lock);
    158 return NULL;
    159 }
    160
    161 /*
    162 * This disgusting hack is needed so that zillions of threads
    163 * are not created on a burst of FORGET messages
    164 */
    165 if (!(w->fbuf.flags & FUSE_BUF_IS_FD)) {
    166 struct fuse_in_header *in = w->fbuf.mem;
    167
    168 if (in->opcode == FUSE_FORGET ||
    169 in->opcode == FUSE_BATCH_FORGET)
    170 isforget = 1;
    171 }
    172
    173 if (!isforget)
    174 mt->numavail--;
    175 if (mt->numavail == 0 && mt->numworker < mt->max_threads)
    176 fuse_loop_start_thread(mt);
    177 pthread_mutex_unlock(&mt->lock);
    178
    179 fuse_session_process_buf_internal(mt->se, &w->fbuf, w->ch);
    180
    181 pthread_mutex_lock(&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 (mt->exit) {
    193 pthread_mutex_unlock(&mt->lock);
    194 return NULL;
    195 }
    196 list_del_worker(w);
    197 mt->numavail--;
    198 mt->numworker--;
    199 pthread_mutex_unlock(&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(&mt->lock);
    208 }
    209
    210 sem_post(&mt->finish);
    211
    212 return NULL;
    213}
    214
    215int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
    216{
    217 sigset_t oldset;
    218 sigset_t newset;
    219 int res;
    220 pthread_attr_t attr;
    221 char *stack_size;
    222
    223 /* Override default stack size
    224 * XXX: This should ideally be a parameter option. It is rather
    225 * well hidden here.
    226 */
    227 pthread_attr_init(&attr);
    228 stack_size = getenv(ENVNAME_THREAD_STACK);
    229 if (stack_size) {
    230 long size;
    231
    232 res = libfuse_strtol(stack_size, &size);
    233 if (res)
    234 fuse_log(FUSE_LOG_ERR, "fuse: invalid stack size: %s\n",
    235 stack_size);
    236 else if (pthread_attr_setstacksize(&attr, size))
    237 fuse_log(FUSE_LOG_ERR, "fuse: could not set stack size: %ld\n",
    238 size);
    239 }
    240
    241 /* Disallow signal reception in worker threads */
    242 sigemptyset(&newset);
    243 sigaddset(&newset, SIGTERM);
    244 sigaddset(&newset, SIGINT);
    245 sigaddset(&newset, SIGHUP);
    246 sigaddset(&newset, SIGQUIT);
    247 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
    248 res = pthread_create(thread_id, &attr, func, arg);
    249 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
    250 pthread_attr_destroy(&attr);
    251 if (res != 0) {
    252 fuse_log(FUSE_LOG_ERR, "fuse: error creating thread: %s\n",
    253 strerror(res));
    254 return -1;
    255 }
    256
    257 return 0;
    258}
    259
    260static int fuse_clone_chan_fd_default(struct fuse_session *se)
    261{
    262 int res;
    263 int clonefd;
    264 uint32_t masterfd;
    265 const char *devname = "/dev/fuse";
    266
    267#ifndef O_CLOEXEC
    268#define O_CLOEXEC 0
    269#endif
    270 clonefd = open(devname, O_RDWR | O_CLOEXEC);
    271 if (clonefd == -1) {
    272 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname,
    273 strerror(errno));
    274 return -1;
    275 }
    276#ifndef O_CLOEXEC
    277 fcntl(clonefd, F_SETFD, FD_CLOEXEC);
    278#endif
    279
    280 masterfd = se->fd;
    281 res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
    282 if (res == -1) {
    283 fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
    284 strerror(errno));
    285 close(clonefd);
    286 return -1;
    287 }
    288 return clonefd;
    289}
    290
    291static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt)
    292{
    293 int clonefd;
    294 struct fuse_session *se = mt->se;
    295 struct fuse_chan *newch;
    296
    297 if (se->io != NULL) {
    298 if (se->io->clone_fd != NULL)
    299 clonefd = se->io->clone_fd(se->fd);
    300 else
    301 return NULL;
    302 } else {
    303 clonefd = fuse_clone_chan_fd_default(se);
    304 }
    305 if (clonefd < 0)
    306 return NULL;
    307
    308 newch = fuse_chan_new(clonefd);
    309 if (newch == NULL)
    310 close(clonefd);
    311
    312 return newch;
    313}
    314
    315static int fuse_loop_start_thread(struct fuse_mt *mt)
    316{
    317 int res;
    318
    319 struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
    320 if (!w) {
    321 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
    322 return -1;
    323 }
    324 memset(w, 0, sizeof(struct fuse_worker));
    325 w->fbuf.mem = NULL;
    326 w->mt = mt;
    327
    328 w->ch = NULL;
    329 if (mt->clone_fd) {
    330 w->ch = fuse_clone_chan(mt);
    331 if(!w->ch) {
    332 /* Don't attempt this again */
    333 fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
    334 "without -o clone_fd.\n");
    335 mt->clone_fd = 0;
    336 }
    337 }
    338
    339 res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
    340 if (res == -1) {
    341 fuse_chan_put(w->ch);
    342 free(w);
    343 return -1;
    344 }
    345 list_add_worker(w, &mt->main);
    346 mt->numavail ++;
    347 mt->numworker ++;
    348
    349 return 0;
    350}
    351
    352static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
    353{
    354 pthread_join(w->thread_id, NULL);
    355 pthread_mutex_lock(&mt->lock);
    356 list_del_worker(w);
    357 pthread_mutex_unlock(&mt->lock);
    358 fuse_buf_free(&w->fbuf);
    359 fuse_chan_put(w->ch);
    360 free(w);
    361}
    362
    363int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
    364FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
    365int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
    366{
    367int err;
    368 struct fuse_mt mt;
    369 struct fuse_worker *w;
    370 int created_config = 0;
    371
    372 if (config) {
    373 err = fuse_loop_cfg_verify(config);
    374 if (err)
    375 return err;
    376 } else {
    377 /* The caller does not care about parameters - use the default */
    378 config = fuse_loop_cfg_create();
    379 created_config = 1;
    380 }
    381
    382
    383 memset(&mt, 0, sizeof(struct fuse_mt));
    384 mt.se = se;
    385 mt.clone_fd = config->clone_fd;
    386 mt.error = 0;
    387 mt.numworker = 0;
    388 mt.numavail = 0;
    389 mt.max_idle = config->max_idle_threads;
    390 mt.max_threads = config->max_threads;
    391 mt.main.thread_id = pthread_self();
    392 mt.main.prev = mt.main.next = &mt.main;
    393 sem_init(&mt.finish, 0, 0);
    394 pthread_mutex_init(&mt.lock, NULL);
    395
    396 pthread_mutex_lock(&mt.lock);
    397 err = fuse_loop_start_thread(&mt);
    398 pthread_mutex_unlock(&mt.lock);
    399 if (!err) {
    400 /* sem_wait() is interruptible */
    401 while (!fuse_session_exited(se))
    402 sem_wait(&mt.finish);
    403
    404 pthread_mutex_lock(&mt.lock);
    405 for (w = mt.main.next; w != &mt.main; w = w->next)
    406 pthread_cancel(w->thread_id);
    407 mt.exit = 1;
    408 pthread_mutex_unlock(&mt.lock);
    409
    410 while (mt.main.next != &mt.main)
    411 fuse_join_worker(&mt, mt.main.next);
    412
    413 err = mt.error;
    414 }
    415
    416 pthread_mutex_destroy(&mt.lock);
    417 sem_destroy(&mt.finish);
    418 if(se->error != 0)
    419 err = se->error;
    421
    422 if (created_config) {
    423 fuse_loop_cfg_destroy(config);
    424 config = NULL;
    425 }
    426
    427 return err;
    428}
    429
    430int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
    431FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
    432int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
    433{
    434 int err;
    435 struct fuse_loop_config *config = NULL;
    436
    437 if (config_v1 != NULL) {
    438 /* convert the given v1 config */
    439 config = fuse_loop_cfg_create();
    440 if (config == NULL)
    441 return ENOMEM;
    442
    443 fuse_loop_cfg_convert(config, config_v1);
    444 }
    445
    446 err = fuse_session_loop_mt_312(se, config);
    447
    448 fuse_loop_cfg_destroy(config);
    449
    450 return err;
    451}
    452
    453
    454int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
    455FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
    456int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
    457{
    458 int err;
    459 struct fuse_loop_config *config = fuse_loop_cfg_create();
    460 if (clone_fd > 0)
    461 fuse_loop_cfg_set_clone_fd(config, clone_fd);
    462 err = fuse_session_loop_mt_312(se, config);
    463
    464 fuse_loop_cfg_destroy(config);
    465
    466 return err;
    467}
    468
    469struct fuse_loop_config *fuse_loop_cfg_create(void)
    470{
    471 struct fuse_loop_config *config = calloc(1, sizeof(*config));
    472 if (config == NULL)
    473 return NULL;
    474
    475 config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
    476 config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
    477 config->max_threads = FUSE_LOOP_MT_DEF_MAX_THREADS;
    478 config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
    479
    480 return config;
    481}
    482
    483void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
    484{
    485 free(config);
    486}
    487
    488int fuse_loop_cfg_verify(struct fuse_loop_config *config)
    489{
    490 if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
    491 return -EINVAL;
    492
    493 return 0;
    494}
    495
    496void fuse_loop_cfg_convert(struct fuse_loop_config *config,
    497 struct fuse_loop_config_v1 *v1_conf)
    498{
    499 fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
    500
    501 fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
    502}
    503
    504void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
    505 unsigned int value)
    506{
    507 if (value > FUSE_LOOP_MT_MAX_THREADS) {
    508 if (value != UINT_MAX)
    509 fuse_log(FUSE_LOG_ERR,
    510 "Ignoring invalid max threads value "
    511 "%u > max (%u).\n", value,
    512 FUSE_LOOP_MT_MAX_THREADS);
    513 return;
    514 }
    515 config->max_idle_threads = value;
    516}
    517
    518void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
    519 unsigned int value)
    520{
    521 config->max_threads = value;
    522}
    523
    524void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
    525 unsigned int value)
    526{
    527 config->clone_fd = value;
    528}
    529
    @ 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:153
    unsigned int max_idle_threads
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2lib_2fuse__lowlevel_8c_source.html0000644000175000017500000240310314770234736024665 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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#include <stdbool.h>
    13#define _GNU_SOURCE
    14
    15#include "fuse_config.h"
    16#include "fuse_i.h"
    17#include "fuse_kernel.h"
    18#include "fuse_opt.h"
    19#include "fuse_misc.h"
    20#include "mount_util.h"
    21#include "util.h"
    22
    23#include <stdio.h>
    24#include <stdlib.h>
    25#include <stddef.h>
    26#include <stdalign.h>
    27#include <string.h>
    28#include <unistd.h>
    29#include <limits.h>
    30#include <errno.h>
    31#include <assert.h>
    32#include <sys/file.h>
    33#include <sys/ioctl.h>
    34
    35#ifndef F_LINUX_SPECIFIC_BASE
    36#define F_LINUX_SPECIFIC_BASE 1024
    37#endif
    38#ifndef F_SETPIPE_SZ
    39#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
    40#endif
    41
    42
    43#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
    44#define OFFSET_MAX 0x7fffffffffffffffLL
    45
    46#define container_of(ptr, type, member) ({ \
    47 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    48 (type *)( (char *)__mptr - offsetof(type,member) );})
    49
    50struct fuse_pollhandle {
    51 uint64_t kh;
    52 struct fuse_session *se;
    53};
    54
    55static size_t pagesize;
    56
    57static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
    58{
    59 pagesize = getpagesize();
    60}
    61
    62static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
    63{
    64 attr->ino = stbuf->st_ino;
    65 attr->mode = stbuf->st_mode;
    66 attr->nlink = stbuf->st_nlink;
    67 attr->uid = stbuf->st_uid;
    68 attr->gid = stbuf->st_gid;
    69 attr->rdev = stbuf->st_rdev;
    70 attr->size = stbuf->st_size;
    71 attr->blksize = stbuf->st_blksize;
    72 attr->blocks = stbuf->st_blocks;
    73 attr->atime = stbuf->st_atime;
    74 attr->mtime = stbuf->st_mtime;
    75 attr->ctime = stbuf->st_ctime;
    76 attr->atimensec = ST_ATIM_NSEC(stbuf);
    77 attr->mtimensec = ST_MTIM_NSEC(stbuf);
    78 attr->ctimensec = ST_CTIM_NSEC(stbuf);
    79}
    80
    81static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
    82{
    83 stbuf->st_mode = attr->mode;
    84 stbuf->st_uid = attr->uid;
    85 stbuf->st_gid = attr->gid;
    86 stbuf->st_size = attr->size;
    87 stbuf->st_atime = attr->atime;
    88 stbuf->st_mtime = attr->mtime;
    89 stbuf->st_ctime = attr->ctime;
    90 ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
    91 ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
    92 ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
    93}
    94
    95static size_t iov_length(const struct iovec *iov, size_t count)
    96{
    97 size_t seg;
    98 size_t ret = 0;
    99
    100 for (seg = 0; seg < count; seg++)
    101 ret += iov[seg].iov_len;
    102 return ret;
    103}
    104
    105static void list_init_req(struct fuse_req *req)
    106{
    107 req->next = req;
    108 req->prev = req;
    109}
    110
    111static void list_del_req(struct fuse_req *req)
    112{
    113 struct fuse_req *prev = req->prev;
    114 struct fuse_req *next = req->next;
    115 prev->next = next;
    116 next->prev = prev;
    117}
    118
    119static void list_add_req(struct fuse_req *req, struct fuse_req *next)
    120{
    121 struct fuse_req *prev = next->prev;
    122 req->next = next;
    123 req->prev = prev;
    124 prev->next = req;
    125 next->prev = req;
    126}
    127
    128static void destroy_req(fuse_req_t req)
    129{
    130 assert(req->ch == NULL);
    131 pthread_mutex_destroy(&req->lock);
    132 free(req);
    133}
    134
    135void fuse_free_req(fuse_req_t req)
    136{
    137 int ctr;
    138 struct fuse_session *se = req->se;
    139
    140 if (se->conn.no_interrupt) {
    141 ctr = --req->ref_cnt;
    142 fuse_chan_put(req->ch);
    143 req->ch = NULL;
    144 } else {
    145 pthread_mutex_lock(&se->lock);
    146 req->u.ni.func = NULL;
    147 req->u.ni.data = NULL;
    148 list_del_req(req);
    149 ctr = --req->ref_cnt;
    150 fuse_chan_put(req->ch);
    151 req->ch = NULL;
    152 pthread_mutex_unlock(&se->lock);
    153 }
    154 if (!ctr)
    155 destroy_req(req);
    156}
    157
    158static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
    159{
    160 struct fuse_req *req;
    161
    162 req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
    163 if (req == NULL) {
    164 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
    165 } else {
    166 req->se = se;
    167 req->ref_cnt = 1;
    168 list_init_req(req);
    169 pthread_mutex_init(&req->lock, NULL);
    170 }
    171
    172 return req;
    173}
    174
    175/* Send data. If *ch* is NULL, send via session master fd */
    176static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
    177 struct iovec *iov, int count)
    178{
    179 struct fuse_out_header *out = iov[0].iov_base;
    180
    181 assert(se != NULL);
    182 out->len = iov_length(iov, count);
    183 if (se->debug) {
    184 if (out->unique == 0) {
    185 fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
    186 out->error, out->len);
    187 } else if (out->error) {
    188 fuse_log(FUSE_LOG_DEBUG,
    189 " unique: %llu, error: %i (%s), outsize: %i\n",
    190 (unsigned long long) out->unique, out->error,
    191 strerror(-out->error), out->len);
    192 } else {
    193 fuse_log(FUSE_LOG_DEBUG,
    194 " unique: %llu, success, outsize: %i\n",
    195 (unsigned long long) out->unique, out->len);
    196 }
    197 }
    198
    199 ssize_t res;
    200 if (se->io != NULL)
    201 /* se->io->writev is never NULL if se->io is not NULL as
    202 specified by fuse_session_custom_io()*/
    203 res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
    204 se->userdata);
    205 else
    206 res = writev(ch ? ch->fd : se->fd, iov, count);
    207
    208 int err = errno;
    209
    210 if (res == -1) {
    211 /* ENOENT means the operation was interrupted */
    212 if (!fuse_session_exited(se) && err != ENOENT)
    213 perror("fuse: writing device");
    214 return -err;
    215 }
    216
    217 return 0;
    218}
    219
    220
    221int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
    222 int count)
    223{
    224 struct fuse_out_header out;
    225
    226#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
    227 const char *str = strerrordesc_np(error * -1);
    228 if ((str == NULL && error != 0) || error > 0) {
    229#else
    230 if (error <= -1000 || error > 0) {
    231#endif
    232 fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
    233 error = -ERANGE;
    234 }
    235
    236 out.unique = req->unique;
    237 out.error = error;
    238
    239 iov[0].iov_base = &out;
    240 iov[0].iov_len = sizeof(struct fuse_out_header);
    241
    242 return fuse_send_msg(req->se, req->ch, iov, count);
    243}
    244
    245static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
    246 int count)
    247{
    248 int res;
    249
    250 res = fuse_send_reply_iov_nofree(req, error, iov, count);
    251 fuse_free_req(req);
    252 return res;
    253}
    254
    255static int send_reply(fuse_req_t req, int error, const void *arg,
    256 size_t argsize)
    257{
    258 struct iovec iov[2];
    259 int count = 1;
    260 if (argsize) {
    261 iov[1].iov_base = (void *) arg;
    262 iov[1].iov_len = argsize;
    263 count++;
    264 }
    265 return send_reply_iov(req, error, iov, count);
    266}
    267
    268int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
    269{
    270 int res;
    271 struct iovec *padded_iov;
    272
    273 padded_iov = malloc((count + 1) * sizeof(struct iovec));
    274 if (padded_iov == NULL)
    275 return fuse_reply_err(req, ENOMEM);
    276
    277 memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
    278 count++;
    279
    280 res = send_reply_iov(req, 0, padded_iov, count);
    281 free(padded_iov);
    282
    283 return res;
    284}
    285
    286
    287/* `buf` is allowed to be empty so that the proper size may be
    288 allocated by the caller */
    289size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
    290 const char *name, const struct stat *stbuf, off_t off)
    291{
    292 (void)req;
    293 size_t namelen;
    294 size_t entlen;
    295 size_t entlen_padded;
    296 struct fuse_dirent *dirent;
    297
    298 namelen = strlen(name);
    299 entlen = FUSE_NAME_OFFSET + namelen;
    300 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
    301
    302 if ((buf == NULL) || (entlen_padded > bufsize))
    303 return entlen_padded;
    304
    305 dirent = (struct fuse_dirent*) buf;
    306 dirent->ino = stbuf->st_ino;
    307 dirent->off = off;
    308 dirent->namelen = namelen;
    309 dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
    310 memcpy(dirent->name, name, namelen);
    311 memset(dirent->name + namelen, 0, entlen_padded - entlen);
    312
    313 return entlen_padded;
    314}
    315
    316static void convert_statfs(const struct statvfs *stbuf,
    317 struct fuse_kstatfs *kstatfs)
    318{
    319 kstatfs->bsize = stbuf->f_bsize;
    320 kstatfs->frsize = stbuf->f_frsize;
    321 kstatfs->blocks = stbuf->f_blocks;
    322 kstatfs->bfree = stbuf->f_bfree;
    323 kstatfs->bavail = stbuf->f_bavail;
    324 kstatfs->files = stbuf->f_files;
    325 kstatfs->ffree = stbuf->f_ffree;
    326 kstatfs->namelen = stbuf->f_namemax;
    327}
    328
    329static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
    330{
    331 return send_reply(req, 0, arg, argsize);
    332}
    333
    334int fuse_reply_err(fuse_req_t req, int err)
    335{
    336 return send_reply(req, -err, NULL, 0);
    337}
    338
    340{
    341 fuse_free_req(req);
    342}
    343
    344static unsigned long calc_timeout_sec(double t)
    345{
    346 if (t > (double) ULONG_MAX)
    347 return ULONG_MAX;
    348 else if (t < 0.0)
    349 return 0;
    350 else
    351 return (unsigned long) t;
    352}
    353
    354static unsigned int calc_timeout_nsec(double t)
    355{
    356 double f = t - (double) calc_timeout_sec(t);
    357 if (f < 0.0)
    358 return 0;
    359 else if (f >= 0.999999999)
    360 return 999999999;
    361 else
    362 return (unsigned int) (f * 1.0e9);
    363}
    364
    365static void fill_entry(struct fuse_entry_out *arg,
    366 const struct fuse_entry_param *e)
    367{
    368 arg->nodeid = e->ino;
    369 arg->generation = e->generation;
    370 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
    371 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
    372 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
    373 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
    374 convert_stat(&e->attr, &arg->attr);
    375}
    376
    377/* `buf` is allowed to be empty so that the proper size may be
    378 allocated by the caller */
    379size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
    380 const char *name,
    381 const struct fuse_entry_param *e, off_t off)
    382{
    383 (void)req;
    384 size_t namelen;
    385 size_t entlen;
    386 size_t entlen_padded;
    387
    388 namelen = strlen(name);
    389 entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
    390 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
    391 if ((buf == NULL) || (entlen_padded > bufsize))
    392 return entlen_padded;
    393
    394 struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
    395 memset(&dp->entry_out, 0, sizeof(dp->entry_out));
    396 fill_entry(&dp->entry_out, e);
    397
    398 struct fuse_dirent *dirent = &dp->dirent;
    399 dirent->ino = e->attr.st_ino;
    400 dirent->off = off;
    401 dirent->namelen = namelen;
    402 dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
    403 memcpy(dirent->name, name, namelen);
    404 memset(dirent->name + namelen, 0, entlen_padded - entlen);
    405
    406 return entlen_padded;
    407}
    408
    409static void fill_open(struct fuse_open_out *arg,
    410 const struct fuse_file_info *f)
    411{
    412 arg->fh = f->fh;
    413 if (f->backing_id > 0) {
    414 arg->backing_id = f->backing_id;
    415 arg->open_flags |= FOPEN_PASSTHROUGH;
    416 }
    417 if (f->direct_io)
    418 arg->open_flags |= FOPEN_DIRECT_IO;
    419 if (f->keep_cache)
    420 arg->open_flags |= FOPEN_KEEP_CACHE;
    421 if (f->cache_readdir)
    422 arg->open_flags |= FOPEN_CACHE_DIR;
    423 if (f->nonseekable)
    424 arg->open_flags |= FOPEN_NONSEEKABLE;
    425 if (f->noflush)
    426 arg->open_flags |= FOPEN_NOFLUSH;
    428 arg->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
    429}
    430
    431int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
    432{
    433 struct fuse_entry_out arg;
    434 size_t size = req->se->conn.proto_minor < 9 ?
    435 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
    436
    437 /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
    438 negative entry */
    439 if (!e->ino && req->se->conn.proto_minor < 4)
    440 return fuse_reply_err(req, ENOENT);
    441
    442 memset(&arg, 0, sizeof(arg));
    443 fill_entry(&arg, e);
    444 return send_reply_ok(req, &arg, size);
    445}
    446
    447int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
    448 const struct fuse_file_info *f)
    449{
    450 alignas(uint64_t) char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
    451 size_t entrysize = req->se->conn.proto_minor < 9 ?
    452 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
    453 struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
    454 struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
    455
    456 memset(buf, 0, sizeof(buf));
    457 fill_entry(earg, e);
    458 fill_open(oarg, f);
    459 return send_reply_ok(req, buf,
    460 entrysize + sizeof(struct fuse_open_out));
    461}
    462
    463int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
    464 double attr_timeout)
    465{
    466 struct fuse_attr_out arg;
    467 size_t size = req->se->conn.proto_minor < 9 ?
    468 FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
    469
    470 memset(&arg, 0, sizeof(arg));
    471 arg.attr_valid = calc_timeout_sec(attr_timeout);
    472 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
    473 convert_stat(attr, &arg.attr);
    474
    475 return send_reply_ok(req, &arg, size);
    476}
    477
    478int fuse_reply_readlink(fuse_req_t req, const char *linkname)
    479{
    480 return send_reply_ok(req, linkname, strlen(linkname));
    481}
    482
    483int fuse_passthrough_open(fuse_req_t req, int fd)
    484{
    485 struct fuse_backing_map map = { .fd = fd };
    486 int ret;
    487
    488 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map);
    489 if (ret <= 0) {
    490 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno));
    491 return 0;
    492 }
    493
    494 return ret;
    495}
    496
    497int fuse_passthrough_close(fuse_req_t req, int backing_id)
    498{
    499 int ret;
    500
    501 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id);
    502 if (ret < 0)
    503 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno));
    504
    505 return ret;
    506}
    507
    508int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
    509{
    510 struct fuse_open_out arg;
    511
    512 memset(&arg, 0, sizeof(arg));
    513 fill_open(&arg, f);
    514 return send_reply_ok(req, &arg, sizeof(arg));
    515}
    516
    517int fuse_reply_write(fuse_req_t req, size_t count)
    518{
    519 struct fuse_write_out arg;
    520
    521 memset(&arg, 0, sizeof(arg));
    522 arg.size = count;
    523
    524 return send_reply_ok(req, &arg, sizeof(arg));
    525}
    526
    527int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
    528{
    529 return send_reply_ok(req, buf, size);
    530}
    531
    532static int fuse_send_data_iov_fallback(struct fuse_session *se,
    533 struct fuse_chan *ch,
    534 struct iovec *iov, int iov_count,
    535 struct fuse_bufvec *buf,
    536 size_t len)
    537{
    538 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
    539 void *mbuf;
    540 int res;
    541
    542 /* Optimize common case */
    543 if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
    544 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
    545 /* FIXME: also avoid memory copy if there are multiple buffers
    546 but none of them contain an fd */
    547
    548 iov[iov_count].iov_base = buf->buf[0].mem;
    549 iov[iov_count].iov_len = len;
    550 iov_count++;
    551 return fuse_send_msg(se, ch, iov, iov_count);
    552 }
    553
    554 res = posix_memalign(&mbuf, pagesize, len);
    555 if (res != 0)
    556 return res;
    557
    558 mem_buf.buf[0].mem = mbuf;
    559 res = fuse_buf_copy(&mem_buf, buf, 0);
    560 if (res < 0) {
    561 free(mbuf);
    562 return -res;
    563 }
    564 len = res;
    565
    566 iov[iov_count].iov_base = mbuf;
    567 iov[iov_count].iov_len = len;
    568 iov_count++;
    569 res = fuse_send_msg(se, ch, iov, iov_count);
    570 free(mbuf);
    571
    572 return res;
    573}
    574
    575struct fuse_ll_pipe {
    576 size_t size;
    577 int can_grow;
    578 int pipe[2];
    579};
    580
    581static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
    582{
    583 close(llp->pipe[0]);
    584 close(llp->pipe[1]);
    585 free(llp);
    586}
    587
    588#ifdef HAVE_SPLICE
    589#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC)
    590static int fuse_pipe(int fds[2])
    591{
    592 int rv = pipe(fds);
    593
    594 if (rv == -1)
    595 return rv;
    596
    597 if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
    598 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 ||
    599 fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
    600 fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
    601 close(fds[0]);
    602 close(fds[1]);
    603 rv = -1;
    604 }
    605 return rv;
    606}
    607#else
    608static int fuse_pipe(int fds[2])
    609{
    610 return pipe2(fds, O_CLOEXEC | O_NONBLOCK);
    611}
    612#endif
    613
    614static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
    615{
    616 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
    617 if (llp == NULL) {
    618 int res;
    619
    620 llp = malloc(sizeof(struct fuse_ll_pipe));
    621 if (llp == NULL)
    622 return NULL;
    623
    624 res = fuse_pipe(llp->pipe);
    625 if (res == -1) {
    626 free(llp);
    627 return NULL;
    628 }
    629
    630 /*
    631 *the default size is 16 pages on linux
    632 */
    633 llp->size = pagesize * 16;
    634 llp->can_grow = 1;
    635
    636 pthread_setspecific(se->pipe_key, llp);
    637 }
    638
    639 return llp;
    640}
    641#endif
    642
    643static void fuse_ll_clear_pipe(struct fuse_session *se)
    644{
    645 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
    646 if (llp) {
    647 pthread_setspecific(se->pipe_key, NULL);
    648 fuse_ll_pipe_free(llp);
    649 }
    650}
    651
    652#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
    653static int read_back(int fd, char *buf, size_t len)
    654{
    655 int res;
    656
    657 res = read(fd, buf, len);
    658 if (res == -1) {
    659 fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
    660 return -EIO;
    661 }
    662 if (res != len) {
    663 fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
    664 return -EIO;
    665 }
    666 return 0;
    667}
    668
    669static int grow_pipe_to_max(int pipefd)
    670{
    671 int res;
    672 long max;
    673 long maxfd;
    674 char buf[32];
    675
    676 maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY);
    677 if (maxfd < 0)
    678 return -errno;
    679
    680 res = read(maxfd, buf, sizeof(buf) - 1);
    681 if (res < 0) {
    682 int saved_errno;
    683
    684 saved_errno = errno;
    685 close(maxfd);
    686 return -saved_errno;
    687 }
    688 close(maxfd);
    689 buf[res] = '\0';
    690
    691 res = libfuse_strtol(buf, &max);
    692 if (res)
    693 return res;
    694 res = fcntl(pipefd, F_SETPIPE_SZ, max);
    695 if (res < 0)
    696 return -errno;
    697 return max;
    698}
    699
    700static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
    701 struct iovec *iov, int iov_count,
    702 struct fuse_bufvec *buf, unsigned int flags)
    703{
    704 int res;
    705 size_t len = fuse_buf_size(buf);
    706 struct fuse_out_header *out = iov[0].iov_base;
    707 struct fuse_ll_pipe *llp;
    708 int splice_flags;
    709 size_t pipesize;
    710 size_t total_buf_size;
    711 size_t idx;
    712 size_t headerlen;
    713 struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
    714
    715 if (se->broken_splice_nonblock)
    716 goto fallback;
    717
    718 if (flags & FUSE_BUF_NO_SPLICE)
    719 goto fallback;
    720
    721 total_buf_size = 0;
    722 for (idx = buf->idx; idx < buf->count; idx++) {
    723 total_buf_size += buf->buf[idx].size;
    724 if (idx == buf->idx)
    725 total_buf_size -= buf->off;
    726 }
    727 if (total_buf_size < 2 * pagesize)
    728 goto fallback;
    729
    730 if (se->conn.proto_minor < 14 ||
    731 !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE))
    732 goto fallback;
    733
    734 llp = fuse_ll_get_pipe(se);
    735 if (llp == NULL)
    736 goto fallback;
    737
    738
    739 headerlen = iov_length(iov, iov_count);
    740
    741 out->len = headerlen + len;
    742
    743 /*
    744 * Heuristic for the required pipe size, does not work if the
    745 * source contains less than page size fragments
    746 */
    747 pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
    748
    749 if (llp->size < pipesize) {
    750 if (llp->can_grow) {
    751 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
    752 if (res == -1) {
    753 res = grow_pipe_to_max(llp->pipe[0]);
    754 if (res > 0)
    755 llp->size = res;
    756 llp->can_grow = 0;
    757 goto fallback;
    758 }
    759 llp->size = res;
    760 }
    761 if (llp->size < pipesize)
    762 goto fallback;
    763 }
    764
    765
    766 res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
    767 if (res == -1)
    768 goto fallback;
    769
    770 if (res != headerlen) {
    771 res = -EIO;
    772 fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
    773 headerlen);
    774 goto clear_pipe;
    775 }
    776
    777 pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
    778 pipe_buf.buf[0].fd = llp->pipe[1];
    779
    780 res = fuse_buf_copy(&pipe_buf, buf,
    782 if (res < 0) {
    783 if (res == -EAGAIN || res == -EINVAL) {
    784 /*
    785 * Should only get EAGAIN on kernels with
    786 * broken SPLICE_F_NONBLOCK support (<=
    787 * 2.6.35) where this error or a short read is
    788 * returned even if the pipe itself is not
    789 * full
    790 *
    791 * EINVAL might mean that splice can't handle
    792 * this combination of input and output.
    793 */
    794 if (res == -EAGAIN)
    795 se->broken_splice_nonblock = 1;
    796
    797 pthread_setspecific(se->pipe_key, NULL);
    798 fuse_ll_pipe_free(llp);
    799 goto fallback;
    800 }
    801 res = -res;
    802 goto clear_pipe;
    803 }
    804
    805 if (res != 0 && res < len) {
    806 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
    807 void *mbuf;
    808 size_t now_len = res;
    809 /*
    810 * For regular files a short count is either
    811 * 1) due to EOF, or
    812 * 2) because of broken SPLICE_F_NONBLOCK (see above)
    813 *
    814 * For other inputs it's possible that we overflowed
    815 * the pipe because of small buffer fragments.
    816 */
    817
    818 res = posix_memalign(&mbuf, pagesize, len);
    819 if (res != 0)
    820 goto clear_pipe;
    821
    822 mem_buf.buf[0].mem = mbuf;
    823 mem_buf.off = now_len;
    824 res = fuse_buf_copy(&mem_buf, buf, 0);
    825 if (res > 0) {
    826 char *tmpbuf;
    827 size_t extra_len = res;
    828 /*
    829 * Trickiest case: got more data. Need to get
    830 * back the data from the pipe and then fall
    831 * back to regular write.
    832 */
    833 tmpbuf = malloc(headerlen);
    834 if (tmpbuf == NULL) {
    835 free(mbuf);
    836 res = ENOMEM;
    837 goto clear_pipe;
    838 }
    839 res = read_back(llp->pipe[0], tmpbuf, headerlen);
    840 free(tmpbuf);
    841 if (res != 0) {
    842 free(mbuf);
    843 goto clear_pipe;
    844 }
    845 res = read_back(llp->pipe[0], mbuf, now_len);
    846 if (res != 0) {
    847 free(mbuf);
    848 goto clear_pipe;
    849 }
    850 len = now_len + extra_len;
    851 iov[iov_count].iov_base = mbuf;
    852 iov[iov_count].iov_len = len;
    853 iov_count++;
    854 res = fuse_send_msg(se, ch, iov, iov_count);
    855 free(mbuf);
    856 return res;
    857 }
    858 free(mbuf);
    859 res = now_len;
    860 }
    861 len = res;
    862 out->len = headerlen + len;
    863
    864 if (se->debug) {
    865 fuse_log(FUSE_LOG_DEBUG,
    866 " unique: %llu, success, outsize: %i (splice)\n",
    867 (unsigned long long) out->unique, out->len);
    868 }
    869
    870 splice_flags = 0;
    871 if ((flags & FUSE_BUF_SPLICE_MOVE) &&
    872 (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE))
    873 splice_flags |= SPLICE_F_MOVE;
    874
    875 if (se->io != NULL && se->io->splice_send != NULL) {
    876 res = se->io->splice_send(llp->pipe[0], NULL,
    877 ch ? ch->fd : se->fd, NULL, out->len,
    878 splice_flags, se->userdata);
    879 } else {
    880 res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
    881 out->len, splice_flags);
    882 }
    883 if (res == -1) {
    884 res = -errno;
    885 perror("fuse: splice from pipe");
    886 goto clear_pipe;
    887 }
    888 if (res != out->len) {
    889 res = -EIO;
    890 fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
    891 res, out->len);
    892 goto clear_pipe;
    893 }
    894 return 0;
    895
    896clear_pipe:
    897 fuse_ll_clear_pipe(se);
    898 return res;
    899
    900fallback:
    901 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
    902}
    903#else
    904static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
    905 struct iovec *iov, int iov_count,
    906 struct fuse_bufvec *buf, unsigned int flags)
    907{
    908 size_t len = fuse_buf_size(buf);
    909 (void) flags;
    910
    911 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
    912}
    913#endif
    914
    915int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv,
    916 enum fuse_buf_copy_flags flags)
    917{
    918 struct iovec iov[2];
    919 struct fuse_out_header out;
    920 int res;
    921
    922 iov[0].iov_base = &out;
    923 iov[0].iov_len = sizeof(struct fuse_out_header);
    924
    925 out.unique = req->unique;
    926 out.error = 0;
    927
    928 res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags);
    929 if (res <= 0) {
    930 fuse_free_req(req);
    931 return res;
    932 } else {
    933 return fuse_reply_err(req, res);
    934 }
    935}
    936
    937int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
    938{
    939 struct fuse_statfs_out arg;
    940 size_t size = req->se->conn.proto_minor < 4 ?
    941 FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
    942
    943 memset(&arg, 0, sizeof(arg));
    944 convert_statfs(stbuf, &arg.st);
    945
    946 return send_reply_ok(req, &arg, size);
    947}
    948
    949int fuse_reply_xattr(fuse_req_t req, size_t count)
    950{
    951 struct fuse_getxattr_out arg;
    952
    953 memset(&arg, 0, sizeof(arg));
    954 arg.size = count;
    955
    956 return send_reply_ok(req, &arg, sizeof(arg));
    957}
    958
    959int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
    960{
    961 struct fuse_lk_out arg;
    962
    963 memset(&arg, 0, sizeof(arg));
    964 arg.lk.type = lock->l_type;
    965 if (lock->l_type != F_UNLCK) {
    966 arg.lk.start = lock->l_start;
    967 if (lock->l_len == 0)
    968 arg.lk.end = OFFSET_MAX;
    969 else
    970 arg.lk.end = lock->l_start + lock->l_len - 1;
    971 }
    972 arg.lk.pid = lock->l_pid;
    973 return send_reply_ok(req, &arg, sizeof(arg));
    974}
    975
    976int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
    977{
    978 struct fuse_bmap_out arg;
    979
    980 memset(&arg, 0, sizeof(arg));
    981 arg.block = idx;
    982
    983 return send_reply_ok(req, &arg, sizeof(arg));
    984}
    985
    986static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
    987 size_t count)
    988{
    989 struct fuse_ioctl_iovec *fiov;
    990 size_t i;
    991
    992 fiov = malloc(sizeof(fiov[0]) * count);
    993 if (!fiov)
    994 return NULL;
    995
    996 for (i = 0; i < count; i++) {
    997 fiov[i].base = (uintptr_t) iov[i].iov_base;
    998 fiov[i].len = iov[i].iov_len;
    999 }
    1000
    1001 return fiov;
    1002}
    1003
    1005 const struct iovec *in_iov, size_t in_count,
    1006 const struct iovec *out_iov, size_t out_count)
    1007{
    1008 struct fuse_ioctl_out arg;
    1009 struct fuse_ioctl_iovec *in_fiov = NULL;
    1010 struct fuse_ioctl_iovec *out_fiov = NULL;
    1011 struct iovec iov[4];
    1012 size_t count = 1;
    1013 int res;
    1014
    1015 memset(&arg, 0, sizeof(arg));
    1016 arg.flags |= FUSE_IOCTL_RETRY;
    1017 arg.in_iovs = in_count;
    1018 arg.out_iovs = out_count;
    1019 iov[count].iov_base = &arg;
    1020 iov[count].iov_len = sizeof(arg);
    1021 count++;
    1022
    1023 if (req->se->conn.proto_minor < 16) {
    1024 if (in_count) {
    1025 iov[count].iov_base = (void *)in_iov;
    1026 iov[count].iov_len = sizeof(in_iov[0]) * in_count;
    1027 count++;
    1028 }
    1029
    1030 if (out_count) {
    1031 iov[count].iov_base = (void *)out_iov;
    1032 iov[count].iov_len = sizeof(out_iov[0]) * out_count;
    1033 count++;
    1034 }
    1035 } else {
    1036 /* Can't handle non-compat 64bit ioctls on 32bit */
    1037 if (sizeof(void *) == 4 && req->ioctl_64bit) {
    1038 res = fuse_reply_err(req, EINVAL);
    1039 goto out;
    1040 }
    1041
    1042 if (in_count) {
    1043 in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
    1044 if (!in_fiov)
    1045 goto enomem;
    1046
    1047 iov[count].iov_base = (void *)in_fiov;
    1048 iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
    1049 count++;
    1050 }
    1051 if (out_count) {
    1052 out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
    1053 if (!out_fiov)
    1054 goto enomem;
    1055
    1056 iov[count].iov_base = (void *)out_fiov;
    1057 iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
    1058 count++;
    1059 }
    1060 }
    1061
    1062 res = send_reply_iov(req, 0, iov, count);
    1063out:
    1064 free(in_fiov);
    1065 free(out_fiov);
    1066
    1067 return res;
    1068
    1069enomem:
    1070 res = fuse_reply_err(req, ENOMEM);
    1071 goto out;
    1072}
    1073
    1074int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
    1075{
    1076 struct fuse_ioctl_out arg;
    1077 struct iovec iov[3];
    1078 size_t count = 1;
    1079
    1080 memset(&arg, 0, sizeof(arg));
    1081 arg.result = result;
    1082 iov[count].iov_base = &arg;
    1083 iov[count].iov_len = sizeof(arg);
    1084 count++;
    1085
    1086 if (size) {
    1087 iov[count].iov_base = (char *) buf;
    1088 iov[count].iov_len = size;
    1089 count++;
    1090 }
    1091
    1092 return send_reply_iov(req, 0, iov, count);
    1093}
    1094
    1095int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
    1096 int count)
    1097{
    1098 struct iovec *padded_iov;
    1099 struct fuse_ioctl_out arg;
    1100 int res;
    1101
    1102 padded_iov = malloc((count + 2) * sizeof(struct iovec));
    1103 if (padded_iov == NULL)
    1104 return fuse_reply_err(req, ENOMEM);
    1105
    1106 memset(&arg, 0, sizeof(arg));
    1107 arg.result = result;
    1108 padded_iov[1].iov_base = &arg;
    1109 padded_iov[1].iov_len = sizeof(arg);
    1110
    1111 memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
    1112
    1113 res = send_reply_iov(req, 0, padded_iov, count + 2);
    1114 free(padded_iov);
    1115
    1116 return res;
    1117}
    1118
    1119int fuse_reply_poll(fuse_req_t req, unsigned revents)
    1120{
    1121 struct fuse_poll_out arg;
    1122
    1123 memset(&arg, 0, sizeof(arg));
    1124 arg.revents = revents;
    1125
    1126 return send_reply_ok(req, &arg, sizeof(arg));
    1127}
    1128
    1129int fuse_reply_lseek(fuse_req_t req, off_t off)
    1130{
    1131 struct fuse_lseek_out arg;
    1132
    1133 memset(&arg, 0, sizeof(arg));
    1134 arg.offset = off;
    1135
    1136 return send_reply_ok(req, &arg, sizeof(arg));
    1137}
    1138
    1139static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1140{
    1141 char *name = (char *) inarg;
    1142
    1143 if (req->se->op.lookup)
    1144 req->se->op.lookup(req, nodeid, name);
    1145 else
    1146 fuse_reply_err(req, ENOSYS);
    1147}
    1148
    1149static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1150{
    1151 struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
    1152
    1153 if (req->se->op.forget)
    1154 req->se->op.forget(req, nodeid, arg->nlookup);
    1155 else
    1156 fuse_reply_none(req);
    1157}
    1158
    1159static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
    1160 const void *inarg)
    1161{
    1162 struct fuse_batch_forget_in *arg = (void *) inarg;
    1163 struct fuse_forget_one *param = (void *) PARAM(arg);
    1164 unsigned int i;
    1165
    1166 (void) nodeid;
    1167
    1168 if (req->se->op.forget_multi) {
    1169 req->se->op.forget_multi(req, arg->count,
    1170 (struct fuse_forget_data *) param);
    1171 } else if (req->se->op.forget) {
    1172 for (i = 0; i < arg->count; i++) {
    1173 struct fuse_forget_one *forget = &param[i];
    1174 struct fuse_req *dummy_req;
    1175
    1176 dummy_req = fuse_ll_alloc_req(req->se);
    1177 if (dummy_req == NULL)
    1178 break;
    1179
    1180 dummy_req->unique = req->unique;
    1181 dummy_req->ctx = req->ctx;
    1182 dummy_req->ch = NULL;
    1183
    1184 req->se->op.forget(dummy_req, forget->nodeid,
    1185 forget->nlookup);
    1186 }
    1187 fuse_reply_none(req);
    1188 } else {
    1189 fuse_reply_none(req);
    1190 }
    1191}
    1192
    1193static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1194{
    1195 struct fuse_file_info *fip = NULL;
    1196 struct fuse_file_info fi;
    1197
    1198 if (req->se->conn.proto_minor >= 9) {
    1199 struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
    1200
    1201 if (arg->getattr_flags & FUSE_GETATTR_FH) {
    1202 memset(&fi, 0, sizeof(fi));
    1203 fi.fh = arg->fh;
    1204 fip = &fi;
    1205 }
    1206 }
    1207
    1208 if (req->se->op.getattr)
    1209 req->se->op.getattr(req, nodeid, fip);
    1210 else
    1211 fuse_reply_err(req, ENOSYS);
    1212}
    1213
    1214static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1215{
    1216 struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
    1217
    1218 if (req->se->op.setattr) {
    1219 struct fuse_file_info *fi = NULL;
    1220 struct fuse_file_info fi_store;
    1221 struct stat stbuf;
    1222 memset(&stbuf, 0, sizeof(stbuf));
    1223 convert_attr(arg, &stbuf);
    1224 if (arg->valid & FATTR_FH) {
    1225 arg->valid &= ~FATTR_FH;
    1226 memset(&fi_store, 0, sizeof(fi_store));
    1227 fi = &fi_store;
    1228 fi->fh = arg->fh;
    1229 }
    1230 arg->valid &=
    1231 FUSE_SET_ATTR_MODE |
    1232 FUSE_SET_ATTR_UID |
    1233 FUSE_SET_ATTR_GID |
    1234 FUSE_SET_ATTR_SIZE |
    1235 FUSE_SET_ATTR_ATIME |
    1236 FUSE_SET_ATTR_MTIME |
    1237 FUSE_SET_ATTR_KILL_SUID |
    1238 FUSE_SET_ATTR_KILL_SGID |
    1239 FUSE_SET_ATTR_ATIME_NOW |
    1240 FUSE_SET_ATTR_MTIME_NOW |
    1241 FUSE_SET_ATTR_CTIME;
    1242
    1243 req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
    1244 } else
    1245 fuse_reply_err(req, ENOSYS);
    1246}
    1247
    1248static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1249{
    1250 struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
    1251
    1252 if (req->se->op.access)
    1253 req->se->op.access(req, nodeid, arg->mask);
    1254 else
    1255 fuse_reply_err(req, ENOSYS);
    1256}
    1257
    1258static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1259{
    1260 (void) inarg;
    1261
    1262 if (req->se->op.readlink)
    1263 req->se->op.readlink(req, nodeid);
    1264 else
    1265 fuse_reply_err(req, ENOSYS);
    1266}
    1267
    1268static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1269{
    1270 struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
    1271 char *name = PARAM(arg);
    1272
    1273 if (req->se->conn.proto_minor >= 12)
    1274 req->ctx.umask = arg->umask;
    1275 else
    1276 name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
    1277
    1278 if (req->se->op.mknod)
    1279 req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
    1280 else
    1281 fuse_reply_err(req, ENOSYS);
    1282}
    1283
    1284static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1285{
    1286 struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
    1287
    1288 if (req->se->conn.proto_minor >= 12)
    1289 req->ctx.umask = arg->umask;
    1290
    1291 if (req->se->op.mkdir)
    1292 req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
    1293 else
    1294 fuse_reply_err(req, ENOSYS);
    1295}
    1296
    1297static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1298{
    1299 char *name = (char *) inarg;
    1300
    1301 if (req->se->op.unlink)
    1302 req->se->op.unlink(req, nodeid, name);
    1303 else
    1304 fuse_reply_err(req, ENOSYS);
    1305}
    1306
    1307static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1308{
    1309 char *name = (char *) inarg;
    1310
    1311 if (req->se->op.rmdir)
    1312 req->se->op.rmdir(req, nodeid, name);
    1313 else
    1314 fuse_reply_err(req, ENOSYS);
    1315}
    1316
    1317static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1318{
    1319 char *name = (char *) inarg;
    1320 char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
    1321
    1322 if (req->se->op.symlink)
    1323 req->se->op.symlink(req, linkname, nodeid, name);
    1324 else
    1325 fuse_reply_err(req, ENOSYS);
    1326}
    1327
    1328static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1329{
    1330 struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
    1331 char *oldname = PARAM(arg);
    1332 char *newname = oldname + strlen(oldname) + 1;
    1333
    1334 if (req->se->op.rename)
    1335 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
    1336 0);
    1337 else
    1338 fuse_reply_err(req, ENOSYS);
    1339}
    1340
    1341static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1342{
    1343 struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg;
    1344 char *oldname = PARAM(arg);
    1345 char *newname = oldname + strlen(oldname) + 1;
    1346
    1347 if (req->se->op.rename)
    1348 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
    1349 arg->flags);
    1350 else
    1351 fuse_reply_err(req, ENOSYS);
    1352}
    1353
    1354static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1355{
    1356 struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
    1357
    1358 if (req->se->op.link)
    1359 req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
    1360 else
    1361 fuse_reply_err(req, ENOSYS);
    1362}
    1363
    1364static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1365{
    1366 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
    1367
    1368 if (req->se->op.tmpfile) {
    1369 struct fuse_file_info fi;
    1370
    1371 memset(&fi, 0, sizeof(fi));
    1372 fi.flags = arg->flags;
    1373
    1374 if (req->se->conn.proto_minor >= 12)
    1375 req->ctx.umask = arg->umask;
    1376
    1377 req->se->op.tmpfile(req, nodeid, arg->mode, &fi);
    1378 } else
    1379 fuse_reply_err(req, ENOSYS);
    1380}
    1381
    1382static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1383{
    1384 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
    1385
    1386 if (req->se->op.create) {
    1387 struct fuse_file_info fi;
    1388 char *name = PARAM(arg);
    1389
    1390 memset(&fi, 0, sizeof(fi));
    1391 fi.flags = arg->flags;
    1392
    1393 if (req->se->conn.proto_minor >= 12)
    1394 req->ctx.umask = arg->umask;
    1395 else
    1396 name = (char *) inarg + sizeof(struct fuse_open_in);
    1397
    1398 req->se->op.create(req, nodeid, name, arg->mode, &fi);
    1399 } else
    1400 fuse_reply_err(req, ENOSYS);
    1401}
    1402
    1403static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1404{
    1405 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
    1406 struct fuse_file_info fi;
    1407
    1408 memset(&fi, 0, sizeof(fi));
    1409 fi.flags = arg->flags;
    1410
    1411 if (req->se->op.open)
    1412 req->se->op.open(req, nodeid, &fi);
    1413 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT)
    1414 fuse_reply_err(req, ENOSYS);
    1415 else
    1416 fuse_reply_open(req, &fi);
    1417}
    1418
    1419static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1420{
    1421 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1422
    1423 if (req->se->op.read) {
    1424 struct fuse_file_info fi;
    1425
    1426 memset(&fi, 0, sizeof(fi));
    1427 fi.fh = arg->fh;
    1428 if (req->se->conn.proto_minor >= 9) {
    1429 fi.lock_owner = arg->lock_owner;
    1430 fi.flags = arg->flags;
    1431 }
    1432 req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
    1433 } else
    1434 fuse_reply_err(req, ENOSYS);
    1435}
    1436
    1437static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1438{
    1439 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
    1440 struct fuse_file_info fi;
    1441 char *param;
    1442
    1443 memset(&fi, 0, sizeof(fi));
    1444 fi.fh = arg->fh;
    1445 fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
    1446
    1447 if (req->se->conn.proto_minor < 9) {
    1448 param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
    1449 } else {
    1450 fi.lock_owner = arg->lock_owner;
    1451 fi.flags = arg->flags;
    1452 param = PARAM(arg);
    1453 }
    1454
    1455 if (req->se->op.write)
    1456 req->se->op.write(req, nodeid, param, arg->size,
    1457 arg->offset, &fi);
    1458 else
    1459 fuse_reply_err(req, ENOSYS);
    1460}
    1461
    1462static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
    1463 const struct fuse_buf *ibuf)
    1464{
    1465 struct fuse_session *se = req->se;
    1466 struct fuse_bufvec bufv = {
    1467 .buf[0] = *ibuf,
    1468 .count = 1,
    1469 };
    1470 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
    1471 struct fuse_file_info fi;
    1472
    1473 memset(&fi, 0, sizeof(fi));
    1474 fi.fh = arg->fh;
    1475 fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
    1476
    1477 if (se->conn.proto_minor < 9) {
    1478 bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
    1479 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    1480 FUSE_COMPAT_WRITE_IN_SIZE;
    1481 assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
    1482 } else {
    1483 fi.lock_owner = arg->lock_owner;
    1484 fi.flags = arg->flags;
    1485 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
    1486 bufv.buf[0].mem = PARAM(arg);
    1487
    1488 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    1489 sizeof(struct fuse_write_in);
    1490 }
    1491 if (bufv.buf[0].size < arg->size) {
    1492 fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n");
    1493 fuse_reply_err(req, EIO);
    1494 goto out;
    1495 }
    1496 bufv.buf[0].size = arg->size;
    1497
    1498 se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
    1499
    1500out:
    1501 /* Need to reset the pipe if ->write_buf() didn't consume all data */
    1502 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
    1503 fuse_ll_clear_pipe(se);
    1504}
    1505
    1506static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1507{
    1508 struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
    1509 struct fuse_file_info fi;
    1510
    1511 memset(&fi, 0, sizeof(fi));
    1512 fi.fh = arg->fh;
    1513 fi.flush = 1;
    1514 if (req->se->conn.proto_minor >= 7)
    1515 fi.lock_owner = arg->lock_owner;
    1516
    1517 if (req->se->op.flush)
    1518 req->se->op.flush(req, nodeid, &fi);
    1519 else
    1520 fuse_reply_err(req, ENOSYS);
    1521}
    1522
    1523static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1524{
    1525 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
    1526 struct fuse_file_info fi;
    1527
    1528 memset(&fi, 0, sizeof(fi));
    1529 fi.flags = arg->flags;
    1530 fi.fh = arg->fh;
    1531 if (req->se->conn.proto_minor >= 8) {
    1532 fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
    1533 fi.lock_owner = arg->lock_owner;
    1534 }
    1535 if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
    1536 fi.flock_release = 1;
    1537 fi.lock_owner = arg->lock_owner;
    1538 }
    1539
    1540 if (req->se->op.release)
    1541 req->se->op.release(req, nodeid, &fi);
    1542 else
    1543 fuse_reply_err(req, 0);
    1544}
    1545
    1546static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1547{
    1548 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
    1549 struct fuse_file_info fi;
    1550 int datasync = arg->fsync_flags & 1;
    1551
    1552 memset(&fi, 0, sizeof(fi));
    1553 fi.fh = arg->fh;
    1554
    1555 if (req->se->op.fsync)
    1556 req->se->op.fsync(req, nodeid, datasync, &fi);
    1557 else
    1558 fuse_reply_err(req, ENOSYS);
    1559}
    1560
    1561static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1562{
    1563 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
    1564 struct fuse_file_info fi;
    1565
    1566 memset(&fi, 0, sizeof(fi));
    1567 fi.flags = arg->flags;
    1568
    1569 if (req->se->op.opendir)
    1570 req->se->op.opendir(req, nodeid, &fi);
    1571 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT)
    1572 fuse_reply_err(req, ENOSYS);
    1573 else
    1574 fuse_reply_open(req, &fi);
    1575}
    1576
    1577static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1578{
    1579 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1580 struct fuse_file_info fi;
    1581
    1582 memset(&fi, 0, sizeof(fi));
    1583 fi.fh = arg->fh;
    1584
    1585 if (req->se->op.readdir)
    1586 req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
    1587 else
    1588 fuse_reply_err(req, ENOSYS);
    1589}
    1590
    1591static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1592{
    1593 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1594 struct fuse_file_info fi;
    1595
    1596 memset(&fi, 0, sizeof(fi));
    1597 fi.fh = arg->fh;
    1598
    1599 if (req->se->op.readdirplus)
    1600 req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
    1601 else
    1602 fuse_reply_err(req, ENOSYS);
    1603}
    1604
    1605static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1606{
    1607 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
    1608 struct fuse_file_info fi;
    1609
    1610 memset(&fi, 0, sizeof(fi));
    1611 fi.flags = arg->flags;
    1612 fi.fh = arg->fh;
    1613
    1614 if (req->se->op.releasedir)
    1615 req->se->op.releasedir(req, nodeid, &fi);
    1616 else
    1617 fuse_reply_err(req, 0);
    1618}
    1619
    1620static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1621{
    1622 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
    1623 struct fuse_file_info fi;
    1624 int datasync = arg->fsync_flags & 1;
    1625
    1626 memset(&fi, 0, sizeof(fi));
    1627 fi.fh = arg->fh;
    1628
    1629 if (req->se->op.fsyncdir)
    1630 req->se->op.fsyncdir(req, nodeid, datasync, &fi);
    1631 else
    1632 fuse_reply_err(req, ENOSYS);
    1633}
    1634
    1635static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1636{
    1637 (void) nodeid;
    1638 (void) inarg;
    1639
    1640 if (req->se->op.statfs)
    1641 req->se->op.statfs(req, nodeid);
    1642 else {
    1643 struct statvfs buf = {
    1644 .f_namemax = 255,
    1645 .f_bsize = 512,
    1646 };
    1647 fuse_reply_statfs(req, &buf);
    1648 }
    1649}
    1650
    1651static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1652{
    1653 struct fuse_session *se = req->se;
    1654 unsigned int xattr_ext = !!(se->conn.want_ext & FUSE_CAP_SETXATTR_EXT);
    1655 struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
    1656 char *name = xattr_ext ? PARAM(arg) :
    1657 (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
    1658 char *value = name + strlen(name) + 1;
    1659
    1660 /* XXX:The API should be extended to support extra_flags/setxattr_flags */
    1661 if (req->se->op.setxattr)
    1662 req->se->op.setxattr(req, nodeid, name, value, arg->size,
    1663 arg->flags);
    1664 else
    1665 fuse_reply_err(req, ENOSYS);
    1666}
    1667
    1668static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1669{
    1670 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
    1671
    1672 if (req->se->op.getxattr)
    1673 req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size);
    1674 else
    1675 fuse_reply_err(req, ENOSYS);
    1676}
    1677
    1678static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1679{
    1680 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
    1681
    1682 if (req->se->op.listxattr)
    1683 req->se->op.listxattr(req, nodeid, arg->size);
    1684 else
    1685 fuse_reply_err(req, ENOSYS);
    1686}
    1687
    1688static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1689{
    1690 char *name = (char *) inarg;
    1691
    1692 if (req->se->op.removexattr)
    1693 req->se->op.removexattr(req, nodeid, name);
    1694 else
    1695 fuse_reply_err(req, ENOSYS);
    1696}
    1697
    1698static void convert_fuse_file_lock(struct fuse_file_lock *fl,
    1699 struct flock *flock)
    1700{
    1701 memset(flock, 0, sizeof(struct flock));
    1702 flock->l_type = fl->type;
    1703 flock->l_whence = SEEK_SET;
    1704 flock->l_start = fl->start;
    1705 if (fl->end == OFFSET_MAX)
    1706 flock->l_len = 0;
    1707 else
    1708 flock->l_len = fl->end - fl->start + 1;
    1709 flock->l_pid = fl->pid;
    1710}
    1711
    1712static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1713{
    1714 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
    1715 struct fuse_file_info fi;
    1716 struct flock flock;
    1717
    1718 memset(&fi, 0, sizeof(fi));
    1719 fi.fh = arg->fh;
    1720 fi.lock_owner = arg->owner;
    1721
    1722 convert_fuse_file_lock(&arg->lk, &flock);
    1723 if (req->se->op.getlk)
    1724 req->se->op.getlk(req, nodeid, &fi, &flock);
    1725 else
    1726 fuse_reply_err(req, ENOSYS);
    1727}
    1728
    1729static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
    1730 const void *inarg, int sleep)
    1731{
    1732 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
    1733 struct fuse_file_info fi;
    1734 struct flock flock;
    1735
    1736 memset(&fi, 0, sizeof(fi));
    1737 fi.fh = arg->fh;
    1738 fi.lock_owner = arg->owner;
    1739
    1740 if (arg->lk_flags & FUSE_LK_FLOCK) {
    1741 int op = 0;
    1742
    1743 switch (arg->lk.type) {
    1744 case F_RDLCK:
    1745 op = LOCK_SH;
    1746 break;
    1747 case F_WRLCK:
    1748 op = LOCK_EX;
    1749 break;
    1750 case F_UNLCK:
    1751 op = LOCK_UN;
    1752 break;
    1753 }
    1754 if (!sleep)
    1755 op |= LOCK_NB;
    1756
    1757 if (req->se->op.flock)
    1758 req->se->op.flock(req, nodeid, &fi, op);
    1759 else
    1760 fuse_reply_err(req, ENOSYS);
    1761 } else {
    1762 convert_fuse_file_lock(&arg->lk, &flock);
    1763 if (req->se->op.setlk)
    1764 req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
    1765 else
    1766 fuse_reply_err(req, ENOSYS);
    1767 }
    1768}
    1769
    1770static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1771{
    1772 do_setlk_common(req, nodeid, inarg, 0);
    1773}
    1774
    1775static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1776{
    1777 do_setlk_common(req, nodeid, inarg, 1);
    1778}
    1779
    1780static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
    1781{
    1782 struct fuse_req *curr;
    1783
    1784 for (curr = se->list.next; curr != &se->list; curr = curr->next) {
    1785 if (curr->unique == req->u.i.unique) {
    1787 void *data;
    1788
    1789 curr->ref_cnt++;
    1790 pthread_mutex_unlock(&se->lock);
    1791
    1792 /* Ugh, ugly locking */
    1793 pthread_mutex_lock(&curr->lock);
    1794 pthread_mutex_lock(&se->lock);
    1795 curr->interrupted = 1;
    1796 func = curr->u.ni.func;
    1797 data = curr->u.ni.data;
    1798 pthread_mutex_unlock(&se->lock);
    1799 if (func)
    1800 func(curr, data);
    1801 pthread_mutex_unlock(&curr->lock);
    1802
    1803 pthread_mutex_lock(&se->lock);
    1804 curr->ref_cnt--;
    1805 if (!curr->ref_cnt) {
    1806 destroy_req(curr);
    1807 }
    1808
    1809 return 1;
    1810 }
    1811 }
    1812 for (curr = se->interrupts.next; curr != &se->interrupts;
    1813 curr = curr->next) {
    1814 if (curr->u.i.unique == req->u.i.unique)
    1815 return 1;
    1816 }
    1817 return 0;
    1818}
    1819
    1820static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1821{
    1822 struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
    1823 struct fuse_session *se = req->se;
    1824
    1825 (void) nodeid;
    1826 if (se->debug)
    1827 fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
    1828 (unsigned long long) arg->unique);
    1829
    1830 req->u.i.unique = arg->unique;
    1831
    1832 pthread_mutex_lock(&se->lock);
    1833 if (find_interrupted(se, req)) {
    1834 fuse_chan_put(req->ch);
    1835 req->ch = NULL;
    1836 destroy_req(req);
    1837 } else
    1838 list_add_req(req, &se->interrupts);
    1839 pthread_mutex_unlock(&se->lock);
    1840}
    1841
    1842static struct fuse_req *check_interrupt(struct fuse_session *se,
    1843 struct fuse_req *req)
    1844{
    1845 struct fuse_req *curr;
    1846
    1847 for (curr = se->interrupts.next; curr != &se->interrupts;
    1848 curr = curr->next) {
    1849 if (curr->u.i.unique == req->unique) {
    1850 req->interrupted = 1;
    1851 list_del_req(curr);
    1852 fuse_chan_put(curr->ch);
    1853 curr->ch = NULL;
    1854 destroy_req(curr);
    1855 return NULL;
    1856 }
    1857 }
    1858 curr = se->interrupts.next;
    1859 if (curr != &se->interrupts) {
    1860 list_del_req(curr);
    1861 list_init_req(curr);
    1862 return curr;
    1863 } else
    1864 return NULL;
    1865}
    1866
    1867static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1868{
    1869 struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
    1870
    1871 if (req->se->op.bmap)
    1872 req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
    1873 else
    1874 fuse_reply_err(req, ENOSYS);
    1875}
    1876
    1877static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1878{
    1879 struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
    1880 unsigned int flags = arg->flags;
    1881 void *in_buf = arg->in_size ? PARAM(arg) : NULL;
    1882 struct fuse_file_info fi;
    1883
    1884 if (flags & FUSE_IOCTL_DIR &&
    1885 !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) {
    1886 fuse_reply_err(req, ENOTTY);
    1887 return;
    1888 }
    1889
    1890 memset(&fi, 0, sizeof(fi));
    1891 fi.fh = arg->fh;
    1892
    1893 if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
    1894 !(flags & FUSE_IOCTL_32BIT)) {
    1895 req->ioctl_64bit = 1;
    1896 }
    1897
    1898 if (req->se->op.ioctl)
    1899 req->se->op.ioctl(req, nodeid, arg->cmd,
    1900 (void *)(uintptr_t)arg->arg, &fi, flags,
    1901 in_buf, arg->in_size, arg->out_size);
    1902 else
    1903 fuse_reply_err(req, ENOSYS);
    1904}
    1905
    1906void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
    1907{
    1908 free(ph);
    1909}
    1910
    1911static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1912{
    1913 struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
    1914 struct fuse_file_info fi;
    1915
    1916 memset(&fi, 0, sizeof(fi));
    1917 fi.fh = arg->fh;
    1918 fi.poll_events = arg->events;
    1919
    1920 if (req->se->op.poll) {
    1921 struct fuse_pollhandle *ph = NULL;
    1922
    1923 if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
    1924 ph = malloc(sizeof(struct fuse_pollhandle));
    1925 if (ph == NULL) {
    1926 fuse_reply_err(req, ENOMEM);
    1927 return;
    1928 }
    1929 ph->kh = arg->kh;
    1930 ph->se = req->se;
    1931 }
    1932
    1933 req->se->op.poll(req, nodeid, &fi, ph);
    1934 } else {
    1935 fuse_reply_err(req, ENOSYS);
    1936 }
    1937}
    1938
    1939static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1940{
    1941 struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
    1942 struct fuse_file_info fi;
    1943
    1944 memset(&fi, 0, sizeof(fi));
    1945 fi.fh = arg->fh;
    1946
    1947 if (req->se->op.fallocate)
    1948 req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
    1949 else
    1950 fuse_reply_err(req, ENOSYS);
    1951}
    1952
    1953static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg)
    1954{
    1955 struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg;
    1956 struct fuse_file_info fi_in, fi_out;
    1957
    1958 memset(&fi_in, 0, sizeof(fi_in));
    1959 fi_in.fh = arg->fh_in;
    1960
    1961 memset(&fi_out, 0, sizeof(fi_out));
    1962 fi_out.fh = arg->fh_out;
    1963
    1964
    1965 if (req->se->op.copy_file_range)
    1966 req->se->op.copy_file_range(req, nodeid_in, arg->off_in,
    1967 &fi_in, arg->nodeid_out,
    1968 arg->off_out, &fi_out, arg->len,
    1969 arg->flags);
    1970 else
    1971 fuse_reply_err(req, ENOSYS);
    1972}
    1973
    1974static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1975{
    1976 struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg;
    1977 struct fuse_file_info fi;
    1978
    1979 memset(&fi, 0, sizeof(fi));
    1980 fi.fh = arg->fh;
    1981
    1982 if (req->se->op.lseek)
    1983 req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
    1984 else
    1985 fuse_reply_err(req, ENOSYS);
    1986}
    1987
    1988static bool want_flags_valid(uint64_t capable, uint64_t want)
    1989{
    1990 uint64_t unknown_flags = want & (~capable);
    1991 if (unknown_flags != 0) {
    1992 fuse_log(FUSE_LOG_ERR,
    1993 "fuse: unknown connection 'want' flags: 0x%08lx\n",
    1994 unknown_flags);
    1995 return false;
    1996 }
    1997 return true;
    1998}
    1999
    2003static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn,
    2004 uint64_t want_ext_default)
    2005{
    2006 /* Convert want to want_ext if necessary */
    2007 if (conn->want != 0) {
    2008 if (conn->want_ext != want_ext_default) {
    2009 fuse_log(FUSE_LOG_ERR,
    2010 "fuse: both 'want' and 'want_ext' are set\n");
    2011 return -EINVAL;
    2012 }
    2013 conn->want_ext |= conn->want;
    2014 }
    2015
    2016 return 0;
    2017}
    2018
    2019/* Prevent bogus data races (bogus since "init" is called before
    2020 * multi-threading becomes relevant */
    2021static __attribute__((no_sanitize("thread")))
    2022void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    2023{
    2024 struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
    2025 struct fuse_init_out outarg;
    2026 struct fuse_session *se = req->se;
    2027 size_t bufsize = se->bufsize;
    2028 size_t outargsize = sizeof(outarg);
    2029 uint64_t inargflags = 0;
    2030 uint64_t outargflags = 0;
    2031 bool buf_reallocable = se->buf_reallocable;
    2032 (void) nodeid;
    2033 if (se->debug) {
    2034 fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
    2035 if (arg->major == 7 && arg->minor >= 6) {
    2036 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
    2037 fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
    2038 arg->max_readahead);
    2039 }
    2040 }
    2041 se->conn.proto_major = arg->major;
    2042 se->conn.proto_minor = arg->minor;
    2043 se->conn.capable_ext = 0;
    2044 se->conn.want_ext = 0;
    2045
    2046 memset(&outarg, 0, sizeof(outarg));
    2047 outarg.major = FUSE_KERNEL_VERSION;
    2048 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
    2049
    2050 if (arg->major < 7) {
    2051 fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
    2052 arg->major, arg->minor);
    2053 fuse_reply_err(req, EPROTO);
    2054 return;
    2055 }
    2056
    2057 if (arg->major > 7) {
    2058 /* Wait for a second INIT request with a 7.X version */
    2059 send_reply_ok(req, &outarg, sizeof(outarg));
    2060 return;
    2061 }
    2062
    2063 if (arg->minor >= 6) {
    2064 if (arg->max_readahead < se->conn.max_readahead)
    2065 se->conn.max_readahead = arg->max_readahead;
    2066 inargflags = arg->flags;
    2067 if (inargflags & FUSE_INIT_EXT)
    2068 inargflags = inargflags | (uint64_t) arg->flags2 << 32;
    2069 if (inargflags & FUSE_ASYNC_READ)
    2070 se->conn.capable_ext |= FUSE_CAP_ASYNC_READ;
    2071 if (inargflags & FUSE_POSIX_LOCKS)
    2072 se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS;
    2073 if (inargflags & FUSE_ATOMIC_O_TRUNC)
    2074 se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC;
    2075 if (inargflags & FUSE_EXPORT_SUPPORT)
    2076 se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT;
    2077 if (inargflags & FUSE_DONT_MASK)
    2078 se->conn.capable_ext |= FUSE_CAP_DONT_MASK;
    2079 if (inargflags & FUSE_FLOCK_LOCKS)
    2080 se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS;
    2081 if (inargflags & FUSE_AUTO_INVAL_DATA)
    2082 se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA;
    2083 if (inargflags & FUSE_DO_READDIRPLUS)
    2084 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS;
    2085 if (inargflags & FUSE_READDIRPLUS_AUTO)
    2086 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO;
    2087 if (inargflags & FUSE_ASYNC_DIO)
    2088 se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO;
    2089 if (inargflags & FUSE_WRITEBACK_CACHE)
    2090 se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE;
    2091 if (inargflags & FUSE_NO_OPEN_SUPPORT)
    2092 se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT;
    2093 if (inargflags & FUSE_PARALLEL_DIROPS)
    2094 se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS;
    2095 if (inargflags & FUSE_POSIX_ACL)
    2096 se->conn.capable_ext |= FUSE_CAP_POSIX_ACL;
    2097 if (inargflags & FUSE_HANDLE_KILLPRIV)
    2098 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV;
    2099 if (inargflags & FUSE_HANDLE_KILLPRIV_V2)
    2100 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2;
    2101 if (inargflags & FUSE_CACHE_SYMLINKS)
    2102 se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS;
    2103 if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
    2104 se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT;
    2105 if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
    2106 se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA;
    2107 if (inargflags & FUSE_SETXATTR_EXT)
    2108 se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT;
    2109 if (!(inargflags & FUSE_MAX_PAGES)) {
    2110 size_t max_bufsize =
    2111 FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
    2112 + FUSE_BUFFER_HEADER_SIZE;
    2113 if (bufsize > max_bufsize) {
    2114 bufsize = max_bufsize;
    2115 }
    2116 buf_reallocable = false;
    2117 }
    2118 if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP)
    2119 se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
    2120 if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY))
    2121 se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY;
    2122 if (inargflags & FUSE_PASSTHROUGH)
    2123 se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH;
    2124 if (inargflags & FUSE_NO_EXPORT_SUPPORT)
    2125 se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
    2126 } else {
    2127 se->conn.max_readahead = 0;
    2128 }
    2129
    2130 if (se->conn.proto_minor >= 14) {
    2131#ifdef HAVE_SPLICE
    2132#ifdef HAVE_VMSPLICE
    2133 if ((se->io == NULL) || (se->io->splice_send != NULL)) {
    2134 se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE |
    2136 }
    2137#endif
    2138 if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
    2139 se->conn.capable_ext |= FUSE_CAP_SPLICE_READ;
    2140 }
    2141#endif
    2142 }
    2143 if (se->conn.proto_minor >= 18)
    2144 se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR;
    2145
    2146 /* Default settings for modern filesystems.
    2147 *
    2148 * Most of these capabilities were disabled by default in
    2149 * libfuse2 for backwards compatibility reasons. In libfuse3,
    2150 * we can finally enable them by default (as long as they're
    2151 * supported by the kernel).
    2152 */
    2153#define LL_SET_DEFAULT(cond, cap) \
    2154 if ((cond)) \
    2155 fuse_set_feature_flag(&se->conn, cap)
    2156
    2157 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
    2158 LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
    2159 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
    2160 LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
    2161 LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
    2162 LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
    2163 LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
    2165 LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
    2166 LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
    2167 LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
    2169
    2170 /* This could safely become default, but libfuse needs an API extension
    2171 * to support it
    2172 * LL_SET_DEFAULT(1, FUSE_CAP_SETXATTR_EXT);
    2173 */
    2174
    2175 se->conn.time_gran = 1;
    2176
    2177 se->got_init = 1;
    2178 if (se->op.init) {
    2179 uint64_t want_ext_default = se->conn.want_ext;
    2180 int rc;
    2181
    2182 // Apply the first 32 bits of capable_ext to capable
    2183 se->conn.capable =
    2184 (uint32_t)(se->conn.capable_ext & 0xFFFFFFFF);
    2185
    2186 se->op.init(se->userdata, &se->conn);
    2187
    2188 /*
    2189 * se->conn.want is 32-bit value and deprecated in favour of
    2190 * se->conn.want_ext
    2191 * Userspace might still use conn.want - we need to convert it
    2192 */
    2193 rc = convert_to_conn_want_ext(&se->conn, want_ext_default);
    2194 if (rc != 0) {
    2195 fuse_reply_err(req, EPROTO);
    2196 se->error = -EPROTO;
    2198 return;
    2199 }
    2200 }
    2201
    2202 if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
    2203 fuse_reply_err(req, EPROTO);
    2204 se->error = -EPROTO;
    2206 return;
    2207 }
    2208
    2209 unsigned max_read_mo = get_max_read(se->mo);
    2210 if (se->conn.max_read != max_read_mo) {
    2211 fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() "
    2212 "requested different maximum read size (%u vs %u)\n",
    2213 se->conn.max_read, max_read_mo);
    2214 fuse_reply_err(req, EPROTO);
    2215 se->error = -EPROTO;
    2217 return;
    2218 }
    2219
    2220 if (bufsize < FUSE_MIN_READ_BUFFER) {
    2221 fuse_log(FUSE_LOG_ERR,
    2222 "fuse: warning: buffer size too small: %zu\n",
    2223 bufsize);
    2224 bufsize = FUSE_MIN_READ_BUFFER;
    2225 }
    2226
    2227 if (buf_reallocable)
    2228 bufsize = UINT_MAX;
    2229 se->conn.max_write = MIN(se->conn.max_write, bufsize - FUSE_BUFFER_HEADER_SIZE);
    2230 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
    2231
    2232 if (arg->flags & FUSE_MAX_PAGES) {
    2233 outarg.flags |= FUSE_MAX_PAGES;
    2234 outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
    2235 }
    2236 outargflags = outarg.flags;
    2237 /* Always enable big writes, this is superseded
    2238 by the max_write option */
    2239 outargflags |= FUSE_BIG_WRITES;
    2240
    2241 if (se->conn.want_ext & FUSE_CAP_ASYNC_READ)
    2242 outargflags |= FUSE_ASYNC_READ;
    2243 if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS)
    2244 outargflags |= FUSE_POSIX_LOCKS;
    2245 if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC)
    2246 outargflags |= FUSE_ATOMIC_O_TRUNC;
    2247 if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT)
    2248 outargflags |= FUSE_EXPORT_SUPPORT;
    2249 if (se->conn.want_ext & FUSE_CAP_DONT_MASK)
    2250 outargflags |= FUSE_DONT_MASK;
    2251 if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS)
    2252 outargflags |= FUSE_FLOCK_LOCKS;
    2253 if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA)
    2254 outargflags |= FUSE_AUTO_INVAL_DATA;
    2255 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS)
    2256 outargflags |= FUSE_DO_READDIRPLUS;
    2257 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO)
    2258 outargflags |= FUSE_READDIRPLUS_AUTO;
    2259 if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO)
    2260 outargflags |= FUSE_ASYNC_DIO;
    2261 if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE)
    2262 outargflags |= FUSE_WRITEBACK_CACHE;
    2263 if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS)
    2264 outargflags |= FUSE_PARALLEL_DIROPS;
    2265 if (se->conn.want_ext & FUSE_CAP_POSIX_ACL)
    2266 outargflags |= FUSE_POSIX_ACL;
    2267 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV)
    2268 outargflags |= FUSE_HANDLE_KILLPRIV;
    2269 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2)
    2270 outargflags |= FUSE_HANDLE_KILLPRIV_V2;
    2271 if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS)
    2272 outargflags |= FUSE_CACHE_SYMLINKS;
    2273 if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA)
    2274 outargflags |= FUSE_EXPLICIT_INVAL_DATA;
    2275 if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT)
    2276 outargflags |= FUSE_SETXATTR_EXT;
    2277 if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
    2278 outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
    2279 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) {
    2280 outargflags |= FUSE_PASSTHROUGH;
    2281 /*
    2282 * outarg.max_stack_depth includes the fuse stack layer,
    2283 * so it is one more than max_backing_stack_depth.
    2284 */
    2285 outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1;
    2286 }
    2287 if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT)
    2288 outargflags |= FUSE_NO_EXPORT_SUPPORT;
    2289
    2290 if (inargflags & FUSE_INIT_EXT) {
    2291 outargflags |= FUSE_INIT_EXT;
    2292 outarg.flags2 = outargflags >> 32;
    2293 }
    2294
    2295 outarg.flags = outargflags;
    2296
    2297 outarg.max_readahead = se->conn.max_readahead;
    2298 outarg.max_write = se->conn.max_write;
    2299 if (se->conn.proto_minor >= 13) {
    2300 if (se->conn.max_background >= (1 << 16))
    2301 se->conn.max_background = (1 << 16) - 1;
    2302 if (se->conn.congestion_threshold > se->conn.max_background)
    2303 se->conn.congestion_threshold = se->conn.max_background;
    2304 if (!se->conn.congestion_threshold) {
    2305 se->conn.congestion_threshold =
    2306 se->conn.max_background * 3 / 4;
    2307 }
    2308
    2309 outarg.max_background = se->conn.max_background;
    2310 outarg.congestion_threshold = se->conn.congestion_threshold;
    2311 }
    2312 if (se->conn.proto_minor >= 23)
    2313 outarg.time_gran = se->conn.time_gran;
    2314
    2315 if (se->debug) {
    2316 fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
    2317 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
    2318 fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
    2319 outarg.max_readahead);
    2320 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
    2321 fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
    2322 outarg.max_background);
    2323 fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
    2324 outarg.congestion_threshold);
    2325 fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
    2326 outarg.time_gran);
    2327 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH)
    2328 fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n",
    2329 outarg.max_stack_depth);
    2330 }
    2331 if (arg->minor < 5)
    2332 outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
    2333 else if (arg->minor < 23)
    2334 outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
    2335
    2336 send_reply_ok(req, &outarg, outargsize);
    2337}
    2338
    2339static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    2340{
    2341 struct fuse_session *se = req->se;
    2342
    2343 (void) nodeid;
    2344 (void) inarg;
    2345
    2346 se->got_destroy = 1;
    2347 se->got_init = 0;
    2348 if (se->op.destroy)
    2349 se->op.destroy(se->userdata);
    2350
    2351 send_reply_ok(req, NULL, 0);
    2352}
    2353
    2354static void list_del_nreq(struct fuse_notify_req *nreq)
    2355{
    2356 struct fuse_notify_req *prev = nreq->prev;
    2357 struct fuse_notify_req *next = nreq->next;
    2358 prev->next = next;
    2359 next->prev = prev;
    2360}
    2361
    2362static void list_add_nreq(struct fuse_notify_req *nreq,
    2363 struct fuse_notify_req *next)
    2364{
    2365 struct fuse_notify_req *prev = next->prev;
    2366 nreq->next = next;
    2367 nreq->prev = prev;
    2368 prev->next = nreq;
    2369 next->prev = nreq;
    2370}
    2371
    2372static void list_init_nreq(struct fuse_notify_req *nreq)
    2373{
    2374 nreq->next = nreq;
    2375 nreq->prev = nreq;
    2376}
    2377
    2378static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
    2379 const void *inarg, const struct fuse_buf *buf)
    2380{
    2381 struct fuse_session *se = req->se;
    2382 struct fuse_notify_req *nreq;
    2383 struct fuse_notify_req *head;
    2384
    2385 pthread_mutex_lock(&se->lock);
    2386 head = &se->notify_list;
    2387 for (nreq = head->next; nreq != head; nreq = nreq->next) {
    2388 if (nreq->unique == req->unique) {
    2389 list_del_nreq(nreq);
    2390 break;
    2391 }
    2392 }
    2393 pthread_mutex_unlock(&se->lock);
    2394
    2395 if (nreq != head)
    2396 nreq->reply(nreq, req, nodeid, inarg, buf);
    2397}
    2398
    2399static int send_notify_iov(struct fuse_session *se, int notify_code,
    2400 struct iovec *iov, int count)
    2401{
    2402 struct fuse_out_header out;
    2403
    2404 if (!se->got_init)
    2405 return -ENOTCONN;
    2406
    2407 out.unique = 0;
    2408 out.error = notify_code;
    2409 iov[0].iov_base = &out;
    2410 iov[0].iov_len = sizeof(struct fuse_out_header);
    2411
    2412 return fuse_send_msg(se, NULL, iov, count);
    2413}
    2414
    2415int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
    2416{
    2417 if (ph != NULL) {
    2418 struct fuse_notify_poll_wakeup_out outarg;
    2419 struct iovec iov[2];
    2420
    2421 outarg.kh = ph->kh;
    2422
    2423 iov[1].iov_base = &outarg;
    2424 iov[1].iov_len = sizeof(outarg);
    2425
    2426 return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
    2427 } else {
    2428 return 0;
    2429 }
    2430}
    2431
    2432int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
    2433 off_t off, off_t len)
    2434{
    2435 struct fuse_notify_inval_inode_out outarg;
    2436 struct iovec iov[2];
    2437
    2438 if (!se)
    2439 return -EINVAL;
    2440
    2441 if (se->conn.proto_minor < 12)
    2442 return -ENOSYS;
    2443
    2444 outarg.ino = ino;
    2445 outarg.off = off;
    2446 outarg.len = len;
    2447
    2448 iov[1].iov_base = &outarg;
    2449 iov[1].iov_len = sizeof(outarg);
    2450
    2451 return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
    2452}
    2453
    2473static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
    2474 const char *name, size_t namelen,
    2475 enum fuse_notify_entry_flags flags)
    2476{
    2477 struct fuse_notify_inval_entry_out outarg;
    2478 struct iovec iov[3];
    2479
    2480 if (!se)
    2481 return -EINVAL;
    2482
    2483 if (se->conn.proto_minor < 12)
    2484 return -ENOSYS;
    2485
    2486 outarg.parent = parent;
    2487 outarg.namelen = namelen;
    2488 outarg.flags = 0;
    2489 if (flags & FUSE_LL_EXPIRE_ONLY)
    2490 outarg.flags |= FUSE_EXPIRE_ONLY;
    2491
    2492 iov[1].iov_base = &outarg;
    2493 iov[1].iov_len = sizeof(outarg);
    2494 iov[2].iov_base = (void *)name;
    2495 iov[2].iov_len = namelen + 1;
    2496
    2497 return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
    2498}
    2499
    2500int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
    2501 const char *name, size_t namelen)
    2502{
    2503 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
    2504}
    2505
    2506int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
    2507 const char *name, size_t namelen)
    2508{
    2509 if (!se)
    2510 return -EINVAL;
    2511
    2512 if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY))
    2513 return -ENOSYS;
    2514
    2515 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
    2516}
    2517
    2518
    2519int fuse_lowlevel_notify_delete(struct fuse_session *se,
    2520 fuse_ino_t parent, fuse_ino_t child,
    2521 const char *name, size_t namelen)
    2522{
    2523 struct fuse_notify_delete_out outarg;
    2524 struct iovec iov[3];
    2525
    2526 if (!se)
    2527 return -EINVAL;
    2528
    2529 if (se->conn.proto_minor < 18)
    2530 return -ENOSYS;
    2531
    2532 outarg.parent = parent;
    2533 outarg.child = child;
    2534 outarg.namelen = namelen;
    2535 outarg.padding = 0;
    2536
    2537 iov[1].iov_base = &outarg;
    2538 iov[1].iov_len = sizeof(outarg);
    2539 iov[2].iov_base = (void *)name;
    2540 iov[2].iov_len = namelen + 1;
    2541
    2542 return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
    2543}
    2544
    2545int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
    2546 off_t offset, struct fuse_bufvec *bufv,
    2547 enum fuse_buf_copy_flags flags)
    2548{
    2549 struct fuse_out_header out;
    2550 struct fuse_notify_store_out outarg;
    2551 struct iovec iov[3];
    2552 size_t size = fuse_buf_size(bufv);
    2553 int res;
    2554
    2555 if (!se)
    2556 return -EINVAL;
    2557
    2558 if (se->conn.proto_minor < 15)
    2559 return -ENOSYS;
    2560
    2561 out.unique = 0;
    2562 out.error = FUSE_NOTIFY_STORE;
    2563
    2564 outarg.nodeid = ino;
    2565 outarg.offset = offset;
    2566 outarg.size = size;
    2567 outarg.padding = 0;
    2568
    2569 iov[0].iov_base = &out;
    2570 iov[0].iov_len = sizeof(out);
    2571 iov[1].iov_base = &outarg;
    2572 iov[1].iov_len = sizeof(outarg);
    2573
    2574 res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags);
    2575 if (res > 0)
    2576 res = -res;
    2577
    2578 return res;
    2579}
    2580
    2581struct fuse_retrieve_req {
    2582 struct fuse_notify_req nreq;
    2583 void *cookie;
    2584};
    2585
    2586static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
    2587 fuse_req_t req, fuse_ino_t ino,
    2588 const void *inarg,
    2589 const struct fuse_buf *ibuf)
    2590{
    2591 struct fuse_session *se = req->se;
    2592 struct fuse_retrieve_req *rreq =
    2593 container_of(nreq, struct fuse_retrieve_req, nreq);
    2594 const struct fuse_notify_retrieve_in *arg = inarg;
    2595 struct fuse_bufvec bufv = {
    2596 .buf[0] = *ibuf,
    2597 .count = 1,
    2598 };
    2599
    2600 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
    2601 bufv.buf[0].mem = PARAM(arg);
    2602
    2603 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    2604 sizeof(struct fuse_notify_retrieve_in);
    2605
    2606 if (bufv.buf[0].size < arg->size) {
    2607 fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
    2608 fuse_reply_none(req);
    2609 goto out;
    2610 }
    2611 bufv.buf[0].size = arg->size;
    2612
    2613 if (se->op.retrieve_reply) {
    2614 se->op.retrieve_reply(req, rreq->cookie, ino,
    2615 arg->offset, &bufv);
    2616 } else {
    2617 fuse_reply_none(req);
    2618 }
    2619out:
    2620 free(rreq);
    2621 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
    2622 fuse_ll_clear_pipe(se);
    2623}
    2624
    2625int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
    2626 size_t size, off_t offset, void *cookie)
    2627{
    2628 struct fuse_notify_retrieve_out outarg;
    2629 struct iovec iov[2];
    2630 struct fuse_retrieve_req *rreq;
    2631 int err;
    2632
    2633 if (!se)
    2634 return -EINVAL;
    2635
    2636 if (se->conn.proto_minor < 15)
    2637 return -ENOSYS;
    2638
    2639 rreq = malloc(sizeof(*rreq));
    2640 if (rreq == NULL)
    2641 return -ENOMEM;
    2642
    2643 pthread_mutex_lock(&se->lock);
    2644 rreq->cookie = cookie;
    2645 rreq->nreq.unique = se->notify_ctr++;
    2646 rreq->nreq.reply = fuse_ll_retrieve_reply;
    2647 list_add_nreq(&rreq->nreq, &se->notify_list);
    2648 pthread_mutex_unlock(&se->lock);
    2649
    2650 outarg.notify_unique = rreq->nreq.unique;
    2651 outarg.nodeid = ino;
    2652 outarg.offset = offset;
    2653 outarg.size = size;
    2654 outarg.padding = 0;
    2655
    2656 iov[1].iov_base = &outarg;
    2657 iov[1].iov_len = sizeof(outarg);
    2658
    2659 err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
    2660 if (err) {
    2661 pthread_mutex_lock(&se->lock);
    2662 list_del_nreq(&rreq->nreq);
    2663 pthread_mutex_unlock(&se->lock);
    2664 free(rreq);
    2665 }
    2666
    2667 return err;
    2668}
    2669
    2671{
    2672 return req->se->userdata;
    2673}
    2674
    2675const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
    2676{
    2677 return &req->ctx;
    2678}
    2679
    2681 void *data)
    2682{
    2683 pthread_mutex_lock(&req->lock);
    2684 pthread_mutex_lock(&req->se->lock);
    2685 req->u.ni.func = func;
    2686 req->u.ni.data = data;
    2687 pthread_mutex_unlock(&req->se->lock);
    2688 if (req->interrupted && func)
    2689 func(req, data);
    2690 pthread_mutex_unlock(&req->lock);
    2691}
    2692
    2694{
    2695 int interrupted;
    2696
    2697 pthread_mutex_lock(&req->se->lock);
    2698 interrupted = req->interrupted;
    2699 pthread_mutex_unlock(&req->se->lock);
    2700
    2701 return interrupted;
    2702}
    2703
    2704static struct {
    2705 void (*func)(fuse_req_t, fuse_ino_t, const void *);
    2706 const char *name;
    2707} fuse_ll_ops[] = {
    2708 [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
    2709 [FUSE_FORGET] = { do_forget, "FORGET" },
    2710 [FUSE_GETATTR] = { do_getattr, "GETATTR" },
    2711 [FUSE_SETATTR] = { do_setattr, "SETATTR" },
    2712 [FUSE_READLINK] = { do_readlink, "READLINK" },
    2713 [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
    2714 [FUSE_MKNOD] = { do_mknod, "MKNOD" },
    2715 [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
    2716 [FUSE_UNLINK] = { do_unlink, "UNLINK" },
    2717 [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
    2718 [FUSE_RENAME] = { do_rename, "RENAME" },
    2719 [FUSE_LINK] = { do_link, "LINK" },
    2720 [FUSE_OPEN] = { do_open, "OPEN" },
    2721 [FUSE_READ] = { do_read, "READ" },
    2722 [FUSE_WRITE] = { do_write, "WRITE" },
    2723 [FUSE_STATFS] = { do_statfs, "STATFS" },
    2724 [FUSE_RELEASE] = { do_release, "RELEASE" },
    2725 [FUSE_FSYNC] = { do_fsync, "FSYNC" },
    2726 [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
    2727 [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
    2728 [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
    2729 [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
    2730 [FUSE_FLUSH] = { do_flush, "FLUSH" },
    2731 [FUSE_INIT] = { do_init, "INIT" },
    2732 [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
    2733 [FUSE_READDIR] = { do_readdir, "READDIR" },
    2734 [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
    2735 [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
    2736 [FUSE_GETLK] = { do_getlk, "GETLK" },
    2737 [FUSE_SETLK] = { do_setlk, "SETLK" },
    2738 [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
    2739 [FUSE_ACCESS] = { do_access, "ACCESS" },
    2740 [FUSE_CREATE] = { do_create, "CREATE" },
    2741 [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" },
    2742 [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
    2743 [FUSE_BMAP] = { do_bmap, "BMAP" },
    2744 [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
    2745 [FUSE_POLL] = { do_poll, "POLL" },
    2746 [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
    2747 [FUSE_DESTROY] = { do_destroy, "DESTROY" },
    2748 [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
    2749 [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
    2750 [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
    2751 [FUSE_RENAME2] = { do_rename2, "RENAME2" },
    2752 [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
    2753 [FUSE_LSEEK] = { do_lseek, "LSEEK" },
    2754 [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
    2755};
    2756
    2757/*
    2758 * For ABI compatibility we cannot allow higher values than CUSE_INIT.
    2759 * Without ABI compatibility we could use the size of the array.
    2760 * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
    2761 */
    2762#define FUSE_MAXOP (CUSE_INIT + 1)
    2763
    2764static const char *opname(enum fuse_opcode opcode)
    2765{
    2766 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
    2767 return "???";
    2768 else
    2769 return fuse_ll_ops[opcode].name;
    2770}
    2771
    2772static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
    2773 struct fuse_bufvec *src)
    2774{
    2775 ssize_t res = fuse_buf_copy(dst, src, 0);
    2776 if (res < 0) {
    2777 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res));
    2778 return res;
    2779 }
    2780 if ((size_t)res < fuse_buf_size(dst)) {
    2781 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
    2782 return -1;
    2783 }
    2784 return 0;
    2785}
    2786
    2787void fuse_session_process_buf(struct fuse_session *se,
    2788 const struct fuse_buf *buf)
    2789{
    2790 fuse_session_process_buf_internal(se, buf, NULL);
    2791}
    2792
    2793/* libfuse internal handler */
    2794void fuse_session_process_buf_internal(struct fuse_session *se,
    2795 const struct fuse_buf *buf, struct fuse_chan *ch)
    2796{
    2797 const size_t write_header_size = sizeof(struct fuse_in_header) +
    2798 sizeof(struct fuse_write_in);
    2799 struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
    2800 struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
    2801 struct fuse_in_header *in;
    2802 const void *inarg;
    2803 struct fuse_req *req;
    2804 void *mbuf = NULL;
    2805 int err;
    2806 int res;
    2807
    2808 if (buf->flags & FUSE_BUF_IS_FD) {
    2809 if (buf->size < tmpbuf.buf[0].size)
    2810 tmpbuf.buf[0].size = buf->size;
    2811
    2812 mbuf = malloc(tmpbuf.buf[0].size);
    2813 if (mbuf == NULL) {
    2814 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n");
    2815 goto clear_pipe;
    2816 }
    2817 tmpbuf.buf[0].mem = mbuf;
    2818
    2819 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
    2820 if (res < 0)
    2821 goto clear_pipe;
    2822
    2823 in = mbuf;
    2824 } else {
    2825 in = buf->mem;
    2826 }
    2827
    2828 if (se->debug) {
    2829 fuse_log(FUSE_LOG_DEBUG,
    2830 "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
    2831 (unsigned long long) in->unique,
    2832 opname((enum fuse_opcode) in->opcode), in->opcode,
    2833 (unsigned long long) in->nodeid, buf->size, in->pid);
    2834 }
    2835
    2836 req = fuse_ll_alloc_req(se);
    2837 if (req == NULL) {
    2838 struct fuse_out_header out = {
    2839 .unique = in->unique,
    2840 .error = -ENOMEM,
    2841 };
    2842 struct iovec iov = {
    2843 .iov_base = &out,
    2844 .iov_len = sizeof(struct fuse_out_header),
    2845 };
    2846
    2847 fuse_send_msg(se, ch, &iov, 1);
    2848 goto clear_pipe;
    2849 }
    2850
    2851 req->unique = in->unique;
    2852 req->ctx.uid = in->uid;
    2853 req->ctx.gid = in->gid;
    2854 req->ctx.pid = in->pid;
    2855 req->ch = ch ? fuse_chan_get(ch) : NULL;
    2856
    2857 err = EIO;
    2858 if (!se->got_init) {
    2859 enum fuse_opcode expected;
    2860
    2861 expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
    2862 if (in->opcode != expected)
    2863 goto reply_err;
    2864 } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
    2865 goto reply_err;
    2866
    2867 err = EACCES;
    2868 /* Implement -o allow_root */
    2869 if (se->deny_others && in->uid != se->owner && in->uid != 0 &&
    2870 in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
    2871 in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
    2872 in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
    2873 in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
    2874 in->opcode != FUSE_NOTIFY_REPLY &&
    2875 in->opcode != FUSE_READDIRPLUS)
    2876 goto reply_err;
    2877
    2878 err = ENOSYS;
    2879 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
    2880 goto reply_err;
    2881 /* Do not process interrupt request */
    2882 if (se->conn.no_interrupt && in->opcode == FUSE_INTERRUPT) {
    2883 if (se->debug)
    2884 fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n");
    2885 goto reply_err;
    2886 }
    2887 if (!se->conn.no_interrupt && in->opcode != FUSE_INTERRUPT) {
    2888 struct fuse_req *intr;
    2889 pthread_mutex_lock(&se->lock);
    2890 intr = check_interrupt(se, req);
    2891 list_add_req(req, &se->list);
    2892 pthread_mutex_unlock(&se->lock);
    2893 if (intr)
    2894 fuse_reply_err(intr, EAGAIN);
    2895 }
    2896
    2897 if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
    2898 (in->opcode != FUSE_WRITE || !se->op.write_buf) &&
    2899 in->opcode != FUSE_NOTIFY_REPLY) {
    2900 void *newmbuf;
    2901
    2902 err = ENOMEM;
    2903 newmbuf = realloc(mbuf, buf->size);
    2904 if (newmbuf == NULL)
    2905 goto reply_err;
    2906 mbuf = newmbuf;
    2907
    2908 tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
    2909 tmpbuf.buf[0].mem = (char *)mbuf + write_header_size;
    2910
    2911 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
    2912 err = -res;
    2913 if (res < 0)
    2914 goto reply_err;
    2915
    2916 in = mbuf;
    2917 }
    2918
    2919 inarg = (void *) &in[1];
    2920 if (in->opcode == FUSE_WRITE && se->op.write_buf)
    2921 do_write_buf(req, in->nodeid, inarg, buf);
    2922 else if (in->opcode == FUSE_NOTIFY_REPLY)
    2923 do_notify_reply(req, in->nodeid, inarg, buf);
    2924 else
    2925 fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
    2926
    2927out_free:
    2928 free(mbuf);
    2929 return;
    2930
    2931reply_err:
    2932 fuse_reply_err(req, err);
    2933clear_pipe:
    2934 if (buf->flags & FUSE_BUF_IS_FD)
    2935 fuse_ll_clear_pipe(se);
    2936 goto out_free;
    2937}
    2938
    2939#define LL_OPTION(n,o,v) \
    2940 { n, offsetof(struct fuse_session, o), v }
    2941
    2942static const struct fuse_opt fuse_ll_opts[] = {
    2943 LL_OPTION("debug", debug, 1),
    2944 LL_OPTION("-d", debug, 1),
    2945 LL_OPTION("--debug", debug, 1),
    2946 LL_OPTION("allow_root", deny_others, 1),
    2948};
    2949
    2950void fuse_lowlevel_version(void)
    2951{
    2952 printf("using FUSE kernel interface version %i.%i\n",
    2953 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
    2954 fuse_mount_version();
    2955}
    2956
    2957void fuse_lowlevel_help(void)
    2958{
    2959 /* These are not all options, but the ones that are
    2960 potentially of interest to an end-user */
    2961 printf(
    2962" -o allow_other allow access by all users\n"
    2963" -o allow_root allow access by root\n"
    2964" -o auto_unmount auto unmount on process termination\n");
    2965}
    2966
    2967void fuse_session_destroy(struct fuse_session *se)
    2968{
    2969 struct fuse_ll_pipe *llp;
    2970
    2971 if (se->got_init && !se->got_destroy) {
    2972 if (se->op.destroy)
    2973 se->op.destroy(se->userdata);
    2974 }
    2975 llp = pthread_getspecific(se->pipe_key);
    2976 if (llp != NULL)
    2977 fuse_ll_pipe_free(llp);
    2978 pthread_key_delete(se->pipe_key);
    2979 pthread_mutex_destroy(&se->lock);
    2980 free(se->cuse_data);
    2981 if (se->fd != -1)
    2982 close(se->fd);
    2983 if (se->io != NULL)
    2984 free(se->io);
    2985 destroy_mount_opts(se->mo);
    2986 free(se);
    2987}
    2988
    2989
    2990static void fuse_ll_pipe_destructor(void *data)
    2991{
    2992 struct fuse_ll_pipe *llp = data;
    2993 fuse_ll_pipe_free(llp);
    2994}
    2995
    2996void fuse_buf_free(struct fuse_buf *buf)
    2997{
    2998 if (buf->mem == NULL)
    2999 return;
    3000
    3001 size_t write_header_sz =
    3002 sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in);
    3003
    3004 char *ptr = (char *)buf->mem - pagesize + write_header_sz;
    3005 free(ptr);
    3006 buf->mem = NULL;
    3007}
    3008
    3009/*
    3010 * This is used to allocate buffers that hold fuse requests
    3011 */
    3012static void *buf_alloc(size_t size, bool internal)
    3013{
    3014 /*
    3015 * For libfuse internal caller add in alignment. That cannot be done
    3016 * for an external caller, as it is not guaranteed that the external
    3017 * caller frees the raw pointer.
    3018 */
    3019 if (internal) {
    3020 size_t write_header_sz = sizeof(struct fuse_in_header) +
    3021 sizeof(struct fuse_write_in);
    3022 size_t new_size = ROUND_UP(size + write_header_sz, pagesize);
    3023
    3024 char *buf = aligned_alloc(pagesize, new_size);
    3025 if (buf == NULL)
    3026 return NULL;
    3027
    3028 buf += pagesize - write_header_sz;
    3029
    3030 return buf;
    3031 } else {
    3032 return malloc(size);
    3033 }
    3034}
    3035
    3036/*
    3037 *@param internal true if called from libfuse internal code
    3038 */
    3039static int _fuse_session_receive_buf(struct fuse_session *se,
    3040 struct fuse_buf *buf, struct fuse_chan *ch,
    3041 bool internal)
    3042{
    3043 int err;
    3044 ssize_t res;
    3045 size_t bufsize = se->bufsize;
    3046#ifdef HAVE_SPLICE
    3047 struct fuse_ll_pipe *llp;
    3048 struct fuse_buf tmpbuf;
    3049
    3050 if (se->conn.proto_minor < 14 ||
    3051 !(se->conn.want_ext & FUSE_CAP_SPLICE_READ))
    3052 goto fallback;
    3053
    3054 llp = fuse_ll_get_pipe(se);
    3055 if (llp == NULL)
    3056 goto fallback;
    3057
    3058 if (llp->size < bufsize) {
    3059 if (llp->can_grow) {
    3060 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
    3061 if (res == -1) {
    3062 llp->can_grow = 0;
    3063 res = grow_pipe_to_max(llp->pipe[0]);
    3064 if (res > 0)
    3065 llp->size = res;
    3066 goto fallback;
    3067 }
    3068 llp->size = res;
    3069 }
    3070 if (llp->size < bufsize)
    3071 goto fallback;
    3072 }
    3073
    3074 if (se->io != NULL && se->io->splice_receive != NULL) {
    3075 res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
    3076 llp->pipe[1], NULL, bufsize, 0,
    3077 se->userdata);
    3078 } else {
    3079 res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
    3080 bufsize, 0);
    3081 }
    3082 err = errno;
    3083
    3084 if (fuse_session_exited(se))
    3085 return 0;
    3086
    3087 if (res == -1) {
    3088 if (err == ENODEV) {
    3089 /* Filesystem was unmounted, or connection was aborted
    3090 via /sys/fs/fuse/connections */
    3092 return 0;
    3093 }
    3094 if (err != EINTR && err != EAGAIN)
    3095 perror("fuse: splice from device");
    3096 return -err;
    3097 }
    3098
    3099 if (res < sizeof(struct fuse_in_header)) {
    3100 fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n");
    3101 return -EIO;
    3102 }
    3103
    3104 tmpbuf = (struct fuse_buf){
    3105 .size = res,
    3106 .flags = FUSE_BUF_IS_FD,
    3107 .fd = llp->pipe[0],
    3108 };
    3109
    3110 /*
    3111 * Don't bother with zero copy for small requests.
    3112 * fuse_loop_mt() needs to check for FORGET so this more than
    3113 * just an optimization.
    3114 */
    3115 if (res < sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) +
    3116 pagesize) {
    3117 struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
    3118 struct fuse_bufvec dst = { .count = 1 };
    3119
    3120 if (!buf->mem) {
    3121 buf->mem = buf_alloc(se->bufsize, internal);
    3122 if (!buf->mem) {
    3123 fuse_log(
    3124 FUSE_LOG_ERR,
    3125 "fuse: failed to allocate read buffer\n");
    3126 return -ENOMEM;
    3127 }
    3128 buf->mem_size = se->bufsize;
    3129 if (internal)
    3130 se->buf_reallocable = true;
    3131 }
    3132 buf->size = se->bufsize;
    3133 buf->flags = 0;
    3134 dst.buf[0] = *buf;
    3135
    3136 res = fuse_buf_copy(&dst, &src, 0);
    3137 if (res < 0) {
    3138 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n",
    3139 strerror(-res));
    3140 fuse_ll_clear_pipe(se);
    3141 return res;
    3142 }
    3143 if (res < tmpbuf.size) {
    3144 fuse_log(FUSE_LOG_ERR,
    3145 "fuse: copy from pipe: short read\n");
    3146 fuse_ll_clear_pipe(se);
    3147 return -EIO;
    3148 }
    3149 assert(res == tmpbuf.size);
    3150
    3151 } else {
    3152 /* Don't overwrite buf->mem, as that would cause a leak */
    3153 buf->fd = tmpbuf.fd;
    3154 buf->flags = tmpbuf.flags;
    3155 }
    3156 buf->size = tmpbuf.size;
    3157
    3158 return res;
    3159
    3160fallback:
    3161#endif
    3162 if (!buf->mem) {
    3163 buf->mem = buf_alloc(se->bufsize, internal);
    3164 if (!buf->mem) {
    3165 fuse_log(FUSE_LOG_ERR,
    3166 "fuse: failed to allocate read buffer\n");
    3167 return -ENOMEM;
    3168 }
    3169 buf->mem_size = se->bufsize;
    3170 if (internal)
    3171 se->buf_reallocable = true;
    3172 }
    3173
    3174restart:
    3175 if (se->buf_reallocable)
    3176 bufsize = buf->mem_size;
    3177 if (se->io != NULL) {
    3178 /* se->io->read is never NULL if se->io is not NULL as
    3179 specified by fuse_session_custom_io()*/
    3180 res = se->io->read(ch ? ch->fd : se->fd, buf->mem, bufsize,
    3181 se->userdata);
    3182 } else {
    3183 res = read(ch ? ch->fd : se->fd, buf->mem, bufsize);
    3184 }
    3185 err = errno;
    3186
    3187 if (fuse_session_exited(se))
    3188 return 0;
    3189 if (res == -1) {
    3190 if (err == EINVAL && se->buf_reallocable &&
    3191 se->bufsize > buf->mem_size) {
    3192 void *newbuf = buf_alloc(se->bufsize, internal);
    3193 if (!newbuf) {
    3194 fuse_log(
    3195 FUSE_LOG_ERR,
    3196 "fuse: failed to (re)allocate read buffer\n");
    3197 return -ENOMEM;
    3198 }
    3199 fuse_buf_free(buf);
    3200 buf->mem = newbuf;
    3201 buf->mem_size = se->bufsize;
    3202 se->buf_reallocable = true;
    3203 goto restart;
    3204 }
    3205
    3206 /* ENOENT means the operation was interrupted, it's safe
    3207 to restart */
    3208 if (err == ENOENT)
    3209 goto restart;
    3210
    3211 if (err == ENODEV) {
    3212 /* Filesystem was unmounted, or connection was aborted
    3213 via /sys/fs/fuse/connections */
    3215 return 0;
    3216 }
    3217 /* Errors occurring during normal operation: EINTR (read
    3218 interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
    3219 umounted) */
    3220 if (err != EINTR && err != EAGAIN)
    3221 perror("fuse: reading device");
    3222 return -err;
    3223 }
    3224 if ((size_t)res < sizeof(struct fuse_in_header)) {
    3225 fuse_log(FUSE_LOG_ERR, "short read on fuse device\n");
    3226 return -EIO;
    3227 }
    3228
    3229 buf->size = res;
    3230
    3231 return res;
    3232}
    3233
    3234int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
    3235{
    3236 return _fuse_session_receive_buf(se, buf, NULL, false);
    3237}
    3238
    3239/* libfuse internal handler */
    3240int fuse_session_receive_buf_internal(struct fuse_session *se,
    3241 struct fuse_buf *buf,
    3242 struct fuse_chan *ch)
    3243{
    3244 return _fuse_session_receive_buf(se, buf, ch, true);
    3245}
    3246
    3247struct fuse_session *
    3248fuse_session_new_versioned(struct fuse_args *args,
    3249 const struct fuse_lowlevel_ops *op, size_t op_size,
    3250 struct libfuse_version *version, void *userdata)
    3251{
    3252 int err;
    3253 struct fuse_session *se;
    3254 struct mount_opts *mo;
    3255
    3256 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
    3257 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
    3258 op_size = sizeof(struct fuse_lowlevel_ops);
    3259 }
    3260
    3261 if (args->argc == 0) {
    3262 fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
    3263 return NULL;
    3264 }
    3265
    3266 se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
    3267 if (se == NULL) {
    3268 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
    3269 goto out1;
    3270 }
    3271 se->fd = -1;
    3272 se->conn.max_write = FUSE_DEFAULT_MAX_PAGES_LIMIT * getpagesize();
    3273 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
    3274 se->conn.max_readahead = UINT_MAX;
    3275
    3276 /* Parse options */
    3277 if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
    3278 goto out2;
    3279 if(se->deny_others) {
    3280 /* Allowing access only by root is done by instructing
    3281 * kernel to allow access by everyone, and then restricting
    3282 * access to root and mountpoint owner in libfuse.
    3283 */
    3284 // We may be adding the option a second time, but
    3285 // that doesn't hurt.
    3286 if(fuse_opt_add_arg(args, "-oallow_other") == -1)
    3287 goto out2;
    3288 }
    3289 mo = parse_mount_opts(args);
    3290 if (mo == NULL)
    3291 goto out3;
    3292
    3293 if(args->argc == 1 &&
    3294 args->argv[0][0] == '-') {
    3295 fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
    3296 "will be ignored\n");
    3297 } else if (args->argc != 1) {
    3298 int i;
    3299 fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
    3300 for(i = 1; i < args->argc-1; i++)
    3301 fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
    3302 fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
    3303 goto out4;
    3304 }
    3305
    3306 if (se->debug)
    3307 fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION);
    3308
    3309 list_init_req(&se->list);
    3310 list_init_req(&se->interrupts);
    3311 list_init_nreq(&se->notify_list);
    3312 se->notify_ctr = 1;
    3313 pthread_mutex_init(&se->lock, NULL);
    3314
    3315 err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
    3316 if (err) {
    3317 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
    3318 strerror(err));
    3319 goto out5;
    3320 }
    3321
    3322 memcpy(&se->op, op, op_size);
    3323 se->owner = getuid();
    3324 se->userdata = userdata;
    3325
    3326 se->mo = mo;
    3327
    3328 /* Fuse server application should pass the version it was compiled
    3329 * against and pass it. If a libfuse version accidentally introduces an
    3330 * ABI incompatibility, it might be possible to 'fix' that at run time,
    3331 * by checking the version numbers.
    3332 */
    3333 se->version = *version;
    3334
    3335 return se;
    3336
    3337out5:
    3338 pthread_mutex_destroy(&se->lock);
    3339out4:
    3340 fuse_opt_free_args(args);
    3341out3:
    3342 if (mo != NULL)
    3343 destroy_mount_opts(mo);
    3344out2:
    3345 free(se);
    3346out1:
    3347 return NULL;
    3348}
    3349
    3350struct fuse_session *fuse_session_new_30(struct fuse_args *args,
    3351 const struct fuse_lowlevel_ops *op,
    3352 size_t op_size, void *userdata);
    3353struct fuse_session *fuse_session_new_30(struct fuse_args *args,
    3354 const struct fuse_lowlevel_ops *op,
    3355 size_t op_size,
    3356 void *userdata)
    3357{
    3358 /* unknown version */
    3359 struct libfuse_version version = { 0 };
    3360
    3361 return fuse_session_new_versioned(args, op, op_size, &version,
    3362 userdata);
    3363}
    3364
    3365FUSE_SYMVER("fuse_session_custom_io_317", "fuse_session_custom_io@@FUSE_3.17")
    3366int fuse_session_custom_io_317(struct fuse_session *se,
    3367 const struct fuse_custom_io *io, size_t op_size, int fd)
    3368{
    3369 if (sizeof(struct fuse_custom_io) < op_size) {
    3370 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
    3371 op_size = sizeof(struct fuse_custom_io);
    3372 }
    3373
    3374 if (fd < 0) {
    3375 fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
    3376 "fuse_session_custom_io()\n", fd);
    3377 return -EBADF;
    3378 }
    3379 if (io == NULL) {
    3380 fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
    3381 "fuse_session_custom_io()\n");
    3382 return -EINVAL;
    3383 } else if (io->read == NULL || io->writev == NULL) {
    3384 /* If the user provides their own file descriptor, we can't
    3385 guarantee that the default behavior of the io operations made
    3386 in libfuse will function properly. Therefore, we enforce the
    3387 user to implement these io operations when using custom io. */
    3388 fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
    3389 "implement both io->read() and io->writev\n");
    3390 return -EINVAL;
    3391 }
    3392
    3393 se->io = calloc(1, sizeof(struct fuse_custom_io));
    3394 if (se->io == NULL) {
    3395 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
    3396 "Error: %s\n", strerror(errno));
    3397 return -errno;
    3398 }
    3399
    3400 se->fd = fd;
    3401 memcpy(se->io, io, op_size);
    3402 return 0;
    3403}
    3404
    3405int fuse_session_custom_io_30(struct fuse_session *se,
    3406 const struct fuse_custom_io *io, int fd);
    3407FUSE_SYMVER("fuse_session_custom_io_30", "fuse_session_custom_io@FUSE_3.0")
    3408int fuse_session_custom_io_30(struct fuse_session *se,
    3409 const struct fuse_custom_io *io, int fd)
    3410{
    3411 return fuse_session_custom_io_317(se, io,
    3412 offsetof(struct fuse_custom_io, clone_fd), fd);
    3413}
    3414
    3415int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
    3416{
    3417 int fd;
    3418
    3419 if (mountpoint == NULL) {
    3420 fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n");
    3421 return -1;
    3422 }
    3423
    3424 /*
    3425 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
    3426 * would ensue.
    3427 */
    3428 do {
    3429 fd = open("/dev/null", O_RDWR);
    3430 if (fd > 2)
    3431 close(fd);
    3432 } while (fd >= 0 && fd <= 2);
    3433
    3434 /*
    3435 * To allow FUSE daemons to run without privileges, the caller may open
    3436 * /dev/fuse before launching the file system and pass on the file
    3437 * descriptor by specifying /dev/fd/N as the mount point. Note that the
    3438 * parent process takes care of performing the mount in this case.
    3439 */
    3440 fd = fuse_mnt_parse_fuse_fd(mountpoint);
    3441 if (fd != -1) {
    3442 if (fcntl(fd, F_GETFD) == -1) {
    3443 fuse_log(FUSE_LOG_ERR,
    3444 "fuse: Invalid file descriptor /dev/fd/%u\n",
    3445 fd);
    3446 return -1;
    3447 }
    3448 se->fd = fd;
    3449 return 0;
    3450 }
    3451
    3452 /* Open channel */
    3453 fd = fuse_kern_mount(mountpoint, se->mo);
    3454 if (fd == -1)
    3455 return -1;
    3456 se->fd = fd;
    3457
    3458 /* Save mountpoint */
    3459 se->mountpoint = strdup(mountpoint);
    3460 if (se->mountpoint == NULL)
    3461 goto error_out;
    3462
    3463 return 0;
    3464
    3465error_out:
    3466 fuse_kern_unmount(mountpoint, fd);
    3467 return -1;
    3468}
    3469
    3470int fuse_session_fd(struct fuse_session *se)
    3471{
    3472 return se->fd;
    3473}
    3474
    3475void fuse_session_unmount(struct fuse_session *se)
    3476{
    3477 if (se->mountpoint != NULL) {
    3478 fuse_kern_unmount(se->mountpoint, se->fd);
    3479 se->fd = -1;
    3480 free(se->mountpoint);
    3481 se->mountpoint = NULL;
    3482 }
    3483}
    3484
    3485#ifdef linux
    3486int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
    3487{
    3488 char *buf;
    3489 size_t bufsize = 1024;
    3490 char path[128];
    3491 int ret;
    3492 int fd;
    3493 unsigned long pid = req->ctx.pid;
    3494 char *s;
    3495
    3496 sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
    3497
    3498retry:
    3499 buf = malloc(bufsize);
    3500 if (buf == NULL)
    3501 return -ENOMEM;
    3502
    3503 ret = -EIO;
    3504 fd = open(path, O_RDONLY);
    3505 if (fd == -1)
    3506 goto out_free;
    3507
    3508 ret = read(fd, buf, bufsize);
    3509 close(fd);
    3510 if (ret < 0) {
    3511 ret = -EIO;
    3512 goto out_free;
    3513 }
    3514
    3515 if ((size_t)ret == bufsize) {
    3516 free(buf);
    3517 bufsize *= 4;
    3518 goto retry;
    3519 }
    3520
    3521 buf[ret] = '\0';
    3522 ret = -EIO;
    3523 s = strstr(buf, "\nGroups:");
    3524 if (s == NULL)
    3525 goto out_free;
    3526
    3527 s += 8;
    3528 ret = 0;
    3529 while (1) {
    3530 char *end;
    3531 unsigned long val = strtoul(s, &end, 0);
    3532 if (end == s)
    3533 break;
    3534
    3535 s = end;
    3536 if (ret < size)
    3537 list[ret] = val;
    3538 ret++;
    3539 }
    3540
    3541out_free:
    3542 free(buf);
    3543 return ret;
    3544}
    3545#else /* linux */
    3546/*
    3547 * This is currently not implemented on other than Linux...
    3548 */
    3549int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
    3550{
    3551 (void) req; (void) size; (void) list;
    3552 return -ENOSYS;
    3553}
    3554#endif
    3555
    3556/* Prevent spurious data race warning - we don't care
    3557 * about races for this flag */
    3558__attribute__((no_sanitize_thread))
    3559void fuse_session_exit(struct fuse_session *se)
    3560{
    3561 se->exited = 1;
    3562}
    3563
    3564__attribute__((no_sanitize_thread))
    3565void fuse_session_reset(struct fuse_session *se)
    3566{
    3567 se->exited = 0;
    3568 se->error = 0;
    3569}
    3570
    3571__attribute__((no_sanitize_thread))
    3572int fuse_session_exited(struct fuse_session *se)
    3573{
    3574 return se->exited;
    3575}
    @ FUSE_CAP_POSIX_ACL
    @ FUSE_CAP_READDIRPLUS
    @ FUSE_CAP_NO_OPENDIR_SUPPORT
    @ FUSE_CAP_PARALLEL_DIROPS
    @ FUSE_CAP_ASYNC_DIO
    @ FUSE_CAP_NO_EXPORT_SUPPORT
    @ FUSE_CAP_WRITEBACK_CACHE
    @ FUSE_CAP_IOCTL_DIR
    @ FUSE_CAP_AUTO_INVAL_DATA
    @ FUSE_CAP_SPLICE_READ
    @ FUSE_CAP_SPLICE_MOVE
    @ FUSE_CAP_POSIX_LOCKS
    @ FUSE_CAP_HANDLE_KILLPRIV_V2
    @ FUSE_CAP_HANDLE_KILLPRIV
    @ FUSE_CAP_DONT_MASK
    @ FUSE_CAP_ATOMIC_O_TRUNC
    @ FUSE_CAP_SPLICE_WRITE
    @ FUSE_CAP_PASSTHROUGH
    @ FUSE_CAP_FLOCK_LOCKS
    @ FUSE_CAP_EXPIRE_ONLY
    @ FUSE_CAP_EXPORT_SUPPORT
    @ FUSE_CAP_READDIRPLUS_AUTO
    @ FUSE_CAP_NO_OPEN_SUPPORT
    @ FUSE_CAP_DIRECT_IO_ALLOW_MMAP
    @ FUSE_CAP_SETXATTR_EXT
    @ FUSE_CAP_ASYNC_READ
    @ FUSE_CAP_CACHE_SYMLINKS
    @ FUSE_CAP_EXPLICIT_INVAL_DATA
    size_t fuse_buf_size(const struct fuse_bufvec *bufv)
    Definition buffer.c:22
    @ 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_copy_flags
    @ FUSE_BUF_SPLICE_NONBLOCK
    @ FUSE_BUF_FORCE_SPLICE
    @ FUSE_BUF_NO_SPLICE
    @ FUSE_BUF_SPLICE_MOVE
    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
    @ 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 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:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t nonseekable
    Definition fuse_common.h:86
    int32_t backing_id
    uint32_t parallel_direct_writes
    uint32_t noflush
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2lib_2fuse__misc_8h_source.html0000644000175000017500000002435414770234736024001 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2fuse__opt_8c_source.html0000644000175000017500000023570314770234736023645 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2fuse__signals_8c_source.html0000644000175000017500000010277614770234736024506 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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#define BT_STACK_SZ (1024 * 1024)
    31static void *backtrace_buffer[BT_STACK_SZ];
    32
    33static void dump_stack(void)
    34{
    35#ifdef HAVE_BACKTRACE
    36 char **strings;
    37
    38 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
    39 strings = backtrace_symbols(backtrace_buffer, nptrs);
    40
    41 if (strings == NULL) {
    42 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
    43 strerror(errno));
    44 return;
    45 }
    46
    47 for (int idx = 0; idx < nptrs; idx++)
    48 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
    49
    50 free(strings);
    51#endif
    52}
    53
    54static void exit_handler(int sig)
    55{
    56 if (fuse_instance == NULL)
    57 return;
    58
    59 fuse_session_exit(fuse_instance);
    60
    61 if (sig < 0) {
    62 fuse_log(FUSE_LOG_ERR,
    63 "assertion error: signal value <= 0\n");
    64 dump_stack();
    65 abort();
    66 fuse_instance->error = sig;
    67 }
    68
    69 fuse_instance->error = sig;
    70}
    71
    72static void exit_backtrace(int sig)
    73{
    74 if (fuse_instance == NULL)
    75 return;
    76
    77 fuse_session_exit(fuse_instance);
    78
    79 fuse_remove_signal_handlers(fuse_instance);
    80 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
    81 dump_stack();
    82 abort();
    83}
    84
    85
    86static void do_nothing(int sig)
    87{
    88 (void) sig;
    89}
    90
    91static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
    92{
    93 struct sigaction sa;
    94 struct sigaction old_sa;
    95
    96 memset(&sa, 0, sizeof(struct sigaction));
    97 sa.sa_handler = remove ? SIG_DFL : handler;
    98 sigemptyset(&(sa.sa_mask));
    99 sa.sa_flags = 0;
    100
    101 if (sigaction(sig, NULL, &old_sa) == -1) {
    102 perror("fuse: cannot get old signal handler");
    103 return -1;
    104 }
    105
    106 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
    107 sigaction(sig, &sa, NULL) == -1) {
    108 perror("fuse: cannot set signal handler");
    109 return -1;
    110 }
    111 return 0;
    112}
    113
    114static int _fuse_set_signal_handlers(int signals[], int nr_signals,
    115 void (*handler)(int))
    116{
    117 for (int idx = 0; idx < nr_signals; idx++) {
    118 int signal = signals[idx];
    119
    120 /*
    121 * If we used SIG_IGN instead of the do_nothing function,
    122 * then we would be unable to tell if we set SIG_IGN (and
    123 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
    124 * or if it was already set to SIG_IGN (and should be left
    125 * untouched.
    126 */
    127 if (set_one_signal_handler(signal, handler, 0) == -1) {
    128 fuse_log(FUSE_LOG_ERR,
    129 "Failed to install signal handler for sig %d\n",
    130 signal);
    131 return -1;
    132 }
    133 }
    134
    135 return 0;
    136}
    137
    138int fuse_set_signal_handlers(struct fuse_session *se)
    139{
    140 size_t nr_signals;
    141 int rc;
    142
    143 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
    144 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
    145 if (rc < 0)
    146 return rc;
    147
    148 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
    149 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
    150 if (rc < 0)
    151 return rc;
    152
    153 if (fuse_instance == NULL)
    154 fuse_instance = se;
    155 return 0;
    156}
    157
    158int fuse_set_fail_signal_handlers(struct fuse_session *se)
    159{
    160 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
    161
    162 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
    163 exit_backtrace);
    164 if (rc < 0)
    165 return rc;
    166
    167 if (fuse_instance == NULL)
    168 fuse_instance = se;
    169
    170 return 0;
    171}
    172
    173static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
    174 void (*handler)(int))
    175{
    176 for (int idx = 0; idx < nr_signals; idx++)
    177 set_one_signal_handler(signals[idx], handler, 1);
    178}
    179
    180void fuse_remove_signal_handlers(struct fuse_session *se)
    181{
    182 size_t nr_signals;
    183
    184 if (fuse_instance != se)
    185 fuse_log(FUSE_LOG_ERR,
    186 "fuse: fuse_remove_signal_handlers: unknown session\n");
    187 else
    188 fuse_instance = NULL;
    189
    190 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
    191 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
    192
    193 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
    194 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
    195
    196 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
    197 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
    198}
    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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2helper_8c_source.html0000644000175000017500000032456314770234736023144 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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
    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 if (cond) conn->want |= (cap)
    428#define LL_DISABLE(cond,cap) \
    429 if (cond) conn->want &= ~(cap)
    430
    431 LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
    432 LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
    433
    434 LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
    435 LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
    436
    437 LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
    438 LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
    439
    440 LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
    441 LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
    442
    443 LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
    444 LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
    445
    446 LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
    447 LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
    448
    449 LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
    450 LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
    451
    452 LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
    453 LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
    454
    455 LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
    456 LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
    457}
    458
    459struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
    460{
    461 struct fuse_conn_info_opts *opts;
    462
    463 opts = calloc(1, sizeof(struct fuse_conn_info_opts));
    464 if(opts == NULL) {
    465 fuse_log(FUSE_LOG_ERR, "calloc failed\n");
    466 return NULL;
    467 }
    468 if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
    469 free(opts);
    470 return NULL;
    471 }
    472 return opts;
    473}
    474
    475int fuse_open_channel(const char *mountpoint, const char* options)
    476{
    477 struct mount_opts *opts = NULL;
    478 int fd = -1;
    479 const char *argv[] = { "", "-o", options };
    480 int argc = sizeof(argv) / sizeof(argv[0]);
    481 struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv);
    482
    483 opts = parse_mount_opts(&args);
    484 if (opts == NULL)
    485 return -1;
    486
    487 fd = fuse_kern_mount(mountpoint, opts);
    488 destroy_mount_opts(opts);
    489
    490 return fd;
    491}
    int fuse_mount(struct fuse *f, const char *mountpoint)
    Definition fuse.c:5204
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5153
    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:479
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4520
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5209
    int fuse_set_signal_handlers(struct fuse_session *se)
    @ FUSE_CAP_READDIRPLUS
    @ FUSE_CAP_ASYNC_DIO
    @ FUSE_CAP_WRITEBACK_CACHE
    @ FUSE_CAP_AUTO_INVAL_DATA
    @ FUSE_CAP_SPLICE_READ
    @ FUSE_CAP_SPLICE_MOVE
    @ FUSE_CAP_POSIX_LOCKS
    @ FUSE_CAP_SPLICE_WRITE
    @ FUSE_CAP_FLOCK_LOCKS
    @ FUSE_CAP_READDIRPLUS_AUTO
    @ FUSE_CAP_ASYNC_READ
    struct fuse_conn_info_opts * fuse_parse_conn_info_opts(struct fuse_args *args)
    Definition helper.c:463
    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_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:416
    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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2modules_2iconv_8c_source.html0000644000175000017500000035030414770234736024605 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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:1415
    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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2modules_2subdir_8c_source.html0000644000175000017500000033367614770234736024774 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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:1415
    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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2mount_8c_source.html0000644000175000017500000035300714770234736023022 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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);
    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, "Spawaning %s to unumount failed",
    345 FUSERMOUNT_PROG);
    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_addclose(&action, 1);
    394 posix_spawn_file_actions_addclose(&action, 2);
    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");
    410 return -1;
    411 }
    412 // passed to child now, so can close here.
    413 close(fds[0]);
    414
    415 // Now fusermount3 will only exit when fds[1] closes automatically when our
    416 // process exits.
    417 return 0;
    418 // Note: fds[1] is leakend and doesn't get FD_CLOEXEC
    419}
    420
    421static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
    422 const char *opts, int quiet)
    423{
    424 int fds[2];
    425 pid_t pid;
    426 int res;
    427
    428 if (!mountpoint) {
    429 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
    430 return -1;
    431 }
    432
    433 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
    434 if(res == -1) {
    435 fuse_log(FUSE_LOG_ERR, "Running %s: socketpair() failed: %s\n",
    436 FUSERMOUNT_PROG, strerror(errno));
    437 return -1;
    438 }
    439
    440 char arg_fd_entry[30];
    441 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[0]);
    442 setenv(FUSE_COMMFD_ENV, arg_fd_entry, 1);
    443 /*
    444 * This helps to identify the FD hold by parent process.
    445 * In auto-unmount case, parent process can close this FD explicitly to do unmount.
    446 * The FD[1] can be got via getenv(FUSE_COMMFD2_ENV).
    447 * One potential use case is to satisfy FD-Leak checks.
    448 */
    449 snprintf(arg_fd_entry, sizeof(arg_fd_entry), "%i", fds[1]);
    450 setenv(FUSE_COMMFD2_ENV, arg_fd_entry, 1);
    451
    452 char const *const argv[] = {
    453 FUSERMOUNT_PROG,
    454 "-o", opts ? opts : "",
    455 "--",
    456 mountpoint,
    457 NULL,
    458 };
    459
    460
    461 posix_spawn_file_actions_t action;
    462 posix_spawn_file_actions_init(&action);
    463
    464 if (quiet) {
    465 posix_spawn_file_actions_addclose(&action, 1);
    466 posix_spawn_file_actions_addclose(&action, 2);
    467 }
    468 posix_spawn_file_actions_addclose(&action, fds[1]);
    469
    470 int status = fusermount_posix_spawn(&action, argv, &pid);
    471
    472 posix_spawn_file_actions_destroy(&action);
    473
    474 if(status != 0) {
    475 close(fds[0]);
    476 close(fds[1]);
    477 fuse_log(FUSE_LOG_ERR, "posix_spawnp() for %s failed",
    478 FUSERMOUNT_PROG, strerror(errno));
    479 return -1;
    480 }
    481
    482 // passed to child now, so can close here.
    483 close(fds[0]);
    484
    485 int fd = receive_fd(fds[1]);
    486
    487 if (!mo->auto_unmount) {
    488 /* with auto_unmount option fusermount3 will not exit until
    489 this socket is closed */
    490 close(fds[1]);
    491 waitpid(pid, NULL, 0); /* bury zombie */
    492 }
    493
    494 if (fd >= 0)
    495 fcntl(fd, F_SETFD, FD_CLOEXEC);
    496
    497 return fd;
    498}
    499
    500#ifndef O_CLOEXEC
    501#define O_CLOEXEC 0
    502#endif
    503
    504static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
    505 const char *mnt_opts)
    506{
    507 char tmp[128];
    508 const char *devname = "/dev/fuse";
    509 char *source = NULL;
    510 char *type = NULL;
    511 struct stat stbuf;
    512 int fd;
    513 int res;
    514
    515 if (!mnt) {
    516 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
    517 return -1;
    518 }
    519
    520 res = stat(mnt, &stbuf);
    521 if (res == -1) {
    522 fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n",
    523 mnt, strerror(errno));
    524 return -1;
    525 }
    526
    527 fd = open(devname, O_RDWR | O_CLOEXEC);
    528 if (fd == -1) {
    529 if (errno == ENODEV || errno == ENOENT)
    530 fuse_log(FUSE_LOG_ERR, "fuse: device not found, try 'modprobe fuse' first\n");
    531 else
    532 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n",
    533 devname, strerror(errno));
    534 return -1;
    535 }
    536 if (!O_CLOEXEC)
    537 fcntl(fd, F_SETFD, FD_CLOEXEC);
    538
    539 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
    540 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
    541
    542 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
    543 if (res == -1)
    544 goto out_close;
    545
    546 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
    547 (mo->subtype ? strlen(mo->subtype) : 0) +
    548 strlen(devname) + 32);
    549
    550 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
    551 if (!type || !source) {
    552 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n");
    553 goto out_close;
    554 }
    555
    556 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
    557 if (mo->subtype) {
    558 strcat(type, ".");
    559 strcat(type, mo->subtype);
    560 }
    561 strcpy(source,
    562 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
    563
    564 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
    565 if (res == -1 && errno == ENODEV && mo->subtype) {
    566 /* Probably missing subtype support */
    567 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
    568 if (mo->fsname) {
    569 if (!mo->blkdev)
    570 sprintf(source, "%s#%s", mo->subtype,
    571 mo->fsname);
    572 } else {
    573 strcpy(source, type);
    574 }
    575 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
    576 }
    577 if (res == -1) {
    578 /*
    579 * Maybe kernel doesn't support unprivileged mounts, in this
    580 * case try falling back to fusermount3
    581 */
    582 if (errno == EPERM) {
    583 res = -2;
    584 } else {
    585 int errno_save = errno;
    586 if (mo->blkdev && errno == ENODEV &&
    587 !fuse_mnt_check_fuseblk())
    588 fuse_log(FUSE_LOG_ERR,
    589 "fuse: 'fuseblk' support missing\n");
    590 else
    591 fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n",
    592 strerror(errno_save));
    593 }
    594
    595 goto out_close;
    596 }
    597
    598#ifndef IGNORE_MTAB
    599 if (geteuid() == 0) {
    600 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
    601 res = -1;
    602 if (!newmnt)
    603 goto out_umount;
    604
    605 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
    606 mnt_opts);
    607 free(newmnt);
    608 if (res == -1)
    609 goto out_umount;
    610 }
    611#endif /* IGNORE_MTAB */
    612 free(type);
    613 free(source);
    614
    615 return fd;
    616
    617out_umount:
    618 umount2(mnt, 2); /* lazy umount */
    619out_close:
    620 free(type);
    621 free(source);
    622 close(fd);
    623 return res;
    624}
    625
    626static int get_mnt_flag_opts(char **mnt_optsp, int flags)
    627{
    628 int i;
    629
    630 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
    631 return -1;
    632
    633 for (i = 0; mount_flags[i].opt != NULL; i++) {
    634 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
    635 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
    636 return -1;
    637 }
    638 return 0;
    639}
    640
    641struct mount_opts *parse_mount_opts(struct fuse_args *args)
    642{
    643 struct mount_opts *mo;
    644
    645 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
    646 if (mo == NULL)
    647 return NULL;
    648
    649 memset(mo, 0, sizeof(struct mount_opts));
    650 mo->flags = MS_NOSUID | MS_NODEV;
    651
    652 if (args &&
    653 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
    654 goto err_out;
    655
    656 return mo;
    657
    658err_out:
    659 destroy_mount_opts(mo);
    660 return NULL;
    661}
    662
    663void destroy_mount_opts(struct mount_opts *mo)
    664{
    665 free(mo->fsname);
    666 free(mo->subtype);
    667 free(mo->fusermount_opts);
    668 free(mo->subtype_opt);
    669 free(mo->kernel_opts);
    670 free(mo->mtab_opts);
    671 free(mo);
    672}
    673
    674
    675int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
    676{
    677 int res = -1;
    678 char *mnt_opts = NULL;
    679
    680 res = -1;
    681 if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1)
    682 goto out;
    683 if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1)
    684 goto out;
    685 if (mo->mtab_opts && fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1)
    686 goto out;
    687
    688 res = fuse_mount_sys(mountpoint, mo, mnt_opts);
    689 if (res >= 0 && mo->auto_unmount) {
    690 if(0 > setup_auto_unmount(mountpoint, 0)) {
    691 // Something went wrong, let's umount like in fuse_mount_sys.
    692 umount2(mountpoint, MNT_DETACH); /* lazy umount */
    693 res = -1;
    694 }
    695 } else if (res == -2) {
    696 if (mo->fusermount_opts &&
    697 fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
    698 goto out;
    699
    700 if (mo->subtype) {
    701 char *tmp_opts = NULL;
    702
    703 res = -1;
    704 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
    705 fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) {
    706 free(tmp_opts);
    707 goto out;
    708 }
    709
    710 res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1);
    711 free(tmp_opts);
    712 if (res == -1)
    713 res = fuse_mount_fusermount(mountpoint, mo,
    714 mnt_opts, 0);
    715 } else {
    716 res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
    717 }
    718 }
    719out:
    720 free(mnt_opts);
    721 return res;
    722}
    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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2mount__bsd_8c_source.html0000644000175000017500000013147614770234736024015 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2mount__util_8c_source.html0000644000175000017500000015714214770234736024220 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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
    80 return 1;
    81}
    82#endif /* IGNORE_MTAB */
    83
    84static int add_mount(const char *progname, const char *fsname,
    85 const char *mnt, const char *type, const char *opts)
    86{
    87 int res;
    88 int status;
    89 sigset_t blockmask;
    90 sigset_t oldmask;
    91
    92 sigemptyset(&blockmask);
    93 sigaddset(&blockmask, SIGCHLD);
    94 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    95 if (res == -1) {
    96 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    97 return -1;
    98 }
    99
    100 res = fork();
    101 if (res == -1) {
    102 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    103 goto out_restore;
    104 }
    105 if (res == 0) {
    106 char *env = NULL;
    107
    108 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    109
    110 if(setuid(geteuid()) == -1) {
    111 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    112 res = -1;
    113 goto out_restore;
    114 }
    115
    116 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
    117 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
    118 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
    119 progname, strerror(errno));
    120 exit(1);
    121 }
    122 res = waitpid(res, &status, 0);
    123 if (res == -1)
    124 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    125
    126 if (status != 0)
    127 res = -1;
    128
    129 out_restore:
    130 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    131
    132 return res;
    133}
    134
    135int fuse_mnt_add_mount(const char *progname, const char *fsname,
    136 const char *mnt, const char *type, const char *opts)
    137{
    138 if (!mtab_needs_update(mnt))
    139 return 0;
    140
    141 return add_mount(progname, fsname, mnt, type, opts);
    142}
    143
    144static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
    145{
    146 int res;
    147 int status;
    148 sigset_t blockmask;
    149 sigset_t oldmask;
    150
    151 sigemptyset(&blockmask);
    152 sigaddset(&blockmask, SIGCHLD);
    153 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    154 if (res == -1) {
    155 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    156 return -1;
    157 }
    158
    159 res = fork();
    160 if (res == -1) {
    161 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    162 goto out_restore;
    163 }
    164 if (res == 0) {
    165 char *env = NULL;
    166
    167 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    168
    169 if(setuid(geteuid()) == -1) {
    170 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    171 res = -1;
    172 goto out_restore;
    173 }
    174
    175 if (lazy) {
    176 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
    177 "-l", NULL, &env);
    178 } else {
    179 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
    180 NULL, &env);
    181 }
    182 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
    183 progname, strerror(errno));
    184 exit(1);
    185 }
    186 res = waitpid(res, &status, 0);
    187 if (res == -1)
    188 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    189
    190 if (status != 0) {
    191 res = -1;
    192 }
    193
    194 out_restore:
    195 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    196 return res;
    197
    198}
    199
    200int fuse_mnt_umount(const char *progname, const char *abs_mnt,
    201 const char *rel_mnt, int lazy)
    202{
    203 int res;
    204
    205 if (!mtab_needs_update(abs_mnt)) {
    206 res = umount2(rel_mnt, lazy ? 2 : 0);
    207 if (res == -1)
    208 fprintf(stderr, "%s: failed to unmount %s: %s\n",
    209 progname, abs_mnt, strerror(errno));
    210 return res;
    211 }
    212
    213 return exec_umount(progname, rel_mnt, lazy);
    214}
    215
    216static int remove_mount(const char *progname, const char *mnt)
    217{
    218 int res;
    219 int status;
    220 sigset_t blockmask;
    221 sigset_t oldmask;
    222
    223 sigemptyset(&blockmask);
    224 sigaddset(&blockmask, SIGCHLD);
    225 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    226 if (res == -1) {
    227 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    228 return -1;
    229 }
    230
    231 res = fork();
    232 if (res == -1) {
    233 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    234 goto out_restore;
    235 }
    236 if (res == 0) {
    237 char *env = NULL;
    238
    239 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    240
    241 if(setuid(geteuid()) == -1) {
    242 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    243 res = -1;
    244 goto out_restore;
    245 }
    246
    247 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
    248 "--fake", mnt, NULL, &env);
    249 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
    250 progname, strerror(errno));
    251 exit(1);
    252 }
    253 res = waitpid(res, &status, 0);
    254 if (res == -1)
    255 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    256
    257 if (status != 0)
    258 res = -1;
    259
    260 out_restore:
    261 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    262 return res;
    263}
    264
    265int fuse_mnt_remove_mount(const char *progname, const char *mnt)
    266{
    267 if (!mtab_needs_update(mnt))
    268 return 0;
    269
    270 return remove_mount(progname, mnt);
    271}
    272
    273char *fuse_mnt_resolve_path(const char *progname, const char *orig)
    274{
    275 char buf[PATH_MAX];
    276 char *copy;
    277 char *dst;
    278 char *end;
    279 char *lastcomp;
    280 const char *toresolv;
    281
    282 if (!orig[0]) {
    283 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
    284 orig);
    285 return NULL;
    286 }
    287
    288 copy = strdup(orig);
    289 if (copy == NULL) {
    290 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    291 return NULL;
    292 }
    293
    294 toresolv = copy;
    295 lastcomp = NULL;
    296 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
    297 if (end[0] != '/') {
    298 char *tmp;
    299 end[1] = '\0';
    300 tmp = strrchr(copy, '/');
    301 if (tmp == NULL) {
    302 lastcomp = copy;
    303 toresolv = ".";
    304 } else {
    305 lastcomp = tmp + 1;
    306 if (tmp == copy)
    307 toresolv = "/";
    308 }
    309 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
    310 lastcomp = NULL;
    311 toresolv = copy;
    312 }
    313 else if (tmp)
    314 tmp[0] = '\0';
    315 }
    316 if (realpath(toresolv, buf) == NULL) {
    317 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
    318 strerror(errno));
    319 free(copy);
    320 return NULL;
    321 }
    322 if (lastcomp == NULL)
    323 dst = strdup(buf);
    324 else {
    325 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
    326 if (dst) {
    327 unsigned buflen = strlen(buf);
    328 if (buflen && buf[buflen-1] == '/')
    329 sprintf(dst, "%s%s", buf, lastcomp);
    330 else
    331 sprintf(dst, "%s/%s", buf, lastcomp);
    332 }
    333 }
    334 free(copy);
    335 if (dst == NULL)
    336 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    337 return dst;
    338}
    339
    340int fuse_mnt_check_fuseblk(void)
    341{
    342 char buf[256];
    343 FILE *f = fopen("/proc/filesystems", "r");
    344 if (!f)
    345 return 1;
    346
    347 while (fgets(buf, sizeof(buf), f))
    348 if (strstr(buf, "fuseblk\n")) {
    349 fclose(f);
    350 return 1;
    351 }
    352
    353 fclose(f);
    354 return 0;
    355}
    356
    357int fuse_mnt_parse_fuse_fd(const char *mountpoint)
    358{
    359 int fd = -1;
    360 int len = 0;
    361
    362 if (mountpoint == NULL) {
    363 fprintf(stderr, "Invalid null-ptr mount-point!\n");
    364 return -1;
    365 }
    366
    367 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
    368 len == strlen(mountpoint)) {
    369 return fd;
    370 }
    371
    372 return -1;
    373}
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2lib_2mount__util_8h_source.html0000644000175000017500000001364414770234736024223 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2lib_2util_8c_source.html0000644000175000017500000001343514770234736022633 0ustar berndbernd libfuse: fuse-3.17.1-rc1/lib/util.c Source File
    libfuse
    util.c
    1#include <stdlib.h>
    2#include <errno.h>
    3
    4#include "util.h"
    5
    6int libfuse_strtol(const char *str, long *res)
    7{
    8 char *endptr;
    9 int base = 10;
    10 long val;
    11
    12 errno = 0;
    13
    14 if (!str)
    15 return -EINVAL;
    16
    17 val = strtol(str, &endptr, base);
    18
    19 if (errno)
    20 return -errno;
    21
    22 if (endptr == str || *endptr != '\0')
    23 return -EINVAL;
    24
    25 *res = val;
    26 return 0;
    27}
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2lib_2util_8h_source.html0000644000175000017500000000562014770234736022635 0ustar berndbernd libfuse: fuse-3.17.1-rc1/lib/util.h Source File
    libfuse
    util.h
    1#define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
    2
    3int libfuse_strtol(const char *str, long *res);
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2test_2readdir__inode_8c_source.html0000644000175000017500000002532414770234736025016 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2test_2release__unlink__race_8c_source.html0000644000175000017500000005426414770234736026364 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2test_2stracedecode_8c_source.html0000644000175000017500000010623214770234736024512 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2test_2test__setattr_8c_source.html0000644000175000017500000012165514770234736024757 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2test_2test__syscalls_8c_source.html0000644000175000017500000114274214770234736025127 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2test_2test__write__cache_8c_source.html0000644000175000017500000017415614770234736025711 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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) {
    71 assert(fuse_get_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE));
    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
    int fuse_set_signal_handlers(struct fuse_session *se)
    @ FUSE_CAP_WRITEBACK_CACHE
    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
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2test_2wrong__command_8c_source.html0000644000175000017500000001204214770234736025051 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2util_2fusermount_8c_source.html0000644000175000017500000076724414770234736024312 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 and strchrnul */
    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#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
    40
    41#define FUSE_DEV "/dev/fuse"
    42
    43static const char *progname;
    44
    45static int user_allow_other = 0;
    46static int mount_max = 1000;
    47
    48static int auto_unmount = 0;
    49
    50#ifdef GETMNTENT_NEEDS_UNESCAPING
    51// Older versions of musl libc don't unescape entries in /etc/mtab
    52
    53// unescapes octal sequences like \040 in-place
    54// That's ok, because unescaping can not extend the length of the string.
    55static void unescape(char *buf) {
    56 char *src = buf;
    57 char *dest = buf;
    58 while (1) {
    59 char *next_src = strchrnul(src, '\\');
    60 int offset = next_src - src;
    61 memmove(dest, src, offset);
    62 src = next_src;
    63 dest += offset;
    64
    65 if(*src == '\0') {
    66 *dest = *src;
    67 return;
    68 }
    69 src++;
    70
    71 if('0' <= src[0] && src[0] < '2' &&
    72 '0' <= src[1] && src[1] < '8' &&
    73 '0' <= src[2] && src[2] < '8') {
    74 *dest++ = (src[0] - '0') << 6
    75 | (src[1] - '0') << 3
    76 | (src[2] - '0') << 0;
    77 src += 3;
    78 } else if (src[0] == '\\') {
    79 *dest++ = '\\';
    80 src += 1;
    81 } else {
    82 *dest++ = '\\';
    83 }
    84 }
    85}
    86
    87static struct mntent *GETMNTENT(FILE *stream)
    88{
    89 struct mntent *entp = getmntent(stream);
    90 if(entp != NULL) {
    91 unescape(entp->mnt_fsname);
    92 unescape(entp->mnt_dir);
    93 unescape(entp->mnt_type);
    94 unescape(entp->mnt_opts);
    95 }
    96 return entp;
    97}
    98#else
    99#define GETMNTENT getmntent
    100#endif // GETMNTENT_NEEDS_UNESCAPING
    101
    102/*
    103 * Take a ',' separated option string and extract "x-" options
    104 */
    105static int extract_x_options(const char *original, char **non_x_opts,
    106 char **x_opts)
    107{
    108 size_t orig_len;
    109 const char *opt, *opt_end;
    110
    111 orig_len = strlen(original) + 1;
    112
    113 *non_x_opts = calloc(1, orig_len);
    114 *x_opts = calloc(1, orig_len);
    115
    116 size_t non_x_opts_len = orig_len;
    117 size_t x_opts_len = orig_len;
    118
    119 if (*non_x_opts == NULL || *x_opts == NULL) {
    120 fprintf(stderr, "%s: Failed to allocate %zuB.\n",
    121 __func__, orig_len);
    122 return -ENOMEM;
    123 }
    124
    125 for (opt = original; opt < original + orig_len; opt = opt_end + 1) {
    126 char *opt_buf;
    127
    128 opt_end = strchr(opt, ',');
    129 if (opt_end == NULL)
    130 opt_end = original + orig_len;
    131
    132 size_t opt_len = opt_end - opt;
    133 size_t opt_len_left = orig_len - (opt - original);
    134 size_t buf_len;
    135 bool is_x_opts;
    136
    137 if (strncmp(opt, "x-", MIN(2, opt_len_left)) == 0) {
    138 buf_len = x_opts_len;
    139 is_x_opts = true;
    140 opt_buf = *x_opts;
    141 } else {
    142 buf_len = non_x_opts_len;
    143 is_x_opts = false;
    144 opt_buf = *non_x_opts;
    145 }
    146
    147 if (buf_len < orig_len) {
    148 strncat(opt_buf, ",", 2);
    149 buf_len -= 1;
    150 }
    151
    152 /* omits ',' */
    153 if ((ssize_t)(buf_len - opt_len) < 0) {
    154 /* This would be a bug */
    155 fprintf(stderr, "%s: no buf space left in copy, orig='%s'\n",
    156 __func__, original);
    157 return -EIO;
    158 }
    159
    160 strncat(opt_buf, opt, opt_end - opt);
    161 buf_len -= opt_len;
    162
    163 if (is_x_opts)
    164 x_opts_len = buf_len;
    165 else
    166 non_x_opts_len = buf_len;
    167 }
    168
    169 return 0;
    170}
    171
    172static const char *get_user_name(void)
    173{
    174 struct passwd *pw = getpwuid(getuid());
    175 if (pw != NULL && pw->pw_name != NULL)
    176 return pw->pw_name;
    177 else {
    178 fprintf(stderr, "%s: could not determine username\n", progname);
    179 return NULL;
    180 }
    181}
    182
    183static uid_t oldfsuid;
    184static gid_t oldfsgid;
    185
    186static void drop_privs(void)
    187{
    188 if (getuid() != 0) {
    189 oldfsuid = setfsuid(getuid());
    190 oldfsgid = setfsgid(getgid());
    191 }
    192}
    193
    194static void restore_privs(void)
    195{
    196 if (getuid() != 0) {
    197 setfsuid(oldfsuid);
    198 setfsgid(oldfsgid);
    199 }
    200}
    201
    202#ifndef IGNORE_MTAB
    203/*
    204 * Make sure that /etc/mtab is checked and updated atomically
    205 */
    206static int lock_umount(void)
    207{
    208 const char *mtab_lock = _PATH_MOUNTED ".fuselock";
    209 int mtablock;
    210 int res;
    211 struct stat mtab_stat;
    212
    213 /* /etc/mtab could be a symlink to /proc/mounts */
    214 if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
    215 return -1;
    216
    217 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
    218 if (mtablock == -1) {
    219 fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
    220 progname, strerror(errno));
    221 return -1;
    222 }
    223 res = lockf(mtablock, F_LOCK, 0);
    224 if (res < 0) {
    225 fprintf(stderr, "%s: error getting lock: %s\n", progname,
    226 strerror(errno));
    227 close(mtablock);
    228 return -1;
    229 }
    230
    231 return mtablock;
    232}
    233
    234static void unlock_umount(int mtablock)
    235{
    236 if (mtablock >= 0) {
    237 int res;
    238
    239 res = lockf(mtablock, F_ULOCK, 0);
    240 if (res < 0) {
    241 fprintf(stderr, "%s: error releasing lock: %s\n",
    242 progname, strerror(errno));
    243 }
    244 close(mtablock);
    245 }
    246}
    247
    248static int add_mount(const char *source, const char *mnt, const char *type,
    249 const char *opts)
    250{
    251 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
    252}
    253
    254static int may_unmount(const char *mnt, int quiet)
    255{
    256 struct mntent *entp;
    257 FILE *fp;
    258 const char *user = NULL;
    259 char uidstr[32];
    260 unsigned uidlen = 0;
    261 int found;
    262 const char *mtab = _PATH_MOUNTED;
    263
    264 user = get_user_name();
    265 if (user == NULL)
    266 return -1;
    267
    268 fp = setmntent(mtab, "r");
    269 if (fp == NULL) {
    270 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
    271 strerror(errno));
    272 return -1;
    273 }
    274
    275 uidlen = sprintf(uidstr, "%u", getuid());
    276
    277 found = 0;
    278 while ((entp = GETMNTENT(fp)) != NULL) {
    279 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
    280 (strcmp(entp->mnt_type, "fuse") == 0 ||
    281 strcmp(entp->mnt_type, "fuseblk") == 0 ||
    282 strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
    283 strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
    284 char *p = strstr(entp->mnt_opts, "user=");
    285 if (p &&
    286 (p == entp->mnt_opts || *(p-1) == ',') &&
    287 strcmp(p + 5, user) == 0) {
    288 found = 1;
    289 break;
    290 }
    291 /* /etc/mtab is a link pointing to
    292 /proc/mounts: */
    293 else if ((p =
    294 strstr(entp->mnt_opts, "user_id=")) &&
    295 (p == entp->mnt_opts ||
    296 *(p-1) == ',') &&
    297 strncmp(p + 8, uidstr, uidlen) == 0 &&
    298 (*(p+8+uidlen) == ',' ||
    299 *(p+8+uidlen) == '\0')) {
    300 found = 1;
    301 break;
    302 }
    303 }
    304 }
    305 endmntent(fp);
    306
    307 if (!found) {
    308 if (!quiet)
    309 fprintf(stderr,
    310 "%s: entry for %s not found in %s\n",
    311 progname, mnt, mtab);
    312 return -1;
    313 }
    314
    315 return 0;
    316}
    317#endif
    318
    319/*
    320 * Check whether the file specified in "fusermount3 -u" is really a
    321 * mountpoint and not a symlink. This is necessary otherwise the user
    322 * could move the mountpoint away and replace it with a symlink
    323 * pointing to an arbitrary mount, thereby tricking fusermount3 into
    324 * unmounting that (umount(2) will follow symlinks).
    325 *
    326 * This is the child process running in a separate mount namespace, so
    327 * we don't mess with the global namespace and if the process is
    328 * killed for any reason, mounts are automatically cleaned up.
    329 *
    330 * First make sure nothing is propagated back into the parent
    331 * namespace by marking all mounts "private".
    332 *
    333 * Then bind mount parent onto a stable base where the user can't move
    334 * it around.
    335 *
    336 * Finally check /proc/mounts for an entry matching the requested
    337 * mountpoint. If it's found then we are OK, and the user can't move
    338 * it around within the parent directory as rename() will return
    339 * EBUSY. Be careful to ignore any mounts that existed before the
    340 * bind.
    341 */
    342static int check_is_mount_child(void *p)
    343{
    344 const char **a = p;
    345 const char *last = a[0];
    346 const char *mnt = a[1];
    347 const char *type = a[2];
    348 int res;
    349 const char *procmounts = "/proc/mounts";
    350 int found;
    351 FILE *fp;
    352 struct mntent *entp;
    353 int count;
    354
    355 res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
    356 if (res == -1) {
    357 fprintf(stderr, "%s: failed to mark mounts private: %s\n",
    358 progname, strerror(errno));
    359 return 1;
    360 }
    361
    362 fp = setmntent(procmounts, "r");
    363 if (fp == NULL) {
    364 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
    365 procmounts, strerror(errno));
    366 return 1;
    367 }
    368
    369 count = 0;
    370 while (GETMNTENT(fp) != NULL)
    371 count++;
    372 endmntent(fp);
    373
    374 fp = setmntent(procmounts, "r");
    375 if (fp == NULL) {
    376 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
    377 procmounts, strerror(errno));
    378 return 1;
    379 }
    380
    381 res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
    382 if (res == -1) {
    383 fprintf(stderr, "%s: failed to bind parent to /: %s\n",
    384 progname, strerror(errno));
    385 return 1;
    386 }
    387
    388 found = 0;
    389 while ((entp = GETMNTENT(fp)) != NULL) {
    390 if (count > 0) {
    391 count--;
    392 continue;
    393 }
    394 if (entp->mnt_dir[0] == '/' &&
    395 strcmp(entp->mnt_dir + 1, last) == 0 &&
    396 (!type || strcmp(entp->mnt_type, type) == 0)) {
    397 found = 1;
    398 break;
    399 }
    400 }
    401 endmntent(fp);
    402
    403 if (!found) {
    404 fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
    405 return 1;
    406 }
    407
    408 return 0;
    409}
    410
    411static pid_t clone_newns(void *a)
    412{
    413 char buf[131072];
    414 char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
    415
    416#ifdef __ia64__
    417 extern int __clone2(int (*fn)(void *),
    418 void *child_stack_base, size_t stack_size,
    419 int flags, void *arg, pid_t *ptid,
    420 void *tls, pid_t *ctid);
    421
    422 return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
    423 CLONE_NEWNS, a, NULL, NULL, NULL);
    424#else
    425 return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
    426#endif
    427}
    428
    429static int check_is_mount(const char *last, const char *mnt, const char *type)
    430{
    431 pid_t pid, p;
    432 int status;
    433 const char *a[3] = { last, mnt, type };
    434
    435 pid = clone_newns((void *) a);
    436 if (pid == (pid_t) -1) {
    437 fprintf(stderr, "%s: failed to clone namespace: %s\n",
    438 progname, strerror(errno));
    439 return -1;
    440 }
    441 p = waitpid(pid, &status, __WCLONE);
    442 if (p == (pid_t) -1) {
    443 fprintf(stderr, "%s: waitpid failed: %s\n",
    444 progname, strerror(errno));
    445 return -1;
    446 }
    447 if (!WIFEXITED(status)) {
    448 fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
    449 progname, status);
    450 return -1;
    451 }
    452 if (WEXITSTATUS(status) != 0)
    453 return -1;
    454
    455 return 0;
    456}
    457
    458static int chdir_to_parent(char *copy, const char **lastp)
    459{
    460 char *tmp;
    461 const char *parent;
    462 char buf[65536];
    463 int res;
    464
    465 tmp = strrchr(copy, '/');
    466 if (tmp == NULL || tmp[1] == '\0') {
    467 fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
    468 progname, copy);
    469 return -1;
    470 }
    471 if (tmp != copy) {
    472 *tmp = '\0';
    473 parent = copy;
    474 *lastp = tmp + 1;
    475 } else if (tmp[1] != '\0') {
    476 *lastp = tmp + 1;
    477 parent = "/";
    478 } else {
    479 *lastp = ".";
    480 parent = "/";
    481 }
    482
    483 res = chdir(parent);
    484 if (res == -1) {
    485 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
    486 progname, parent, strerror(errno));
    487 return -1;
    488 }
    489
    490 if (getcwd(buf, sizeof(buf)) == NULL) {
    491 fprintf(stderr, "%s: failed to obtain current directory: %s\n",
    492 progname, strerror(errno));
    493 return -1;
    494 }
    495 if (strcmp(buf, parent) != 0) {
    496 fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
    497 parent, buf);
    498 return -1;
    499
    500 }
    501
    502 return 0;
    503}
    504
    505#ifndef IGNORE_MTAB
    506static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
    507{
    508 int res;
    509 char *copy;
    510 const char *last;
    511 int umount_flags = (lazy ? UMOUNT_DETACH : 0) | UMOUNT_NOFOLLOW;
    512
    513 if (getuid() != 0) {
    514 res = may_unmount(mnt, quiet);
    515 if (res == -1)
    516 return -1;
    517 }
    518
    519 copy = strdup(mnt);
    520 if (copy == NULL) {
    521 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    522 return -1;
    523 }
    524
    525 drop_privs();
    526 res = chdir_to_parent(copy, &last);
    527 if (res == -1) {
    528 restore_privs();
    529 goto out;
    530 }
    531
    532 res = umount2(last, umount_flags);
    533 restore_privs();
    534 if (res == -1 && !quiet) {
    535 fprintf(stderr, "%s: failed to unmount %s: %s\n",
    536 progname, mnt, strerror(errno));
    537 }
    538
    539out:
    540 free(copy);
    541 if (res == -1)
    542 return -1;
    543
    544 res = chdir("/");
    545 if (res == -1) {
    546 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
    547 return -1;
    548 }
    549
    550 return fuse_mnt_remove_mount(progname, mnt);
    551}
    552
    553static int unmount_fuse(const char *mnt, int quiet, int lazy)
    554{
    555 int res;
    556 int mtablock = lock_umount();
    557
    558 res = unmount_fuse_locked(mnt, quiet, lazy);
    559 unlock_umount(mtablock);
    560
    561 return res;
    562}
    563
    564static int count_fuse_fs(void)
    565{
    566 struct mntent *entp;
    567 int count = 0;
    568 const char *mtab = _PATH_MOUNTED;
    569 FILE *fp = setmntent(mtab, "r");
    570 if (fp == NULL) {
    571 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
    572 strerror(errno));
    573 return -1;
    574 }
    575 while ((entp = GETMNTENT(fp)) != NULL) {
    576 if (strcmp(entp->mnt_type, "fuse") == 0 ||
    577 strncmp(entp->mnt_type, "fuse.", 5) == 0)
    578 count ++;
    579 }
    580 endmntent(fp);
    581 return count;
    582}
    583
    584
    585#else /* IGNORE_MTAB */
    586static int count_fuse_fs(void)
    587{
    588 return 0;
    589}
    590
    591static int add_mount(const char *source, const char *mnt, const char *type,
    592 const char *opts)
    593{
    594 (void) source;
    595 (void) mnt;
    596 (void) type;
    597 (void) opts;
    598 return 0;
    599}
    600
    601static int unmount_fuse(const char *mnt, int quiet, int lazy)
    602{
    603 (void) quiet;
    604 return fuse_mnt_umount(progname, mnt, mnt, lazy);
    605}
    606#endif /* IGNORE_MTAB */
    607
    608static void strip_line(char *line)
    609{
    610 char *s = strchr(line, '#');
    611 if (s != NULL)
    612 s[0] = '\0';
    613 for (s = line + strlen(line) - 1;
    614 s >= line && isspace((unsigned char) *s); s--);
    615 s[1] = '\0';
    616 for (s = line; isspace((unsigned char) *s); s++);
    617 if (s != line)
    618 memmove(line, s, strlen(s)+1);
    619}
    620
    621static void parse_line(char *line, int linenum)
    622{
    623 int tmp;
    624 if (strcmp(line, "user_allow_other") == 0)
    625 user_allow_other = 1;
    626 else if (sscanf(line, "mount_max = %i", &tmp) == 1)
    627 mount_max = tmp;
    628 else if(line[0])
    629 fprintf(stderr,
    630 "%s: unknown parameter in %s at line %i: '%s'\n",
    631 progname, FUSE_CONF, linenum, line);
    632}
    633
    634static void read_conf(void)
    635{
    636 FILE *fp = fopen(FUSE_CONF, "r");
    637 if (fp != NULL) {
    638 int linenum = 1;
    639 char line[256];
    640 int isnewline = 1;
    641 while (fgets(line, sizeof(line), fp) != NULL) {
    642 if (isnewline) {
    643 if (line[strlen(line)-1] == '\n') {
    644 strip_line(line);
    645 parse_line(line, linenum);
    646 } else {
    647 isnewline = 0;
    648 }
    649 } else if(line[strlen(line)-1] == '\n') {
    650 fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
    651
    652 isnewline = 1;
    653 }
    654 if (isnewline)
    655 linenum ++;
    656 }
    657 if (!isnewline) {
    658 fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
    659
    660 }
    661 if (ferror(fp)) {
    662 fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF);
    663 exit(1);
    664 }
    665 fclose(fp);
    666 } else if (errno != ENOENT) {
    667 bool fatal = (errno != EACCES && errno != ELOOP &&
    668 errno != ENAMETOOLONG && errno != ENOTDIR &&
    669 errno != EOVERFLOW);
    670 fprintf(stderr, "%s: failed to open %s: %s\n",
    671 progname, FUSE_CONF, strerror(errno));
    672 if (fatal)
    673 exit(1);
    674 }
    675}
    676
    677static int begins_with(const char *s, const char *beg)
    678{
    679 if (strncmp(s, beg, strlen(beg)) == 0)
    680 return 1;
    681 else
    682 return 0;
    683}
    684
    685struct mount_flags {
    686 const char *opt;
    687 unsigned long flag;
    688 int on;
    689 int safe;
    690};
    691
    692static struct mount_flags mount_flags[] = {
    693 {"rw", MS_RDONLY, 0, 1},
    694 {"ro", MS_RDONLY, 1, 1},
    695 {"suid", MS_NOSUID, 0, 0},
    696 {"nosuid", MS_NOSUID, 1, 1},
    697 {"dev", MS_NODEV, 0, 0},
    698 {"nodev", MS_NODEV, 1, 1},
    699 {"exec", MS_NOEXEC, 0, 1},
    700 {"noexec", MS_NOEXEC, 1, 1},
    701 {"async", MS_SYNCHRONOUS, 0, 1},
    702 {"sync", MS_SYNCHRONOUS, 1, 1},
    703 {"atime", MS_NOATIME, 0, 1},
    704 {"noatime", MS_NOATIME, 1, 1},
    705 {"diratime", MS_NODIRATIME, 0, 1},
    706 {"nodiratime", MS_NODIRATIME, 1, 1},
    707 {"lazytime", MS_LAZYTIME, 1, 1},
    708 {"nolazytime", MS_LAZYTIME, 0, 1},
    709 {"relatime", MS_RELATIME, 1, 1},
    710 {"norelatime", MS_RELATIME, 0, 1},
    711 {"strictatime", MS_STRICTATIME, 1, 1},
    712 {"nostrictatime", MS_STRICTATIME, 0, 1},
    713 {"dirsync", MS_DIRSYNC, 1, 1},
    714 {"symfollow", MS_NOSYMFOLLOW, 0, 1},
    715 {"nosymfollow", MS_NOSYMFOLLOW, 1, 1},
    716 {NULL, 0, 0, 0}
    717};
    718
    719static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
    720{
    721 int i;
    722
    723 for (i = 0; mount_flags[i].opt != NULL; i++) {
    724 const char *opt = mount_flags[i].opt;
    725 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
    726 *on = mount_flags[i].on;
    727 *flag = mount_flags[i].flag;
    728 if (!mount_flags[i].safe && getuid() != 0) {
    729 *flag = 0;
    730 fprintf(stderr,
    731 "%s: unsafe option %s ignored\n",
    732 progname, opt);
    733 }
    734 return 1;
    735 }
    736 }
    737 return 0;
    738}
    739
    740static int add_option(char **optsp, const char *opt, unsigned expand)
    741{
    742 char *newopts;
    743 if (*optsp == NULL)
    744 newopts = strdup(opt);
    745 else {
    746 unsigned oldsize = strlen(*optsp);
    747 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
    748 newopts = (char *) realloc(*optsp, newsize);
    749 if (newopts)
    750 sprintf(newopts + oldsize, ",%s", opt);
    751 }
    752 if (newopts == NULL) {
    753 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    754 return -1;
    755 }
    756 *optsp = newopts;
    757 return 0;
    758}
    759
    760static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
    761{
    762 int i;
    763 int l;
    764
    765 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
    766 return -1;
    767
    768 for (i = 0; mount_flags[i].opt != NULL; i++) {
    769 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
    770 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
    771 return -1;
    772 }
    773
    774 if (add_option(mnt_optsp, opts, 0) == -1)
    775 return -1;
    776 /* remove comma from end of opts*/
    777 l = strlen(*mnt_optsp);
    778 if ((*mnt_optsp)[l-1] == ',')
    779 (*mnt_optsp)[l-1] = '\0';
    780 if (getuid() != 0) {
    781 const char *user = get_user_name();
    782 if (user == NULL)
    783 return -1;
    784
    785 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
    786 return -1;
    787 strcat(*mnt_optsp, user);
    788 }
    789 return 0;
    790}
    791
    792static int opt_eq(const char *s, unsigned len, const char *opt)
    793{
    794 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
    795 return 1;
    796 else
    797 return 0;
    798}
    799
    800static int get_string_opt(const char *s, unsigned len, const char *opt,
    801 char **val)
    802{
    803 int i;
    804 unsigned opt_len = strlen(opt);
    805 char *d;
    806
    807 if (*val)
    808 free(*val);
    809 *val = (char *) malloc(len - opt_len + 1);
    810 if (!*val) {
    811 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    812 return 0;
    813 }
    814
    815 d = *val;
    816 s += opt_len;
    817 len -= opt_len;
    818 for (i = 0; i < len; i++) {
    819 if (s[i] == '\\' && i + 1 < len)
    820 i++;
    821 *d++ = s[i];
    822 }
    823 *d = '\0';
    824 return 1;
    825}
    826
    827/* The kernel silently truncates the "data" argument to PAGE_SIZE-1 characters.
    828 * This can be dangerous if it e.g. truncates the option "group_id=1000" to
    829 * "group_id=1".
    830 * This wrapper detects this case and bails out with an error.
    831 */
    832static int mount_notrunc(const char *source, const char *target,
    833 const char *filesystemtype, unsigned long mountflags,
    834 const char *data) {
    835 if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) {
    836 fprintf(stderr, "%s: mount options too long\n", progname);
    837 errno = EINVAL;
    838 return -1;
    839 }
    840 return mount(source, target, filesystemtype, mountflags, data);
    841}
    842
    843
    844static int do_mount(const char *mnt, const char **typep, mode_t rootmode,
    845 int fd, const char *opts, const char *dev, char **sourcep,
    846 char **mnt_optsp)
    847{
    848 int res;
    849 int flags = MS_NOSUID | MS_NODEV;
    850 char *optbuf;
    851 char *mnt_opts = NULL;
    852 const char *s;
    853 char *d;
    854 char *fsname = NULL;
    855 char *subtype = NULL;
    856 char *source = NULL;
    857 char *type = NULL;
    858 int blkdev = 0;
    859
    860 optbuf = (char *) malloc(strlen(opts) + 128);
    861 if (!optbuf) {
    862 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    863 return -1;
    864 }
    865
    866 for (s = opts, d = optbuf; *s;) {
    867 unsigned len;
    868 const char *fsname_str = "fsname=";
    869 const char *subtype_str = "subtype=";
    870 bool escape_ok = begins_with(s, fsname_str) ||
    871 begins_with(s, subtype_str);
    872 for (len = 0; s[len]; len++) {
    873 if (escape_ok && s[len] == '\\' && s[len + 1])
    874 len++;
    875 else if (s[len] == ',')
    876 break;
    877 }
    878 if (begins_with(s, fsname_str)) {
    879 if (!get_string_opt(s, len, fsname_str, &fsname))
    880 goto err;
    881 } else if (begins_with(s, subtype_str)) {
    882 if (!get_string_opt(s, len, subtype_str, &subtype))
    883 goto err;
    884 } else if (opt_eq(s, len, "blkdev")) {
    885 if (getuid() != 0) {
    886 fprintf(stderr,
    887 "%s: option blkdev is privileged\n",
    888 progname);
    889 goto err;
    890 }
    891 blkdev = 1;
    892 } else if (opt_eq(s, len, "auto_unmount")) {
    893 auto_unmount = 1;
    894 } else if (!opt_eq(s, len, "nonempty") &&
    895 !begins_with(s, "fd=") &&
    896 !begins_with(s, "rootmode=") &&
    897 !begins_with(s, "user_id=") &&
    898 !begins_with(s, "group_id=")) {
    899 int on;
    900 int flag;
    901 int skip_option = 0;
    902 if (opt_eq(s, len, "large_read")) {
    903 struct utsname utsname;
    904 unsigned kmaj, kmin;
    905 res = uname(&utsname);
    906 if (res == 0 &&
    907 sscanf(utsname.release, "%u.%u",
    908 &kmaj, &kmin) == 2 &&
    909 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
    910 fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
    911 skip_option = 1;
    912 }
    913 }
    914 if (getuid() != 0 && !user_allow_other &&
    915 (opt_eq(s, len, "allow_other") ||
    916 opt_eq(s, len, "allow_root"))) {
    917 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
    918 goto err;
    919 }
    920 if (!skip_option) {
    921 if (find_mount_flag(s, len, &on, &flag)) {
    922 if (on)
    923 flags |= flag;
    924 else
    925 flags &= ~flag;
    926 } else if (opt_eq(s, len, "default_permissions") ||
    927 opt_eq(s, len, "allow_other") ||
    928 begins_with(s, "max_read=") ||
    929 begins_with(s, "blksize=")) {
    930 memcpy(d, s, len);
    931 d += len;
    932 *d++ = ',';
    933 } else {
    934 fprintf(stderr, "%s: unknown option '%.*s'\n", progname, len, s);
    935 exit(1);
    936 }
    937 }
    938 }
    939 s += len;
    940 if (*s)
    941 s++;
    942 }
    943 *d = '\0';
    944 res = get_mnt_opts(flags, optbuf, &mnt_opts);
    945 if (res == -1)
    946 goto err;
    947
    948 sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
    949 fd, rootmode, getuid(), getgid());
    950
    951 source = malloc((fsname ? strlen(fsname) : 0) +
    952 (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
    953
    954 type = malloc((subtype ? strlen(subtype) : 0) + 32);
    955 if (!type || !source) {
    956 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    957 goto err;
    958 }
    959
    960 if (subtype)
    961 sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
    962 else
    963 strcpy(type, blkdev ? "fuseblk" : "fuse");
    964
    965 if (fsname)
    966 strcpy(source, fsname);
    967 else
    968 strcpy(source, subtype ? subtype : dev);
    969
    970 res = mount_notrunc(source, mnt, type, flags, optbuf);
    971 if (res == -1 && errno == ENODEV && subtype) {
    972 /* Probably missing subtype support */
    973 strcpy(type, blkdev ? "fuseblk" : "fuse");
    974 if (fsname) {
    975 if (!blkdev)
    976 sprintf(source, "%s#%s", subtype, fsname);
    977 } else {
    978 strcpy(source, type);
    979 }
    980
    981 res = mount_notrunc(source, mnt, type, flags, optbuf);
    982 }
    983 if (res == -1 && errno == EINVAL) {
    984 /* It could be an old version not supporting group_id */
    985 sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
    986 fd, rootmode, getuid());
    987 res = mount_notrunc(source, mnt, type, flags, optbuf);
    988 }
    989 if (res == -1) {
    990 int errno_save = errno;
    991 if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
    992 fprintf(stderr, "%s: 'fuseblk' support missing\n",
    993 progname);
    994 else
    995 fprintf(stderr, "%s: mount failed: %s\n", progname,
    996 strerror(errno_save));
    997 goto err;
    998 }
    999 *sourcep = source;
    1000 *typep = type;
    1001 *mnt_optsp = mnt_opts;
    1002 free(fsname);
    1003 free(optbuf);
    1004
    1005 return 0;
    1006
    1007err:
    1008 free(fsname);
    1009 free(subtype);
    1010 free(source);
    1011 free(type);
    1012 free(mnt_opts);
    1013 free(optbuf);
    1014 return -1;
    1015}
    1016
    1017static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
    1018{
    1019 int res;
    1020 const char *mnt = *mntp;
    1021 const char *origmnt = mnt;
    1022 struct statfs fs_buf;
    1023 size_t i;
    1024
    1025 res = lstat(mnt, stbuf);
    1026 if (res == -1) {
    1027 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
    1028 progname, mnt, strerror(errno));
    1029 return -1;
    1030 }
    1031
    1032 /* No permission checking is done for root */
    1033 if (getuid() == 0)
    1034 return 0;
    1035
    1036 if (S_ISDIR(stbuf->st_mode)) {
    1037 res = chdir(mnt);
    1038 if (res == -1) {
    1039 fprintf(stderr,
    1040 "%s: failed to chdir to mountpoint: %s\n",
    1041 progname, strerror(errno));
    1042 return -1;
    1043 }
    1044 mnt = *mntp = ".";
    1045 res = lstat(mnt, stbuf);
    1046 if (res == -1) {
    1047 fprintf(stderr,
    1048 "%s: failed to access mountpoint %s: %s\n",
    1049 progname, origmnt, strerror(errno));
    1050 return -1;
    1051 }
    1052
    1053 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
    1054 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
    1055 progname, origmnt);
    1056 return -1;
    1057 }
    1058
    1059 res = access(mnt, W_OK);
    1060 if (res == -1) {
    1061 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
    1062 progname, origmnt);
    1063 return -1;
    1064 }
    1065 } else if (S_ISREG(stbuf->st_mode)) {
    1066 static char procfile[256];
    1067 *mountpoint_fd = open(mnt, O_WRONLY);
    1068 if (*mountpoint_fd == -1) {
    1069 fprintf(stderr, "%s: failed to open %s: %s\n",
    1070 progname, mnt, strerror(errno));
    1071 return -1;
    1072 }
    1073 res = fstat(*mountpoint_fd, stbuf);
    1074 if (res == -1) {
    1075 fprintf(stderr,
    1076 "%s: failed to access mountpoint %s: %s\n",
    1077 progname, mnt, strerror(errno));
    1078 return -1;
    1079 }
    1080 if (!S_ISREG(stbuf->st_mode)) {
    1081 fprintf(stderr,
    1082 "%s: mountpoint %s is no longer a regular file\n",
    1083 progname, mnt);
    1084 return -1;
    1085 }
    1086
    1087 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
    1088 *mntp = procfile;
    1089 } else {
    1090 fprintf(stderr,
    1091 "%s: mountpoint %s is not a directory or a regular file\n",
    1092 progname, mnt);
    1093 return -1;
    1094 }
    1095
    1096 /* Do not permit mounting over anything in procfs - it has a couple
    1097 * places to which we have "write access" without being supposed to be
    1098 * able to just put anything we want there.
    1099 * Luckily, without allow_other, we can't get other users to actually
    1100 * use any fake information we try to put there anyway.
    1101 * Use a whitelist to be safe. */
    1102 if (statfs(*mntp, &fs_buf)) {
    1103 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
    1104 progname, mnt, strerror(errno));
    1105 return -1;
    1106 }
    1107
    1108 /* Define permitted filesystems for the mount target. This was
    1109 * originally the same list as used by the ecryptfs mount helper
    1110 * (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
    1111 * but got expanded as we found more filesystems that needed to be
    1112 * overlaid. */
    1113 typeof(fs_buf.f_type) f_type_whitelist[] = {
    1114 0x61756673 /* AUFS_SUPER_MAGIC */,
    1115 0x00000187 /* AUTOFS_SUPER_MAGIC */,
    1116 0xCA451A4E /* BCACHEFS_STATFS_MAGIC */,
    1117 0x9123683E /* BTRFS_SUPER_MAGIC */,
    1118 0x00C36400 /* CEPH_SUPER_MAGIC */,
    1119 0xFF534D42 /* CIFS_MAGIC_NUMBER */,
    1120 0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
    1121 0X2011BAB0 /* EXFAT_SUPER_MAGIC */,
    1122 0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
    1123 0xF2F52010 /* F2FS_SUPER_MAGIC */,
    1124 0x65735546 /* FUSE_SUPER_MAGIC */,
    1125 0x01161970 /* GFS2_MAGIC */,
    1126 0x47504653 /* GPFS_SUPER_MAGIC */,
    1127 0x0000482b /* HFSPLUS_SUPER_MAGIC */,
    1128 0x000072B6 /* JFFS2_SUPER_MAGIC */,
    1129 0x3153464A /* JFS_SUPER_MAGIC */,
    1130 0x0BD00BD0 /* LL_SUPER_MAGIC */,
    1131 0X00004D44 /* MSDOS_SUPER_MAGIC */,
    1132 0x0000564C /* NCP_SUPER_MAGIC */,
    1133 0x00006969 /* NFS_SUPER_MAGIC */,
    1134 0x00003434 /* NILFS_SUPER_MAGIC */,
    1135 0x5346544E /* NTFS_SB_MAGIC */,
    1136 0x7366746E /* NTFS3_SUPER_MAGIC */,
    1137 0x5346414f /* OPENAFS_SUPER_MAGIC */,
    1138 0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
    1139 0x52654973 /* REISERFS_SUPER_MAGIC */,
    1140 0xFE534D42 /* SMB2_SUPER_MAGIC */,
    1141 0x73717368 /* SQUASHFS_MAGIC */,
    1142 0x01021994 /* TMPFS_MAGIC */,
    1143 0x24051905 /* UBIFS_SUPER_MAGIC */,
    1144#if __SIZEOF_LONG__ > 4
    1145 0x736675005346544e /* UFSD */,
    1146#endif
    1147 0x58465342 /* XFS_SB_MAGIC */,
    1148 0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
    1149 0x858458f6 /* RAMFS_MAGIC */,
    1150 };
    1151 for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
    1152 if (f_type_whitelist[i] == fs_buf.f_type)
    1153 return 0;
    1154 }
    1155
    1156 fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
    1157 progname, (unsigned long)fs_buf.f_type);
    1158 return -1;
    1159}
    1160
    1161static int try_open(const char *dev, char **devp, int silent)
    1162{
    1163 int fd = open(dev, O_RDWR);
    1164 if (fd != -1) {
    1165 *devp = strdup(dev);
    1166 if (*devp == NULL) {
    1167 fprintf(stderr, "%s: failed to allocate memory\n",
    1168 progname);
    1169 close(fd);
    1170 fd = -1;
    1171 }
    1172 } else if (errno == ENODEV ||
    1173 errno == ENOENT)/* check for ENOENT too, for the udev case */
    1174 return -2;
    1175 else if (!silent) {
    1176 fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
    1177 strerror(errno));
    1178 }
    1179 return fd;
    1180}
    1181
    1182static int try_open_fuse_device(char **devp)
    1183{
    1184 int fd;
    1185
    1186 drop_privs();
    1187 fd = try_open(FUSE_DEV, devp, 0);
    1188 restore_privs();
    1189 return fd;
    1190}
    1191
    1192static int open_fuse_device(char **devp)
    1193{
    1194 int fd = try_open_fuse_device(devp);
    1195 if (fd >= -1)
    1196 return fd;
    1197
    1198 fprintf(stderr,
    1199 "%s: fuse device not found, try 'modprobe fuse' first\n",
    1200 progname);
    1201
    1202 return -1;
    1203}
    1204
    1205
    1206static int mount_fuse(const char *mnt, const char *opts, const char **type)
    1207{
    1208 int res;
    1209 int fd;
    1210 char *dev;
    1211 struct stat stbuf;
    1212 char *source = NULL;
    1213 char *mnt_opts = NULL;
    1214 const char *real_mnt = mnt;
    1215 int mountpoint_fd = -1;
    1216 char *do_mount_opts = NULL;
    1217 char *x_opts = NULL;
    1218
    1219 fd = open_fuse_device(&dev);
    1220 if (fd == -1)
    1221 return -1;
    1222
    1223 drop_privs();
    1224 read_conf();
    1225
    1226 if (getuid() != 0 && mount_max != -1) {
    1227 int mount_count = count_fuse_fs();
    1228 if (mount_count >= mount_max) {
    1229 fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
    1230 goto fail_close_fd;
    1231 }
    1232 }
    1233
    1234 // Extract any options starting with "x-"
    1235 res= extract_x_options(opts, &do_mount_opts, &x_opts);
    1236 if (res)
    1237 goto fail_close_fd;
    1238
    1239 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
    1240 restore_privs();
    1241 if (res != -1)
    1242 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
    1243 fd, do_mount_opts, dev, &source, &mnt_opts);
    1244
    1245 if (mountpoint_fd != -1)
    1246 close(mountpoint_fd);
    1247
    1248 if (res == -1)
    1249 goto fail_close_fd;
    1250
    1251 res = chdir("/");
    1252 if (res == -1) {
    1253 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
    1254 goto fail_close_fd;
    1255 }
    1256
    1257 if (geteuid() == 0) {
    1258 if (x_opts && strlen(x_opts) > 0) {
    1259 /*
    1260 * Add back the options starting with "x-" to opts from
    1261 * do_mount. +2 for ',' and '\0'
    1262 */
    1263 size_t mnt_opts_len = strlen(mnt_opts);
    1264 size_t x_mnt_opts_len = mnt_opts_len+
    1265 strlen(x_opts) + 2;
    1266 char *x_mnt_opts = calloc(1, x_mnt_opts_len);
    1267
    1268 if (mnt_opts_len) {
    1269 strcpy(x_mnt_opts, mnt_opts);
    1270 strncat(x_mnt_opts, ",", 2);
    1271 }
    1272
    1273 strncat(x_mnt_opts, x_opts,
    1274 x_mnt_opts_len - mnt_opts_len - 2);
    1275
    1276 free(mnt_opts);
    1277 mnt_opts = x_mnt_opts;
    1278 }
    1279
    1280 res = add_mount(source, mnt, *type, mnt_opts);
    1281 if (res == -1) {
    1282 /* Can't clean up mount in a non-racy way */
    1283 goto fail_close_fd;
    1284 }
    1285 }
    1286
    1287out_free:
    1288 free(source);
    1289 free(mnt_opts);
    1290 free(dev);
    1291 free(x_opts);
    1292 free(do_mount_opts);
    1293
    1294 return fd;
    1295
    1296fail_close_fd:
    1297 close(fd);
    1298 fd = -1;
    1299 goto out_free;
    1300}
    1301
    1302static int send_fd(int sock_fd, int fd)
    1303{
    1304 int retval;
    1305 struct msghdr msg;
    1306 struct cmsghdr *p_cmsg;
    1307 struct iovec vec;
    1308 size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
    1309 int *p_fds;
    1310 char sendchar = 0;
    1311
    1312 msg.msg_control = cmsgbuf;
    1313 msg.msg_controllen = sizeof(cmsgbuf);
    1314 p_cmsg = CMSG_FIRSTHDR(&msg);
    1315 p_cmsg->cmsg_level = SOL_SOCKET;
    1316 p_cmsg->cmsg_type = SCM_RIGHTS;
    1317 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
    1318 p_fds = (int *) CMSG_DATA(p_cmsg);
    1319 *p_fds = fd;
    1320 msg.msg_controllen = p_cmsg->cmsg_len;
    1321 msg.msg_name = NULL;
    1322 msg.msg_namelen = 0;
    1323 msg.msg_iov = &vec;
    1324 msg.msg_iovlen = 1;
    1325 msg.msg_flags = 0;
    1326 /* "To pass file descriptors or credentials you need to send/read at
    1327 * least one byte" (man 7 unix) */
    1328 vec.iov_base = &sendchar;
    1329 vec.iov_len = sizeof(sendchar);
    1330 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
    1331 if (retval != 1) {
    1332 perror("sending file descriptor");
    1333 return -1;
    1334 }
    1335 return 0;
    1336}
    1337
    1338/* Helper for should_auto_unmount
    1339 *
    1340 * fusermount typically has the s-bit set - initial open of `mnt` was as root
    1341 * and got EACCESS as 'allow_other' was not specified.
    1342 * Try opening `mnt` again with uid and guid of the calling process.
    1343 */
    1344static int recheck_ENOTCONN_as_owner(const char *mnt)
    1345{
    1346 int pid = fork();
    1347 if(pid == -1) {
    1348 perror("fuse: recheck_ENOTCONN_as_owner can't fork");
    1349 _exit(EXIT_FAILURE);
    1350 } else if(pid == 0) {
    1351 uid_t uid = getuid();
    1352 gid_t gid = getgid();
    1353 if(setresgid(gid, gid, gid) == -1) {
    1354 perror("fuse: can't set resgid");
    1355 _exit(EXIT_FAILURE);
    1356 }
    1357 if(setresuid(uid, uid, uid) == -1) {
    1358 perror("fuse: can't set resuid");
    1359 _exit(EXIT_FAILURE);
    1360 }
    1361
    1362 int fd = open(mnt, O_RDONLY);
    1363 if(fd == -1 && errno == ENOTCONN)
    1364 _exit(EXIT_SUCCESS);
    1365 else
    1366 _exit(EXIT_FAILURE);
    1367 } else {
    1368 int status;
    1369 int res = waitpid(pid, &status, 0);
    1370 if (res == -1) {
    1371 perror("fuse: waiting for child failed");
    1372 _exit(EXIT_FAILURE);
    1373 }
    1374 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
    1375 }
    1376}
    1377
    1378/* The parent fuse process has died: decide whether to auto_unmount.
    1379 *
    1380 * In the normal case (umount or fusermount -u), the filesystem
    1381 * has already been unmounted. If we simply unmount again we can
    1382 * cause problems with stacked mounts (e.g. autofs).
    1383 *
    1384 * So we unmount here only in abnormal case where fuse process has
    1385 * died without unmount happening. To detect this, we first look in
    1386 * the mount table to make sure the mountpoint is still mounted and
    1387 * has proper type. If so, we then see if opening the mount dir is
    1388 * returning 'Transport endpoint is not connected'.
    1389 *
    1390 * The order of these is important, because if autofs is in use,
    1391 * opening the dir to check for ENOTCONN will cause a new mount
    1392 * in the normal case where filesystem has been unmounted cleanly.
    1393 */
    1394static int should_auto_unmount(const char *mnt, const char *type)
    1395{
    1396 char *copy;
    1397 const char *last;
    1398 int result = 0;
    1399 int fd;
    1400
    1401 copy = strdup(mnt);
    1402 if (copy == NULL) {
    1403 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    1404 return 0;
    1405 }
    1406
    1407 if (chdir_to_parent(copy, &last) == -1)
    1408 goto out;
    1409 if (check_is_mount(last, mnt, type) == -1)
    1410 goto out;
    1411
    1412 fd = open(mnt, O_RDONLY);
    1413
    1414 if (fd != -1) {
    1415 close(fd);
    1416 } else {
    1417 switch(errno) {
    1418 case ENOTCONN:
    1419 result = 1;
    1420 break;
    1421 case EACCES:
    1422 result = recheck_ENOTCONN_as_owner(mnt);
    1423 break;
    1424 default:
    1425 result = 0;
    1426 break;
    1427 }
    1428 }
    1429out:
    1430 free(copy);
    1431 return result;
    1432}
    1433
    1434static void usage(void)
    1435{
    1436 printf("%s: [options] mountpoint\n"
    1437 "Options:\n"
    1438 " -h print help\n"
    1439 " -V print version\n"
    1440 " -o opt[,opt...] mount options\n"
    1441 " -u unmount\n"
    1442 " -q quiet\n"
    1443 " -z lazy unmount\n",
    1444 progname);
    1445 exit(1);
    1446}
    1447
    1448static void show_version(void)
    1449{
    1450 printf("fusermount3 version: %s\n", PACKAGE_VERSION);
    1451 exit(0);
    1452}
    1453
    1454/*
    1455 * Close all inherited fds that are not needed
    1456 * Ideally these wouldn't come up at all, applications should better
    1457 * use FD_CLOEXEC / O_CLOEXEC
    1458 */
    1459static void close_inherited_fds(int cfd)
    1460{
    1461 int max_fd = sysconf(_SC_OPEN_MAX);
    1462 int rc;
    1463
    1464#ifdef CLOSE_RANGE_CLOEXEC
    1465 /* high range first to be able to log errors through stdout/err*/
    1466 rc = close_range(cfd + 1, ~0U, 0);
    1467 if (rc < 0) {
    1468 fprintf(stderr, "Failed to close high range of FDs: %s",
    1469 strerror(errno));
    1470 goto fallback;
    1471 }
    1472
    1473 rc = close_range(0, cfd - 1, 0);
    1474 if (rc < 0) {
    1475 fprintf(stderr, "Failed to close low range of FDs: %s",
    1476 strerror(errno));
    1477 goto fallback;
    1478 }
    1479#endif
    1480
    1481fallback:
    1482 /*
    1483 * This also needs to close stdout/stderr, as the application
    1484 * using libfuse might have closed these FDs and might be using
    1485 * it. Although issue is now that logging errors won't be possible
    1486 * after that.
    1487 */
    1488 for (int fd = 0; fd <= max_fd; fd++) {
    1489 if (fd != cfd)
    1490 close(fd);
    1491 }
    1492}
    1493
    1494int main(int argc, char *argv[])
    1495{
    1496 sigset_t sigset;
    1497 int ch;
    1498 int fd;
    1499 int res;
    1500 char *origmnt;
    1501 char *mnt;
    1502 static int unmount = 0;
    1503 static int lazy = 0;
    1504 static int quiet = 0;
    1505 char *commfd = NULL;
    1506 long cfd;
    1507 const char *opts = "";
    1508 const char *type = NULL;
    1509 int setup_auto_unmount_only = 0;
    1510
    1511 static const struct option long_opts[] = {
    1512 {"unmount", no_argument, NULL, 'u'},
    1513 {"lazy", no_argument, NULL, 'z'},
    1514 {"quiet", no_argument, NULL, 'q'},
    1515 {"help", no_argument, NULL, 'h'},
    1516 {"version", no_argument, NULL, 'V'},
    1517 {"options", required_argument, NULL, 'o'},
    1518 // Note: auto-unmount and comm-fd don't have short versions.
    1519 // They'ne meant for internal use by mount.c
    1520 {"auto-unmount", no_argument, NULL, 'U'},
    1521 {"comm-fd", required_argument, NULL, 'c'},
    1522 {0, 0, 0, 0}};
    1523
    1524 progname = strdup(argc > 0 ? argv[0] : "fusermount");
    1525 if (progname == NULL) {
    1526 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
    1527 exit(1);
    1528 }
    1529
    1530 while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
    1531 NULL)) != -1) {
    1532 switch (ch) {
    1533 case 'h':
    1534 usage();
    1535 break;
    1536
    1537 case 'V':
    1538 show_version();
    1539 break;
    1540
    1541 case 'o':
    1542 opts = optarg;
    1543 break;
    1544
    1545 case 'u':
    1546 unmount = 1;
    1547 break;
    1548 case 'U':
    1549 unmount = 1;
    1550 auto_unmount = 1;
    1551 setup_auto_unmount_only = 1;
    1552 break;
    1553 case 'c':
    1554 commfd = optarg;
    1555 break;
    1556 case 'z':
    1557 lazy = 1;
    1558 break;
    1559
    1560 case 'q':
    1561 quiet = 1;
    1562 break;
    1563
    1564 default:
    1565 exit(1);
    1566 }
    1567 }
    1568
    1569 if (lazy && !unmount) {
    1570 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
    1571 exit(1);
    1572 }
    1573
    1574 if (optind >= argc) {
    1575 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
    1576 exit(1);
    1577 } else if (argc > optind + 1) {
    1578 fprintf(stderr, "%s: extra arguments after the mountpoint\n",
    1579 progname);
    1580 exit(1);
    1581 }
    1582
    1583 origmnt = argv[optind];
    1584
    1585 drop_privs();
    1586 mnt = fuse_mnt_resolve_path(progname, origmnt);
    1587 if (mnt != NULL) {
    1588 res = chdir("/");
    1589 if (res == -1) {
    1590 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
    1591 goto err_out;
    1592 }
    1593 }
    1594 restore_privs();
    1595 if (mnt == NULL)
    1596 exit(1);
    1597
    1598 umask(033);
    1599 if (!setup_auto_unmount_only && unmount)
    1600 goto do_unmount;
    1601
    1602 if(commfd == NULL)
    1603 commfd = getenv(FUSE_COMMFD_ENV);
    1604 if (commfd == NULL) {
    1605 fprintf(stderr, "%s: old style mounting not supported\n",
    1606 progname);
    1607 goto err_out;
    1608 }
    1609
    1610 res = libfuse_strtol(commfd, &cfd);
    1611 if (res) {
    1612 fprintf(stderr,
    1613 "%s: invalid _FUSE_COMMFD: %s\n",
    1614 progname, commfd);
    1615 goto err_out;
    1616
    1617 }
    1618 {
    1619 struct stat statbuf;
    1620 fstat(cfd, &statbuf);
    1621 if(!S_ISSOCK(statbuf.st_mode)) {
    1622 fprintf(stderr,
    1623 "%s: file descriptor %li is not a socket, can't send fuse fd\n",
    1624 progname, cfd);
    1625 goto err_out;
    1626 }
    1627 }
    1628
    1629 if (setup_auto_unmount_only)
    1630 goto wait_for_auto_unmount;
    1631
    1632 fd = mount_fuse(mnt, opts, &type);
    1633 if (fd == -1)
    1634 goto err_out;
    1635
    1636 res = send_fd(cfd, fd);
    1637 if (res != 0) {
    1638 umount2(mnt, MNT_DETACH); /* lazy umount */
    1639 goto err_out;
    1640 }
    1641 close(fd);
    1642
    1643 if (!auto_unmount) {
    1644 free(mnt);
    1645 free((void*) type);
    1646 return 0;
    1647 }
    1648
    1649wait_for_auto_unmount:
    1650 /* Become a daemon and wait for the parent to exit or die.
    1651 ie For the control socket to get closed.
    1652 Btw, we don't want to use daemon() function here because
    1653 it forks and messes with the file descriptors. */
    1654
    1655 close_inherited_fds(cfd);
    1656
    1657 setsid();
    1658 res = chdir("/");
    1659 if (res == -1) {
    1660 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
    1661 goto err_out;
    1662 }
    1663
    1664 sigfillset(&sigset);
    1665 sigprocmask(SIG_BLOCK, &sigset, NULL);
    1666
    1667 lazy = 1;
    1668 quiet = 1;
    1669
    1670 while (1) {
    1671 unsigned char buf[16];
    1672 int n = recv(cfd, buf, sizeof(buf), 0);
    1673 if (!n)
    1674 break;
    1675
    1676 if (n < 0) {
    1677 if (errno == EINTR)
    1678 continue;
    1679 break;
    1680 }
    1681 }
    1682
    1683 if (!should_auto_unmount(mnt, type)) {
    1684 goto success_out;
    1685 }
    1686
    1687do_unmount:
    1688 if (geteuid() == 0)
    1689 res = unmount_fuse(mnt, quiet, lazy);
    1690 else {
    1691 res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
    1692 if (res == -1 && !quiet)
    1693 fprintf(stderr,
    1694 "%s: failed to unmount %s: %s\n",
    1695 progname, mnt, strerror(errno));
    1696 }
    1697 if (res == -1)
    1698 goto err_out;
    1699
    1700success_out:
    1701 free((void*) type);
    1702 free(mnt);
    1703 return 0;
    1704
    1705err_out:
    1706 free((void*) type);
    1707 free(mnt);
    1708 exit(1);
    1709}
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2util_2mount_8fuse_8c_source.html0000644000175000017500000021167714770234736024351 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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:479
    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2cuse_8c.html0000644000175000017500000012047114770234736022121 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc1_2example_2cuse__client_8c.html0000644000175000017500000003227114770234736023616 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc1_2example_2hello_8c.html0000644000175000017500000006545214770234736022274 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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;
    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
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2hello__ll_8c.html0000644000175000017500000012720714770234737023120 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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
    

    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 COPYING.
    */
    #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;
    }
    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)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2hello__ll__uds_8c.html0000644000175000017500000012762414770234737024135 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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;
    }
    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)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2invalidate__path_8c.html0000644000175000017500000012470214770234737024457 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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:5204
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5153
    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:5209
    @ 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:5218
    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:77
    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.17.2/doc/html/fuse-3_817_81-rc1_2example_2ioctl_8c.html0000644000175000017500000005521214770234737022275 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc1_2example_2ioctl_8h.html0000644000175000017500000001256314770234737022304 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc1_2example_2ioctl__client_8c.html0000644000175000017500000001776014770234737024000 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 tests the ioctl.c example file systsem.
    This program can be distributed under the terms of the GNU GPLv2.
    See the file COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc1_2example_2notify__inval__entry_8c.html0000644000175000017500000015024014770234737025400 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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

    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 COPYING.
    */
    #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;
    };
    static struct options options = {
    .timeout = 5,
    .no_notify = 0,
    .update_interval = 1,
    .only_expire = 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),
    };
    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;
    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");
    printf("Exiting...\n");
    // Make sure to exit now, rather than on next request from userspace
    pthread_kill(main_thread, SIGPIPE);
    break;
    }
    // 1) ret == 0: successful expire of an existing entry
    // 2) ret == -ENOENT: kernel has already expired the entry /
    // entry does not exist anymore in the kernel
    assert(ret == 0 || ret == -ENOENT);
    } else { // invalidate entry
    (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
    }
    }
    free(old_name);
    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"
    " --timeout=<secs> Timeout for kernel caches\n"
    " --update-interval=<secs> Update-rate of file system contents\n"
    " --no-notify Disable kernel notifications\n"
    " --only-expire Expire entries instead of invalidating them\n"
    "\n");
    }
    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;
    }
    /* 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:5218
    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
    #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.17.2/doc/html/fuse-3_817_81-rc1_2example_2notify__inval__inode_8c.html0000644000175000017500000015146014770234737025342 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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:5218
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file notify_inval_inode.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2notify__store__retrieve_8c.html0000644000175000017500000017141714770234737026120 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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:5218
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file notify_store_retrieve.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2null_8c.html0000644000175000017500000021641114770234737022135 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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) */
    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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    static int xmp_fallocate(const char *path, int mode,
    off_t offset, off_t length, struct fuse_file_info *fi)
    {
    (void) path;
    if (mode)
    return -EOPNOTSUPP;
    return -posix_fallocate(fi->fh, offset, length);
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file null.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2passthrough_8c.html0000644000175000017500000015234214770234737023534 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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>
    #ifdef __FreeBSD__
    #include <sys/socket.h>
    #include <sys/un.h>
    #endif
    #include <sys/time.h>
    #ifdef HAVE_SETXATTR
    #include <sys/xattr.h>
    #endif
    #include "passthrough_helpers.h"
    static int fill_dir_plus = 0;
    static void *xmp_init(struct fuse_conn_info *conn,
    struct fuse_config *cfg)
    {
    (void) conn;
    cfg->use_ino = 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. */
    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 (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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    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 (mode)
    return -EOPNOTSUPP;
    if(fi == NULL)
    fd = open(path, O_WRONLY);
    else
    fd = fi->fh;
    if (fd == -1)
    return -errno;
    res = -posix_fallocate(fd, offset, length);
    if(fi == NULL)
    close(fd);
    return res;
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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 {
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file passthrough.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2passthrough__fh_8c.html0000644000175000017500000021716514770234737024355 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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) */
    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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    static int xmp_fallocate(const char *path, int mode,
    off_t offset, off_t length, struct fuse_file_info *fi)
    {
    (void) path;
    if (mode)
    return -EOPNOTSUPP;
    return -posix_fallocate(fi->fh, offset, length);
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file passthrough_fh.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2passthrough__ll_8c.html0000644000175000017500000052423514770234737024366 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #define _GNU_SOURCE
    #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    #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) {
    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/%lli -> %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=%lu)\n",
    ino, out_buf.buf[0].size, (unsigned long) 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 = EOPNOTSUPP;
    (void) ino;
    #ifdef HAVE_FALLOCATE
    err = fallocate(fi->fh, mode, offset, length);
    if (err < 0)
    err = errno;
    #elif defined(HAVE_POSIX_FALLOCATE)
    if (mode) {
    fuse_reply_err(req, EOPNOTSUPP);
    return;
    }
    err = posix_fallocate(fi->fh, offset, length);
    #endif
    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, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
    "off=%lu, ino=%" PRIu64 "/fd=%lu, "
    "off=%lu, size=%zd, flags=0x%x)\n",
    ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, 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);
    }
    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,
    };
    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;
    }
    int fuse_set_signal_handlers(struct fuse_session *se)
    @ FUSE_CAP_WRITEBACK_CACHE
    @ FUSE_CAP_FLOCK_LOCKS
    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
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    void fuse_remove_signal_handlers(struct fuse_session *se)
    @ FUSE_BUF_SPLICE_MOVE
    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_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:97
    uint32_t parallel_direct_writes
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file passthrough_ll.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2poll_8c.html0000644000175000017500000010020414770234737022121 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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:86
    uint32_t direct_io
    Definition fuse_common.h:71
    void(* destroy)(void *private_data)
    Definition fuse.h:649

    Definition in file poll.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2example_2poll__client_8c.html0000644000175000017500000002134414770234737023625 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81-rc1_2example_2printcap_8c.html0000644000175000017500000012224514770234737023004 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 COPYING.
    */
    #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;
    }
    int fuse_set_signal_handlers(struct fuse_session *se)
    @ FUSE_CAP_POSIX_ACL
    @ FUSE_CAP_READDIRPLUS
    @ FUSE_CAP_NO_OPENDIR_SUPPORT
    @ FUSE_CAP_PARALLEL_DIROPS
    @ FUSE_CAP_ASYNC_DIO
    @ FUSE_CAP_NO_EXPORT_SUPPORT
    @ FUSE_CAP_WRITEBACK_CACHE
    @ FUSE_CAP_IOCTL_DIR
    @ FUSE_CAP_AUTO_INVAL_DATA
    @ FUSE_CAP_SPLICE_READ
    @ FUSE_CAP_SPLICE_MOVE
    @ FUSE_CAP_POSIX_LOCKS
    @ FUSE_CAP_HANDLE_KILLPRIV_V2
    @ FUSE_CAP_HANDLE_KILLPRIV
    @ FUSE_CAP_DONT_MASK
    @ FUSE_CAP_ATOMIC_O_TRUNC
    @ FUSE_CAP_SPLICE_WRITE
    @ FUSE_CAP_PASSTHROUGH
    @ FUSE_CAP_FLOCK_LOCKS
    @ FUSE_CAP_EXPIRE_ONLY
    @ FUSE_CAP_EXPORT_SUPPORT
    @ FUSE_CAP_READDIRPLUS_AUTO
    @ FUSE_CAP_NO_OPEN_SUPPORT
    @ FUSE_CAP_DIRECT_IO_ALLOW_MMAP
    @ FUSE_CAP_SETXATTR_EXT
    @ FUSE_CAP_ASYNC_READ
    @ FUSE_CAP_CACHE_SYMLINKS
    @ FUSE_CAP_EXPLICIT_INVAL_DATA
    const char * fuse_pkgversion(void)
    Definition fuse.c:5218
    void fuse_remove_signal_handlers(struct fuse_session *se)
    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.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse_8h.html0000644000175000017500000014342414770234737022125 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 1398 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 1369 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 5153 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()

    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

    Parameters
    argcthe argument counter passed to the main() function
    argvthe argument vector passed to the main() function
    opthe file system operation
    private_dataInitial value for the private_data field of struct fuse_context. May be overridden by the struct fuse_operations.init handler.
    Returns
    0 on success, nonzero on failure

    Example usage, see hello.c

    Definition at line 311 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 5204 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 479 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 4904 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 4912 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 5209 of file fuse.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse__common_8h.html0000644000175000017500000017705414770234737023642 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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_IOCTL_COMPAT   (1 << 0)
     
    #define FUSE_BACKING_STACKED_UNDER   (0)
     

    Enumerations

    enum  fuse_capability {
      FUSE_CAP_ASYNC_READ = (1 << 0) , FUSE_CAP_POSIX_LOCKS = (1 << 1) , FUSE_CAP_ATOMIC_O_TRUNC = (1 << 3) , FUSE_CAP_EXPORT_SUPPORT = (1 << 4) ,
      FUSE_CAP_DONT_MASK = (1 << 6) , FUSE_CAP_SPLICE_WRITE = (1 << 7) , FUSE_CAP_SPLICE_MOVE = (1 << 8) , FUSE_CAP_SPLICE_READ = (1 << 9) ,
      FUSE_CAP_FLOCK_LOCKS = (1 << 10) , FUSE_CAP_IOCTL_DIR = (1 << 11) , FUSE_CAP_AUTO_INVAL_DATA = (1 << 12) , FUSE_CAP_READDIRPLUS = (1 << 13) ,
      FUSE_CAP_READDIRPLUS_AUTO = (1 << 14) , FUSE_CAP_ASYNC_DIO = (1 << 15) , FUSE_CAP_WRITEBACK_CACHE = (1 << 16) , FUSE_CAP_NO_OPEN_SUPPORT = (1 << 17) ,
      FUSE_CAP_PARALLEL_DIROPS = (1 << 18) , FUSE_CAP_POSIX_ACL = (1 << 19) , FUSE_CAP_HANDLE_KILLPRIV = (1 << 20) , FUSE_CAP_HANDLE_KILLPRIV_V2 = (1 << 21) ,
      FUSE_CAP_CACHE_SYMLINKS = (1 << 23) , FUSE_CAP_NO_OPENDIR_SUPPORT = (1 << 24) , FUSE_CAP_EXPLICIT_INVAL_DATA = (1 << 25) , FUSE_CAP_EXPIRE_ONLY = (1 << 26) ,
      FUSE_CAP_SETXATTR_EXT = (1 << 27) , FUSE_CAP_DIRECT_IO_ALLOW_MMAP = (1 << 28) , FUSE_CAP_PASSTHROUGH = (1 << 29) , FUSE_CAP_NO_EXPORT_SUPPORT = (1 << 30) ,
      FUSE_CAP_CURRENT_MAX
    }
     
    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)
     

    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 682 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 537 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 848 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 817 of file fuse_common.h.

    ◆ fuse_capability

    Enumerator
    FUSE_CAP_ASYNC_READ 

    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.

    FUSE_CAP_POSIX_LOCKS 

    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.

    FUSE_CAP_ATOMIC_O_TRUNC 

    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.

    FUSE_CAP_EXPORT_SUPPORT 

    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.

    FUSE_CAP_DONT_MASK 

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

    This feature is disabled by default.

    FUSE_CAP_SPLICE_WRITE 

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

    This feature is disabled by default.

    FUSE_CAP_SPLICE_MOVE 

    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.

    FUSE_CAP_SPLICE_READ 

    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.

    FUSE_CAP_FLOCK_LOCKS 

    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.

    FUSE_CAP_IOCTL_DIR 

    Indicates that the filesystem supports ioctl's on directories.

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

    FUSE_CAP_AUTO_INVAL_DATA 

    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.

    FUSE_CAP_READDIRPLUS 

    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.

    FUSE_CAP_READDIRPLUS_AUTO 

    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.

    FUSE_CAP_ASYNC_DIO 

    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.

    FUSE_CAP_WRITEBACK_CACHE 

    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.

    FUSE_CAP_NO_OPEN_SUPPORT 

    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.

    FUSE_CAP_PARALLEL_DIROPS 

    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.

    FUSE_CAP_POSIX_ACL 

    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.

    FUSE_CAP_HANDLE_KILLPRIV 

    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.

    FUSE_CAP_HANDLE_KILLPRIV_V2 

    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.

    FUSE_CAP_CACHE_SYMLINKS 

    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.

    FUSE_CAP_NO_OPENDIR_SUPPORT 

    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.

    FUSE_CAP_EXPLICIT_INVAL_DATA 

    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.

    FUSE_CAP_EXPIRE_ONLY 

    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.

    FUSE_CAP_SETXATTR_EXT 

    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.

    FUSE_CAP_DIRECT_IO_ALLOW_MMAP 

    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.

    FUSE_CAP_PASSTHROUGH 

    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.

    FUSE_CAP_NO_EXPORT_SUPPORT 

    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.

    FUSE_CAP_CURRENT_MAX 

    Current maximum capability value.

    Definition at line 177 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 416 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_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_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 463 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 5218 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 1906 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 180 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 158 of file fuse_signals.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 138 of file fuse_signals.c.

    ◆ fuse_version()

    int fuse_version ( void  )

    Get the version of the library

    Returns
    the version

    Definition at line 5213 of file fuse.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse__log_8h.html0000644000175000017500000003445314770234737023126 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse__lowlevel_8h.html0000644000175000017500000041504714770234737024200 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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 289 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 379 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 2957 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 2519 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 2506 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 2500 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 2432 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 2415 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 2625 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 2545 of file fuse_lowlevel.c.

    ◆ fuse_lowlevel_version()

    void fuse_lowlevel_version ( void  )

    Print low-level version information to stdout.

    Definition at line 2950 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

    Setup passthrough backing file for open reply

    Possible requests: open, opendir, create

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

    Definition at line 483 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 463 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 976 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 527 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 447 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 915 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 431 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 334 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 1074 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 1095 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 1004 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 268 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 959 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 1129 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 339 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 508 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 1119 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 478 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 937 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 517 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 949 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 2675 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 3553 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 2680 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 2693 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 2670 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 2967 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 3474 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 3419 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 2787 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 3234 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 3479 of file fuse_lowlevel.c.

    fuse-3.17.2/doc/html/fuse-3_817_81-rc1_2include_2fuse__opt_8h.html0000644000175000017500000007750414770234737023153 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/dir_3f3840dea793d3e59c45e9730587a31c.html0000644000175000017500000000415714770234737021234 0ustar berndbernd libfuse: fuse-3.17.1-rc1/doc Directory Reference
    libfuse
    doc Directory Reference
    fuse-3.17.2/doc/html/dir_a9479a6a9e1f41fd821cf4117f966d1e.html0000644000175000017500000002372314770234737021375 0ustar berndbernd libfuse: fuse-3.17.1-rc1/example Directory Reference
    libfuse
    example Directory Reference
    fuse-3.17.2/doc/html/dir_7c10f509f528fa84faee6538d3d65d31.html0000644000175000017500000000735114770234737021370 0ustar berndbernd libfuse: fuse-3.17.1-rc1 Directory Reference
    libfuse
    fuse-3.17.1-rc1 Directory Reference

    Directories

     example
     
     include
     
     lib
     
     test
     
     util
     
    fuse-3.17.2/doc/html/dir_0dc60d031bb972b0c2e26fefb85d65c3.html0000644000175000017500000001224614770234737021477 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/dir_9ce3b66b67a1b1b09c830f7629696f5b.html0000644000175000017500000002126414770234737021312 0ustar berndbernd libfuse: fuse-3.17.1-rc1/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.17.2/doc/html/dir_3bd2abc0f0eca4df2097a8b21b48c13b.html0000644000175000017500000000577414770234737021630 0ustar berndbernd libfuse: fuse-3.17.1-rc1/lib/modules Directory Reference
    libfuse
    modules Directory Reference

    Files

     iconv.c
     
     subdir.c
     
    fuse-3.17.2/doc/html/dir_e0b70151b30581f59638c2f3a5122668.html0000644000175000017500000001105314770234737020760 0ustar berndbernd libfuse: fuse-3.17.1-rc1/test Directory Reference
    libfuse
    test Directory Reference

    Files

     readdir_inode.c
     
     release_unlink_race.c
     
     stracedecode.c
     
     test_setattr.c
     
     test_syscalls.c
     
     test_write_cache.c
     
     wrong_command.c
     
    fuse-3.17.2/doc/html/dir_a8feaf0c066680727663edb1c44e1229.html0000644000175000017500000000562314770234737021275 0ustar berndbernd libfuse: fuse-3.17.1-rc1/util Directory Reference
    libfuse
    util Directory Reference

    Files

     fusermount.c
     
     mount.fuse.c
     
    fuse-3.17.2/doc/html/test__want__conversion_8c_source.html0000644000175000017500000011161115002273413022636 0ustar berndbernd libfuse: test/test_want_conversion.c Source File
    libfuse
    test_want_conversion.c
    1#include "util.h"
    2#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
    3
    4#include "fuse_i.h"
    5#include <stdio.h>
    6#include <assert.h>
    7#include <inttypes.h>
    8#include <stdbool.h>
    9
    10static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
    11{
    12 printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64 "\n", prefix,
    13 conn->want, conn->want_ext);
    14}
    15
    16static void application_init_old_style(struct fuse_conn_info *conn)
    17{
    18 /* Simulate application init the old style */
    20 conn->want &= ~FUSE_CAP_SPLICE_READ;
    21}
    22
    23static void application_init_new_style(struct fuse_conn_info *conn)
    24{
    25 /* Simulate application init the new style */
    26 fuse_set_feature_flag(conn, FUSE_CAP_ASYNC_READ);
    27 fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ);
    28}
    29
    30static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style)
    31{
    32 uint64_t want_ext_default = conn->want_ext;
    33 uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
    34 int rc;
    35
    36 /* High-level init */
    37 fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
    38
    39 conn->want = want_default;
    40
    41 if (new_style)
    42 application_init_new_style(conn);
    43 else
    44 application_init_old_style(conn);
    45
    46 rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
    47 assert(rc == 0);
    48}
    49
    50static void test_do_init(struct fuse_conn_info *conn, bool new_style)
    51{
    52 /* Initial setup */
    57 conn->capable = fuse_lower_32_bits(conn->capable_ext);
    58 conn->want_ext = conn->capable_ext;
    59
    60 print_conn_info("Initial state", conn);
    61
    62 uint64_t want_ext_default = conn->want_ext;
    63 uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
    64 int rc;
    65
    66 conn->want = want_default;
    67 conn->capable = fuse_lower_32_bits(conn->capable_ext);
    68
    69 test_fuse_fs_init(conn, new_style);
    70
    71 rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
    72 assert(rc == 0);
    73
    74 /* Verify all expected flags are set */
    75 assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ));
    76 assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE);
    77 assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE);
    78 assert(conn->want_ext & FUSE_CAP_POSIX_LOCKS);
    79 assert(conn->want_ext & FUSE_CAP_FLOCK_LOCKS);
    80 assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
    81 assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
    82 /* Verify no other flags are set */
    83 assert(conn->want_ext ==
    87
    88 print_conn_info("After init", conn);
    89}
    90
    91static void test_want_conversion_basic(void)
    92{
    93 struct fuse_conn_info conn = { 0 };
    94
    95 printf("\nTesting basic want conversion:\n");
    96 test_do_init(&conn, false);
    97 test_do_init(&conn, true);
    98 print_conn_info("After init", &conn);
    99}
    100
    101static void test_want_conversion_conflict(void)
    102{
    103 struct fuse_conn_info conn = { 0 };
    104 int rc;
    105
    106 printf("\nTesting want conversion conflict:\n");
    107
    108 /* Test conflicting values */
    109 /* Initialize like fuse_lowlevel.c does */
    113 conn.capable = fuse_lower_32_bits(conn.capable_ext);
    114 conn.want_ext = conn.capable_ext;
    115 conn.want = fuse_lower_32_bits(conn.want_ext);
    116 print_conn_info("Test conflict initial", &conn);
    117
    118 /* Initialize default values like in basic test */
    119 uint64_t want_ext_default_ll = conn.want_ext;
    120 uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);
    121
    122 /* Simulate application init modifying capabilities */
    123 conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */
    124 conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */
    125
    126 rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
    127 want_default_ll);
    128 assert(rc == -EINVAL);
    129 print_conn_info("Test conflict after", &conn);
    130
    131 printf("Want conversion conflict test passed\n");
    132}
    133
    134static void test_want_conversion_high_bits(void)
    135{
    136 struct fuse_conn_info conn = { 0 };
    137 int rc;
    138
    139 printf("\nTesting want conversion high bits preservation:\n");
    140
    141 /* Test high bits preservation */
    142 conn.want_ext = (1ULL << 33) | FUSE_CAP_ASYNC_READ;
    143 conn.want = fuse_lower_32_bits(conn.want_ext);
    144 print_conn_info("Test high bits initial", &conn);
    145
    146 uint64_t want_ext_default_ll = conn.want_ext;
    147 uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);
    148
    149 rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
    150 want_default_ll);
    151 assert(rc == 0);
    152 assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ));
    153 print_conn_info("Test high bits after", &conn);
    154
    155 printf("Want conversion high bits test passed\n");
    156}
    157
    158int main(void)
    159{
    160 test_want_conversion_basic();
    161 test_want_conversion_conflict();
    162 test_want_conversion_high_bits();
    163 return 0;
    164}
    #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
    uint64_t capable_ext
    uint32_t capable
    uint64_t want_ext
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2build_2fuse__config_8h_source.html0000644000175000017500000001731315002273247025071 0ustar berndbernd libfuse: fuse-3.17.1.dir/build/fuse_config.h Source File
    libfuse
    fuse_config.h
    1/*
    2 * Autogenerated by the Meson build system.
    3 * Do not edit, your changes will be lost.
    4 */
    5
    6#pragma once
    7
    8#define HAVE_BACKTRACE
    9
    10#define HAVE_CLOSE_RANGE
    11
    12#define HAVE_COPY_FILE_RANGE
    13
    14#define HAVE_FALLOCATE
    15
    16#define HAVE_FDATASYNC
    17
    18#define HAVE_FORK
    19
    20#define HAVE_FSTATAT
    21
    22#define HAVE_ICONV
    23
    24#define HAVE_OPENAT
    25
    26#define HAVE_PIPE2
    27
    28#define HAVE_POSIX_FALLOCATE
    29
    30#define HAVE_READLINKAT
    31
    32#define HAVE_SETXATTR
    33
    34#define HAVE_SPLICE
    35
    36#define HAVE_STRUCT_STAT_ST_ATIM
    37
    38#undef HAVE_STRUCT_STAT_ST_ATIMESPEC
    39
    40#define HAVE_UTIMENSAT
    41
    42#define HAVE_VMSPLICE
    43
    44#define PACKAGE_VERSION "3.17.1"
    45
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2build_2libfuse__config_8h_source.html0000644000175000017500000001071115002273247025553 0ustar berndbernd libfuse: fuse-3.17.1.dir/build/libfuse_config.h Source File
    libfuse
    libfuse_config.h
    1/*
    2 * Autogenerated by the Meson build system.
    3 * Do not edit, your changes will be lost.
    4 */
    5
    6#pragma once
    7
    8#define FUSE_HOTFIX_VERSION 1
    9
    10#define FUSE_MAJOR_VERSION 3
    11
    12#define FUSE_MINOR_VERSION 17
    13
    14#define FUSE_RC_VERSION
    15
    16#define LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS 1
    17
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2build_2meson-private_2sanitycheckc_8c_source.html0000644000175000017500000000545615002273247030046 0ustar berndbernd libfuse: fuse-3.17.1.dir/build/meson-private/sanitycheckc.c Source File
    libfuse
    sanitycheckc.c
    1int main(void) { int class=0; return class; }
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2cuse_8c_source.html0000644000175000017500000016706015002273247023736 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    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.17.2/doc/html/fuse-3_817_81_8dir_2example_2cuse__client_8c_source.html0000644000175000017500000005063515002273247025432 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    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.17.2/doc/html/fuse-3_817_81_8dir_2example_2hello_8c_source.html0000644000175000017500000011524615002273247024101 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    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 */
    63 conn->want &= ~FUSE_CAP_ASYNC_READ;
    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
    #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.17.2/doc/html/test_2hello_8c_source.html0000644000175000017500000011161215002273413020310 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 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.17.2/doc/html/fuse-3_817_81_8dir_2example_2hello__ll_8c_source.html0000644000175000017500000017512315002273247024727 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    7*/
    8
    21#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    22
    23#include <fuse_lowlevel.h>
    24#include <stdio.h>
    25#include <stdlib.h>
    26#include <string.h>
    27#include <errno.h>
    28#include <fcntl.h>
    29#include <unistd.h>
    30#include <assert.h>
    31
    32static const char *hello_str = "Hello World!\n";
    33static const char *hello_name = "hello";
    34
    35static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
    36{
    37 stbuf->st_ino = ino;
    38 switch (ino) {
    39 case 1:
    40 stbuf->st_mode = S_IFDIR | 0755;
    41 stbuf->st_nlink = 2;
    42 break;
    43
    44 case 2:
    45 stbuf->st_mode = S_IFREG | 0444;
    46 stbuf->st_nlink = 1;
    47 stbuf->st_size = strlen(hello_str);
    48 break;
    49
    50 default:
    51 return -1;
    52 }
    53 return 0;
    54}
    55
    56static void hello_ll_init(void *userdata, struct fuse_conn_info *conn)
    57{
    58 (void)userdata;
    59
    60 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    61 conn->no_interrupt = 1;
    62
    63 /* Test setting flags the old way */
    65 conn->want &= ~FUSE_CAP_ASYNC_READ;
    66}
    67
    68static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
    69 struct fuse_file_info *fi)
    70{
    71 struct stat stbuf;
    72
    73 (void) fi;
    74
    75 memset(&stbuf, 0, sizeof(stbuf));
    76 if (hello_stat(ino, &stbuf) == -1)
    77 fuse_reply_err(req, ENOENT);
    78 else
    79 fuse_reply_attr(req, &stbuf, 1.0);
    80}
    81
    82static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
    83{
    84 struct fuse_entry_param e;
    85
    86 if (parent != 1 || strcmp(name, hello_name) != 0)
    87 fuse_reply_err(req, ENOENT);
    88 else {
    89 memset(&e, 0, sizeof(e));
    90 e.ino = 2;
    91 e.attr_timeout = 1.0;
    92 e.entry_timeout = 1.0;
    93 hello_stat(e.ino, &e.attr);
    94
    95 fuse_reply_entry(req, &e);
    96 }
    97}
    98
    99struct dirbuf {
    100 char *p;
    101 size_t size;
    102};
    103
    104static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
    105 fuse_ino_t ino)
    106{
    107 struct stat stbuf;
    108 size_t oldsize = b->size;
    109 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
    110 b->p = (char *) realloc(b->p, b->size);
    111 memset(&stbuf, 0, sizeof(stbuf));
    112 stbuf.st_ino = ino;
    113 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
    114 b->size);
    115}
    116
    117#define min(x, y) ((x) < (y) ? (x) : (y))
    118
    119static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
    120 off_t off, size_t maxsize)
    121{
    122 if (off < bufsize)
    123 return fuse_reply_buf(req, buf + off,
    124 min(bufsize - off, maxsize));
    125 else
    126 return fuse_reply_buf(req, NULL, 0);
    127}
    128
    129static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    130 off_t off, struct fuse_file_info *fi)
    131{
    132 (void) fi;
    133
    134 if (ino != 1)
    135 fuse_reply_err(req, ENOTDIR);
    136 else {
    137 struct dirbuf b;
    138
    139 memset(&b, 0, sizeof(b));
    140 dirbuf_add(req, &b, ".", 1);
    141 dirbuf_add(req, &b, "..", 1);
    142 dirbuf_add(req, &b, hello_name, 2);
    143 reply_buf_limited(req, b.p, b.size, off, size);
    144 free(b.p);
    145 }
    146}
    147
    148static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
    149 struct fuse_file_info *fi)
    150{
    151 if (ino != 2)
    152 fuse_reply_err(req, EISDIR);
    153 else if ((fi->flags & O_ACCMODE) != O_RDONLY)
    154 fuse_reply_err(req, EACCES);
    155 else
    156 fuse_reply_open(req, fi);
    157}
    158
    159static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
    160 off_t off, struct fuse_file_info *fi)
    161{
    162 (void) fi;
    163
    164 assert(ino == 2);
    165 reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
    166}
    167
    168static void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    169 size_t size)
    170{
    171 (void)size;
    172 assert(ino == 1 || ino == 2);
    173 if (strcmp(name, "hello_ll_getxattr_name") == 0)
    174 {
    175 const char *buf = "hello_ll_getxattr_value";
    176 fuse_reply_buf(req, buf, strlen(buf));
    177 }
    178 else
    179 {
    180 fuse_reply_err(req, ENOTSUP);
    181 }
    182}
    183
    184static void hello_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    185 const char *value, size_t size, int flags)
    186{
    187 (void)flags;
    188 (void)size;
    189 assert(ino == 1 || ino == 2);
    190 const char* exp_val = "hello_ll_setxattr_value";
    191 if (strcmp(name, "hello_ll_setxattr_name") == 0 &&
    192 strlen(exp_val) == size &&
    193 strncmp(value, exp_val, size) == 0)
    194 {
    195 fuse_reply_err(req, 0);
    196 }
    197 else
    198 {
    199 fuse_reply_err(req, ENOTSUP);
    200 }
    201}
    202
    203static void hello_ll_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
    204{
    205 assert(ino == 1 || ino == 2);
    206 if (strcmp(name, "hello_ll_removexattr_name") == 0)
    207 {
    208 fuse_reply_err(req, 0);
    209 }
    210 else
    211 {
    212 fuse_reply_err(req, ENOTSUP);
    213 }
    214}
    215
    216static const struct fuse_lowlevel_ops hello_ll_oper = {
    217 .init = hello_ll_init,
    218 .lookup = hello_ll_lookup,
    219 .getattr = hello_ll_getattr,
    220 .readdir = hello_ll_readdir,
    221 .open = hello_ll_open,
    222 .read = hello_ll_read,
    223 .setxattr = hello_ll_setxattr,
    224 .getxattr = hello_ll_getxattr,
    225 .removexattr = hello_ll_removexattr,
    226};
    227
    228int main(int argc, char *argv[])
    229{
    230 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    231 struct fuse_session *se;
    232 struct fuse_cmdline_opts opts;
    233 struct fuse_loop_config *config;
    234 int ret = -1;
    235
    236 if (fuse_parse_cmdline(&args, &opts) != 0)
    237 return 1;
    238 if (opts.show_help) {
    239 printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
    242 ret = 0;
    243 goto err_out1;
    244 } else if (opts.show_version) {
    245 printf("FUSE library version %s\n", fuse_pkgversion());
    247 ret = 0;
    248 goto err_out1;
    249 }
    250
    251 if(opts.mountpoint == NULL) {
    252 printf("usage: %s [options] <mountpoint>\n", argv[0]);
    253 printf(" %s --help\n", argv[0]);
    254 ret = 1;
    255 goto err_out1;
    256 }
    257
    258 se = fuse_session_new(&args, &hello_ll_oper,
    259 sizeof(hello_ll_oper), NULL);
    260 if (se == NULL)
    261 goto err_out1;
    262
    263 if (fuse_set_signal_handlers(se) != 0)
    264 goto err_out2;
    265
    266 if (fuse_session_mount(se, opts.mountpoint) != 0)
    267 goto err_out3;
    268
    269 fuse_daemonize(opts.foreground);
    270
    271 /* Block until ctrl+c or fusermount -u */
    272 if (opts.singlethread)
    273 ret = fuse_session_loop(se);
    274 else {
    275 config = fuse_loop_cfg_create();
    276 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
    277 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
    278 ret = fuse_session_loop_mt(se, config);
    279 fuse_loop_cfg_destroy(config);
    280 config = NULL;
    281 }
    282
    284err_out3:
    286err_out2:
    288err_out1:
    289 free(opts.mountpoint);
    290 fuse_opt_free_args(&args);
    291
    292 return ret ? 1 : 0;
    293}
    int fuse_set_signal_handlers(struct fuse_session *se)
    #define FUSE_CAP_ASYNC_READ
    const char * fuse_pkgversion(void)
    Definition fuse.c:5234
    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.17.2/doc/html/fuse-3_817_81_8dir_2example_2hello__ll__uds_8c_source.html0000644000175000017500000021046415002273247025737 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    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:5234
    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.17.2/doc/html/fuse-3_817_81_8dir_2example_2invalidate__path_8c_source.html0000644000175000017500000016602515002273247026272 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    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:5220
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5169
    int fuse_invalidate_path(struct fuse *f, const char *path)
    Definition fuse.c:4698
    int fuse_loop(struct fuse *f)
    Definition fuse.c:4602
    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:4769
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4545
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5225
    @ 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:5234
    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:77
    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.17.2/doc/html/fuse-3_817_81_8dir_2example_2ioctl_8c_source.html0000644000175000017500000010763315002273247024111 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    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.17.2/doc/html/fuse-3_817_81_8dir_2example_2ioctl_8h_source.html0000644000175000017500000001612315002273247024107 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    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.17.2/doc/html/fuse-3_817_81_8dir_2example_2ioctl__client_8c_source.html0000644000175000017500000002700515002273247025600 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 tests the ioctl.c example file systsem.
    7
    8 This program can be distributed under the terms of the GNU GPLv2.
    9 See the file COPYING.
    10*/
    11
    24#include <sys/types.h>
    25#include <fcntl.h>
    26#include <sys/stat.h>
    27#include <sys/ioctl.h>
    28#include <stdio.h>
    29#include <stdlib.h>
    30#include <ctype.h>
    31#include <errno.h>
    32#include <unistd.h>
    33#include "ioctl.h"
    34
    35const char *usage =
    36"Usage: fioclient FIOC_FILE [size]\n"
    37"\n"
    38"Get size if <size> is omitted, set size otherwise\n"
    39"\n";
    40
    41int main(int argc, char **argv)
    42{
    43 size_t size;
    44 int fd;
    45 int ret = 0;
    46
    47 if (argc < 2) {
    48 fprintf(stderr, "%s", usage);
    49 return 1;
    50 }
    51
    52 fd = open(argv[1], O_RDWR);
    53 if (fd < 0) {
    54 perror("open");
    55 return 1;
    56 }
    57
    58 if (argc == 2) {
    59 if (ioctl(fd, FIOC_GET_SIZE, &size)) {
    60 perror("ioctl");
    61 ret = 1;
    62 goto out;
    63 }
    64 printf("%zu\n", size);
    65 } else {
    66 size = strtoul(argv[2], NULL, 0);
    67 if (ioctl(fd, FIOC_SET_SIZE, &size)) {
    68 perror("ioctl");
    69 ret = 1;
    70 goto out;
    71 }
    72 }
    73out:
    74 close(fd);
    75 return ret;
    76}
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2notify__inval__entry_8c_source.html0000644000175000017500000021265015002273247027213 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    7*/
    8
    79#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    80
    81#include <fuse_lowlevel.h>
    82#include <stdio.h>
    83#include <stdlib.h>
    84#include <string.h>
    85#include <errno.h>
    86#include <fcntl.h>
    87#include <assert.h>
    88#include <signal.h>
    89#include <stddef.h>
    90#include <sys/stat.h>
    91#include <unistd.h>
    92#include <pthread.h>
    93
    94#define MAX_STR_LEN 128
    95static char file_name[MAX_STR_LEN];
    96static fuse_ino_t file_ino = 2;
    97static int lookup_cnt = 0;
    98static pthread_t main_thread;
    99
    100/* Command line parsing */
    101struct options {
    102 int no_notify;
    103 float timeout;
    104 int update_interval;
    105 int only_expire;
    106};
    107static struct options options = {
    108 .timeout = 5,
    109 .no_notify = 0,
    110 .update_interval = 1,
    111 .only_expire = 0,
    112};
    113
    114#define OPTION(t, p) \
    115 { t, offsetof(struct options, p), 1 }
    116static const struct fuse_opt option_spec[] = {
    117 OPTION("--no-notify", no_notify),
    118 OPTION("--update-interval=%d", update_interval),
    119 OPTION("--timeout=%f", timeout),
    120 OPTION("--only-expire", only_expire),
    122};
    123
    124static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
    125 stbuf->st_ino = ino;
    126 if (ino == FUSE_ROOT_ID) {
    127 stbuf->st_mode = S_IFDIR | 0755;
    128 stbuf->st_nlink = 1;
    129 }
    130
    131 else if (ino == file_ino) {
    132 stbuf->st_mode = S_IFREG | 0000;
    133 stbuf->st_nlink = 1;
    134 stbuf->st_size = 0;
    135 }
    136
    137 else
    138 return -1;
    139
    140 return 0;
    141}
    142
    143static void tfs_init(void *userdata, struct fuse_conn_info *conn) {
    144 (void)userdata;
    145
    146 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    147 conn->no_interrupt = 1;
    148}
    149
    150static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
    151 const char *name) {
    152 struct fuse_entry_param e;
    153 memset(&e, 0, sizeof(e));
    154
    155 if (parent != FUSE_ROOT_ID)
    156 goto err_out;
    157 else if (strcmp(name, file_name) == 0) {
    158 e.ino = file_ino;
    159 lookup_cnt++;
    160 } else
    161 goto err_out;
    162
    163 e.attr_timeout = options.timeout;
    164 e.entry_timeout = options.timeout;
    165 if (tfs_stat(e.ino, &e.attr) != 0)
    166 goto err_out;
    167 fuse_reply_entry(req, &e);
    168 return;
    169
    170err_out:
    171 fuse_reply_err(req, ENOENT);
    172}
    173
    174static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
    175 uint64_t nlookup) {
    176 (void) req;
    177 if(ino == file_ino)
    178 lookup_cnt -= nlookup;
    179 else
    180 assert(ino == FUSE_ROOT_ID);
    181 fuse_reply_none(req);
    182}
    183
    184static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
    185 struct fuse_file_info *fi) {
    186 struct stat stbuf;
    187
    188 (void) fi;
    189
    190 memset(&stbuf, 0, sizeof(stbuf));
    191 if (tfs_stat(ino, &stbuf) != 0)
    192 fuse_reply_err(req, ENOENT);
    193 else
    194 fuse_reply_attr(req, &stbuf, options.timeout);
    195}
    196
    197struct dirbuf {
    198 char *p;
    199 size_t size;
    200};
    201
    202static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
    203 fuse_ino_t ino) {
    204 struct stat stbuf;
    205 size_t oldsize = b->size;
    206 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
    207 b->p = (char *) realloc(b->p, b->size);
    208 memset(&stbuf, 0, sizeof(stbuf));
    209 stbuf.st_ino = ino;
    210 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
    211 b->size);
    212}
    213
    214#define min(x, y) ((x) < (y) ? (x) : (y))
    215
    216static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
    217 off_t off, size_t maxsize) {
    218 if (off < bufsize)
    219 return fuse_reply_buf(req, buf + off,
    220 min(bufsize - off, maxsize));
    221 else
    222 return fuse_reply_buf(req, NULL, 0);
    223}
    224
    225static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    226 off_t off, struct fuse_file_info *fi) {
    227 (void) fi;
    228
    229 if (ino != FUSE_ROOT_ID)
    230 fuse_reply_err(req, ENOTDIR);
    231 else {
    232 struct dirbuf b;
    233
    234 memset(&b, 0, sizeof(b));
    235 dirbuf_add(req, &b, file_name, file_ino);
    236 reply_buf_limited(req, b.p, b.size, off, size);
    237 free(b.p);
    238 }
    239}
    240
    241static const struct fuse_lowlevel_ops tfs_oper = {
    242 .init = tfs_init,
    243 .lookup = tfs_lookup,
    244 .getattr = tfs_getattr,
    245 .readdir = tfs_readdir,
    246 .forget = tfs_forget,
    247};
    248
    249static void update_fs(void) {
    250 time_t t;
    251 struct tm *now;
    252 ssize_t ret;
    253
    254 t = time(NULL);
    255 now = localtime(&t);
    256 assert(now != NULL);
    257
    258 ret = strftime(file_name, MAX_STR_LEN,
    259 "Time_is_%Hh_%Mm_%Ss", now);
    260 assert(ret != 0);
    261}
    262
    263static void* update_fs_loop(void *data) {
    264 struct fuse_session *se = (struct fuse_session*) data;
    265 char *old_name;
    266
    267
    268 while(!fuse_session_exited(se)) {
    269 old_name = strdup(file_name);
    270 update_fs();
    271
    272 if (!options.no_notify && lookup_cnt) {
    273 if(options.only_expire) { // expire entry
    275 (se, FUSE_ROOT_ID, old_name, strlen(old_name));
    276
    277 // no kernel support
    278 if (ret == -ENOSYS) {
    279 printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n");
    280 printf("Exiting...\n");
    281
    283 // Make sure to exit now, rather than on next request from userspace
    284 pthread_kill(main_thread, SIGPIPE);
    285
    286 break;
    287 }
    288 // 1) ret == 0: successful expire of an existing entry
    289 // 2) ret == -ENOENT: kernel has already expired the entry /
    290 // entry does not exist anymore in the kernel
    291 assert(ret == 0 || ret == -ENOENT);
    292 } else { // invalidate entry
    294 (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
    295 }
    296 }
    297 free(old_name);
    298 sleep(options.update_interval);
    299 }
    300 return NULL;
    301}
    302
    303static void show_help(const char *progname)
    304{
    305 printf("usage: %s [options] <mountpoint>\n\n", progname);
    306 printf("File-system specific options:\n"
    307 " --timeout=<secs> Timeout for kernel caches\n"
    308 " --update-interval=<secs> Update-rate of file system contents\n"
    309 " --no-notify Disable kernel notifications\n"
    310 " --only-expire Expire entries instead of invalidating them\n"
    311 "\n");
    312}
    313
    314int main(int argc, char *argv[]) {
    315 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    316 struct fuse_session *se;
    317 struct fuse_cmdline_opts opts;
    318 struct fuse_loop_config *config;
    319 pthread_t updater;
    320 int ret = -1;
    321
    322 if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
    323 return 1;
    324
    325 if (fuse_parse_cmdline(&args, &opts) != 0)
    326 return 1;
    327 if (opts.show_help) {
    328 show_help(argv[0]);
    331 ret = 0;
    332 goto err_out1;
    333 } else if (opts.show_version) {
    334 printf("FUSE library version %s\n", fuse_pkgversion());
    336 ret = 0;
    337 goto err_out1;
    338 }
    339
    340 /* Initial contents */
    341 update_fs();
    342
    343 se = fuse_session_new(&args, &tfs_oper,
    344 sizeof(tfs_oper), &se);
    345 if (se == NULL)
    346 goto err_out1;
    347
    348 if (fuse_set_signal_handlers(se) != 0)
    349 goto err_out2;
    350
    351 if (fuse_session_mount(se, opts.mountpoint) != 0)
    352 goto err_out3;
    353
    354 fuse_daemonize(opts.foreground);
    355
    356 // Needed to ensure that the main thread continues/restarts processing as soon
    357 // as the fuse session ends (immediately after calling fuse_session_exit() )
    358 // and not only on the next request from userspace
    359 main_thread = pthread_self();
    360
    361 /* Start thread to update file contents */
    362 ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
    363 if (ret != 0) {
    364 fprintf(stderr, "pthread_create failed with %s\n",
    365 strerror(ret));
    366 goto err_out3;
    367 }
    368
    369 /* Block until ctrl+c or fusermount -u */
    370 if (opts.singlethread) {
    371 ret = fuse_session_loop(se);
    372 } else {
    373 config = fuse_loop_cfg_create();
    374 fuse_loop_cfg_set_clone_fd(config, opts.clone_fd);
    375 fuse_loop_cfg_set_max_threads(config, opts.max_threads);
    376 ret = fuse_session_loop_mt(se, config);
    377 fuse_loop_cfg_destroy(config);
    378 config = NULL;
    379 }
    380
    382err_out3:
    384err_out2:
    386err_out1:
    387 free(opts.mountpoint);
    388 fuse_opt_free_args(&args);
    389
    390 return ret ? 1 : 0;
    391}
    392
    393
    int fuse_set_signal_handlers(struct fuse_session *se)
    const char * fuse_pkgversion(void)
    Definition fuse.c:5234
    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
    #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.17.2/doc/html/fuse-3_817_81_8dir_2example_2notify__inval__inode_8c_source.html0000644000175000017500000022073115002273247027147 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    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:5234
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2notify__store__retrieve_8c_source.html0000644000175000017500000025251315002273247027724 0ustar berndbernd libfuse: fuse-3.17.1.dir/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:5234
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2null_8c_source.html0000644000175000017500000005746715002273247023762 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2example_2passthrough_8c_source.html0000644000175000017500000026220115002273247025337 0ustar berndbernd libfuse: fuse-3.17.1.dir/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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2passthrough__fh_8c_source.html0000644000175000017500000034213115002273247026154 0ustar berndbernd libfuse: fuse-3.17.1.dir/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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2passthrough__helpers_8h_source.html0000644000175000017500000003424115002273247027226 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2example_2passthrough__ll_8c_source.html0000644000175000017500000100204115002273247026160 0ustar berndbernd libfuse: fuse-3.17.1.dir/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) {
    175 has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
    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) {
    181 has_flag = fuse_set_feature_flag(conn, 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}
    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:5234
    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:97
    uint32_t parallel_direct_writes
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2poll_8c_source.html0000644000175000017500000014370015002273247023740 0ustar berndbernd libfuse: fuse-3.17.1.dir/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:4669
    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:86
    uint32_t direct_io
    Definition fuse_common.h:71
    void(* destroy)(void *private_data)
    Definition fuse.h:649
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2poll__client_8c_source.html0000644000175000017500000003105415002273247025433 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2example_2printcap_8c_source.html0000644000175000017500000013402515002273247024612 0ustar berndbernd libfuse: fuse-3.17.1.dir/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
    #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:5234
    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.17.2/doc/html/fuse-3_817_81_8dir_2include_2cuse__lowlevel_8h_source.html0000644000175000017500000004613115002273247025776 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse_8h_source.html0000644000175000017500000044327115002273247023737 0ustar berndbernd libfuse: fuse-3.17.1.dir/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
    959int fuse_main_real_versioned(int argc, char *argv[],
    960 const struct fuse_operations *op, size_t op_size,
    961 struct libfuse_version *version, void *user_data);
    962static inline int fuse_main_fn(int argc, char *argv[],
    963 const struct fuse_operations *op,
    964 void *user_data)
    965{
    966 struct libfuse_version version = {
    967 .major = FUSE_MAJOR_VERSION,
    968 .minor = FUSE_MINOR_VERSION,
    969 .hotfix = FUSE_HOTFIX_VERSION,
    970 .padding = 0
    971 };
    972
    973 return fuse_main_real_versioned(argc, argv, op, sizeof(*(op)), &version,
    974 user_data);
    975}
    976#define fuse_main(argc, argv, op, user_data) \
    977 fuse_main_fn(argc, argv, op, user_data)
    978
    979/* ----------------------------------------------------------- *
    980 * More detailed API *
    981 * ----------------------------------------------------------- */
    982
    994void fuse_lib_help(struct fuse_args *args);
    995
    996/* Do not call this directly, use fuse_new() instead */
    997struct fuse *_fuse_new_30(struct fuse_args *args,
    998 const struct fuse_operations *op, size_t op_size,
    999 struct libfuse_version *version, void *user_data);
    1000struct fuse *_fuse_new_31(struct fuse_args *args,
    1001 const struct fuse_operations *op, size_t op_size,
    1002 struct libfuse_version *version, void *user_data);
    1003
    1031#if FUSE_USE_VERSION == 30
    1032static inline struct fuse *fuse_new_fn(struct fuse_args *args,
    1033 const struct fuse_operations *op,
    1034 size_t op_size, void *user_data)
    1035{
    1036 struct libfuse_version version = {
    1037 .major = FUSE_MAJOR_VERSION,
    1038 .minor = FUSE_MINOR_VERSION,
    1039 .hotfix = FUSE_HOTFIX_VERSION,
    1040 .padding = 0
    1041 };
    1042
    1043 return _fuse_new_30(args, op, op_size, &version, user_data);
    1044}
    1045#else /* FUSE_USE_VERSION */
    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_31(args, op, op_size, &version, user_data);
    1058}
    1059#endif
    1060#define fuse_new(args, op, size, data) fuse_new_fn(args, op, size, data)
    1061
    1070int fuse_mount(struct fuse *f, const char *mountpoint);
    1071
    1079void fuse_unmount(struct fuse *f);
    1080
    1089void fuse_destroy(struct fuse *f);
    1090
    1106int fuse_loop(struct fuse *f);
    1107
    1116void fuse_exit(struct fuse *f);
    1117
    1118#if FUSE_USE_VERSION < 32
    1119int fuse_loop_mt_31(struct fuse *f, int clone_fd);
    1120#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd)
    1121#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
    1122int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
    1123#define fuse_loop_mt(f, config) fuse_loop_mt_32(f, config)
    1124#else
    1156#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
    1157int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config);
    1158#else
    1159#define fuse_loop_mt(f, config) fuse_loop_mt_312(f, config)
    1160#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
    1161#endif
    1162
    1163
    1172struct fuse_context *fuse_get_context(void);
    1173
    1192int fuse_getgroups(int size, gid_t list[]);
    1193
    1199int fuse_interrupted(void);
    1200
    1212int fuse_invalidate_path(struct fuse *f, const char *path);
    1213
    1222
    1229void fuse_stop_cleanup_thread(struct fuse *fuse);
    1230
    1240int fuse_clean_cache(struct fuse *fuse);
    1241
    1242/*
    1243 * Stacking API
    1244 */
    1245
    1251struct fuse_fs;
    1252
    1253/*
    1254 * These functions call the relevant filesystem operation, and return
    1255 * the result.
    1256 *
    1257 * If the operation is not defined, they return -ENOSYS, with the
    1258 * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
    1259 * fuse_fs_releasedir and fuse_fs_statfs, which return 0.
    1260 */
    1261
    1262int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
    1263 struct fuse_file_info *fi);
    1264int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
    1265 const char *newpath, unsigned int flags);
    1266int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
    1267int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
    1268int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
    1269 const char *path);
    1270int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
    1271int fuse_fs_release(struct fuse_fs *fs, const char *path,
    1272 struct fuse_file_info *fi);
    1273int fuse_fs_open(struct fuse_fs *fs, const char *path,
    1274 struct fuse_file_info *fi);
    1275int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
    1276 off_t off, struct fuse_file_info *fi);
    1277int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
    1278 struct fuse_bufvec **bufp, size_t size, off_t off,
    1279 struct fuse_file_info *fi);
    1280int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
    1281 size_t size, off_t off, struct fuse_file_info *fi);
    1282int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
    1283 struct fuse_bufvec *buf, off_t off,
    1284 struct fuse_file_info *fi);
    1285int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
    1286 struct fuse_file_info *fi);
    1287int fuse_fs_flush(struct fuse_fs *fs, const char *path,
    1288 struct fuse_file_info *fi);
    1289int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
    1290int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
    1291 struct fuse_file_info *fi);
    1292int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
    1293 fuse_fill_dir_t filler, off_t off,
    1294 struct fuse_file_info *fi, enum fuse_readdir_flags flags);
    1295int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
    1296 struct fuse_file_info *fi);
    1297int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
    1298 struct fuse_file_info *fi);
    1299int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
    1300 struct fuse_file_info *fi);
    1301int fuse_fs_lock(struct fuse_fs *fs, const char *path,
    1302 struct fuse_file_info *fi, int cmd, struct flock *lock);
    1303int fuse_fs_flock(struct fuse_fs *fs, const char *path,
    1304 struct fuse_file_info *fi, int op);
    1305int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
    1306 struct fuse_file_info *fi);
    1307int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid,
    1308 struct fuse_file_info *fi);
    1309int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
    1310 struct fuse_file_info *fi);
    1311int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
    1312 const struct timespec tv[2], struct fuse_file_info *fi);
    1313int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
    1314int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
    1315 size_t len);
    1316int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
    1317 dev_t rdev);
    1318int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
    1319int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
    1320 const char *value, size_t size, int flags);
    1321int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
    1322 char *value, size_t size);
    1323int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
    1324 size_t size);
    1325int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
    1326 const char *name);
    1327int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
    1328 uint64_t *idx);
    1329#if FUSE_USE_VERSION < 35
    1330int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd,
    1331 void *arg, struct fuse_file_info *fi, unsigned int flags,
    1332 void *data);
    1333#else
    1334int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
    1335 void *arg, struct fuse_file_info *fi, unsigned int flags,
    1336 void *data);
    1337#endif
    1338int fuse_fs_poll(struct fuse_fs *fs, const char *path,
    1339 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
    1340 unsigned *reventsp);
    1341int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
    1342 off_t offset, off_t length, struct fuse_file_info *fi);
    1343ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
    1344 struct fuse_file_info *fi_in, off_t off_in,
    1345 const char *path_out,
    1346 struct fuse_file_info *fi_out, off_t off_out,
    1347 size_t len, int flags);
    1348off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
    1349 struct fuse_file_info *fi);
    1350void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
    1351 struct fuse_config *cfg);
    1352void fuse_fs_destroy(struct fuse_fs *fs);
    1353
    1354int fuse_notify_poll(struct fuse_pollhandle *ph);
    1355
    1369struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
    1370 void *private_data);
    1371
    1386typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args,
    1387 struct fuse_fs *fs[]);
    1398#define FUSE_REGISTER_MODULE(name_, factory_) \
    1399 fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_
    1400
    1402struct fuse_session *fuse_get_session(struct fuse *f);
    1403
    1412int fuse_open_channel(const char *mountpoint, const char *options);
    1413
    1414#ifdef __cplusplus
    1415}
    1416#endif
    1417
    1418#endif /* FUSE_H_ */
    int fuse_getgroups(int size, gid_t list[])
    Definition fuse.c:4679
    int fuse_mount(struct fuse *f, const char *mountpoint)
    Definition fuse.c:5220
    int fuse_interrupted(void)
    Definition fuse.c:4688
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5169
    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:4929
    int fuse_invalidate_path(struct fuse *f, const char *path)
    Definition fuse.c:4698
    struct fuse_context * fuse_get_context(void)
    Definition fuse.c:4669
    int fuse_loop(struct fuse *f)
    Definition fuse.c:4602
    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:4664
    int fuse_clean_cache(struct fuse *fuse)
    Definition fuse.c:4458
    void fuse_lib_help(struct fuse_args *args)
    Definition fuse.c:4769
    int fuse_open_channel(const char *mountpoint, const char *options)
    Definition helper.c:475
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4545
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5225
    struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
    Definition fuse.c:4879
    void fuse_stop_cleanup_thread(struct fuse *fuse)
    Definition fuse.c:4937
    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:87
    fuse_readdir_flags
    Definition fuse.h:42
    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.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse__common_8h_source.html0000644000175000017500000030244315002273247025441 0ustar berndbernd libfuse: fuse-3.17.1.dir/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#if (defined(__cplusplus) && __cplusplus >= 201103L) || \
    34 (!defined(__cplusplus) && defined(__STDC_VERSION__) && \
    35 __STDC_VERSION__ >= 201112L)
    36#define fuse_static_assert(condition, message) static_assert(condition, message)
    37#else
    38#define fuse_static_assert(condition, message)
    39#endif
    40
    41#ifdef __cplusplus
    42extern "C" {
    43#endif
    44
    60 int32_t flags;
    61
    68 uint32_t writepage : 1;
    69
    71 uint32_t direct_io : 1;
    72
    77 uint32_t keep_cache : 1;
    78
    82 uint32_t flush : 1;
    83
    86 uint32_t nonseekable : 1;
    87
    88 /* Indicates that flock locks for this file should be
    89 released. If set, lock_owner shall contain a valid value.
    90 May only be set in ->release(). */
    91 uint32_t flock_release : 1;
    92
    97 uint32_t cache_readdir : 1;
    98
    101 uint32_t noflush : 1;
    102
    106
    108 uint32_t padding : 23;
    109 uint32_t padding2 : 32;
    110 uint32_t padding3 : 32;
    111
    115 uint64_t fh;
    116
    118 uint64_t lock_owner;
    119
    122 uint32_t poll_events;
    123
    127 int32_t backing_id;
    128
    130 uint64_t compat_flags;
    131
    132 uint64_t reserved[2];
    133};
    134fuse_static_assert(sizeof(struct fuse_file_info) == 64,
    135 "fuse_file_info size mismatch");
    136
    147#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
    148struct fuse_loop_config_v1; /* forward declaration */
    150#else
    151struct fuse_loop_config_v1 {
    152#endif
    158
    169 unsigned int max_idle_threads;
    170};
    171
    172
    173/**************************************************************************
    174 * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
    175 **************************************************************************/
    176
    187#define FUSE_CAP_ASYNC_READ (1 << 0)
    188
    195#define FUSE_CAP_POSIX_LOCKS (1 << 1)
    196
    204#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
    205
    216#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
    217
    224#define FUSE_CAP_DONT_MASK (1 << 6)
    225
    232#define FUSE_CAP_SPLICE_WRITE (1 << 7)
    233
    240#define FUSE_CAP_SPLICE_MOVE (1 << 8)
    241
    249#define FUSE_CAP_SPLICE_READ (1 << 9)
    250
    262#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
    263
    269#define FUSE_CAP_IOCTL_DIR (1 << 11)
    270
    291#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12)
    292
    299#define FUSE_CAP_READDIRPLUS (1 << 13)
    300
    327#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14)
    328
    338#define FUSE_CAP_ASYNC_DIO (1 << 15)
    339
    347#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
    348
    362#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17)
    363
    370#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
    371
    389#define FUSE_CAP_POSIX_ACL (1 << 19)
    390
    398#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20)
    399
    415#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1 << 21)
    416
    428#define FUSE_CAP_CACHE_SYMLINKS (1 << 23)
    429
    443#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24)
    444
    466#define FUSE_CAP_EXPLICIT_INVAL_DATA (1 << 25)
    467
    482#define FUSE_CAP_EXPIRE_ONLY (1 << 26)
    483
    489#define FUSE_CAP_SETXATTR_EXT (1 << 27)
    490
    498#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1 << 28)
    499
    510#define FUSE_CAP_PASSTHROUGH (1 << 29)
    511
    518#define FUSE_CAP_NO_EXPORT_SUPPORT (1 << 30)
    519
    530#define FUSE_IOCTL_COMPAT (1 << 0)
    531#define FUSE_IOCTL_UNRESTRICTED (1 << 1)
    532#define FUSE_IOCTL_RETRY (1 << 2)
    533#define FUSE_IOCTL_DIR (1 << 4)
    534
    535#define FUSE_IOCTL_MAX_IOV 256
    536
    552 uint32_t proto_major;
    553
    557 uint32_t proto_minor;
    558
    562 uint32_t max_write;
    563
    576 uint32_t max_read;
    577
    582
    588 uint32_t capable;
    589
    600 uint32_t want;
    601
    631
    641
    657 uint32_t time_gran;
    658
    675#define FUSE_BACKING_STACKED_UNDER (0)
    676#define FUSE_BACKING_STACKED_OVER (1)
    677 uint32_t max_backing_stack_depth;
    678
    687 uint32_t no_interrupt : 1;
    688
    689 /* reserved bits for future use */
    690 uint32_t padding : 31;
    691
    696 uint64_t capable_ext;
    697
    706 uint64_t want_ext;
    707
    711 uint32_t reserved[16];
    712};
    713fuse_static_assert(sizeof(struct fuse_conn_info) == 128,
    714 "Size of struct fuse_conn_info must be 128 bytes");
    715
    716struct fuse_session;
    717struct fuse_pollhandle;
    718struct fuse_conn_info_opts;
    719
    762struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args);
    763
    771void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
    772 struct fuse_conn_info *conn);
    773
    780int fuse_daemonize(int foreground);
    781
    787int fuse_version(void);
    788
    794const char *fuse_pkgversion(void);
    795
    801void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
    802
    803/* ----------------------------------------------------------- *
    804 * Data buffer *
    805 * ----------------------------------------------------------- */
    806
    817 FUSE_BUF_IS_FD = (1 << 1),
    818
    827
    835 FUSE_BUF_FD_RETRY = (1 << 3)
    837
    879
    886struct fuse_buf {
    890 size_t size;
    891
    896
    902 void *mem;
    903
    909 int fd;
    910
    916 off_t pos;
    917
    924 size_t mem_size;
    925};
    926
    939 size_t count;
    940
    944 size_t idx;
    945
    949 size_t off;
    950
    954 struct fuse_buf buf[1];
    955};
    956
    962{
    963 uint32_t major;
    964 uint32_t minor;
    965 uint32_t hotfix;
    966 uint32_t padding;
    967};
    968
    969/* Initialize bufvec with a single buffer of given size */
    970#define FUSE_BUFVEC_INIT(size__) \
    971 ((struct fuse_bufvec) { \
    972 /* .count= */ 1, \
    973 /* .idx = */ 0, \
    974 /* .off = */ 0, \
    975 /* .buf = */ { /* [0] = */ { \
    976 /* .size = */ (size__), \
    977 /* .flags = */ (enum fuse_buf_flags) 0, \
    978 /* .mem = */ NULL, \
    979 /* .fd = */ -1, \
    980 /* .pos = */ 0, \
    981 /* .mem_size = */ 0, \
    982 } } \
    983 } )
    984
    991size_t fuse_buf_size(const struct fuse_bufvec *bufv);
    992
    1001ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
    1002 enum fuse_buf_copy_flags flags);
    1003
    1004/* ----------------------------------------------------------- *
    1005 * Signal handling *
    1006 * ----------------------------------------------------------- */
    1007
    1023int fuse_set_signal_handlers(struct fuse_session *se);
    1024
    1040int fuse_set_fail_signal_handlers(struct fuse_session *se);
    1041
    1053void fuse_remove_signal_handlers(struct fuse_session *se);
    1054
    1060#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
    1066struct fuse_loop_config *fuse_loop_cfg_create(void);
    1067
    1071void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
    1072
    1076void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
    1077 unsigned int value);
    1078
    1082void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
    1083 unsigned int value);
    1084
    1088void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
    1089 unsigned int value);
    1090
    1097void fuse_loop_cfg_convert(struct fuse_loop_config *config,
    1098 struct fuse_loop_config_v1 *v1_conf);
    1099#endif
    1100
    1101
    1102static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn,
    1103 uint64_t flag)
    1104{
    1105 if (conn->capable_ext & flag) {
    1106 conn->want_ext |= flag;
    1107 return true;
    1108 }
    1109 return false;
    1110}
    1111
    1112static inline void fuse_unset_feature_flag(struct fuse_conn_info *conn,
    1113 uint64_t flag)
    1114{
    1115 conn->want_ext &= ~flag;
    1116}
    1117
    1118static inline bool fuse_get_feature_flag(struct fuse_conn_info *conn,
    1119 uint64_t flag)
    1120{
    1121 return conn->capable_ext & flag ? true : false;
    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_ */
    int fuse_set_fail_signal_handlers(struct fuse_session *se)
    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:459
    const char * fuse_pkgversion(void)
    Definition fuse.c:5234
    void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
    int fuse_version(void)
    Definition fuse.c:5229
    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:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t nonseekable
    Definition fuse_common.h:86
    int32_t backing_id
    uint32_t parallel_direct_writes
    uint32_t padding
    uint32_t noflush
    uint64_t compat_flags
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    unsigned int max_idle_threads
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse__kernel_8h_source.html0000644000175000017500000042346515002273247025441 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse__log_8h_source.html0000644000175000017500000003051515002273247024730 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse__lowlevel_8h_source.html0000644000175000017500000052215115002273247026002 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse__mount__compat_8h_source.html0000644000175000017500000002250315002273247027011 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse__opt_8h_source.html0000644000175000017500000005417615002273247024762 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2buffer_8c_source.html0000644000175000017500000020677415002273247023371 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2compat_8c_source.html0000644000175000017500000004540515002273247023373 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2cuse__lowlevel_8c_source.html0000644000175000017500000021350615002273247025116 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2fuse_8c_source.html0000644000175000017500000331326515002273247023057 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 container_of(ptr, type, member) ({ \
    96 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    97 (type *)( (char *)__mptr - offsetof(type,member) );})
    98
    99#define list_entry(ptr, type, member) \
    100 container_of(ptr, type, member)
    101
    102struct list_head {
    103 struct list_head *next;
    104 struct list_head *prev;
    105};
    106
    107struct node_slab {
    108 struct list_head list; /* must be the first member */
    109 struct list_head freelist;
    110 int used;
    111};
    112
    113struct fuse {
    114 struct fuse_session *se;
    115 struct node_table name_table;
    116 struct node_table id_table;
    117 struct list_head lru_table;
    118 fuse_ino_t ctr;
    119 unsigned int generation;
    120 unsigned int hidectr;
    121 pthread_mutex_t lock;
    122 struct fuse_config conf;
    123 int intr_installed;
    124 struct fuse_fs *fs;
    125 struct lock_queue_element *lockq;
    126 int pagesize;
    127 struct list_head partial_slabs;
    128 struct list_head full_slabs;
    129 pthread_t prune_thread;
    130};
    131
    132struct lock {
    133 int type;
    134 off_t start;
    135 off_t end;
    136 pid_t pid;
    137 uint64_t owner;
    138 struct lock *next;
    139};
    140
    141struct node {
    142 struct node *name_next;
    143 struct node *id_next;
    144 fuse_ino_t nodeid;
    145 unsigned int generation;
    146 int refctr;
    147 struct node *parent;
    148 char *name;
    149 uint64_t nlookup;
    150 int open_count;
    151 struct timespec stat_updated;
    152 struct timespec mtime;
    153 off_t size;
    154 struct lock *locks;
    155 unsigned int is_hidden : 1;
    156 unsigned int cache_valid : 1;
    157 int treelock;
    158 char inline_name[32];
    159};
    160
    161#define TREELOCK_WRITE -1
    162#define TREELOCK_WAIT_OFFSET INT_MIN
    163
    164struct node_lru {
    165 struct node node;
    166 struct list_head lru;
    167 struct timespec forget_time;
    168};
    169
    170struct fuse_direntry {
    171 struct stat stat;
    172 enum fuse_fill_dir_flags flags;
    173 char *name;
    174 struct fuse_direntry *next;
    175};
    176
    177struct fuse_dh {
    178 pthread_mutex_t lock;
    179 struct fuse *fuse;
    180 fuse_req_t req;
    181 char *contents;
    182 struct fuse_direntry *first;
    183 struct fuse_direntry **last;
    184 unsigned len;
    185 unsigned size;
    186 unsigned needlen;
    187 int filled;
    188 uint64_t fh;
    189 int error;
    190 fuse_ino_t nodeid;
    191};
    192
    193struct fuse_context_i {
    194 struct fuse_context ctx;
    195 fuse_req_t req;
    196};
    197
    198/* Defined by FUSE_REGISTER_MODULE() in lib/modules/subdir.c and iconv.c. */
    199extern fuse_module_factory_t fuse_module_subdir_factory;
    200#ifdef HAVE_ICONV
    201extern fuse_module_factory_t fuse_module_iconv_factory;
    202#endif
    203
    204static pthread_key_t fuse_context_key;
    205static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
    206static int fuse_context_ref;
    207static struct fuse_module *fuse_modules = NULL;
    208
    209static int fuse_register_module(const char *name,
    210 fuse_module_factory_t factory,
    211 struct fusemod_so *so)
    212{
    213 struct fuse_module *mod;
    214
    215 mod = calloc(1, sizeof(struct fuse_module));
    216 if (!mod) {
    217 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module\n");
    218 return -1;
    219 }
    220 mod->name = strdup(name);
    221 if (!mod->name) {
    222 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module name\n");
    223 free(mod);
    224 return -1;
    225 }
    226 mod->factory = factory;
    227 mod->ctr = 0;
    228 mod->so = so;
    229 if (mod->so)
    230 mod->so->ctr++;
    231 mod->next = fuse_modules;
    232 fuse_modules = mod;
    233
    234 return 0;
    235}
    236
    237static void fuse_unregister_module(struct fuse_module *m)
    238{
    239 struct fuse_module **mp;
    240 for (mp = &fuse_modules; *mp; mp = &(*mp)->next) {
    241 if (*mp == m) {
    242 *mp = (*mp)->next;
    243 break;
    244 }
    245 }
    246 free(m->name);
    247 free(m);
    248}
    249
    250static int fuse_load_so_module(const char *module)
    251{
    252 int ret = -1;
    253 char *tmp;
    254 struct fusemod_so *so;
    255 fuse_module_factory_t *factory;
    256
    257 tmp = malloc(strlen(module) + 64);
    258 if (!tmp) {
    259 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
    260 return -1;
    261 }
    262 sprintf(tmp, "libfusemod_%s.so", module);
    263 so = calloc(1, sizeof(struct fusemod_so));
    264 if (!so) {
    265 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module so\n");
    266 goto out;
    267 }
    268
    269 so->handle = dlopen(tmp, RTLD_NOW);
    270 if (so->handle == NULL) {
    271 fuse_log(FUSE_LOG_ERR, "fuse: dlopen(%s) failed: %s\n",
    272 tmp, dlerror());
    273 goto out_free_so;
    274 }
    275
    276 sprintf(tmp, "fuse_module_%s_factory", module);
    277 factory = (fuse_module_factory_t*)dlsym(so->handle, tmp);
    278 if (factory == NULL) {
    279 fuse_log(FUSE_LOG_ERR, "fuse: symbol <%s> not found in module: %s\n",
    280 tmp, dlerror());
    281 goto out_dlclose;
    282 }
    283 ret = fuse_register_module(module, *factory, so);
    284 if (ret)
    285 goto out_dlclose;
    286
    287out:
    288 free(tmp);
    289 return ret;
    290
    291out_dlclose:
    292 dlclose(so->handle);
    293out_free_so:
    294 free(so);
    295 goto out;
    296}
    297
    298static struct fuse_module *fuse_find_module(const char *module)
    299{
    300 struct fuse_module *m;
    301 for (m = fuse_modules; m; m = m->next) {
    302 if (strcmp(module, m->name) == 0) {
    303 m->ctr++;
    304 break;
    305 }
    306 }
    307 return m;
    308}
    309
    310static struct fuse_module *fuse_get_module(const char *module)
    311{
    312 struct fuse_module *m;
    313
    314 pthread_mutex_lock(&fuse_context_lock);
    315 m = fuse_find_module(module);
    316 if (!m) {
    317 int err = fuse_load_so_module(module);
    318 if (!err)
    319 m = fuse_find_module(module);
    320 }
    321 pthread_mutex_unlock(&fuse_context_lock);
    322 return m;
    323}
    324
    325static void fuse_put_module(struct fuse_module *m)
    326{
    327 pthread_mutex_lock(&fuse_context_lock);
    328 if (m->so)
    329 assert(m->ctr > 0);
    330 /* Builtin modules may already have m->ctr == 0 */
    331 if (m->ctr > 0)
    332 m->ctr--;
    333 if (!m->ctr && m->so) {
    334 struct fusemod_so *so = m->so;
    335 assert(so->ctr > 0);
    336 so->ctr--;
    337 if (!so->ctr) {
    338 struct fuse_module **mp;
    339 for (mp = &fuse_modules; *mp;) {
    340 if ((*mp)->so == so)
    341 fuse_unregister_module(*mp);
    342 else
    343 mp = &(*mp)->next;
    344 }
    345 dlclose(so->handle);
    346 free(so);
    347 }
    348 } else if (!m->ctr) {
    349 fuse_unregister_module(m);
    350 }
    351 pthread_mutex_unlock(&fuse_context_lock);
    352}
    353
    354static void init_list_head(struct list_head *list)
    355{
    356 list->next = list;
    357 list->prev = list;
    358}
    359
    360static int list_empty(const struct list_head *head)
    361{
    362 return head->next == head;
    363}
    364
    365static void list_add(struct list_head *new, struct list_head *prev,
    366 struct list_head *next)
    367{
    368 next->prev = new;
    369 new->next = next;
    370 new->prev = prev;
    371 prev->next = new;
    372}
    373
    374static inline void list_add_head(struct list_head *new, struct list_head *head)
    375{
    376 list_add(new, head, head->next);
    377}
    378
    379static inline void list_add_tail(struct list_head *new, struct list_head *head)
    380{
    381 list_add(new, head->prev, head);
    382}
    383
    384static inline void list_del(struct list_head *entry)
    385{
    386 struct list_head *prev = entry->prev;
    387 struct list_head *next = entry->next;
    388
    389 next->prev = prev;
    390 prev->next = next;
    391}
    392
    393static inline int lru_enabled(struct fuse *f)
    394{
    395 return f->conf.remember > 0;
    396}
    397
    398static struct node_lru *node_lru(struct node *node)
    399{
    400 return (struct node_lru *) node;
    401}
    402
    403static size_t get_node_size(struct fuse *f)
    404{
    405 if (lru_enabled(f))
    406 return sizeof(struct node_lru);
    407 else
    408 return sizeof(struct node);
    409}
    410
    411#ifdef FUSE_NODE_SLAB
    412static struct node_slab *list_to_slab(struct list_head *head)
    413{
    414 return (struct node_slab *) head;
    415}
    416
    417static struct node_slab *node_to_slab(struct fuse *f, struct node *node)
    418{
    419 return (struct node_slab *) (((uintptr_t) node) & ~((uintptr_t) f->pagesize - 1));
    420}
    421
    422static int alloc_slab(struct fuse *f)
    423{
    424 void *mem;
    425 struct node_slab *slab;
    426 char *start;
    427 size_t num;
    428 size_t i;
    429 size_t node_size = get_node_size(f);
    430
    431 mem = mmap(NULL, f->pagesize, PROT_READ | PROT_WRITE,
    432 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    433
    434 if (mem == MAP_FAILED)
    435 return -1;
    436
    437 slab = mem;
    438 init_list_head(&slab->freelist);
    439 slab->used = 0;
    440 num = (f->pagesize - sizeof(struct node_slab)) / node_size;
    441
    442 start = (char *) mem + f->pagesize - num * node_size;
    443 for (i = 0; i < num; i++) {
    444 struct list_head *n;
    445
    446 n = (struct list_head *) (start + i * node_size);
    447 list_add_tail(n, &slab->freelist);
    448 }
    449 list_add_tail(&slab->list, &f->partial_slabs);
    450
    451 return 0;
    452}
    453
    454static struct node *alloc_node(struct fuse *f)
    455{
    456 struct node_slab *slab;
    457 struct list_head *node;
    458
    459 if (list_empty(&f->partial_slabs)) {
    460 int res = alloc_slab(f);
    461 if (res != 0)
    462 return NULL;
    463 }
    464 slab = list_to_slab(f->partial_slabs.next);
    465 slab->used++;
    466 node = slab->freelist.next;
    467 list_del(node);
    468 if (list_empty(&slab->freelist)) {
    469 list_del(&slab->list);
    470 list_add_tail(&slab->list, &f->full_slabs);
    471 }
    472 memset(node, 0, sizeof(struct node));
    473
    474 return (struct node *) node;
    475}
    476
    477static void free_slab(struct fuse *f, struct node_slab *slab)
    478{
    479 int res;
    480
    481 list_del(&slab->list);
    482 res = munmap(slab, f->pagesize);
    483 if (res == -1)
    484 fuse_log(FUSE_LOG_WARNING, "fuse warning: munmap(%p) failed\n",
    485 slab);
    486}
    487
    488static void free_node_mem(struct fuse *f, struct node *node)
    489{
    490 struct node_slab *slab = node_to_slab(f, node);
    491 struct list_head *n = (struct list_head *) node;
    492
    493 slab->used--;
    494 if (slab->used) {
    495 if (list_empty(&slab->freelist)) {
    496 list_del(&slab->list);
    497 list_add_tail(&slab->list, &f->partial_slabs);
    498 }
    499 list_add_head(n, &slab->freelist);
    500 } else {
    501 free_slab(f, slab);
    502 }
    503}
    504#else
    505static struct node *alloc_node(struct fuse *f)
    506{
    507 return (struct node *) calloc(1, get_node_size(f));
    508}
    509
    510static void free_node_mem(struct fuse *f, struct node *node)
    511{
    512 (void) f;
    513 free(node);
    514}
    515#endif
    516
    517static size_t id_hash(struct fuse *f, fuse_ino_t ino)
    518{
    519 uint64_t hash = ((uint32_t) ino * 2654435761U) % f->id_table.size;
    520 uint64_t oldhash = hash % (f->id_table.size / 2);
    521
    522 if (oldhash >= f->id_table.split)
    523 return oldhash;
    524 else
    525 return hash;
    526}
    527
    528static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
    529{
    530 size_t hash = id_hash(f, nodeid);
    531 struct node *node;
    532
    533 for (node = f->id_table.array[hash]; node != NULL; node = node->id_next)
    534 if (node->nodeid == nodeid)
    535 return node;
    536
    537 return NULL;
    538}
    539
    540static struct node *get_node(struct fuse *f, fuse_ino_t nodeid)
    541{
    542 struct node *node = get_node_nocheck(f, nodeid);
    543 if (!node) {
    544 fuse_log(FUSE_LOG_ERR, "fuse internal error: node %llu not found\n",
    545 (unsigned long long) nodeid);
    546 abort();
    547 }
    548 return node;
    549}
    550
    551static void curr_time(struct timespec *now);
    552static double diff_timespec(const struct timespec *t1,
    553 const struct timespec *t2);
    554
    555static void remove_node_lru(struct node *node)
    556{
    557 struct node_lru *lnode = node_lru(node);
    558 list_del(&lnode->lru);
    559 init_list_head(&lnode->lru);
    560}
    561
    562static void set_forget_time(struct fuse *f, struct node *node)
    563{
    564 struct node_lru *lnode = node_lru(node);
    565
    566 list_del(&lnode->lru);
    567 list_add_tail(&lnode->lru, &f->lru_table);
    568 curr_time(&lnode->forget_time);
    569}
    570
    571static void free_node(struct fuse *f, struct node *node)
    572{
    573 if (node->name != node->inline_name)
    574 free(node->name);
    575 free_node_mem(f, node);
    576}
    577
    578static void node_table_reduce(struct node_table *t)
    579{
    580 size_t newsize = t->size / 2;
    581 void *newarray;
    582
    583 if (newsize < NODE_TABLE_MIN_SIZE)
    584 return;
    585
    586 newarray = realloc(t->array, sizeof(struct node *) * newsize);
    587 if (newarray != NULL)
    588 t->array = newarray;
    589
    590 t->size = newsize;
    591 t->split = t->size / 2;
    592}
    593
    594static void remerge_id(struct fuse *f)
    595{
    596 struct node_table *t = &f->id_table;
    597 int iter;
    598
    599 if (t->split == 0)
    600 node_table_reduce(t);
    601
    602 for (iter = 8; t->split > 0 && iter; iter--) {
    603 struct node **upper;
    604
    605 t->split--;
    606 upper = &t->array[t->split + t->size / 2];
    607 if (*upper) {
    608 struct node **nodep;
    609
    610 for (nodep = &t->array[t->split]; *nodep;
    611 nodep = &(*nodep)->id_next);
    612
    613 *nodep = *upper;
    614 *upper = NULL;
    615 break;
    616 }
    617 }
    618}
    619
    620static void unhash_id(struct fuse *f, struct node *node)
    621{
    622 struct node **nodep = &f->id_table.array[id_hash(f, node->nodeid)];
    623
    624 for (; *nodep != NULL; nodep = &(*nodep)->id_next)
    625 if (*nodep == node) {
    626 *nodep = node->id_next;
    627 f->id_table.use--;
    628
    629 if(f->id_table.use < f->id_table.size / 4)
    630 remerge_id(f);
    631 return;
    632 }
    633}
    634
    635static int node_table_resize(struct node_table *t)
    636{
    637 size_t newsize = t->size * 2;
    638 void *newarray;
    639
    640 newarray = realloc(t->array, sizeof(struct node *) * newsize);
    641 if (newarray == NULL)
    642 return -1;
    643
    644 t->array = newarray;
    645 memset(t->array + t->size, 0, t->size * sizeof(struct node *));
    646 t->size = newsize;
    647 t->split = 0;
    648
    649 return 0;
    650}
    651
    652static void rehash_id(struct fuse *f)
    653{
    654 struct node_table *t = &f->id_table;
    655 struct node **nodep;
    656 struct node **next;
    657 size_t hash;
    658
    659 if (t->split == t->size / 2)
    660 return;
    661
    662 hash = t->split;
    663 t->split++;
    664 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
    665 struct node *node = *nodep;
    666 size_t newhash = id_hash(f, node->nodeid);
    667
    668 if (newhash != hash) {
    669 next = nodep;
    670 *nodep = node->id_next;
    671 node->id_next = t->array[newhash];
    672 t->array[newhash] = node;
    673 } else {
    674 next = &node->id_next;
    675 }
    676 }
    677 if (t->split == t->size / 2)
    678 node_table_resize(t);
    679}
    680
    681static void hash_id(struct fuse *f, struct node *node)
    682{
    683 size_t hash = id_hash(f, node->nodeid);
    684 node->id_next = f->id_table.array[hash];
    685 f->id_table.array[hash] = node;
    686 f->id_table.use++;
    687
    688 if (f->id_table.use >= f->id_table.size / 2)
    689 rehash_id(f);
    690}
    691
    692static size_t name_hash(struct fuse *f, fuse_ino_t parent,
    693 const char *name)
    694{
    695 uint64_t hash = parent;
    696 uint64_t oldhash;
    697
    698 for (; *name; name++)
    699 hash = hash * 31 + (unsigned char) *name;
    700
    701 hash %= f->name_table.size;
    702 oldhash = hash % (f->name_table.size / 2);
    703 if (oldhash >= f->name_table.split)
    704 return oldhash;
    705 else
    706 return hash;
    707}
    708
    709static void unref_node(struct fuse *f, struct node *node);
    710
    711static void remerge_name(struct fuse *f)
    712{
    713 struct node_table *t = &f->name_table;
    714 int iter;
    715
    716 if (t->split == 0)
    717 node_table_reduce(t);
    718
    719 for (iter = 8; t->split > 0 && iter; iter--) {
    720 struct node **upper;
    721
    722 t->split--;
    723 upper = &t->array[t->split + t->size / 2];
    724 if (*upper) {
    725 struct node **nodep;
    726
    727 for (nodep = &t->array[t->split]; *nodep;
    728 nodep = &(*nodep)->name_next);
    729
    730 *nodep = *upper;
    731 *upper = NULL;
    732 break;
    733 }
    734 }
    735}
    736
    737static void unhash_name(struct fuse *f, struct node *node)
    738{
    739 if (node->name) {
    740 size_t hash = name_hash(f, node->parent->nodeid, node->name);
    741 struct node **nodep = &f->name_table.array[hash];
    742
    743 for (; *nodep != NULL; nodep = &(*nodep)->name_next)
    744 if (*nodep == node) {
    745 *nodep = node->name_next;
    746 node->name_next = NULL;
    747 unref_node(f, node->parent);
    748 if (node->name != node->inline_name)
    749 free(node->name);
    750 node->name = NULL;
    751 node->parent = NULL;
    752 f->name_table.use--;
    753
    754 if (f->name_table.use < f->name_table.size / 4)
    755 remerge_name(f);
    756 return;
    757 }
    758 fuse_log(FUSE_LOG_ERR,
    759 "fuse internal error: unable to unhash node: %llu\n",
    760 (unsigned long long) node->nodeid);
    761 abort();
    762 }
    763}
    764
    765static void rehash_name(struct fuse *f)
    766{
    767 struct node_table *t = &f->name_table;
    768 struct node **nodep;
    769 struct node **next;
    770 size_t hash;
    771
    772 if (t->split == t->size / 2)
    773 return;
    774
    775 hash = t->split;
    776 t->split++;
    777 for (nodep = &t->array[hash]; *nodep != NULL; nodep = next) {
    778 struct node *node = *nodep;
    779 size_t newhash = name_hash(f, node->parent->nodeid, node->name);
    780
    781 if (newhash != hash) {
    782 next = nodep;
    783 *nodep = node->name_next;
    784 node->name_next = t->array[newhash];
    785 t->array[newhash] = node;
    786 } else {
    787 next = &node->name_next;
    788 }
    789 }
    790 if (t->split == t->size / 2)
    791 node_table_resize(t);
    792}
    793
    794static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parentid,
    795 const char *name)
    796{
    797 size_t hash = name_hash(f, parentid, name);
    798 struct node *parent = get_node(f, parentid);
    799 if (strlen(name) < sizeof(node->inline_name)) {
    800 strcpy(node->inline_name, name);
    801 node->name = node->inline_name;
    802 } else {
    803 node->name = strdup(name);
    804 if (node->name == NULL)
    805 return -1;
    806 }
    807
    808 parent->refctr ++;
    809 node->parent = parent;
    810 node->name_next = f->name_table.array[hash];
    811 f->name_table.array[hash] = node;
    812 f->name_table.use++;
    813
    814 if (f->name_table.use >= f->name_table.size / 2)
    815 rehash_name(f);
    816
    817 return 0;
    818}
    819
    820static void delete_node(struct fuse *f, struct node *node)
    821{
    822 if (f->conf.debug)
    823 fuse_log(FUSE_LOG_DEBUG, "DELETE: %llu\n",
    824 (unsigned long long) node->nodeid);
    825
    826 assert(node->treelock == 0);
    827 unhash_name(f, node);
    828 if (lru_enabled(f))
    829 remove_node_lru(node);
    830 unhash_id(f, node);
    831 free_node(f, node);
    832}
    833
    834static void unref_node(struct fuse *f, struct node *node)
    835{
    836 assert(node->refctr > 0);
    837 node->refctr --;
    838 if (!node->refctr)
    839 delete_node(f, node);
    840}
    841
    842static fuse_ino_t next_id(struct fuse *f)
    843{
    844 do {
    845 f->ctr = (f->ctr + 1) & 0xffffffff;
    846 if (!f->ctr)
    847 f->generation ++;
    848 } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO ||
    849 get_node_nocheck(f, f->ctr) != NULL);
    850 return f->ctr;
    851}
    852
    853static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
    854 const char *name)
    855{
    856 size_t hash = name_hash(f, parent, name);
    857 struct node *node;
    858
    859 for (node = f->name_table.array[hash]; node != NULL; node = node->name_next)
    860 if (node->parent->nodeid == parent &&
    861 strcmp(node->name, name) == 0)
    862 return node;
    863
    864 return NULL;
    865}
    866
    867static void inc_nlookup(struct node *node)
    868{
    869 if (!node->nlookup)
    870 node->refctr++;
    871 node->nlookup++;
    872}
    873
    874static struct node *find_node(struct fuse *f, fuse_ino_t parent,
    875 const char *name)
    876{
    877 struct node *node;
    878
    879 pthread_mutex_lock(&f->lock);
    880 if (!name)
    881 node = get_node(f, parent);
    882 else
    883 node = lookup_node(f, parent, name);
    884 if (node == NULL) {
    885 node = alloc_node(f);
    886 if (node == NULL)
    887 goto out_err;
    888
    889 node->nodeid = next_id(f);
    890 node->generation = f->generation;
    891 if (f->conf.remember)
    892 inc_nlookup(node);
    893
    894 if (hash_name(f, node, parent, name) == -1) {
    895 free_node(f, node);
    896 node = NULL;
    897 goto out_err;
    898 }
    899 hash_id(f, node);
    900 if (lru_enabled(f)) {
    901 struct node_lru *lnode = node_lru(node);
    902 init_list_head(&lnode->lru);
    903 }
    904 } else if (lru_enabled(f) && node->nlookup == 1) {
    905 remove_node_lru(node);
    906 }
    907 inc_nlookup(node);
    908out_err:
    909 pthread_mutex_unlock(&f->lock);
    910 return node;
    911}
    912
    913static int lookup_path_in_cache(struct fuse *f,
    914 const char *path, fuse_ino_t *inop)
    915{
    916 char *tmp = strdup(path);
    917 if (!tmp)
    918 return -ENOMEM;
    919
    920 pthread_mutex_lock(&f->lock);
    921 fuse_ino_t ino = FUSE_ROOT_ID;
    922
    923 int err = 0;
    924 char *save_ptr;
    925 char *path_element = strtok_r(tmp, "/", &save_ptr);
    926 while (path_element != NULL) {
    927 struct node *node = lookup_node(f, ino, path_element);
    928 if (node == NULL) {
    929 err = -ENOENT;
    930 break;
    931 }
    932 ino = node->nodeid;
    933 path_element = strtok_r(NULL, "/", &save_ptr);
    934 }
    935 pthread_mutex_unlock(&f->lock);
    936 free(tmp);
    937
    938 if (!err)
    939 *inop = ino;
    940 return err;
    941}
    942
    943static char *add_name(char **buf, unsigned *bufsize, char *s, const char *name)
    944{
    945 size_t len = strlen(name);
    946
    947 if (s - len <= *buf) {
    948 unsigned pathlen = *bufsize - (s - *buf);
    949 unsigned newbufsize = *bufsize;
    950 char *newbuf;
    951
    952 while (newbufsize < pathlen + len + 1) {
    953 if (newbufsize >= 0x80000000)
    954 newbufsize = 0xffffffff;
    955 else
    956 newbufsize *= 2;
    957 }
    958
    959 newbuf = realloc(*buf, newbufsize);
    960 if (newbuf == NULL)
    961 return NULL;
    962
    963 *buf = newbuf;
    964 s = newbuf + newbufsize - pathlen;
    965 memmove(s, newbuf + *bufsize - pathlen, pathlen);
    966 *bufsize = newbufsize;
    967 }
    968 s -= len;
    969 memcpy(s, name, len);
    970 s--;
    971 *s = '/';
    972
    973 return s;
    974}
    975
    976static void unlock_path(struct fuse *f, fuse_ino_t nodeid, struct node *wnode,
    977 struct node *end)
    978{
    979 struct node *node;
    980
    981 if (wnode) {
    982 assert(wnode->treelock == TREELOCK_WRITE);
    983 wnode->treelock = 0;
    984 }
    985
    986 for (node = get_node(f, nodeid);
    987 node != end && node->nodeid != FUSE_ROOT_ID; node = node->parent) {
    988 assert(node->treelock != 0);
    989 assert(node->treelock != TREELOCK_WAIT_OFFSET);
    990 assert(node->treelock != TREELOCK_WRITE);
    991 node->treelock--;
    992 if (node->treelock == TREELOCK_WAIT_OFFSET)
    993 node->treelock = 0;
    994 }
    995}
    996
    997static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
    998 char **path, struct node **wnodep, bool need_lock)
    999{
    1000 unsigned bufsize = 256;
    1001 char *buf;
    1002 char *s;
    1003 struct node *node;
    1004 struct node *wnode = NULL;
    1005 int err;
    1006
    1007 *path = NULL;
    1008
    1009 err = -ENOMEM;
    1010 buf = malloc(bufsize);
    1011 if (buf == NULL)
    1012 goto out_err;
    1013
    1014 s = buf + bufsize - 1;
    1015 *s = '\0';
    1016
    1017 if (name != NULL) {
    1018 s = add_name(&buf, &bufsize, s, name);
    1019 err = -ENOMEM;
    1020 if (s == NULL)
    1021 goto out_free;
    1022 }
    1023
    1024 if (wnodep) {
    1025 assert(need_lock);
    1026 wnode = lookup_node(f, nodeid, name);
    1027 if (wnode) {
    1028 if (wnode->treelock != 0) {
    1029 if (wnode->treelock > 0)
    1030 wnode->treelock += TREELOCK_WAIT_OFFSET;
    1031 err = -EAGAIN;
    1032 goto out_free;
    1033 }
    1034 wnode->treelock = TREELOCK_WRITE;
    1035 }
    1036 }
    1037
    1038 for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
    1039 node = node->parent) {
    1040 err = -ESTALE;
    1041 if (node->name == NULL || node->parent == NULL)
    1042 goto out_unlock;
    1043
    1044 err = -ENOMEM;
    1045 s = add_name(&buf, &bufsize, s, node->name);
    1046 if (s == NULL)
    1047 goto out_unlock;
    1048
    1049 if (need_lock) {
    1050 err = -EAGAIN;
    1051 if (node->treelock < 0)
    1052 goto out_unlock;
    1053
    1054 node->treelock++;
    1055 }
    1056 }
    1057
    1058 if (s[0])
    1059 memmove(buf, s, bufsize - (s - buf));
    1060 else
    1061 strcpy(buf, "/");
    1062
    1063 *path = buf;
    1064 if (wnodep)
    1065 *wnodep = wnode;
    1066
    1067 return 0;
    1068
    1069 out_unlock:
    1070 if (need_lock)
    1071 unlock_path(f, nodeid, wnode, node);
    1072 out_free:
    1073 free(buf);
    1074
    1075 out_err:
    1076 return err;
    1077}
    1078
    1079static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
    1080 fuse_ino_t nodeid2, const char *name2,
    1081 char **path1, char **path2,
    1082 struct node **wnode1, struct node **wnode2)
    1083{
    1084 int err;
    1085
    1086 /* FIXME: locking two paths needs deadlock checking */
    1087 err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
    1088 if (!err) {
    1089 err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
    1090 if (err) {
    1091 struct node *wn1 = wnode1 ? *wnode1 : NULL;
    1092
    1093 unlock_path(f, nodeid1, wn1, NULL);
    1094 free(*path1);
    1095 }
    1096 }
    1097 return err;
    1098}
    1099
    1100static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
    1101{
    1102 int err;
    1103
    1104 if (!qe->path1) {
    1105 /* Just waiting for it to be unlocked */
    1106 if (get_node(f, qe->nodeid1)->treelock == 0)
    1107 pthread_cond_signal(&qe->cond);
    1108
    1109 return;
    1110 }
    1111
    1112 if (qe->done)
    1113 return; // Don't try to double-lock the element
    1114
    1115 if (!qe->path2) {
    1116 err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
    1117 qe->wnode1, true);
    1118 } else {
    1119 err = try_get_path2(f, qe->nodeid1, qe->name1, qe->nodeid2,
    1120 qe->name2, qe->path1, qe->path2, qe->wnode1,
    1121 qe->wnode2);
    1122 }
    1123
    1124 if (err == -EAGAIN)
    1125 return; /* keep trying */
    1126
    1127 qe->err = err;
    1128 qe->done = true;
    1129 pthread_cond_signal(&qe->cond);
    1130}
    1131
    1132static void wake_up_queued(struct fuse *f)
    1133{
    1134 struct lock_queue_element *qe;
    1135
    1136 for (qe = f->lockq; qe != NULL; qe = qe->next)
    1137 queue_element_wakeup(f, qe);
    1138}
    1139
    1140static void debug_path(struct fuse *f, const char *msg, fuse_ino_t nodeid,
    1141 const char *name, bool wr)
    1142{
    1143 if (f->conf.debug) {
    1144 struct node *wnode = NULL;
    1145
    1146 if (wr)
    1147 wnode = lookup_node(f, nodeid, name);
    1148
    1149 if (wnode) {
    1150 fuse_log(FUSE_LOG_DEBUG, "%s %llu (w)\n",
    1151 msg, (unsigned long long) wnode->nodeid);
    1152 } else {
    1153 fuse_log(FUSE_LOG_DEBUG, "%s %llu\n",
    1154 msg, (unsigned long long) nodeid);
    1155 }
    1156 }
    1157}
    1158
    1159static void queue_path(struct fuse *f, struct lock_queue_element *qe)
    1160{
    1161 struct lock_queue_element **qp;
    1162
    1163 qe->done = false;
    1164 pthread_cond_init(&qe->cond, NULL);
    1165 qe->next = NULL;
    1166 for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
    1167 *qp = qe;
    1168}
    1169
    1170static void dequeue_path(struct fuse *f, struct lock_queue_element *qe)
    1171{
    1172 struct lock_queue_element **qp;
    1173
    1174 pthread_cond_destroy(&qe->cond);
    1175 for (qp = &f->lockq; *qp != qe; qp = &(*qp)->next);
    1176 *qp = qe->next;
    1177}
    1178
    1179static int wait_path(struct fuse *f, struct lock_queue_element *qe)
    1180{
    1181 queue_path(f, qe);
    1182
    1183 do {
    1184 pthread_cond_wait(&qe->cond, &f->lock);
    1185 } while (!qe->done);
    1186
    1187 dequeue_path(f, qe);
    1188
    1189 return qe->err;
    1190}
    1191
    1192static int get_path_common(struct fuse *f, fuse_ino_t nodeid, const char *name,
    1193 char **path, struct node **wnode)
    1194{
    1195 int err;
    1196
    1197 pthread_mutex_lock(&f->lock);
    1198 err = try_get_path(f, nodeid, name, path, wnode, true);
    1199 if (err == -EAGAIN) {
    1200 struct lock_queue_element qe = {
    1201 .nodeid1 = nodeid,
    1202 .name1 = name,
    1203 .path1 = path,
    1204 .wnode1 = wnode,
    1205 };
    1206 debug_path(f, "QUEUE PATH", nodeid, name, !!wnode);
    1207 err = wait_path(f, &qe);
    1208 debug_path(f, "DEQUEUE PATH", nodeid, name, !!wnode);
    1209 }
    1210 pthread_mutex_unlock(&f->lock);
    1211
    1212 return err;
    1213}
    1214
    1215static int get_path(struct fuse *f, fuse_ino_t nodeid, char **path)
    1216{
    1217 return get_path_common(f, nodeid, NULL, path, NULL);
    1218}
    1219
    1220static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
    1221{
    1222 int err = 0;
    1223
    1224 if (f->conf.nullpath_ok) {
    1225 *path = NULL;
    1226 } else {
    1227 err = get_path_common(f, nodeid, NULL, path, NULL);
    1228 if (err == -ESTALE)
    1229 err = 0;
    1230 }
    1231
    1232 return err;
    1233}
    1234
    1235static int get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name,
    1236 char **path)
    1237{
    1238 return get_path_common(f, nodeid, name, path, NULL);
    1239}
    1240
    1241static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name,
    1242 char **path, struct node **wnode)
    1243{
    1244 return get_path_common(f, nodeid, name, path, wnode);
    1245}
    1246
    1247#if defined(__FreeBSD__)
    1248#define CHECK_DIR_LOOP
    1249#endif
    1250
    1251#if defined(CHECK_DIR_LOOP)
    1252static int check_dir_loop(struct fuse *f,
    1253 fuse_ino_t nodeid1, const char *name1,
    1254 fuse_ino_t nodeid2, const char *name2)
    1255{
    1256 struct node *node, *node1, *node2;
    1257 fuse_ino_t id1, id2;
    1258
    1259 node1 = lookup_node(f, nodeid1, name1);
    1260 id1 = node1 ? node1->nodeid : nodeid1;
    1261
    1262 node2 = lookup_node(f, nodeid2, name2);
    1263 id2 = node2 ? node2->nodeid : nodeid2;
    1264
    1265 for (node = get_node(f, id2); node->nodeid != FUSE_ROOT_ID;
    1266 node = node->parent) {
    1267 if (node->name == NULL || node->parent == NULL)
    1268 break;
    1269
    1270 if (node->nodeid != id2 && node->nodeid == id1)
    1271 return -EINVAL;
    1272 }
    1273
    1274 if (node2)
    1275 {
    1276 for (node = get_node(f, id1); node->nodeid != FUSE_ROOT_ID;
    1277 node = node->parent) {
    1278 if (node->name == NULL || node->parent == NULL)
    1279 break;
    1280
    1281 if (node->nodeid != id1 && node->nodeid == id2)
    1282 return -ENOTEMPTY;
    1283 }
    1284 }
    1285
    1286 return 0;
    1287}
    1288#endif
    1289
    1290static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
    1291 fuse_ino_t nodeid2, const char *name2,
    1292 char **path1, char **path2,
    1293 struct node **wnode1, struct node **wnode2)
    1294{
    1295 int err;
    1296
    1297 pthread_mutex_lock(&f->lock);
    1298
    1299#if defined(CHECK_DIR_LOOP)
    1300 if (name1)
    1301 {
    1302 // called during rename; perform dir loop check
    1303 err = check_dir_loop(f, nodeid1, name1, nodeid2, name2);
    1304 if (err)
    1305 goto out_unlock;
    1306 }
    1307#endif
    1308
    1309 err = try_get_path2(f, nodeid1, name1, nodeid2, name2,
    1310 path1, path2, wnode1, wnode2);
    1311 if (err == -EAGAIN) {
    1312 struct lock_queue_element qe = {
    1313 .nodeid1 = nodeid1,
    1314 .name1 = name1,
    1315 .path1 = path1,
    1316 .wnode1 = wnode1,
    1317 .nodeid2 = nodeid2,
    1318 .name2 = name2,
    1319 .path2 = path2,
    1320 .wnode2 = wnode2,
    1321 };
    1322
    1323 debug_path(f, "QUEUE PATH1", nodeid1, name1, !!wnode1);
    1324 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
    1325 err = wait_path(f, &qe);
    1326 debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1);
    1327 debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
    1328 }
    1329
    1330#if defined(CHECK_DIR_LOOP)
    1331out_unlock:
    1332#endif
    1333 pthread_mutex_unlock(&f->lock);
    1334
    1335 return err;
    1336}
    1337
    1338static void free_path_wrlock(struct fuse *f, fuse_ino_t nodeid,
    1339 struct node *wnode, char *path)
    1340{
    1341 pthread_mutex_lock(&f->lock);
    1342 unlock_path(f, nodeid, wnode, NULL);
    1343 if (f->lockq)
    1344 wake_up_queued(f);
    1345 pthread_mutex_unlock(&f->lock);
    1346 free(path);
    1347}
    1348
    1349static void free_path(struct fuse *f, fuse_ino_t nodeid, char *path)
    1350{
    1351 if (path)
    1352 free_path_wrlock(f, nodeid, NULL, path);
    1353}
    1354
    1355static void free_path2(struct fuse *f, fuse_ino_t nodeid1, fuse_ino_t nodeid2,
    1356 struct node *wnode1, struct node *wnode2,
    1357 char *path1, char *path2)
    1358{
    1359 pthread_mutex_lock(&f->lock);
    1360 unlock_path(f, nodeid1, wnode1, NULL);
    1361 unlock_path(f, nodeid2, wnode2, NULL);
    1362 wake_up_queued(f);
    1363 pthread_mutex_unlock(&f->lock);
    1364 free(path1);
    1365 free(path2);
    1366}
    1367
    1368static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup)
    1369{
    1370 struct node *node;
    1371 if (nodeid == FUSE_ROOT_ID)
    1372 return;
    1373 pthread_mutex_lock(&f->lock);
    1374 node = get_node(f, nodeid);
    1375
    1376 /*
    1377 * Node may still be locked due to interrupt idiocy in open,
    1378 * create and opendir
    1379 */
    1380 while (node->nlookup == nlookup && node->treelock) {
    1381 struct lock_queue_element qe = {
    1382 .nodeid1 = nodeid,
    1383 };
    1384
    1385 debug_path(f, "QUEUE PATH (forget)", nodeid, NULL, false);
    1386 queue_path(f, &qe);
    1387
    1388 do {
    1389 pthread_cond_wait(&qe.cond, &f->lock);
    1390 } while (node->nlookup == nlookup && node->treelock);
    1391
    1392 dequeue_path(f, &qe);
    1393 debug_path(f, "DEQUEUE_PATH (forget)", nodeid, NULL, false);
    1394 }
    1395
    1396 assert(node->nlookup >= nlookup);
    1397 node->nlookup -= nlookup;
    1398 if (!node->nlookup) {
    1399 unref_node(f, node);
    1400 } else if (lru_enabled(f) && node->nlookup == 1) {
    1401 set_forget_time(f, node);
    1402 }
    1403 pthread_mutex_unlock(&f->lock);
    1404}
    1405
    1406static void unlink_node(struct fuse *f, struct node *node)
    1407{
    1408 if (f->conf.remember) {
    1409 assert(node->nlookup > 1);
    1410 node->nlookup--;
    1411 }
    1412 unhash_name(f, node);
    1413}
    1414
    1415static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name)
    1416{
    1417 struct node *node;
    1418
    1419 pthread_mutex_lock(&f->lock);
    1420 node = lookup_node(f, dir, name);
    1421 if (node != NULL)
    1422 unlink_node(f, node);
    1423 pthread_mutex_unlock(&f->lock);
    1424}
    1425
    1426static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
    1427 fuse_ino_t newdir, const char *newname, int hide)
    1428{
    1429 struct node *node;
    1430 struct node *newnode;
    1431 int err = 0;
    1432
    1433 pthread_mutex_lock(&f->lock);
    1434 node = lookup_node(f, olddir, oldname);
    1435 newnode = lookup_node(f, newdir, newname);
    1436 if (node == NULL)
    1437 goto out;
    1438
    1439 if (newnode != NULL) {
    1440 if (hide) {
    1441 fuse_log(FUSE_LOG_ERR, "fuse: hidden file got created during hiding\n");
    1442 err = -EBUSY;
    1443 goto out;
    1444 }
    1445 unlink_node(f, newnode);
    1446 }
    1447
    1448 unhash_name(f, node);
    1449 if (hash_name(f, node, newdir, newname) == -1) {
    1450 err = -ENOMEM;
    1451 goto out;
    1452 }
    1453
    1454 if (hide)
    1455 node->is_hidden = 1;
    1456
    1457out:
    1458 pthread_mutex_unlock(&f->lock);
    1459 return err;
    1460}
    1461
    1462static int exchange_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
    1463 fuse_ino_t newdir, const char *newname)
    1464{
    1465 struct node *oldnode;
    1466 struct node *newnode;
    1467 int err;
    1468
    1469 pthread_mutex_lock(&f->lock);
    1470 oldnode = lookup_node(f, olddir, oldname);
    1471 newnode = lookup_node(f, newdir, newname);
    1472
    1473 if (oldnode)
    1474 unhash_name(f, oldnode);
    1475 if (newnode)
    1476 unhash_name(f, newnode);
    1477
    1478 err = -ENOMEM;
    1479 if (oldnode) {
    1480 if (hash_name(f, oldnode, newdir, newname) == -1)
    1481 goto out;
    1482 }
    1483 if (newnode) {
    1484 if (hash_name(f, newnode, olddir, oldname) == -1)
    1485 goto out;
    1486 }
    1487 err = 0;
    1488out:
    1489 pthread_mutex_unlock(&f->lock);
    1490 return err;
    1491}
    1492
    1493static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
    1494{
    1495 if (!f->conf.use_ino)
    1496 stbuf->st_ino = nodeid;
    1497 if (f->conf.set_mode) {
    1498 if (f->conf.dmask && S_ISDIR(stbuf->st_mode))
    1499 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
    1500 (0777 & ~f->conf.dmask);
    1501 else if (f->conf.fmask)
    1502 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
    1503 (0777 & ~f->conf.fmask);
    1504 else
    1505 stbuf->st_mode = (stbuf->st_mode & S_IFMT) |
    1506 (0777 & ~f->conf.umask);
    1507 }
    1508 if (f->conf.set_uid)
    1509 stbuf->st_uid = f->conf.uid;
    1510 if (f->conf.set_gid)
    1511 stbuf->st_gid = f->conf.gid;
    1512}
    1513
    1514static struct fuse *req_fuse(fuse_req_t req)
    1515{
    1516 return (struct fuse *) fuse_req_userdata(req);
    1517}
    1518
    1519static void fuse_intr_sighandler(int sig)
    1520{
    1521 (void) sig;
    1522 /* Nothing to do */
    1523}
    1524
    1525struct fuse_intr_data {
    1526 pthread_t id;
    1527 pthread_cond_t cond;
    1528 int finished;
    1529};
    1530
    1531static void fuse_interrupt(fuse_req_t req, void *d_)
    1532{
    1533 struct fuse_intr_data *d = d_;
    1534 struct fuse *f = req_fuse(req);
    1535
    1536 if (d->id == pthread_self())
    1537 return;
    1538
    1539 pthread_mutex_lock(&f->lock);
    1540 while (!d->finished) {
    1541 struct timeval now;
    1542 struct timespec timeout;
    1543
    1544 pthread_kill(d->id, f->conf.intr_signal);
    1545 gettimeofday(&now, NULL);
    1546 timeout.tv_sec = now.tv_sec + 1;
    1547 timeout.tv_nsec = now.tv_usec * 1000;
    1548 pthread_cond_timedwait(&d->cond, &f->lock, &timeout);
    1549 }
    1550 pthread_mutex_unlock(&f->lock);
    1551}
    1552
    1553static void fuse_do_finish_interrupt(struct fuse *f, fuse_req_t req,
    1554 struct fuse_intr_data *d)
    1555{
    1556 pthread_mutex_lock(&f->lock);
    1557 d->finished = 1;
    1558 pthread_cond_broadcast(&d->cond);
    1559 pthread_mutex_unlock(&f->lock);
    1560 fuse_req_interrupt_func(req, NULL, NULL);
    1561 pthread_cond_destroy(&d->cond);
    1562}
    1563
    1564static void fuse_do_prepare_interrupt(fuse_req_t req, struct fuse_intr_data *d)
    1565{
    1566 d->id = pthread_self();
    1567 pthread_cond_init(&d->cond, NULL);
    1568 d->finished = 0;
    1569 fuse_req_interrupt_func(req, fuse_interrupt, d);
    1570}
    1571
    1572static inline void fuse_finish_interrupt(struct fuse *f, fuse_req_t req,
    1573 struct fuse_intr_data *d)
    1574{
    1575 if (f->conf.intr)
    1576 fuse_do_finish_interrupt(f, req, d);
    1577}
    1578
    1579static inline void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req,
    1580 struct fuse_intr_data *d)
    1581{
    1582 if (f->conf.intr)
    1583 fuse_do_prepare_interrupt(req, d);
    1584}
    1585
    1586static const char* file_info_string(struct fuse_file_info *fi,
    1587 char* buf, size_t len)
    1588{
    1589 if(fi == NULL)
    1590 return "NULL";
    1591 snprintf(buf, len, "%llu", (unsigned long long) fi->fh);
    1592 return buf;
    1593}
    1594
    1595int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf,
    1596 struct fuse_file_info *fi)
    1597{
    1598 fuse_get_context()->private_data = fs->user_data;
    1599 if (fs->op.getattr) {
    1600 if (fs->debug) {
    1601 char buf[10];
    1602 fuse_log(FUSE_LOG_DEBUG, "getattr[%s] %s\n",
    1603 file_info_string(fi, buf, sizeof(buf)),
    1604 path);
    1605 }
    1606 return fs->op.getattr(path, buf, fi);
    1607 } else {
    1608 return -ENOSYS;
    1609 }
    1610}
    1611
    1612int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
    1613 const char *newpath, unsigned int flags)
    1614{
    1615 fuse_get_context()->private_data = fs->user_data;
    1616 if (fs->op.rename) {
    1617 if (fs->debug)
    1618 fuse_log(FUSE_LOG_DEBUG, "rename %s %s 0x%x\n", oldpath, newpath,
    1619 flags);
    1620
    1621 return fs->op.rename(oldpath, newpath, flags);
    1622 } else {
    1623 return -ENOSYS;
    1624 }
    1625}
    1626
    1627int fuse_fs_unlink(struct fuse_fs *fs, const char *path)
    1628{
    1629 fuse_get_context()->private_data = fs->user_data;
    1630 if (fs->op.unlink) {
    1631 if (fs->debug)
    1632 fuse_log(FUSE_LOG_DEBUG, "unlink %s\n", path);
    1633
    1634 return fs->op.unlink(path);
    1635 } else {
    1636 return -ENOSYS;
    1637 }
    1638}
    1639
    1640int fuse_fs_rmdir(struct fuse_fs *fs, const char *path)
    1641{
    1642 fuse_get_context()->private_data = fs->user_data;
    1643 if (fs->op.rmdir) {
    1644 if (fs->debug)
    1645 fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path);
    1646
    1647 return fs->op.rmdir(path);
    1648 } else {
    1649 return -ENOSYS;
    1650 }
    1651}
    1652
    1653int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path)
    1654{
    1655 fuse_get_context()->private_data = fs->user_data;
    1656 if (fs->op.symlink) {
    1657 if (fs->debug)
    1658 fuse_log(FUSE_LOG_DEBUG, "symlink %s %s\n", linkname, path);
    1659
    1660 return fs->op.symlink(linkname, path);
    1661 } else {
    1662 return -ENOSYS;
    1663 }
    1664}
    1665
    1666int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath)
    1667{
    1668 fuse_get_context()->private_data = fs->user_data;
    1669 if (fs->op.link) {
    1670 if (fs->debug)
    1671 fuse_log(FUSE_LOG_DEBUG, "link %s %s\n", oldpath, newpath);
    1672
    1673 return fs->op.link(oldpath, newpath);
    1674 } else {
    1675 return -ENOSYS;
    1676 }
    1677}
    1678
    1679int fuse_fs_release(struct fuse_fs *fs, const char *path,
    1680 struct fuse_file_info *fi)
    1681{
    1682 fuse_get_context()->private_data = fs->user_data;
    1683 if (fs->op.release) {
    1684 if (fs->debug)
    1685 fuse_log(FUSE_LOG_DEBUG, "release%s[%llu] flags: 0x%x\n",
    1686 fi->flush ? "+flush" : "",
    1687 (unsigned long long) fi->fh, fi->flags);
    1688
    1689 return fs->op.release(path, fi);
    1690 } else {
    1691 return 0;
    1692 }
    1693}
    1694
    1695int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
    1696 struct fuse_file_info *fi)
    1697{
    1698 fuse_get_context()->private_data = fs->user_data;
    1699 if (fs->op.opendir) {
    1700 int err;
    1701
    1702 if (fs->debug)
    1703 fuse_log(FUSE_LOG_DEBUG, "opendir flags: 0x%x %s\n", fi->flags,
    1704 path);
    1705
    1706 err = fs->op.opendir(path, fi);
    1707
    1708 if (fs->debug && !err)
    1709 fuse_log(FUSE_LOG_DEBUG, " opendir[%llu] flags: 0x%x %s\n",
    1710 (unsigned long long) fi->fh, fi->flags, path);
    1711
    1712 return err;
    1713 } else {
    1714 return 0;
    1715 }
    1716}
    1717
    1718int fuse_fs_open(struct fuse_fs *fs, const char *path,
    1719 struct fuse_file_info *fi)
    1720{
    1721 fuse_get_context()->private_data = fs->user_data;
    1722 if (fs->op.open) {
    1723 int err;
    1724
    1725 if (fs->debug)
    1726 fuse_log(FUSE_LOG_DEBUG, "open flags: 0x%x %s\n", fi->flags,
    1727 path);
    1728
    1729 err = fs->op.open(path, fi);
    1730
    1731 if (fs->debug && !err)
    1732 fuse_log(FUSE_LOG_DEBUG, " open[%llu] flags: 0x%x %s\n",
    1733 (unsigned long long) fi->fh, fi->flags, path);
    1734
    1735 return err;
    1736 } else {
    1737 return 0;
    1738 }
    1739}
    1740
    1741static void fuse_free_buf(struct fuse_bufvec *buf)
    1742{
    1743 if (buf != NULL) {
    1744 size_t i;
    1745
    1746 for (i = 0; i < buf->count; i++)
    1747 if (!(buf->buf[i].flags & FUSE_BUF_IS_FD))
    1748 free(buf->buf[i].mem);
    1749 free(buf);
    1750 }
    1751}
    1752
    1753int fuse_fs_read_buf(struct fuse_fs *fs, const char *path,
    1754 struct fuse_bufvec **bufp, size_t size, off_t off,
    1755 struct fuse_file_info *fi)
    1756{
    1757 fuse_get_context()->private_data = fs->user_data;
    1758 if (fs->op.read || fs->op.read_buf) {
    1759 int res;
    1760
    1761 if (fs->debug)
    1762 fuse_log(FUSE_LOG_DEBUG,
    1763 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
    1764 (unsigned long long) fi->fh,
    1765 size, (unsigned long long) off, fi->flags);
    1766
    1767 if (fs->op.read_buf) {
    1768 res = fs->op.read_buf(path, bufp, size, off, fi);
    1769 } else {
    1770 struct fuse_bufvec *buf;
    1771 void *mem;
    1772
    1773 buf = malloc(sizeof(struct fuse_bufvec));
    1774 if (buf == NULL)
    1775 return -ENOMEM;
    1776
    1777 mem = malloc(size);
    1778 if (mem == NULL) {
    1779 free(buf);
    1780 return -ENOMEM;
    1781 }
    1782 *buf = FUSE_BUFVEC_INIT(size);
    1783 buf->buf[0].mem = mem;
    1784 *bufp = buf;
    1785
    1786 res = fs->op.read(path, mem, size, off, fi);
    1787 if (res >= 0)
    1788 buf->buf[0].size = res;
    1789 }
    1790
    1791 if (fs->debug && res >= 0)
    1792 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %zu bytes from %llu\n",
    1793 (unsigned long long) fi->fh,
    1794 fuse_buf_size(*bufp),
    1795 (unsigned long long) off);
    1796 if (res >= 0 && fuse_buf_size(*bufp) > size)
    1797 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
    1798
    1799 if (res < 0)
    1800 return res;
    1801
    1802 return 0;
    1803 } else {
    1804 return -ENOSYS;
    1805 }
    1806}
    1807
    1808int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size,
    1809 off_t off, struct fuse_file_info *fi)
    1810{
    1811 fuse_get_context()->private_data = fs->user_data;
    1812 if (fs->op.read || fs->op.read_buf) {
    1813 int res;
    1814
    1815 if (fs->debug)
    1816 fuse_log(FUSE_LOG_DEBUG,
    1817 "read[%llu] %zu bytes from %llu flags: 0x%x\n",
    1818 (unsigned long long) fi->fh,
    1819 size, (unsigned long long) off, fi->flags);
    1820
    1821 if (fs->op.read_buf) {
    1822 struct fuse_bufvec *buf = NULL;
    1823
    1824 res = fs->op.read_buf(path, &buf, size, off, fi);
    1825 if (res == 0) {
    1826 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(size);
    1827
    1828 dst.buf[0].mem = mem;
    1829 res = fuse_buf_copy(&dst, buf, 0);
    1830 }
    1831 fuse_free_buf(buf);
    1832 } else {
    1833 res = fs->op.read(path, mem, size, off, fi);
    1834 }
    1835
    1836 if (fs->debug && res >= 0)
    1837 fuse_log(FUSE_LOG_DEBUG, " read[%llu] %u bytes from %llu\n",
    1838 (unsigned long long) fi->fh,
    1839 res,
    1840 (unsigned long long) off);
    1841 if (res >= 0 && res > (int) size)
    1842 fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
    1843
    1844 return res;
    1845 } else {
    1846 return -ENOSYS;
    1847 }
    1848}
    1849
    1850int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
    1851 struct fuse_bufvec *buf, off_t off,
    1852 struct fuse_file_info *fi)
    1853{
    1854 fuse_get_context()->private_data = fs->user_data;
    1855 if (fs->op.write_buf || fs->op.write) {
    1856 int res;
    1857 size_t size = fuse_buf_size(buf);
    1858
    1859 assert(buf->idx == 0 && buf->off == 0);
    1860 if (fs->debug)
    1861 fuse_log(FUSE_LOG_DEBUG,
    1862 "write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
    1863 fi->writepage ? "page" : "",
    1864 (unsigned long long) fi->fh,
    1865 size,
    1866 (unsigned long long) off,
    1867 fi->flags);
    1868
    1869 if (fs->op.write_buf) {
    1870 res = fs->op.write_buf(path, buf, off, fi);
    1871 } else {
    1872 void *mem = NULL;
    1873 struct fuse_buf *flatbuf;
    1874 struct fuse_bufvec tmp = FUSE_BUFVEC_INIT(size);
    1875
    1876 if (buf->count == 1 &&
    1877 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
    1878 flatbuf = &buf->buf[0];
    1879 } else {
    1880 res = -ENOMEM;
    1881 mem = malloc(size);
    1882 if (mem == NULL)
    1883 goto out;
    1884
    1885 tmp.buf[0].mem = mem;
    1886 res = fuse_buf_copy(&tmp, buf, 0);
    1887 if (res <= 0)
    1888 goto out_free;
    1889
    1890 tmp.buf[0].size = res;
    1891 flatbuf = &tmp.buf[0];
    1892 }
    1893
    1894 res = fs->op.write(path, flatbuf->mem, flatbuf->size,
    1895 off, fi);
    1896out_free:
    1897 free(mem);
    1898 }
    1899out:
    1900 if (fs->debug && res >= 0)
    1901 fuse_log(FUSE_LOG_DEBUG, " write%s[%llu] %u bytes to %llu\n",
    1902 fi->writepage ? "page" : "",
    1903 (unsigned long long) fi->fh, res,
    1904 (unsigned long long) off);
    1905 if (res > (int) size)
    1906 fuse_log(FUSE_LOG_ERR, "fuse: wrote too many bytes\n");
    1907
    1908 return res;
    1909 } else {
    1910 return -ENOSYS;
    1911 }
    1912}
    1913
    1914int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *mem,
    1915 size_t size, off_t off, struct fuse_file_info *fi)
    1916{
    1917 struct fuse_bufvec bufv = FUSE_BUFVEC_INIT(size);
    1918
    1919 bufv.buf[0].mem = (void *) mem;
    1920
    1921 return fuse_fs_write_buf(fs, path, &bufv, off, fi);
    1922}
    1923
    1924int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
    1925 struct fuse_file_info *fi)
    1926{
    1927 fuse_get_context()->private_data = fs->user_data;
    1928 if (fs->op.fsync) {
    1929 if (fs->debug)
    1930 fuse_log(FUSE_LOG_DEBUG, "fsync[%llu] datasync: %i\n",
    1931 (unsigned long long) fi->fh, datasync);
    1932
    1933 return fs->op.fsync(path, datasync, fi);
    1934 } else {
    1935 return -ENOSYS;
    1936 }
    1937}
    1938
    1939int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
    1940 struct fuse_file_info *fi)
    1941{
    1942 fuse_get_context()->private_data = fs->user_data;
    1943 if (fs->op.fsyncdir) {
    1944 if (fs->debug)
    1945 fuse_log(FUSE_LOG_DEBUG, "fsyncdir[%llu] datasync: %i\n",
    1946 (unsigned long long) fi->fh, datasync);
    1947
    1948 return fs->op.fsyncdir(path, datasync, fi);
    1949 } else {
    1950 return -ENOSYS;
    1951 }
    1952}
    1953
    1954int fuse_fs_flush(struct fuse_fs *fs, const char *path,
    1955 struct fuse_file_info *fi)
    1956{
    1957 fuse_get_context()->private_data = fs->user_data;
    1958 if (fs->op.flush) {
    1959 if (fs->debug)
    1960 fuse_log(FUSE_LOG_DEBUG, "flush[%llu]\n",
    1961 (unsigned long long) fi->fh);
    1962
    1963 return fs->op.flush(path, fi);
    1964 } else {
    1965 return -ENOSYS;
    1966 }
    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 if (fs->debug)
    1990 fuse_log(FUSE_LOG_DEBUG, "releasedir[%llu] flags: 0x%x\n",
    1991 (unsigned long long) fi->fh, fi->flags);
    1992
    1993 return fs->op.releasedir(path, fi);
    1994 } else {
    1995 return 0;
    1996 }
    1997}
    1998
    1999int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
    2000 fuse_fill_dir_t filler, off_t off,
    2001 struct fuse_file_info *fi,
    2002 enum fuse_readdir_flags flags)
    2003{
    2004 fuse_get_context()->private_data = fs->user_data;
    2005 if (fs->op.readdir) {
    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 } else {
    2015 return -ENOSYS;
    2016 }
    2017}
    2018
    2019int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
    2020 struct fuse_file_info *fi)
    2021{
    2022 fuse_get_context()->private_data = fs->user_data;
    2023 if (fs->op.create) {
    2024 int err;
    2025
    2026 if (fs->debug)
    2027 fuse_log(FUSE_LOG_DEBUG,
    2028 "create flags: 0x%x %s 0%o umask=0%03o\n",
    2029 fi->flags, path, mode,
    2030 fuse_get_context()->umask);
    2031
    2032 err = fs->op.create(path, mode, fi);
    2033
    2034 if (fs->debug && !err)
    2035 fuse_log(FUSE_LOG_DEBUG, " create[%llu] flags: 0x%x %s\n",
    2036 (unsigned long long) fi->fh, fi->flags, path);
    2037
    2038 return err;
    2039 } else {
    2040 return -ENOSYS;
    2041 }
    2042}
    2043
    2044int fuse_fs_lock(struct fuse_fs *fs, const char *path,
    2045 struct fuse_file_info *fi, int cmd, struct flock *lock)
    2046{
    2047 fuse_get_context()->private_data = fs->user_data;
    2048 if (fs->op.lock) {
    2049 if (fs->debug)
    2050 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
    2051 (unsigned long long) fi->fh,
    2052 (cmd == F_GETLK ? "F_GETLK" :
    2053 (cmd == F_SETLK ? "F_SETLK" :
    2054 (cmd == F_SETLKW ? "F_SETLKW" : "???"))),
    2055 (lock->l_type == F_RDLCK ? "F_RDLCK" :
    2056 (lock->l_type == F_WRLCK ? "F_WRLCK" :
    2057 (lock->l_type == F_UNLCK ? "F_UNLCK" :
    2058 "???"))),
    2059 (unsigned long long) lock->l_start,
    2060 (unsigned long long) lock->l_len,
    2061 (unsigned long long) lock->l_pid);
    2062
    2063 return fs->op.lock(path, fi, cmd, lock);
    2064 } else {
    2065 return -ENOSYS;
    2066 }
    2067}
    2068
    2069int fuse_fs_flock(struct fuse_fs *fs, const char *path,
    2070 struct fuse_file_info *fi, int op)
    2071{
    2072 fuse_get_context()->private_data = fs->user_data;
    2073 if (fs->op.flock) {
    2074 if (fs->debug) {
    2075 int xop = op & ~LOCK_NB;
    2076
    2077 fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s%s\n",
    2078 (unsigned long long) fi->fh,
    2079 xop == LOCK_SH ? "LOCK_SH" :
    2080 (xop == LOCK_EX ? "LOCK_EX" :
    2081 (xop == LOCK_UN ? "LOCK_UN" : "???")),
    2082 (op & LOCK_NB) ? "|LOCK_NB" : "");
    2083 }
    2084 return fs->op.flock(path, fi, op);
    2085 } else {
    2086 return -ENOSYS;
    2087 }
    2088}
    2089
    2090int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid,
    2091 gid_t gid, struct fuse_file_info *fi)
    2092{
    2093 fuse_get_context()->private_data = fs->user_data;
    2094 if (fs->op.chown) {
    2095 if (fs->debug) {
    2096 char buf[10];
    2097 fuse_log(FUSE_LOG_DEBUG, "chown[%s] %s %lu %lu\n",
    2098 file_info_string(fi, buf, sizeof(buf)),
    2099 path, (unsigned long) uid, (unsigned long) gid);
    2100 }
    2101 return fs->op.chown(path, uid, gid, fi);
    2102 } else {
    2103 return -ENOSYS;
    2104 }
    2105}
    2106
    2107int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size,
    2108 struct fuse_file_info *fi)
    2109{
    2110 fuse_get_context()->private_data = fs->user_data;
    2111 if (fs->op.truncate) {
    2112 if (fs->debug) {
    2113 char buf[10];
    2114 fuse_log(FUSE_LOG_DEBUG, "truncate[%s] %llu\n",
    2115 file_info_string(fi, buf, sizeof(buf)),
    2116 (unsigned long long) size);
    2117 }
    2118 return fs->op.truncate(path, size, fi);
    2119 } else {
    2120 return -ENOSYS;
    2121 }
    2122}
    2123
    2124int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
    2125 const struct timespec tv[2], struct fuse_file_info *fi)
    2126{
    2127 fuse_get_context()->private_data = fs->user_data;
    2128 if (fs->op.utimens) {
    2129 if (fs->debug) {
    2130 char buf[10];
    2131 fuse_log(FUSE_LOG_DEBUG, "utimens[%s] %s %li.%09lu %li.%09lu\n",
    2132 file_info_string(fi, buf, sizeof(buf)),
    2133 path, tv[0].tv_sec, tv[0].tv_nsec,
    2134 tv[1].tv_sec, tv[1].tv_nsec);
    2135 }
    2136 return fs->op.utimens(path, tv, fi);
    2137 } else {
    2138 return -ENOSYS;
    2139 }
    2140}
    2141
    2142int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask)
    2143{
    2144 fuse_get_context()->private_data = fs->user_data;
    2145 if (fs->op.access) {
    2146 if (fs->debug)
    2147 fuse_log(FUSE_LOG_DEBUG, "access %s 0%o\n", path, mask);
    2148
    2149 return fs->op.access(path, mask);
    2150 } else {
    2151 return -ENOSYS;
    2152 }
    2153}
    2154
    2155int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
    2156 size_t len)
    2157{
    2158 fuse_get_context()->private_data = fs->user_data;
    2159 if (fs->op.readlink) {
    2160 if (fs->debug)
    2161 fuse_log(FUSE_LOG_DEBUG, "readlink %s %lu\n", path,
    2162 (unsigned long) len);
    2163
    2164 return fs->op.readlink(path, buf, len);
    2165 } else {
    2166 return -ENOSYS;
    2167 }
    2168}
    2169
    2170int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
    2171 dev_t rdev)
    2172{
    2173 fuse_get_context()->private_data = fs->user_data;
    2174 if (fs->op.mknod) {
    2175 if (fs->debug)
    2176 fuse_log(FUSE_LOG_DEBUG, "mknod %s 0%o 0x%llx umask=0%03o\n",
    2177 path, mode, (unsigned long long) rdev,
    2178 fuse_get_context()->umask);
    2179
    2180 return fs->op.mknod(path, mode, rdev);
    2181 } else {
    2182 return -ENOSYS;
    2183 }
    2184}
    2185
    2186int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode)
    2187{
    2188 fuse_get_context()->private_data = fs->user_data;
    2189 if (fs->op.mkdir) {
    2190 if (fs->debug)
    2191 fuse_log(FUSE_LOG_DEBUG, "mkdir %s 0%o umask=0%03o\n",
    2192 path, mode, fuse_get_context()->umask);
    2193
    2194 return fs->op.mkdir(path, mode);
    2195 } else {
    2196 return -ENOSYS;
    2197 }
    2198}
    2199
    2200int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
    2201 const char *value, size_t size, int flags)
    2202{
    2203 fuse_get_context()->private_data = fs->user_data;
    2204 if (fs->op.setxattr) {
    2205 if (fs->debug)
    2206 fuse_log(FUSE_LOG_DEBUG, "setxattr %s %s %lu 0x%x\n",
    2207 path, name, (unsigned long) size, flags);
    2208
    2209 return fs->op.setxattr(path, name, value, size, flags);
    2210 } else {
    2211 return -ENOSYS;
    2212 }
    2213}
    2214
    2215int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
    2216 char *value, size_t size)
    2217{
    2218 fuse_get_context()->private_data = fs->user_data;
    2219 if (fs->op.getxattr) {
    2220 if (fs->debug)
    2221 fuse_log(FUSE_LOG_DEBUG, "getxattr %s %s %lu\n",
    2222 path, name, (unsigned long) size);
    2223
    2224 return fs->op.getxattr(path, name, value, size);
    2225 } else {
    2226 return -ENOSYS;
    2227 }
    2228}
    2229
    2230int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
    2231 size_t size)
    2232{
    2233 fuse_get_context()->private_data = fs->user_data;
    2234 if (fs->op.listxattr) {
    2235 if (fs->debug)
    2236 fuse_log(FUSE_LOG_DEBUG, "listxattr %s %lu\n",
    2237 path, (unsigned long) size);
    2238
    2239 return fs->op.listxattr(path, list, size);
    2240 } else {
    2241 return -ENOSYS;
    2242 }
    2243}
    2244
    2245int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
    2246 uint64_t *idx)
    2247{
    2248 fuse_get_context()->private_data = fs->user_data;
    2249 if (fs->op.bmap) {
    2250 if (fs->debug)
    2251 fuse_log(FUSE_LOG_DEBUG, "bmap %s blocksize: %lu index: %llu\n",
    2252 path, (unsigned long) blocksize,
    2253 (unsigned long long) *idx);
    2254
    2255 return fs->op.bmap(path, blocksize, idx);
    2256 } else {
    2257 return -ENOSYS;
    2258 }
    2259}
    2260
    2261int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
    2262{
    2263 fuse_get_context()->private_data = fs->user_data;
    2264 if (fs->op.removexattr) {
    2265 if (fs->debug)
    2266 fuse_log(FUSE_LOG_DEBUG, "removexattr %s %s\n", path, name);
    2267
    2268 return fs->op.removexattr(path, name);
    2269 } else {
    2270 return -ENOSYS;
    2271 }
    2272}
    2273
    2274int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd,
    2275 void *arg, struct fuse_file_info *fi, unsigned int flags,
    2276 void *data)
    2277{
    2278 fuse_get_context()->private_data = fs->user_data;
    2279 if (fs->op.ioctl) {
    2280 if (fs->debug)
    2281 fuse_log(FUSE_LOG_DEBUG, "ioctl[%llu] 0x%x flags: 0x%x\n",
    2282 (unsigned long long) fi->fh, cmd, flags);
    2283
    2284 return fs->op.ioctl(path, cmd, arg, fi, flags, data);
    2285 } else
    2286 return -ENOSYS;
    2287}
    2288
    2289int fuse_fs_poll(struct fuse_fs *fs, const char *path,
    2290 struct fuse_file_info *fi, struct fuse_pollhandle *ph,
    2291 unsigned *reventsp)
    2292{
    2293 fuse_get_context()->private_data = fs->user_data;
    2294 if (fs->op.poll) {
    2295 int res;
    2296
    2297 if (fs->debug)
    2298 fuse_log(FUSE_LOG_DEBUG, "poll[%llu] ph: %p, events 0x%x\n",
    2299 (unsigned long long) fi->fh, ph,
    2300 fi->poll_events);
    2301
    2302 res = fs->op.poll(path, fi, ph, reventsp);
    2303
    2304 if (fs->debug && !res)
    2305 fuse_log(FUSE_LOG_DEBUG, " poll[%llu] revents: 0x%x\n",
    2306 (unsigned long long) fi->fh, *reventsp);
    2307
    2308 return res;
    2309 } else
    2310 return -ENOSYS;
    2311}
    2312
    2313int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode,
    2314 off_t offset, off_t length, struct fuse_file_info *fi)
    2315{
    2316 fuse_get_context()->private_data = fs->user_data;
    2317 if (fs->op.fallocate) {
    2318 if (fs->debug)
    2319 fuse_log(FUSE_LOG_DEBUG, "fallocate %s mode %x, offset: %llu, length: %llu\n",
    2320 path,
    2321 mode,
    2322 (unsigned long long) offset,
    2323 (unsigned long long) length);
    2324
    2325 return fs->op.fallocate(path, mode, offset, length, fi);
    2326 } else
    2327 return -ENOSYS;
    2328}
    2329
    2330ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
    2331 struct fuse_file_info *fi_in, off_t off_in,
    2332 const char *path_out,
    2333 struct fuse_file_info *fi_out, off_t off_out,
    2334 size_t len, int flags)
    2335{
    2336 fuse_get_context()->private_data = fs->user_data;
    2337 if (fs->op.copy_file_range) {
    2338 if (fs->debug)
    2339 fuse_log(FUSE_LOG_DEBUG, "copy_file_range from %s:%llu to "
    2340 "%s:%llu, length: %llu\n",
    2341 path_in,
    2342 (unsigned long long) off_in,
    2343 path_out,
    2344 (unsigned long long) off_out,
    2345 (unsigned long long) len);
    2346
    2347 return fs->op.copy_file_range(path_in, fi_in, off_in, path_out,
    2348 fi_out, off_out, len, flags);
    2349 } else
    2350 return -ENOSYS;
    2351}
    2352
    2353off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
    2354 struct fuse_file_info *fi)
    2355{
    2356 fuse_get_context()->private_data = fs->user_data;
    2357 if (fs->op.lseek) {
    2358 if (fs->debug) {
    2359 char buf[10];
    2360 fuse_log(FUSE_LOG_DEBUG, "lseek[%s] %llu %d\n",
    2361 file_info_string(fi, buf, sizeof(buf)),
    2362 (unsigned long long) off, whence);
    2363 }
    2364 return fs->op.lseek(path, off, whence, fi);
    2365 } else {
    2366 return -ENOSYS;
    2367 }
    2368}
    2369
    2370static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
    2371{
    2372 struct node *node;
    2373 int isopen = 0;
    2374 pthread_mutex_lock(&f->lock);
    2375 node = lookup_node(f, dir, name);
    2376 if (node && node->open_count > 0)
    2377 isopen = 1;
    2378 pthread_mutex_unlock(&f->lock);
    2379 return isopen;
    2380}
    2381
    2382static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname,
    2383 char *newname, size_t bufsize)
    2384{
    2385 struct stat buf;
    2386 struct node *node;
    2387 struct node *newnode;
    2388 char *newpath;
    2389 int res;
    2390 int failctr = 10;
    2391
    2392 do {
    2393 pthread_mutex_lock(&f->lock);
    2394 node = lookup_node(f, dir, oldname);
    2395 if (node == NULL) {
    2396 pthread_mutex_unlock(&f->lock);
    2397 return NULL;
    2398 }
    2399 do {
    2400 f->hidectr ++;
    2401 snprintf(newname, bufsize, ".fuse_hidden%08x%08x",
    2402 (unsigned int) node->nodeid, f->hidectr);
    2403 newnode = lookup_node(f, dir, newname);
    2404 } while(newnode);
    2405
    2406 res = try_get_path(f, dir, newname, &newpath, NULL, false);
    2407 pthread_mutex_unlock(&f->lock);
    2408 if (res)
    2409 break;
    2410
    2411 memset(&buf, 0, sizeof(buf));
    2412 res = fuse_fs_getattr(f->fs, newpath, &buf, NULL);
    2413 if (res == -ENOENT)
    2414 break;
    2415 free(newpath);
    2416 newpath = NULL;
    2417 } while(res == 0 && --failctr);
    2418
    2419 return newpath;
    2420}
    2421
    2422static int hide_node(struct fuse *f, const char *oldpath,
    2423 fuse_ino_t dir, const char *oldname)
    2424{
    2425 char newname[64];
    2426 char *newpath;
    2427 int err = -EBUSY;
    2428
    2429 newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
    2430 if (newpath) {
    2431 err = fuse_fs_rename(f->fs, oldpath, newpath, 0);
    2432 if (!err)
    2433 err = rename_node(f, dir, oldname, dir, newname, 1);
    2434 free(newpath);
    2435 }
    2436 return err;
    2437}
    2438
    2439static int mtime_eq(const struct stat *stbuf, const struct timespec *ts)
    2440{
    2441 return stbuf->st_mtime == ts->tv_sec &&
    2442 ST_MTIM_NSEC(stbuf) == ts->tv_nsec;
    2443}
    2444
    2445#ifndef CLOCK_MONOTONIC
    2446#define CLOCK_MONOTONIC CLOCK_REALTIME
    2447#endif
    2448
    2449static void curr_time(struct timespec *now)
    2450{
    2451 static clockid_t clockid = CLOCK_MONOTONIC;
    2452 int res = clock_gettime(clockid, now);
    2453 if (res == -1 && errno == EINVAL) {
    2454 clockid = CLOCK_REALTIME;
    2455 res = clock_gettime(clockid, now);
    2456 }
    2457 if (res == -1) {
    2458 perror("fuse: clock_gettime");
    2459 abort();
    2460 }
    2461}
    2462
    2463static void update_stat(struct node *node, const struct stat *stbuf)
    2464{
    2465 if (node->cache_valid && (!mtime_eq(stbuf, &node->mtime) ||
    2466 stbuf->st_size != node->size))
    2467 node->cache_valid = 0;
    2468 node->mtime.tv_sec = stbuf->st_mtime;
    2469 node->mtime.tv_nsec = ST_MTIM_NSEC(stbuf);
    2470 node->size = stbuf->st_size;
    2471 curr_time(&node->stat_updated);
    2472}
    2473
    2474static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name,
    2475 struct fuse_entry_param *e)
    2476{
    2477 struct node *node;
    2478
    2479 node = find_node(f, nodeid, name);
    2480 if (node == NULL)
    2481 return -ENOMEM;
    2482
    2483 e->ino = node->nodeid;
    2484 e->generation = node->generation;
    2485 e->entry_timeout = f->conf.entry_timeout;
    2486 e->attr_timeout = f->conf.attr_timeout;
    2487 if (f->conf.auto_cache) {
    2488 pthread_mutex_lock(&f->lock);
    2489 update_stat(node, &e->attr);
    2490 pthread_mutex_unlock(&f->lock);
    2491 }
    2492 set_stat(f, e->ino, &e->attr);
    2493 return 0;
    2494}
    2495
    2496static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
    2497 const char *name, const char *path,
    2498 struct fuse_entry_param *e, struct fuse_file_info *fi)
    2499{
    2500 int res;
    2501
    2502 memset(e, 0, sizeof(struct fuse_entry_param));
    2503 res = fuse_fs_getattr(f->fs, path, &e->attr, fi);
    2504 if (res == 0) {
    2505 res = do_lookup(f, nodeid, name, e);
    2506 if (res == 0 && f->conf.debug) {
    2507 fuse_log(FUSE_LOG_DEBUG, " NODEID: %llu\n",
    2508 (unsigned long long) e->ino);
    2509 }
    2510 }
    2511 return res;
    2512}
    2513
    2514static struct fuse_context_i *fuse_get_context_internal(void)
    2515{
    2516 return (struct fuse_context_i *) pthread_getspecific(fuse_context_key);
    2517}
    2518
    2519static struct fuse_context_i *fuse_create_context(struct fuse *f)
    2520{
    2521 struct fuse_context_i *c = fuse_get_context_internal();
    2522 if (c == NULL) {
    2523 c = (struct fuse_context_i *)
    2524 calloc(1, sizeof(struct fuse_context_i));
    2525 if (c == NULL) {
    2526 /* This is hard to deal with properly, so just
    2527 abort. If memory is so low that the
    2528 context cannot be allocated, there's not
    2529 much hope for the filesystem anyway */
    2530 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate thread specific data\n");
    2531 abort();
    2532 }
    2533 pthread_setspecific(fuse_context_key, c);
    2534 } else {
    2535 memset(c, 0, sizeof(*c));
    2536 }
    2537 c->ctx.fuse = f;
    2538
    2539 return c;
    2540}
    2541
    2542static void fuse_freecontext(void *data)
    2543{
    2544 free(data);
    2545}
    2546
    2547static int fuse_create_context_key(void)
    2548{
    2549 int err = 0;
    2550 pthread_mutex_lock(&fuse_context_lock);
    2551 if (!fuse_context_ref) {
    2552 err = pthread_key_create(&fuse_context_key, fuse_freecontext);
    2553 if (err) {
    2554 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
    2555 strerror(err));
    2556 pthread_mutex_unlock(&fuse_context_lock);
    2557 return -1;
    2558 }
    2559 }
    2560 fuse_context_ref++;
    2561 pthread_mutex_unlock(&fuse_context_lock);
    2562 return 0;
    2563}
    2564
    2565static void fuse_delete_context_key(void)
    2566{
    2567 pthread_mutex_lock(&fuse_context_lock);
    2568 fuse_context_ref--;
    2569 if (!fuse_context_ref) {
    2570 free(pthread_getspecific(fuse_context_key));
    2571 pthread_key_delete(fuse_context_key);
    2572 }
    2573 pthread_mutex_unlock(&fuse_context_lock);
    2574}
    2575
    2576static struct fuse *req_fuse_prepare(fuse_req_t req)
    2577{
    2578 struct fuse_context_i *c = fuse_create_context(req_fuse(req));
    2579 const struct fuse_ctx *ctx = fuse_req_ctx(req);
    2580 c->req = req;
    2581 c->ctx.uid = ctx->uid;
    2582 c->ctx.gid = ctx->gid;
    2583 c->ctx.pid = ctx->pid;
    2584 c->ctx.umask = ctx->umask;
    2585 return c->ctx.fuse;
    2586}
    2587
    2588static inline void reply_err(fuse_req_t req, int err)
    2589{
    2590 /* fuse_reply_err() uses non-negated errno values */
    2591 fuse_reply_err(req, -err);
    2592}
    2593
    2594static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
    2595 int err)
    2596{
    2597 if (!err) {
    2598 struct fuse *f = req_fuse(req);
    2599 if (fuse_reply_entry(req, e) == -ENOENT) {
    2600 /* Skip forget for negative result */
    2601 if (e->ino != 0)
    2602 forget_node(f, e->ino, 1);
    2603 }
    2604 } else
    2605 reply_err(req, err);
    2606}
    2607
    2608void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
    2609 struct fuse_config *cfg)
    2610{
    2611 fuse_get_context()->private_data = fs->user_data;
    2612 if (!fs->op.write_buf)
    2613 fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ);
    2614 if (!fs->op.lock)
    2615 fuse_unset_feature_flag(conn, FUSE_CAP_POSIX_LOCKS);
    2616 if (!fs->op.flock)
    2617 fuse_unset_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS);
    2618 if (fs->op.init) {
    2619 uint64_t want_ext_default = conn->want_ext;
    2620 uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
    2621 int rc;
    2622
    2623 conn->want = want_default;
    2624 fs->user_data = fs->op.init(conn, cfg);
    2625
    2626 rc = convert_to_conn_want_ext(conn, want_ext_default,
    2627 want_default);
    2628
    2629 if (rc != 0) {
    2630 /*
    2631 * This is a grave developer error, but
    2632 * we cannot return an error here, as the function
    2633 * signature does not allow it.
    2634 */
    2635 fuse_log(
    2636 FUSE_LOG_ERR,
    2637 "fuse: Aborting due to invalid conn want flags.\n");
    2638 _exit(EXIT_FAILURE);
    2639 }
    2640 }
    2641}
    2642
    2643static int fuse_init_intr_signal(int signum, int *installed);
    2644
    2645static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
    2646{
    2647 struct fuse *f = (struct fuse *) data;
    2648
    2649 fuse_create_context(f);
    2650 fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
    2651 fuse_fs_init(f->fs, conn, &f->conf);
    2652
    2653 if (f->conf.intr) {
    2654 if (fuse_init_intr_signal(f->conf.intr_signal,
    2655 &f->intr_installed) == -1)
    2656 fuse_log(FUSE_LOG_ERR, "fuse: failed to init interrupt signal\n");
    2657 } else {
    2658 /* Disable the receiving and processing of FUSE_INTERRUPT requests */
    2659 conn->no_interrupt = 1;
    2660 }
    2661}
    2662
    2663void fuse_fs_destroy(struct fuse_fs *fs)
    2664{
    2665 fuse_get_context()->private_data = fs->user_data;
    2666 if (fs->op.destroy)
    2667 fs->op.destroy(fs->user_data);
    2668}
    2669
    2670static void fuse_lib_destroy(void *data)
    2671{
    2672 struct fuse *f = (struct fuse *) data;
    2673
    2674 fuse_create_context(f);
    2675 fuse_fs_destroy(f->fs);
    2676}
    2677
    2678static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
    2679 const char *name)
    2680{
    2681 struct fuse *f = req_fuse_prepare(req);
    2682 struct fuse_entry_param e;
    2683 char *path;
    2684 int err;
    2685 struct node *dot = NULL;
    2686
    2687 if (name[0] == '.') {
    2688 int len = strlen(name);
    2689
    2690 if (len == 1 || (name[1] == '.' && len == 2)) {
    2691 pthread_mutex_lock(&f->lock);
    2692 if (len == 1) {
    2693 if (f->conf.debug)
    2694 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOT\n");
    2695 dot = get_node_nocheck(f, parent);
    2696 if (dot == NULL) {
    2697 pthread_mutex_unlock(&f->lock);
    2698 reply_entry(req, &e, -ESTALE);
    2699 return;
    2700 }
    2701 dot->refctr++;
    2702 } else {
    2703 if (f->conf.debug)
    2704 fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOTDOT\n");
    2705 parent = get_node(f, parent)->parent->nodeid;
    2706 }
    2707 pthread_mutex_unlock(&f->lock);
    2708 name = NULL;
    2709 }
    2710 }
    2711
    2712 err = get_path_name(f, parent, name, &path);
    2713 if (!err) {
    2714 struct fuse_intr_data d;
    2715 if (f->conf.debug)
    2716 fuse_log(FUSE_LOG_DEBUG, "LOOKUP %s\n", path);
    2717 fuse_prepare_interrupt(f, req, &d);
    2718 err = lookup_path(f, parent, name, path, &e, NULL);
    2719 if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
    2720 e.ino = 0;
    2721 e.entry_timeout = f->conf.negative_timeout;
    2722 err = 0;
    2723 }
    2724 fuse_finish_interrupt(f, req, &d);
    2725 free_path(f, parent, path);
    2726 }
    2727 if (dot) {
    2728 pthread_mutex_lock(&f->lock);
    2729 unref_node(f, dot);
    2730 pthread_mutex_unlock(&f->lock);
    2731 }
    2732 reply_entry(req, &e, err);
    2733}
    2734
    2735static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup)
    2736{
    2737 if (f->conf.debug)
    2738 fuse_log(FUSE_LOG_DEBUG, "FORGET %llu/%llu\n", (unsigned long long)ino,
    2739 (unsigned long long) nlookup);
    2740 forget_node(f, ino, nlookup);
    2741}
    2742
    2743static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
    2744{
    2745 do_forget(req_fuse(req), ino, nlookup);
    2746 fuse_reply_none(req);
    2747}
    2748
    2749static void fuse_lib_forget_multi(fuse_req_t req, size_t count,
    2750 struct fuse_forget_data *forgets)
    2751{
    2752 struct fuse *f = req_fuse(req);
    2753 size_t i;
    2754
    2755 for (i = 0; i < count; i++)
    2756 do_forget(f, forgets[i].ino, forgets[i].nlookup);
    2757
    2758 fuse_reply_none(req);
    2759}
    2760
    2761
    2762static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino,
    2763 struct fuse_file_info *fi)
    2764{
    2765 struct fuse *f = req_fuse_prepare(req);
    2766 struct stat buf;
    2767 char *path;
    2768 int err;
    2769
    2770 memset(&buf, 0, sizeof(buf));
    2771
    2772 if (fi != NULL)
    2773 err = get_path_nullok(f, ino, &path);
    2774 else
    2775 err = get_path(f, ino, &path);
    2776 if (!err) {
    2777 struct fuse_intr_data d;
    2778 fuse_prepare_interrupt(f, req, &d);
    2779 err = fuse_fs_getattr(f->fs, path, &buf, fi);
    2780 fuse_finish_interrupt(f, req, &d);
    2781 free_path(f, ino, path);
    2782 }
    2783 if (!err) {
    2784 struct node *node;
    2785
    2786 pthread_mutex_lock(&f->lock);
    2787 node = get_node(f, ino);
    2788 if (node->is_hidden && buf.st_nlink > 0)
    2789 buf.st_nlink--;
    2790 if (f->conf.auto_cache)
    2791 update_stat(node, &buf);
    2792 pthread_mutex_unlock(&f->lock);
    2793 set_stat(f, ino, &buf);
    2794 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
    2795 } else
    2796 reply_err(req, err);
    2797}
    2798
    2799int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode,
    2800 struct fuse_file_info *fi)
    2801{
    2802 fuse_get_context()->private_data = fs->user_data;
    2803 if (fs->op.chmod) {
    2804 if (fs->debug) {
    2805 char buf[10];
    2806 fuse_log(FUSE_LOG_DEBUG, "chmod[%s] %s %llo\n",
    2807 file_info_string(fi, buf, sizeof(buf)),
    2808 path, (unsigned long long) mode);
    2809 }
    2810 return fs->op.chmod(path, mode, fi);
    2811 }
    2812 else
    2813 return -ENOSYS;
    2814}
    2815
    2816static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
    2817 int valid, struct fuse_file_info *fi)
    2818{
    2819 struct fuse *f = req_fuse_prepare(req);
    2820 struct stat buf;
    2821 char *path;
    2822 int err;
    2823
    2824 memset(&buf, 0, sizeof(buf));
    2825 if (fi != NULL)
    2826 err = get_path_nullok(f, ino, &path);
    2827 else
    2828 err = get_path(f, ino, &path);
    2829 if (!err) {
    2830 struct fuse_intr_data d;
    2831 fuse_prepare_interrupt(f, req, &d);
    2832 err = 0;
    2833 if (!err && (valid & FUSE_SET_ATTR_MODE))
    2834 err = fuse_fs_chmod(f->fs, path, attr->st_mode, fi);
    2835 if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) {
    2836 uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
    2837 attr->st_uid : (uid_t) -1;
    2838 gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
    2839 attr->st_gid : (gid_t) -1;
    2840 err = fuse_fs_chown(f->fs, path, uid, gid, fi);
    2841 }
    2842 if (!err && (valid & FUSE_SET_ATTR_SIZE)) {
    2843 err = fuse_fs_truncate(f->fs, path,
    2844 attr->st_size, fi);
    2845 }
    2846#ifdef HAVE_UTIMENSAT
    2847 if (!err &&
    2848 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))) {
    2849 struct timespec tv[2];
    2850
    2851 tv[0].tv_sec = 0;
    2852 tv[1].tv_sec = 0;
    2853 tv[0].tv_nsec = UTIME_OMIT;
    2854 tv[1].tv_nsec = UTIME_OMIT;
    2855
    2856 if (valid & FUSE_SET_ATTR_ATIME_NOW)
    2857 tv[0].tv_nsec = UTIME_NOW;
    2858 else if (valid & FUSE_SET_ATTR_ATIME)
    2859 tv[0] = attr->st_atim;
    2860
    2861 if (valid & FUSE_SET_ATTR_MTIME_NOW)
    2862 tv[1].tv_nsec = UTIME_NOW;
    2863 else if (valid & FUSE_SET_ATTR_MTIME)
    2864 tv[1] = attr->st_mtim;
    2865
    2866 err = fuse_fs_utimens(f->fs, path, tv, fi);
    2867 } else
    2868#endif
    2869 if (!err &&
    2870 (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ==
    2871 (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
    2872 struct timespec tv[2];
    2873 tv[0].tv_sec = attr->st_atime;
    2874 tv[0].tv_nsec = ST_ATIM_NSEC(attr);
    2875 tv[1].tv_sec = attr->st_mtime;
    2876 tv[1].tv_nsec = ST_MTIM_NSEC(attr);
    2877 err = fuse_fs_utimens(f->fs, path, tv, fi);
    2878 }
    2879 if (!err) {
    2880 err = fuse_fs_getattr(f->fs, path, &buf, fi);
    2881 }
    2882 fuse_finish_interrupt(f, req, &d);
    2883 free_path(f, ino, path);
    2884 }
    2885 if (!err) {
    2886 if (f->conf.auto_cache) {
    2887 pthread_mutex_lock(&f->lock);
    2888 update_stat(get_node(f, ino), &buf);
    2889 pthread_mutex_unlock(&f->lock);
    2890 }
    2891 set_stat(f, ino, &buf);
    2892 fuse_reply_attr(req, &buf, f->conf.attr_timeout);
    2893 } else
    2894 reply_err(req, err);
    2895}
    2896
    2897static void fuse_lib_access(fuse_req_t req, fuse_ino_t ino, int mask)
    2898{
    2899 struct fuse *f = req_fuse_prepare(req);
    2900 char *path;
    2901 int err;
    2902
    2903 err = get_path(f, ino, &path);
    2904 if (!err) {
    2905 struct fuse_intr_data d;
    2906
    2907 fuse_prepare_interrupt(f, req, &d);
    2908 err = fuse_fs_access(f->fs, path, mask);
    2909 fuse_finish_interrupt(f, req, &d);
    2910 free_path(f, ino, path);
    2911 }
    2912 reply_err(req, err);
    2913}
    2914
    2915static void fuse_lib_readlink(fuse_req_t req, fuse_ino_t ino)
    2916{
    2917 struct fuse *f = req_fuse_prepare(req);
    2918 char linkname[PATH_MAX + 1];
    2919 char *path;
    2920 int err;
    2921
    2922 err = get_path(f, ino, &path);
    2923 if (!err) {
    2924 struct fuse_intr_data d;
    2925 fuse_prepare_interrupt(f, req, &d);
    2926 err = fuse_fs_readlink(f->fs, path, linkname, sizeof(linkname));
    2927 fuse_finish_interrupt(f, req, &d);
    2928 free_path(f, ino, path);
    2929 }
    2930 if (!err) {
    2931 linkname[PATH_MAX] = '\0';
    2932 fuse_reply_readlink(req, linkname);
    2933 } else
    2934 reply_err(req, err);
    2935}
    2936
    2937static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
    2938 mode_t mode, dev_t rdev)
    2939{
    2940 struct fuse *f = req_fuse_prepare(req);
    2941 struct fuse_entry_param e;
    2942 char *path;
    2943 int err;
    2944
    2945 err = get_path_name(f, parent, name, &path);
    2946 if (!err) {
    2947 struct fuse_intr_data d;
    2948
    2949 fuse_prepare_interrupt(f, req, &d);
    2950 err = -ENOSYS;
    2951 if (S_ISREG(mode)) {
    2952 struct fuse_file_info fi;
    2953
    2954 memset(&fi, 0, sizeof(fi));
    2955 fi.flags = O_CREAT | O_EXCL | O_WRONLY;
    2956 err = fuse_fs_create(f->fs, path, mode, &fi);
    2957 if (!err) {
    2958 err = lookup_path(f, parent, name, path, &e,
    2959 &fi);
    2960 fuse_fs_release(f->fs, path, &fi);
    2961 }
    2962 }
    2963 if (err == -ENOSYS) {
    2964 err = fuse_fs_mknod(f->fs, path, mode, rdev);
    2965 if (!err)
    2966 err = lookup_path(f, parent, name, path, &e,
    2967 NULL);
    2968 }
    2969 fuse_finish_interrupt(f, req, &d);
    2970 free_path(f, parent, path);
    2971 }
    2972 reply_entry(req, &e, err);
    2973}
    2974
    2975static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
    2976 mode_t mode)
    2977{
    2978 struct fuse *f = req_fuse_prepare(req);
    2979 struct fuse_entry_param e;
    2980 char *path;
    2981 int err;
    2982
    2983 err = get_path_name(f, parent, name, &path);
    2984 if (!err) {
    2985 struct fuse_intr_data d;
    2986
    2987 fuse_prepare_interrupt(f, req, &d);
    2988 err = fuse_fs_mkdir(f->fs, path, mode);
    2989 if (!err)
    2990 err = lookup_path(f, parent, name, path, &e, NULL);
    2991 fuse_finish_interrupt(f, req, &d);
    2992 free_path(f, parent, path);
    2993 }
    2994 reply_entry(req, &e, err);
    2995}
    2996
    2997static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent,
    2998 const char *name)
    2999{
    3000 struct fuse *f = req_fuse_prepare(req);
    3001 struct node *wnode;
    3002 char *path;
    3003 int err;
    3004
    3005 err = get_path_wrlock(f, parent, name, &path, &wnode);
    3006 if (!err) {
    3007 struct fuse_intr_data d;
    3008
    3009 fuse_prepare_interrupt(f, req, &d);
    3010 if (!f->conf.hard_remove && is_open(f, parent, name)) {
    3011 err = hide_node(f, path, parent, name);
    3012 if (!err) {
    3013 /* we have hidden the node so now check again under a lock in case it is not used any more */
    3014 if (!is_open(f, parent, wnode->name)) {
    3015 char *unlinkpath;
    3016
    3017 /* get the hidden file path, to unlink it */
    3018 if (try_get_path(f, wnode->nodeid, NULL, &unlinkpath, NULL, false) == 0) {
    3019 err = fuse_fs_unlink(f->fs, unlinkpath);
    3020 if (!err)
    3021 remove_node(f, parent, wnode->name);
    3022 free(unlinkpath);
    3023 }
    3024 }
    3025 }
    3026 } else {
    3027 err = fuse_fs_unlink(f->fs, path);
    3028 if (!err)
    3029 remove_node(f, parent, name);
    3030 }
    3031 fuse_finish_interrupt(f, req, &d);
    3032 free_path_wrlock(f, parent, wnode, path);
    3033 }
    3034 reply_err(req, err);
    3035}
    3036
    3037static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
    3038{
    3039 struct fuse *f = req_fuse_prepare(req);
    3040 struct node *wnode;
    3041 char *path;
    3042 int err;
    3043
    3044 err = get_path_wrlock(f, parent, name, &path, &wnode);
    3045 if (!err) {
    3046 struct fuse_intr_data d;
    3047
    3048 fuse_prepare_interrupt(f, req, &d);
    3049 err = fuse_fs_rmdir(f->fs, path);
    3050 fuse_finish_interrupt(f, req, &d);
    3051 if (!err)
    3052 remove_node(f, parent, name);
    3053 free_path_wrlock(f, parent, wnode, path);
    3054 }
    3055 reply_err(req, err);
    3056}
    3057
    3058static void fuse_lib_symlink(fuse_req_t req, const char *linkname,
    3059 fuse_ino_t parent, const char *name)
    3060{
    3061 struct fuse *f = req_fuse_prepare(req);
    3062 struct fuse_entry_param e;
    3063 char *path;
    3064 int err;
    3065
    3066 err = get_path_name(f, parent, name, &path);
    3067 if (!err) {
    3068 struct fuse_intr_data d;
    3069
    3070 fuse_prepare_interrupt(f, req, &d);
    3071 err = fuse_fs_symlink(f->fs, linkname, path);
    3072 if (!err)
    3073 err = lookup_path(f, parent, name, path, &e, NULL);
    3074 fuse_finish_interrupt(f, req, &d);
    3075 free_path(f, parent, path);
    3076 }
    3077 reply_entry(req, &e, err);
    3078}
    3079
    3080static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
    3081 const char *oldname, fuse_ino_t newdir,
    3082 const char *newname, unsigned int flags)
    3083{
    3084 struct fuse *f = req_fuse_prepare(req);
    3085 char *oldpath;
    3086 char *newpath;
    3087 struct node *wnode1;
    3088 struct node *wnode2;
    3089 int err;
    3090
    3091 err = get_path2(f, olddir, oldname, newdir, newname,
    3092 &oldpath, &newpath, &wnode1, &wnode2);
    3093 if (!err) {
    3094 struct fuse_intr_data d;
    3095 err = 0;
    3096 fuse_prepare_interrupt(f, req, &d);
    3097 if (!f->conf.hard_remove && !(flags & RENAME_EXCHANGE) &&
    3098 is_open(f, newdir, newname))
    3099 err = hide_node(f, newpath, newdir, newname);
    3100 if (!err) {
    3101 err = fuse_fs_rename(f->fs, oldpath, newpath, flags);
    3102 if (!err) {
    3103 if (flags & RENAME_EXCHANGE) {
    3104 err = exchange_node(f, olddir, oldname,
    3105 newdir, newname);
    3106 } else {
    3107 err = rename_node(f, olddir, oldname,
    3108 newdir, newname, 0);
    3109 }
    3110 }
    3111 }
    3112 fuse_finish_interrupt(f, req, &d);
    3113 free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath);
    3114 }
    3115 reply_err(req, err);
    3116}
    3117
    3118static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
    3119 const char *newname)
    3120{
    3121 struct fuse *f = req_fuse_prepare(req);
    3122 struct fuse_entry_param e;
    3123 char *oldpath;
    3124 char *newpath;
    3125 int err;
    3126
    3127 err = get_path2(f, ino, NULL, newparent, newname,
    3128 &oldpath, &newpath, NULL, NULL);
    3129 if (!err) {
    3130 struct fuse_intr_data d;
    3131
    3132 fuse_prepare_interrupt(f, req, &d);
    3133 err = fuse_fs_link(f->fs, oldpath, newpath);
    3134 if (!err)
    3135 err = lookup_path(f, newparent, newname, newpath,
    3136 &e, NULL);
    3137 fuse_finish_interrupt(f, req, &d);
    3138 free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath);
    3139 }
    3140 reply_entry(req, &e, err);
    3141}
    3142
    3143static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path,
    3144 struct fuse_file_info *fi)
    3145{
    3146 struct node *node;
    3147 int unlink_hidden = 0;
    3148
    3149 fuse_fs_release(f->fs, path, fi);
    3150
    3151 pthread_mutex_lock(&f->lock);
    3152 node = get_node(f, ino);
    3153 assert(node->open_count > 0);
    3154 --node->open_count;
    3155 if (node->is_hidden && !node->open_count) {
    3156 unlink_hidden = 1;
    3157 node->is_hidden = 0;
    3158 }
    3159 pthread_mutex_unlock(&f->lock);
    3160
    3161 if(unlink_hidden) {
    3162 if (path) {
    3163 fuse_fs_unlink(f->fs, path);
    3164 } else if (f->conf.nullpath_ok) {
    3165 char *unlinkpath;
    3166
    3167 if (get_path(f, ino, &unlinkpath) == 0)
    3168 fuse_fs_unlink(f->fs, unlinkpath);
    3169
    3170 free_path(f, ino, unlinkpath);
    3171 }
    3172 }
    3173}
    3174
    3175static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent,
    3176 const char *name, mode_t mode,
    3177 struct fuse_file_info *fi)
    3178{
    3179 struct fuse *f = req_fuse_prepare(req);
    3180 struct fuse_intr_data d;
    3181 struct fuse_entry_param e;
    3182 char *path;
    3183 int err;
    3184
    3185 err = get_path_name(f, parent, name, &path);
    3186 if (!err) {
    3187 fuse_prepare_interrupt(f, req, &d);
    3188 err = fuse_fs_create(f->fs, path, mode, fi);
    3189 if (!err) {
    3190 err = lookup_path(f, parent, name, path, &e, fi);
    3191 if (err)
    3192 fuse_fs_release(f->fs, path, fi);
    3193 else if (!S_ISREG(e.attr.st_mode)) {
    3194 err = -EIO;
    3195 fuse_fs_release(f->fs, path, fi);
    3196 forget_node(f, e.ino, 1);
    3197 } else {
    3198 if (f->conf.direct_io)
    3199 fi->direct_io = 1;
    3200 if (f->conf.kernel_cache)
    3201 fi->keep_cache = 1;
    3202 if (fi->direct_io &&
    3203 f->conf.parallel_direct_writes)
    3204 fi->parallel_direct_writes = 1;
    3205 }
    3206 }
    3207 fuse_finish_interrupt(f, req, &d);
    3208 }
    3209 if (!err) {
    3210 pthread_mutex_lock(&f->lock);
    3211 get_node(f, e.ino)->open_count++;
    3212 pthread_mutex_unlock(&f->lock);
    3213 if (fuse_reply_create(req, &e, fi) == -ENOENT) {
    3214 /* The open syscall was interrupted, so it
    3215 must be cancelled */
    3216 fuse_do_release(f, e.ino, path, fi);
    3217 forget_node(f, e.ino, 1);
    3218 }
    3219 } else {
    3220 reply_err(req, err);
    3221 }
    3222
    3223 free_path(f, parent, path);
    3224}
    3225
    3226static double diff_timespec(const struct timespec *t1,
    3227 const struct timespec *t2)
    3228{
    3229 return (t1->tv_sec - t2->tv_sec) +
    3230 ((double) t1->tv_nsec - (double) t2->tv_nsec) / 1000000000.0;
    3231}
    3232
    3233static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path,
    3234 struct fuse_file_info *fi)
    3235{
    3236 struct node *node;
    3237
    3238 pthread_mutex_lock(&f->lock);
    3239 node = get_node(f, ino);
    3240 if (node->cache_valid) {
    3241 struct timespec now;
    3242
    3243 curr_time(&now);
    3244 if (diff_timespec(&now, &node->stat_updated) >
    3245 f->conf.ac_attr_timeout) {
    3246 struct stat stbuf;
    3247 int err;
    3248 pthread_mutex_unlock(&f->lock);
    3249 err = fuse_fs_getattr(f->fs, path, &stbuf, fi);
    3250 pthread_mutex_lock(&f->lock);
    3251 if (!err)
    3252 update_stat(node, &stbuf);
    3253 else
    3254 node->cache_valid = 0;
    3255 }
    3256 }
    3257 if (node->cache_valid)
    3258 fi->keep_cache = 1;
    3259
    3260 node->cache_valid = 1;
    3261 pthread_mutex_unlock(&f->lock);
    3262}
    3263
    3264static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino,
    3265 struct fuse_file_info *fi)
    3266{
    3267 struct fuse *f = req_fuse_prepare(req);
    3268 struct fuse_intr_data d;
    3269 char *path;
    3270 int err;
    3271
    3272 err = get_path(f, ino, &path);
    3273 if (!err) {
    3274 fuse_prepare_interrupt(f, req, &d);
    3275 err = fuse_fs_open(f->fs, path, fi);
    3276 if (!err) {
    3277 if (f->conf.direct_io)
    3278 fi->direct_io = 1;
    3279 if (f->conf.kernel_cache)
    3280 fi->keep_cache = 1;
    3281
    3282 if (f->conf.auto_cache)
    3283 open_auto_cache(f, ino, path, fi);
    3284
    3285 if (f->conf.no_rofd_flush &&
    3286 (fi->flags & O_ACCMODE) == O_RDONLY)
    3287 fi->noflush = 1;
    3288
    3289 if (fi->direct_io && f->conf.parallel_direct_writes)
    3290 fi->parallel_direct_writes = 1;
    3291
    3292 }
    3293 fuse_finish_interrupt(f, req, &d);
    3294 }
    3295 if (!err) {
    3296 pthread_mutex_lock(&f->lock);
    3297 get_node(f, ino)->open_count++;
    3298 pthread_mutex_unlock(&f->lock);
    3299 if (fuse_reply_open(req, fi) == -ENOENT) {
    3300 /* The open syscall was interrupted, so it
    3301 must be cancelled */
    3302 fuse_do_release(f, ino, path, fi);
    3303 }
    3304 } else
    3305 reply_err(req, err);
    3306
    3307 free_path(f, ino, path);
    3308}
    3309
    3310static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
    3311 off_t off, struct fuse_file_info *fi)
    3312{
    3313 struct fuse *f = req_fuse_prepare(req);
    3314 struct fuse_bufvec *buf = NULL;
    3315 char *path;
    3316 int res;
    3317
    3318 res = get_path_nullok(f, ino, &path);
    3319 if (res == 0) {
    3320 struct fuse_intr_data d;
    3321
    3322 fuse_prepare_interrupt(f, req, &d);
    3323 res = fuse_fs_read_buf(f->fs, path, &buf, size, off, fi);
    3324 fuse_finish_interrupt(f, req, &d);
    3325 free_path(f, ino, path);
    3326 }
    3327
    3328 if (res == 0)
    3330 else
    3331 reply_err(req, res);
    3332
    3333 fuse_free_buf(buf);
    3334}
    3335
    3336static void fuse_lib_write_buf(fuse_req_t req, fuse_ino_t ino,
    3337 struct fuse_bufvec *buf, off_t off,
    3338 struct fuse_file_info *fi)
    3339{
    3340 struct fuse *f = req_fuse_prepare(req);
    3341 char *path;
    3342 int res;
    3343
    3344 res = get_path_nullok(f, ino, &path);
    3345 if (res == 0) {
    3346 struct fuse_intr_data d;
    3347
    3348 fuse_prepare_interrupt(f, req, &d);
    3349 res = fuse_fs_write_buf(f->fs, path, buf, off, fi);
    3350 fuse_finish_interrupt(f, req, &d);
    3351 free_path(f, ino, path);
    3352 }
    3353
    3354 if (res >= 0)
    3355 fuse_reply_write(req, res);
    3356 else
    3357 reply_err(req, res);
    3358}
    3359
    3360static void fuse_lib_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
    3361 struct fuse_file_info *fi)
    3362{
    3363 struct fuse *f = req_fuse_prepare(req);
    3364 char *path;
    3365 int err;
    3366
    3367 err = get_path_nullok(f, ino, &path);
    3368 if (!err) {
    3369 struct fuse_intr_data d;
    3370
    3371 fuse_prepare_interrupt(f, req, &d);
    3372 err = fuse_fs_fsync(f->fs, path, datasync, fi);
    3373 fuse_finish_interrupt(f, req, &d);
    3374 free_path(f, ino, path);
    3375 }
    3376 reply_err(req, err);
    3377}
    3378
    3379static struct fuse_dh *get_dirhandle(const struct fuse_file_info *llfi,
    3380 struct fuse_file_info *fi)
    3381{
    3382 struct fuse_dh *dh = (struct fuse_dh *) (uintptr_t) llfi->fh;
    3383 memset(fi, 0, sizeof(struct fuse_file_info));
    3384 fi->fh = dh->fh;
    3385 return dh;
    3386}
    3387
    3388static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
    3389 struct fuse_file_info *llfi)
    3390{
    3391 struct fuse *f = req_fuse_prepare(req);
    3392 struct fuse_intr_data d;
    3393 struct fuse_dh *dh;
    3394 struct fuse_file_info fi;
    3395 char *path;
    3396 int err;
    3397
    3398 dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
    3399 if (dh == NULL) {
    3400 reply_err(req, -ENOMEM);
    3401 return;
    3402 }
    3403 memset(dh, 0, sizeof(struct fuse_dh));
    3404 dh->fuse = f;
    3405 dh->contents = NULL;
    3406 dh->first = NULL;
    3407 dh->len = 0;
    3408 dh->filled = 0;
    3409 dh->nodeid = ino;
    3410 pthread_mutex_init(&dh->lock, NULL);
    3411
    3412 llfi->fh = (uintptr_t) dh;
    3413
    3414 memset(&fi, 0, sizeof(fi));
    3415 fi.flags = llfi->flags;
    3416
    3417 err = get_path(f, ino, &path);
    3418 if (!err) {
    3419 fuse_prepare_interrupt(f, req, &d);
    3420 err = fuse_fs_opendir(f->fs, path, &fi);
    3421 fuse_finish_interrupt(f, req, &d);
    3422 dh->fh = fi.fh;
    3423 llfi->cache_readdir = fi.cache_readdir;
    3424 llfi->keep_cache = fi.keep_cache;
    3425 }
    3426 if (!err) {
    3427 if (fuse_reply_open(req, llfi) == -ENOENT) {
    3428 /* The opendir syscall was interrupted, so it
    3429 must be cancelled */
    3430 fuse_fs_releasedir(f->fs, path, &fi);
    3431 pthread_mutex_destroy(&dh->lock);
    3432 free(dh);
    3433 }
    3434 } else {
    3435 reply_err(req, err);
    3436 pthread_mutex_destroy(&dh->lock);
    3437 free(dh);
    3438 }
    3439 free_path(f, ino, path);
    3440}
    3441
    3442static int extend_contents(struct fuse_dh *dh, unsigned minsize)
    3443{
    3444 if (minsize > dh->size) {
    3445 char *newptr;
    3446 unsigned newsize = dh->size;
    3447 if (!newsize)
    3448 newsize = 1024;
    3449 while (newsize < minsize) {
    3450 if (newsize >= 0x80000000)
    3451 newsize = 0xffffffff;
    3452 else
    3453 newsize *= 2;
    3454 }
    3455
    3456 newptr = (char *) realloc(dh->contents, newsize);
    3457 if (!newptr) {
    3458 dh->error = -ENOMEM;
    3459 return -1;
    3460 }
    3461 dh->contents = newptr;
    3462 dh->size = newsize;
    3463 }
    3464 return 0;
    3465}
    3466
    3467static int fuse_add_direntry_to_dh(struct fuse_dh *dh, const char *name,
    3468 struct stat *st, enum fuse_fill_dir_flags flags)
    3469{
    3470 struct fuse_direntry *de;
    3471
    3472 de = malloc(sizeof(struct fuse_direntry));
    3473 if (!de) {
    3474 dh->error = -ENOMEM;
    3475 return -1;
    3476 }
    3477 de->name = strdup(name);
    3478 if (!de->name) {
    3479 dh->error = -ENOMEM;
    3480 free(de);
    3481 return -1;
    3482 }
    3483 de->flags = flags;
    3484 de->stat = *st;
    3485 de->next = NULL;
    3486
    3487 *dh->last = de;
    3488 dh->last = &de->next;
    3489
    3490 return 0;
    3491}
    3492
    3493static fuse_ino_t lookup_nodeid(struct fuse *f, fuse_ino_t parent,
    3494 const char *name)
    3495{
    3496 struct node *node;
    3497 fuse_ino_t res = FUSE_UNKNOWN_INO;
    3498
    3499 pthread_mutex_lock(&f->lock);
    3500 node = lookup_node(f, parent, name);
    3501 if (node)
    3502 res = node->nodeid;
    3503 pthread_mutex_unlock(&f->lock);
    3504
    3505 return res;
    3506}
    3507
    3508static int fill_dir(void *dh_, const char *name, const struct stat *statp,
    3509 off_t off, enum fuse_fill_dir_flags flags)
    3510{
    3511 struct fuse_dh *dh = (struct fuse_dh *) dh_;
    3512 struct stat stbuf;
    3513
    3514 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
    3515 dh->error = -EIO;
    3516 return 1;
    3517 }
    3518
    3519 if (statp)
    3520 stbuf = *statp;
    3521 else {
    3522 memset(&stbuf, 0, sizeof(stbuf));
    3523 stbuf.st_ino = FUSE_UNKNOWN_INO;
    3524 }
    3525
    3526 if (!dh->fuse->conf.use_ino) {
    3527 stbuf.st_ino = FUSE_UNKNOWN_INO;
    3528 if (dh->fuse->conf.readdir_ino) {
    3529 stbuf.st_ino = (ino_t)
    3530 lookup_nodeid(dh->fuse, dh->nodeid, name);
    3531 }
    3532 }
    3533
    3534 if (off) {
    3535 size_t newlen;
    3536
    3537 if (dh->filled) {
    3538 dh->error = -EIO;
    3539 return 1;
    3540 }
    3541
    3542 if (dh->first) {
    3543 dh->error = -EIO;
    3544 return 1;
    3545 }
    3546
    3547 if (extend_contents(dh, dh->needlen) == -1)
    3548 return 1;
    3549
    3550 newlen = dh->len +
    3551 fuse_add_direntry(dh->req, dh->contents + dh->len,
    3552 dh->needlen - dh->len, name,
    3553 &stbuf, off);
    3554 if (newlen > dh->needlen)
    3555 return 1;
    3556
    3557 dh->len = newlen;
    3558 } else {
    3559 dh->filled = 1;
    3560
    3561 if (fuse_add_direntry_to_dh(dh, name, &stbuf, flags) == -1)
    3562 return 1;
    3563 }
    3564 return 0;
    3565}
    3566
    3567static int is_dot_or_dotdot(const char *name)
    3568{
    3569 return name[0] == '.' && (name[1] == '\0' ||
    3570 (name[1] == '.' && name[2] == '\0'));
    3571}
    3572
    3573static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp,
    3574 off_t off, enum fuse_fill_dir_flags flags)
    3575{
    3576 struct fuse_dh *dh = (struct fuse_dh *) dh_;
    3577 struct fuse_entry_param e = {
    3578 /* ino=0 tells the kernel to ignore readdirplus stat info */
    3579 .ino = 0,
    3580 };
    3581 struct fuse *f = dh->fuse;
    3582 int res;
    3583
    3584 if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
    3585 dh->error = -EIO;
    3586 return 1;
    3587 }
    3588
    3589 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
    3590 e.attr = *statp;
    3591 } else {
    3592 e.attr.st_ino = FUSE_UNKNOWN_INO;
    3593 if (statp) {
    3594 e.attr.st_mode = statp->st_mode;
    3595 if (f->conf.use_ino)
    3596 e.attr.st_ino = statp->st_ino;
    3597 }
    3598 if (!f->conf.use_ino && f->conf.readdir_ino) {
    3599 e.attr.st_ino = (ino_t)
    3600 lookup_nodeid(f, dh->nodeid, name);
    3601 }
    3602 }
    3603
    3604 if (off) {
    3605 size_t newlen;
    3606
    3607 if (dh->filled) {
    3608 dh->error = -EIO;
    3609 return 1;
    3610 }
    3611
    3612 if (dh->first) {
    3613 dh->error = -EIO;
    3614 return 1;
    3615 }
    3616 if (extend_contents(dh, dh->needlen) == -1)
    3617 return 1;
    3618
    3619 if (statp && (flags & FUSE_FILL_DIR_PLUS)) {
    3620 if (!is_dot_or_dotdot(name)) {
    3621 res = do_lookup(f, dh->nodeid, name, &e);
    3622 if (res) {
    3623 dh->error = res;
    3624 return 1;
    3625 }
    3626 }
    3627 }
    3628
    3629 newlen = dh->len +
    3630 fuse_add_direntry_plus(dh->req, dh->contents + dh->len,
    3631 dh->needlen - dh->len, name,
    3632 &e, off);
    3633 if (newlen > dh->needlen)
    3634 return 1;
    3635 dh->len = newlen;
    3636 } else {
    3637 dh->filled = 1;
    3638
    3639 if (fuse_add_direntry_to_dh(dh, name, &e.attr, flags) == -1)
    3640 return 1;
    3641 }
    3642
    3643 return 0;
    3644}
    3645
    3646static void free_direntries(struct fuse_direntry *de)
    3647{
    3648 while (de) {
    3649 struct fuse_direntry *next = de->next;
    3650 free(de->name);
    3651 free(de);
    3652 de = next;
    3653 }
    3654}
    3655
    3656static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
    3657 size_t size, off_t off, struct fuse_dh *dh,
    3658 struct fuse_file_info *fi,
    3659 enum fuse_readdir_flags flags)
    3660{
    3661 char *path;
    3662 int err;
    3663
    3664 if (f->fs->op.readdir)
    3665 err = get_path_nullok(f, ino, &path);
    3666 else
    3667 err = get_path(f, ino, &path);
    3668 if (!err) {
    3669 struct fuse_intr_data d;
    3670 fuse_fill_dir_t filler = fill_dir;
    3671
    3672 if (flags & FUSE_READDIR_PLUS)
    3673 filler = fill_dir_plus;
    3674
    3675 free_direntries(dh->first);
    3676 dh->first = NULL;
    3677 dh->last = &dh->first;
    3678 dh->len = 0;
    3679 dh->error = 0;
    3680 dh->needlen = size;
    3681 dh->filled = 0;
    3682 dh->req = req;
    3683 fuse_prepare_interrupt(f, req, &d);
    3684 err = fuse_fs_readdir(f->fs, path, dh, filler, off, fi, flags);
    3685 fuse_finish_interrupt(f, req, &d);
    3686 dh->req = NULL;
    3687 if (!err)
    3688 err = dh->error;
    3689 if (err)
    3690 dh->filled = 0;
    3691 free_path(f, ino, path);
    3692 }
    3693 return err;
    3694}
    3695
    3696static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh,
    3697 off_t off, enum fuse_readdir_flags flags)
    3698{
    3699 off_t pos;
    3700 struct fuse_direntry *de = dh->first;
    3701 int res;
    3702
    3703 dh->len = 0;
    3704
    3705 if (extend_contents(dh, dh->needlen) == -1)
    3706 return dh->error;
    3707
    3708 for (pos = 0; pos < off; pos++) {
    3709 if (!de)
    3710 break;
    3711
    3712 de = de->next;
    3713 }
    3714 while (de) {
    3715 char *p = dh->contents + dh->len;
    3716 unsigned rem = dh->needlen - dh->len;
    3717 unsigned thislen;
    3718 unsigned newlen;
    3719 pos++;
    3720
    3721 if (flags & FUSE_READDIR_PLUS) {
    3722 struct fuse_entry_param e = {
    3723 .ino = 0,
    3724 .attr = de->stat,
    3725 };
    3726
    3727 if (de->flags & FUSE_FILL_DIR_PLUS &&
    3728 !is_dot_or_dotdot(de->name)) {
    3729 res = do_lookup(dh->fuse, dh->nodeid,
    3730 de->name, &e);
    3731 if (res) {
    3732 dh->error = res;
    3733 return 1;
    3734 }
    3735 }
    3736
    3737 thislen = fuse_add_direntry_plus(req, p, rem,
    3738 de->name, &e, pos);
    3739 } else {
    3740 thislen = fuse_add_direntry(req, p, rem,
    3741 de->name, &de->stat, pos);
    3742 }
    3743 newlen = dh->len + thislen;
    3744 if (newlen > dh->needlen)
    3745 break;
    3746 dh->len = newlen;
    3747 de = de->next;
    3748 }
    3749 return 0;
    3750}
    3751
    3752static void fuse_readdir_common(fuse_req_t req, fuse_ino_t ino, size_t size,
    3753 off_t off, struct fuse_file_info *llfi,
    3754 enum fuse_readdir_flags flags)
    3755{
    3756 struct fuse *f = req_fuse_prepare(req);
    3757 struct fuse_file_info fi;
    3758 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
    3759 int err;
    3760
    3761 pthread_mutex_lock(&dh->lock);
    3762 /* According to SUS, directory contents need to be refreshed on
    3763 rewinddir() */
    3764 if (!off)
    3765 dh->filled = 0;
    3766
    3767 if (!dh->filled) {
    3768 err = readdir_fill(f, req, ino, size, off, dh, &fi, flags);
    3769 if (err) {
    3770 reply_err(req, err);
    3771 goto out;
    3772 }
    3773 }
    3774 if (dh->filled) {
    3775 dh->needlen = size;
    3776 err = readdir_fill_from_list(req, dh, off, flags);
    3777 if (err) {
    3778 reply_err(req, err);
    3779 goto out;
    3780 }
    3781 }
    3782 fuse_reply_buf(req, dh->contents, dh->len);
    3783out:
    3784 pthread_mutex_unlock(&dh->lock);
    3785}
    3786
    3787static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    3788 off_t off, struct fuse_file_info *llfi)
    3789{
    3790 fuse_readdir_common(req, ino, size, off, llfi, 0);
    3791}
    3792
    3793static void fuse_lib_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
    3794 off_t off, struct fuse_file_info *llfi)
    3795{
    3796 fuse_readdir_common(req, ino, size, off, llfi, FUSE_READDIR_PLUS);
    3797}
    3798
    3799static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
    3800 struct fuse_file_info *llfi)
    3801{
    3802 struct fuse *f = req_fuse_prepare(req);
    3803 struct fuse_intr_data d;
    3804 struct fuse_file_info fi;
    3805 struct fuse_dh *dh = get_dirhandle(llfi, &fi);
    3806 char *path;
    3807
    3808 get_path_nullok(f, ino, &path);
    3809
    3810 fuse_prepare_interrupt(f, req, &d);
    3811 fuse_fs_releasedir(f->fs, path, &fi);
    3812 fuse_finish_interrupt(f, req, &d);
    3813 free_path(f, ino, path);
    3814
    3815 pthread_mutex_lock(&dh->lock);
    3816 pthread_mutex_unlock(&dh->lock);
    3817 pthread_mutex_destroy(&dh->lock);
    3818 free_direntries(dh->first);
    3819 free(dh->contents);
    3820 free(dh);
    3821 reply_err(req, 0);
    3822}
    3823
    3824static void fuse_lib_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
    3825 struct fuse_file_info *llfi)
    3826{
    3827 struct fuse *f = req_fuse_prepare(req);
    3828 struct fuse_file_info fi;
    3829 char *path;
    3830 int err;
    3831
    3832 get_dirhandle(llfi, &fi);
    3833
    3834 err = get_path_nullok(f, ino, &path);
    3835 if (!err) {
    3836 struct fuse_intr_data d;
    3837 fuse_prepare_interrupt(f, req, &d);
    3838 err = fuse_fs_fsyncdir(f->fs, path, datasync, &fi);
    3839 fuse_finish_interrupt(f, req, &d);
    3840 free_path(f, ino, path);
    3841 }
    3842 reply_err(req, err);
    3843}
    3844
    3845static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino)
    3846{
    3847 struct fuse *f = req_fuse_prepare(req);
    3848 struct statvfs buf;
    3849 char *path = NULL;
    3850 int err = 0;
    3851
    3852 memset(&buf, 0, sizeof(buf));
    3853 if (ino)
    3854 err = get_path(f, ino, &path);
    3855
    3856 if (!err) {
    3857 struct fuse_intr_data d;
    3858 fuse_prepare_interrupt(f, req, &d);
    3859 err = fuse_fs_statfs(f->fs, path ? path : "/", &buf);
    3860 fuse_finish_interrupt(f, req, &d);
    3861 free_path(f, ino, path);
    3862 }
    3863
    3864 if (!err)
    3865 fuse_reply_statfs(req, &buf);
    3866 else
    3867 reply_err(req, err);
    3868}
    3869
    3870static void fuse_lib_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    3871 const char *value, size_t size, int flags)
    3872{
    3873 struct fuse *f = req_fuse_prepare(req);
    3874 char *path;
    3875 int err;
    3876
    3877 err = get_path(f, ino, &path);
    3878 if (!err) {
    3879 struct fuse_intr_data d;
    3880 fuse_prepare_interrupt(f, req, &d);
    3881 err = fuse_fs_setxattr(f->fs, path, name, value, size, flags);
    3882 fuse_finish_interrupt(f, req, &d);
    3883 free_path(f, ino, path);
    3884 }
    3885 reply_err(req, err);
    3886}
    3887
    3888static int common_getxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
    3889 const char *name, char *value, size_t size)
    3890{
    3891 int err;
    3892 char *path;
    3893
    3894 err = get_path(f, ino, &path);
    3895 if (!err) {
    3896 struct fuse_intr_data d;
    3897 fuse_prepare_interrupt(f, req, &d);
    3898 err = fuse_fs_getxattr(f->fs, path, name, value, size);
    3899 fuse_finish_interrupt(f, req, &d);
    3900 free_path(f, ino, path);
    3901 }
    3902 return err;
    3903}
    3904
    3905static void fuse_lib_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
    3906 size_t size)
    3907{
    3908 struct fuse *f = req_fuse_prepare(req);
    3909 int res;
    3910
    3911 if (size) {
    3912 char *value = (char *) malloc(size);
    3913 if (value == NULL) {
    3914 reply_err(req, -ENOMEM);
    3915 return;
    3916 }
    3917 res = common_getxattr(f, req, ino, name, value, size);
    3918 if (res > 0)
    3919 fuse_reply_buf(req, value, res);
    3920 else
    3921 reply_err(req, res);
    3922 free(value);
    3923 } else {
    3924 res = common_getxattr(f, req, ino, name, NULL, 0);
    3925 if (res >= 0)
    3926 fuse_reply_xattr(req, res);
    3927 else
    3928 reply_err(req, res);
    3929 }
    3930}
    3931
    3932static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
    3933 char *list, size_t size)
    3934{
    3935 char *path;
    3936 int err;
    3937
    3938 err = get_path(f, ino, &path);
    3939 if (!err) {
    3940 struct fuse_intr_data d;
    3941 fuse_prepare_interrupt(f, req, &d);
    3942 err = fuse_fs_listxattr(f->fs, path, list, size);
    3943 fuse_finish_interrupt(f, req, &d);
    3944 free_path(f, ino, path);
    3945 }
    3946 return err;
    3947}
    3948
    3949static void fuse_lib_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
    3950{
    3951 struct fuse *f = req_fuse_prepare(req);
    3952 int res;
    3953
    3954 if (size) {
    3955 char *list = (char *) malloc(size);
    3956 if (list == NULL) {
    3957 reply_err(req, -ENOMEM);
    3958 return;
    3959 }
    3960 res = common_listxattr(f, req, ino, list, size);
    3961 if (res > 0)
    3962 fuse_reply_buf(req, list, res);
    3963 else
    3964 reply_err(req, res);
    3965 free(list);
    3966 } else {
    3967 res = common_listxattr(f, req, ino, NULL, 0);
    3968 if (res >= 0)
    3969 fuse_reply_xattr(req, res);
    3970 else
    3971 reply_err(req, res);
    3972 }
    3973}
    3974
    3975static void fuse_lib_removexattr(fuse_req_t req, fuse_ino_t ino,
    3976 const char *name)
    3977{
    3978 struct fuse *f = req_fuse_prepare(req);
    3979 char *path;
    3980 int err;
    3981
    3982 err = get_path(f, ino, &path);
    3983 if (!err) {
    3984 struct fuse_intr_data d;
    3985 fuse_prepare_interrupt(f, req, &d);
    3986 err = fuse_fs_removexattr(f->fs, path, name);
    3987 fuse_finish_interrupt(f, req, &d);
    3988 free_path(f, ino, path);
    3989 }
    3990 reply_err(req, err);
    3991}
    3992
    3993static struct lock *locks_conflict(struct node *node, const struct lock *lock)
    3994{
    3995 struct lock *l;
    3996
    3997 for (l = node->locks; l; l = l->next)
    3998 if (l->owner != lock->owner &&
    3999 lock->start <= l->end && l->start <= lock->end &&
    4000 (l->type == F_WRLCK || lock->type == F_WRLCK))
    4001 break;
    4002
    4003 return l;
    4004}
    4005
    4006static void delete_lock(struct lock **lockp)
    4007{
    4008 struct lock *l = *lockp;
    4009 *lockp = l->next;
    4010 free(l);
    4011}
    4012
    4013static void insert_lock(struct lock **pos, struct lock *lock)
    4014{
    4015 lock->next = *pos;
    4016 *pos = lock;
    4017}
    4018
    4019static int locks_insert(struct node *node, struct lock *lock)
    4020{
    4021 struct lock **lp;
    4022 struct lock *newl1 = NULL;
    4023 struct lock *newl2 = NULL;
    4024
    4025 if (lock->type != F_UNLCK || lock->start != 0 ||
    4026 lock->end != OFFSET_MAX) {
    4027 newl1 = malloc(sizeof(struct lock));
    4028 newl2 = malloc(sizeof(struct lock));
    4029
    4030 if (!newl1 || !newl2) {
    4031 free(newl1);
    4032 free(newl2);
    4033 return -ENOLCK;
    4034 }
    4035 }
    4036
    4037 for (lp = &node->locks; *lp;) {
    4038 struct lock *l = *lp;
    4039 if (l->owner != lock->owner)
    4040 goto skip;
    4041
    4042 if (lock->type == l->type) {
    4043 if (l->end < lock->start - 1)
    4044 goto skip;
    4045 if (lock->end < l->start - 1)
    4046 break;
    4047 if (l->start <= lock->start && lock->end <= l->end)
    4048 goto out;
    4049 if (l->start < lock->start)
    4050 lock->start = l->start;
    4051 if (lock->end < l->end)
    4052 lock->end = l->end;
    4053 goto delete;
    4054 } else {
    4055 if (l->end < lock->start)
    4056 goto skip;
    4057 if (lock->end < l->start)
    4058 break;
    4059 if (lock->start <= l->start && l->end <= lock->end)
    4060 goto delete;
    4061 if (l->end <= lock->end) {
    4062 l->end = lock->start - 1;
    4063 goto skip;
    4064 }
    4065 if (lock->start <= l->start) {
    4066 l->start = lock->end + 1;
    4067 break;
    4068 }
    4069 *newl2 = *l;
    4070 newl2->start = lock->end + 1;
    4071 l->end = lock->start - 1;
    4072 insert_lock(&l->next, newl2);
    4073 newl2 = NULL;
    4074 }
    4075 skip:
    4076 lp = &l->next;
    4077 continue;
    4078
    4079 delete:
    4080 delete_lock(lp);
    4081 }
    4082 if (lock->type != F_UNLCK) {
    4083 *newl1 = *lock;
    4084 insert_lock(lp, newl1);
    4085 newl1 = NULL;
    4086 }
    4087out:
    4088 free(newl1);
    4089 free(newl2);
    4090 return 0;
    4091}
    4092
    4093static void flock_to_lock(struct flock *flock, struct lock *lock)
    4094{
    4095 memset(lock, 0, sizeof(struct lock));
    4096 lock->type = flock->l_type;
    4097 lock->start = flock->l_start;
    4098 lock->end =
    4099 flock->l_len ? flock->l_start + flock->l_len - 1 : OFFSET_MAX;
    4100 lock->pid = flock->l_pid;
    4101}
    4102
    4103static void lock_to_flock(struct lock *lock, struct flock *flock)
    4104{
    4105 flock->l_type = lock->type;
    4106 flock->l_start = lock->start;
    4107 flock->l_len =
    4108 (lock->end == OFFSET_MAX) ? 0 : lock->end - lock->start + 1;
    4109 flock->l_pid = lock->pid;
    4110}
    4111
    4112static int fuse_flush_common(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
    4113 const char *path, struct fuse_file_info *fi)
    4114{
    4115 struct fuse_intr_data d;
    4116 struct flock lock;
    4117 struct lock l;
    4118 int err;
    4119 int errlock;
    4120
    4121 fuse_prepare_interrupt(f, req, &d);
    4122 memset(&lock, 0, sizeof(lock));
    4123 lock.l_type = F_UNLCK;
    4124 lock.l_whence = SEEK_SET;
    4125 err = fuse_fs_flush(f->fs, path, fi);
    4126 errlock = fuse_fs_lock(f->fs, path, fi, F_SETLK, &lock);
    4127 fuse_finish_interrupt(f, req, &d);
    4128
    4129 if (errlock != -ENOSYS) {
    4130 flock_to_lock(&lock, &l);
    4131 l.owner = fi->lock_owner;
    4132 pthread_mutex_lock(&f->lock);
    4133 locks_insert(get_node(f, ino), &l);
    4134 pthread_mutex_unlock(&f->lock);
    4135
    4136 /* if op.lock() is defined FLUSH is needed regardless
    4137 of op.flush() */
    4138 if (err == -ENOSYS)
    4139 err = 0;
    4140 }
    4141 return err;
    4142}
    4143
    4144static void fuse_lib_release(fuse_req_t req, fuse_ino_t ino,
    4145 struct fuse_file_info *fi)
    4146{
    4147 struct fuse *f = req_fuse_prepare(req);
    4148 struct fuse_intr_data d;
    4149 char *path;
    4150 int err = 0;
    4151
    4152 get_path_nullok(f, ino, &path);
    4153 if (fi->flush) {
    4154 err = fuse_flush_common(f, req, ino, path, fi);
    4155 if (err == -ENOSYS)
    4156 err = 0;
    4157 }
    4158
    4159 fuse_prepare_interrupt(f, req, &d);
    4160 fuse_do_release(f, ino, path, fi);
    4161 fuse_finish_interrupt(f, req, &d);
    4162 free_path(f, ino, path);
    4163
    4164 reply_err(req, err);
    4165}
    4166
    4167static void fuse_lib_flush(fuse_req_t req, fuse_ino_t ino,
    4168 struct fuse_file_info *fi)
    4169{
    4170 struct fuse *f = req_fuse_prepare(req);
    4171 char *path;
    4172 int err;
    4173
    4174 get_path_nullok(f, ino, &path);
    4175 err = fuse_flush_common(f, req, ino, path, fi);
    4176 free_path(f, ino, path);
    4177
    4178 reply_err(req, err);
    4179}
    4180
    4181static int fuse_lock_common(fuse_req_t req, fuse_ino_t ino,
    4182 struct fuse_file_info *fi, struct flock *lock,
    4183 int cmd)
    4184{
    4185 struct fuse *f = req_fuse_prepare(req);
    4186 char *path;
    4187 int err;
    4188
    4189 err = get_path_nullok(f, ino, &path);
    4190 if (!err) {
    4191 struct fuse_intr_data d;
    4192 fuse_prepare_interrupt(f, req, &d);
    4193 err = fuse_fs_lock(f->fs, path, fi, cmd, lock);
    4194 fuse_finish_interrupt(f, req, &d);
    4195 free_path(f, ino, path);
    4196 }
    4197 return err;
    4198}
    4199
    4200static void fuse_lib_getlk(fuse_req_t req, fuse_ino_t ino,
    4201 struct fuse_file_info *fi, struct flock *lock)
    4202{
    4203 int err;
    4204 struct lock l;
    4205 struct lock *conflict;
    4206 struct fuse *f = req_fuse(req);
    4207
    4208 flock_to_lock(lock, &l);
    4209 l.owner = fi->lock_owner;
    4210 pthread_mutex_lock(&f->lock);
    4211 conflict = locks_conflict(get_node(f, ino), &l);
    4212 if (conflict)
    4213 lock_to_flock(conflict, lock);
    4214 pthread_mutex_unlock(&f->lock);
    4215 if (!conflict)
    4216 err = fuse_lock_common(req, ino, fi, lock, F_GETLK);
    4217 else
    4218 err = 0;
    4219
    4220 if (!err)
    4221 fuse_reply_lock(req, lock);
    4222 else
    4223 reply_err(req, err);
    4224}
    4225
    4226static void fuse_lib_setlk(fuse_req_t req, fuse_ino_t ino,
    4227 struct fuse_file_info *fi, struct flock *lock,
    4228 int sleep)
    4229{
    4230 int err = fuse_lock_common(req, ino, fi, lock,
    4231 sleep ? F_SETLKW : F_SETLK);
    4232 if (!err) {
    4233 struct fuse *f = req_fuse(req);
    4234 struct lock l;
    4235 flock_to_lock(lock, &l);
    4236 l.owner = fi->lock_owner;
    4237 pthread_mutex_lock(&f->lock);
    4238 locks_insert(get_node(f, ino), &l);
    4239 pthread_mutex_unlock(&f->lock);
    4240 }
    4241 reply_err(req, err);
    4242}
    4243
    4244static void fuse_lib_flock(fuse_req_t req, fuse_ino_t ino,
    4245 struct fuse_file_info *fi, int op)
    4246{
    4247 struct fuse *f = req_fuse_prepare(req);
    4248 char *path;
    4249 int err;
    4250
    4251 err = get_path_nullok(f, ino, &path);
    4252 if (err == 0) {
    4253 struct fuse_intr_data d;
    4254 fuse_prepare_interrupt(f, req, &d);
    4255 err = fuse_fs_flock(f->fs, path, fi, op);
    4256 fuse_finish_interrupt(f, req, &d);
    4257 free_path(f, ino, path);
    4258 }
    4259 reply_err(req, err);
    4260}
    4261
    4262static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
    4263 uint64_t idx)
    4264{
    4265 struct fuse *f = req_fuse_prepare(req);
    4266 struct fuse_intr_data d;
    4267 char *path;
    4268 int err;
    4269
    4270 err = get_path(f, ino, &path);
    4271 if (!err) {
    4272 fuse_prepare_interrupt(f, req, &d);
    4273 err = fuse_fs_bmap(f->fs, path, blocksize, &idx);
    4274 fuse_finish_interrupt(f, req, &d);
    4275 free_path(f, ino, path);
    4276 }
    4277 if (!err)
    4278 fuse_reply_bmap(req, idx);
    4279 else
    4280 reply_err(req, err);
    4281}
    4282
    4283static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
    4284 void *arg, struct fuse_file_info *llfi,
    4285 unsigned int flags, const void *in_buf,
    4286 size_t in_bufsz, size_t out_bufsz)
    4287{
    4288 struct fuse *f = req_fuse_prepare(req);
    4289 struct fuse_intr_data d;
    4290 struct fuse_file_info fi;
    4291 char *path, *out_buf = NULL;
    4292 int err;
    4293
    4294 err = -EPERM;
    4295 if (flags & FUSE_IOCTL_UNRESTRICTED)
    4296 goto err;
    4297
    4298 if (flags & FUSE_IOCTL_DIR)
    4299 get_dirhandle(llfi, &fi);
    4300 else
    4301 fi = *llfi;
    4302
    4303 if (out_bufsz) {
    4304 err = -ENOMEM;
    4305 out_buf = malloc(out_bufsz);
    4306 if (!out_buf)
    4307 goto err;
    4308 }
    4309
    4310 assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
    4311 if (out_buf && in_bufsz)
    4312 memcpy(out_buf, in_buf, in_bufsz);
    4313
    4314 err = get_path_nullok(f, ino, &path);
    4315 if (err)
    4316 goto err;
    4317
    4318 fuse_prepare_interrupt(f, req, &d);
    4319
    4320 err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags,
    4321 out_buf ? out_buf : (void *)in_buf);
    4322
    4323 fuse_finish_interrupt(f, req, &d);
    4324 free_path(f, ino, path);
    4325
    4326 if (err < 0)
    4327 goto err;
    4328 fuse_reply_ioctl(req, err, out_buf, out_bufsz);
    4329 goto out;
    4330err:
    4331 reply_err(req, err);
    4332out:
    4333 free(out_buf);
    4334}
    4335
    4336static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino,
    4337 struct fuse_file_info *fi, struct fuse_pollhandle *ph)
    4338{
    4339 struct fuse *f = req_fuse_prepare(req);
    4340 struct fuse_intr_data d;
    4341 char *path;
    4342 int err;
    4343 unsigned revents = 0;
    4344
    4345 err = get_path_nullok(f, ino, &path);
    4346 if (!err) {
    4347 fuse_prepare_interrupt(f, req, &d);
    4348 err = fuse_fs_poll(f->fs, path, fi, ph, &revents);
    4349 fuse_finish_interrupt(f, req, &d);
    4350 free_path(f, ino, path);
    4351 }
    4352 if (!err)
    4353 fuse_reply_poll(req, revents);
    4354 else
    4355 reply_err(req, err);
    4356}
    4357
    4358static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
    4359 off_t offset, off_t length, struct fuse_file_info *fi)
    4360{
    4361 struct fuse *f = req_fuse_prepare(req);
    4362 struct fuse_intr_data d;
    4363 char *path;
    4364 int err;
    4365
    4366 err = get_path_nullok(f, ino, &path);
    4367 if (!err) {
    4368 fuse_prepare_interrupt(f, req, &d);
    4369 err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi);
    4370 fuse_finish_interrupt(f, req, &d);
    4371 free_path(f, ino, path);
    4372 }
    4373 reply_err(req, err);
    4374}
    4375
    4376static void fuse_lib_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
    4377 off_t off_in, struct fuse_file_info *fi_in,
    4378 fuse_ino_t nodeid_out, off_t off_out,
    4379 struct fuse_file_info *fi_out, size_t len,
    4380 int flags)
    4381{
    4382 struct fuse *f = req_fuse_prepare(req);
    4383 struct fuse_intr_data d;
    4384 char *path_in, *path_out;
    4385 int err;
    4386 ssize_t res;
    4387
    4388 err = get_path_nullok(f, nodeid_in, &path_in);
    4389 if (err) {
    4390 reply_err(req, err);
    4391 return;
    4392 }
    4393
    4394 err = get_path_nullok(f, nodeid_out, &path_out);
    4395 if (err) {
    4396 free_path(f, nodeid_in, path_in);
    4397 reply_err(req, err);
    4398 return;
    4399 }
    4400
    4401 fuse_prepare_interrupt(f, req, &d);
    4402 res = fuse_fs_copy_file_range(f->fs, path_in, fi_in, off_in, path_out,
    4403 fi_out, off_out, len, flags);
    4404 fuse_finish_interrupt(f, req, &d);
    4405
    4406 if (res >= 0)
    4407 fuse_reply_write(req, res);
    4408 else
    4409 reply_err(req, res);
    4410
    4411 free_path(f, nodeid_in, path_in);
    4412 free_path(f, nodeid_out, path_out);
    4413}
    4414
    4415static void fuse_lib_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
    4416 struct fuse_file_info *fi)
    4417{
    4418 struct fuse *f = req_fuse_prepare(req);
    4419 struct fuse_intr_data d;
    4420 char *path;
    4421 int err;
    4422 off_t res;
    4423
    4424 err = get_path(f, ino, &path);
    4425 if (err) {
    4426 reply_err(req, err);
    4427 return;
    4428 }
    4429
    4430 fuse_prepare_interrupt(f, req, &d);
    4431 res = fuse_fs_lseek(f->fs, path, off, whence, fi);
    4432 fuse_finish_interrupt(f, req, &d);
    4433 free_path(f, ino, path);
    4434 if (res >= 0)
    4435 fuse_reply_lseek(req, res);
    4436 else
    4437 reply_err(req, res);
    4438}
    4439
    4440static int clean_delay(struct fuse *f)
    4441{
    4442 /*
    4443 * This is calculating the delay between clean runs. To
    4444 * reduce the number of cleans we are doing them 10 times
    4445 * within the remember window.
    4446 */
    4447 int min_sleep = 60;
    4448 int max_sleep = 3600;
    4449 int sleep_time = f->conf.remember / 10;
    4450
    4451 if (sleep_time > max_sleep)
    4452 return max_sleep;
    4453 if (sleep_time < min_sleep)
    4454 return min_sleep;
    4455 return sleep_time;
    4456}
    4457
    4458int fuse_clean_cache(struct fuse *f)
    4459{
    4460 struct node_lru *lnode;
    4461 struct list_head *curr, *next;
    4462 struct node *node;
    4463 struct timespec now;
    4464
    4465 pthread_mutex_lock(&f->lock);
    4466
    4467 curr_time(&now);
    4468
    4469 for (curr = f->lru_table.next; curr != &f->lru_table; curr = next) {
    4470 double age;
    4471
    4472 next = curr->next;
    4473 lnode = list_entry(curr, struct node_lru, lru);
    4474 node = &lnode->node;
    4475
    4476 age = diff_timespec(&now, &lnode->forget_time);
    4477 if (age <= f->conf.remember)
    4478 break;
    4479
    4480 assert(node->nlookup == 1);
    4481
    4482 /* Don't forget active directories */
    4483 if (node->refctr > 1)
    4484 continue;
    4485
    4486 node->nlookup = 0;
    4487 unhash_name(f, node);
    4488 unref_node(f, node);
    4489 }
    4490 pthread_mutex_unlock(&f->lock);
    4491
    4492 return clean_delay(f);
    4493}
    4494
    4495static struct fuse_lowlevel_ops fuse_path_ops = {
    4496 .init = fuse_lib_init,
    4497 .destroy = fuse_lib_destroy,
    4498 .lookup = fuse_lib_lookup,
    4499 .forget = fuse_lib_forget,
    4500 .forget_multi = fuse_lib_forget_multi,
    4501 .getattr = fuse_lib_getattr,
    4502 .setattr = fuse_lib_setattr,
    4503 .access = fuse_lib_access,
    4504 .readlink = fuse_lib_readlink,
    4505 .mknod = fuse_lib_mknod,
    4506 .mkdir = fuse_lib_mkdir,
    4507 .unlink = fuse_lib_unlink,
    4508 .rmdir = fuse_lib_rmdir,
    4509 .symlink = fuse_lib_symlink,
    4510 .rename = fuse_lib_rename,
    4511 .link = fuse_lib_link,
    4512 .create = fuse_lib_create,
    4513 .open = fuse_lib_open,
    4514 .read = fuse_lib_read,
    4515 .write_buf = fuse_lib_write_buf,
    4516 .flush = fuse_lib_flush,
    4517 .release = fuse_lib_release,
    4518 .fsync = fuse_lib_fsync,
    4519 .opendir = fuse_lib_opendir,
    4520 .readdir = fuse_lib_readdir,
    4521 .readdirplus = fuse_lib_readdirplus,
    4522 .releasedir = fuse_lib_releasedir,
    4523 .fsyncdir = fuse_lib_fsyncdir,
    4524 .statfs = fuse_lib_statfs,
    4525 .setxattr = fuse_lib_setxattr,
    4526 .getxattr = fuse_lib_getxattr,
    4527 .listxattr = fuse_lib_listxattr,
    4528 .removexattr = fuse_lib_removexattr,
    4529 .getlk = fuse_lib_getlk,
    4530 .setlk = fuse_lib_setlk,
    4531 .flock = fuse_lib_flock,
    4532 .bmap = fuse_lib_bmap,
    4533 .ioctl = fuse_lib_ioctl,
    4534 .poll = fuse_lib_poll,
    4535 .fallocate = fuse_lib_fallocate,
    4536 .copy_file_range = fuse_lib_copy_file_range,
    4537 .lseek = fuse_lib_lseek,
    4538};
    4539
    4540int fuse_notify_poll(struct fuse_pollhandle *ph)
    4541{
    4542 return fuse_lowlevel_notify_poll(ph);
    4543}
    4544
    4545struct fuse_session *fuse_get_session(struct fuse *f)
    4546{
    4547 return f->se;
    4548}
    4549
    4550static int fuse_session_loop_remember(struct fuse *f)
    4551{
    4552 struct fuse_session *se = f->se;
    4553 int res = 0;
    4554 struct timespec now;
    4555 time_t next_clean;
    4556 struct pollfd fds = {
    4557 .fd = se->fd,
    4558 .events = POLLIN
    4559 };
    4560 struct fuse_buf fbuf = {
    4561 .mem = NULL,
    4562 };
    4563
    4564 curr_time(&now);
    4565 next_clean = now.tv_sec;
    4566 while (!fuse_session_exited(se)) {
    4567 unsigned timeout;
    4568
    4569 curr_time(&now);
    4570 if (now.tv_sec < next_clean)
    4571 timeout = next_clean - now.tv_sec;
    4572 else
    4573 timeout = 0;
    4574
    4575 res = poll(&fds, 1, timeout * 1000);
    4576 if (res == -1) {
    4577 if (errno == EINTR)
    4578 continue;
    4579 else
    4580 break;
    4581 } else if (res > 0) {
    4582 res = fuse_session_receive_buf_internal(se, &fbuf,
    4583 NULL);
    4584 if (res == -EINTR)
    4585 continue;
    4586 if (res <= 0)
    4587 break;
    4588
    4589 fuse_session_process_buf_internal(se, &fbuf, NULL);
    4590 } else {
    4591 timeout = fuse_clean_cache(f);
    4592 curr_time(&now);
    4593 next_clean = now.tv_sec + timeout;
    4594 }
    4595 }
    4596
    4597 free(fbuf.mem);
    4599 return res < 0 ? -1 : 0;
    4600}
    4601
    4602int fuse_loop(struct fuse *f)
    4603{
    4604 if (!f)
    4605 return -1;
    4606
    4607 if (lru_enabled(f))
    4608 return fuse_session_loop_remember(f);
    4609
    4610 return fuse_session_loop(f->se);
    4611}
    4612
    4613FUSE_SYMVER("fuse_loop_mt_312", "fuse_loop_mt@@FUSE_3.12")
    4614int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config)
    4615{
    4616 if (f == NULL)
    4617 return -1;
    4618
    4619 int res = fuse_start_cleanup_thread(f);
    4620 if (res)
    4621 return -1;
    4622
    4623 res = fuse_session_loop_mt_312(fuse_get_session(f), config);
    4625 return res;
    4626}
    4627
    4628int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1);
    4629FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@FUSE_3.2")
    4630int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1)
    4631{
    4632 struct fuse_loop_config *config = fuse_loop_cfg_create();
    4633 if (config == NULL)
    4634 return ENOMEM;
    4635
    4636 fuse_loop_cfg_convert(config, config_v1);
    4637
    4638 int res = fuse_loop_mt_312(f, config);
    4639
    4640 fuse_loop_cfg_destroy(config);
    4641
    4642 return res;
    4643}
    4644
    4645int fuse_loop_mt_31(struct fuse *f, int clone_fd);
    4646FUSE_SYMVER("fuse_loop_mt_31", "fuse_loop_mt@FUSE_3.0")
    4647int fuse_loop_mt_31(struct fuse *f, int clone_fd)
    4648{
    4649 int err;
    4650 struct fuse_loop_config *config = fuse_loop_cfg_create();
    4651
    4652 if (config == NULL)
    4653 return ENOMEM;
    4654
    4655 fuse_loop_cfg_set_clone_fd(config, clone_fd);
    4656
    4657 err = fuse_loop_mt_312(f, config);
    4658
    4659 fuse_loop_cfg_destroy(config);
    4660
    4661 return err;
    4662}
    4663
    4664void fuse_exit(struct fuse *f)
    4665{
    4666 fuse_session_exit(f->se);
    4667}
    4668
    4670{
    4671 struct fuse_context_i *c = fuse_get_context_internal();
    4672
    4673 if (c)
    4674 return &c->ctx;
    4675 else
    4676 return NULL;
    4677}
    4678
    4679int fuse_getgroups(int size, gid_t list[])
    4680{
    4681 struct fuse_context_i *c = fuse_get_context_internal();
    4682 if (!c)
    4683 return -EINVAL;
    4684
    4685 return fuse_req_getgroups(c->req, size, list);
    4686}
    4687
    4689{
    4690 struct fuse_context_i *c = fuse_get_context_internal();
    4691
    4692 if (c)
    4693 return fuse_req_interrupted(c->req);
    4694 else
    4695 return 0;
    4696}
    4697
    4698int fuse_invalidate_path(struct fuse *f, const char *path) {
    4699 fuse_ino_t ino;
    4700 int err = lookup_path_in_cache(f, path, &ino);
    4701 if (err) {
    4702 return err;
    4703 }
    4704
    4705 return fuse_lowlevel_notify_inval_inode(f->se, ino, 0, 0);
    4706}
    4707
    4708#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
    4709
    4710static const struct fuse_opt fuse_lib_opts[] = {
    4713 FUSE_LIB_OPT("debug", debug, 1),
    4714 FUSE_LIB_OPT("-d", debug, 1),
    4715 FUSE_LIB_OPT("kernel_cache", kernel_cache, 1),
    4716 FUSE_LIB_OPT("auto_cache", auto_cache, 1),
    4717 FUSE_LIB_OPT("noauto_cache", auto_cache, 0),
    4718 FUSE_LIB_OPT("no_rofd_flush", no_rofd_flush, 1),
    4719 FUSE_LIB_OPT("umask=", set_mode, 1),
    4720 FUSE_LIB_OPT("umask=%o", umask, 0),
    4721 FUSE_LIB_OPT("fmask=", set_mode, 1),
    4722 FUSE_LIB_OPT("fmask=%o", fmask, 0),
    4723 FUSE_LIB_OPT("dmask=", set_mode, 1),
    4724 FUSE_LIB_OPT("dmask=%o", dmask, 0),
    4725 FUSE_LIB_OPT("uid=", set_uid, 1),
    4726 FUSE_LIB_OPT("uid=%d", uid, 0),
    4727 FUSE_LIB_OPT("gid=", set_gid, 1),
    4728 FUSE_LIB_OPT("gid=%d", gid, 0),
    4729 FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0),
    4730 FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0),
    4731 FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0),
    4732 FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1),
    4733 FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0),
    4734 FUSE_LIB_OPT("noforget", remember, -1),
    4735 FUSE_LIB_OPT("remember=%u", remember, 0),
    4736 FUSE_LIB_OPT("modules=%s", modules, 0),
    4737 FUSE_LIB_OPT("parallel_direct_write=%d", parallel_direct_writes, 0),
    4739};
    4740
    4741static int fuse_lib_opt_proc(void *data, const char *arg, int key,
    4742 struct fuse_args *outargs)
    4743{
    4744 (void) arg; (void) outargs; (void) data; (void) key;
    4745
    4746 /* Pass through unknown options */
    4747 return 1;
    4748}
    4749
    4750
    4751static const struct fuse_opt fuse_help_opts[] = {
    4752 FUSE_LIB_OPT("modules=%s", modules, 1),
    4753 FUSE_OPT_KEY("modules=%s", FUSE_OPT_KEY_KEEP),
    4755};
    4756
    4757static void print_module_help(const char *name,
    4759{
    4760 struct fuse_args a = FUSE_ARGS_INIT(0, NULL);
    4761 if (fuse_opt_add_arg(&a, "") == -1 ||
    4762 fuse_opt_add_arg(&a, "-h") == -1)
    4763 return;
    4764 printf("\nOptions for %s module:\n", name);
    4765 (*fac)(&a, NULL);
    4767}
    4768
    4769void fuse_lib_help(struct fuse_args *args)
    4770{
    4771 /* These are not all options, but only the ones that
    4772 may be of interest to an end-user */
    4773 printf(
    4774" -o kernel_cache cache files in kernel\n"
    4775" -o [no]auto_cache enable caching based on modification times (off)\n"
    4776" -o no_rofd_flush disable flushing of read-only fd on close (off)\n"
    4777" -o umask=M set file permissions (octal)\n"
    4778" -o fmask=M set file permissions (octal)\n"
    4779" -o dmask=M set dir permissions (octal)\n"
    4780" -o uid=N set file owner\n"
    4781" -o gid=N set file group\n"
    4782" -o entry_timeout=T cache timeout for names (1.0s)\n"
    4783" -o negative_timeout=T cache timeout for deleted names (0.0s)\n"
    4784" -o attr_timeout=T cache timeout for attributes (1.0s)\n"
    4785" -o ac_attr_timeout=T auto cache timeout for attributes (attr_timeout)\n"
    4786" -o noforget never forget cached inodes\n"
    4787" -o remember=T remember cached inodes for T seconds (0s)\n"
    4788" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n");
    4789
    4790
    4791 /* Print low-level help */
    4793
    4794 /* Print help for builtin modules */
    4795 print_module_help("subdir", &fuse_module_subdir_factory);
    4796#ifdef HAVE_ICONV
    4797 print_module_help("iconv", &fuse_module_iconv_factory);
    4798#endif
    4799
    4800 /* Parse command line options in case we need to
    4801 activate more modules */
    4802 struct fuse_config conf = { .modules = NULL };
    4803 if (fuse_opt_parse(args, &conf, fuse_help_opts,
    4804 fuse_lib_opt_proc) == -1
    4805 || !conf.modules)
    4806 return;
    4807
    4808 char *module;
    4809 char *next;
    4810 struct fuse_module *m;
    4811
    4812 // Iterate over all modules
    4813 for (module = conf.modules; module; module = next) {
    4814 char *p;
    4815 for (p = module; *p && *p != ':'; p++);
    4816 next = *p ? p + 1 : NULL;
    4817 *p = '\0';
    4818
    4819 m = fuse_get_module(module);
    4820 if (m)
    4821 print_module_help(module, &m->factory);
    4822 }
    4823}
    4824
    4825static int fuse_init_intr_signal(int signum, int *installed)
    4826{
    4827 struct sigaction old_sa;
    4828
    4829 if (sigaction(signum, NULL, &old_sa) == -1) {
    4830 perror("fuse: cannot get old signal handler");
    4831 return -1;
    4832 }
    4833
    4834 if (old_sa.sa_handler == SIG_DFL) {
    4835 struct sigaction sa;
    4836
    4837 memset(&sa, 0, sizeof(struct sigaction));
    4838 sa.sa_handler = fuse_intr_sighandler;
    4839 sigemptyset(&sa.sa_mask);
    4840
    4841 if (sigaction(signum, &sa, NULL) == -1) {
    4842 perror("fuse: cannot set interrupt signal handler");
    4843 return -1;
    4844 }
    4845 *installed = 1;
    4846 }
    4847 return 0;
    4848}
    4849
    4850static void fuse_restore_intr_signal(int signum)
    4851{
    4852 struct sigaction sa;
    4853
    4854 memset(&sa, 0, sizeof(struct sigaction));
    4855 sa.sa_handler = SIG_DFL;
    4856 sigaction(signum, &sa, NULL);
    4857}
    4858
    4859
    4860static int fuse_push_module(struct fuse *f, const char *module,
    4861 struct fuse_args *args)
    4862{
    4863 struct fuse_fs *fs[2] = { f->fs, NULL };
    4864 struct fuse_fs *newfs;
    4865 struct fuse_module *m = fuse_get_module(module);
    4866
    4867 if (!m)
    4868 return -1;
    4869
    4870 newfs = m->factory(args, fs);
    4871 if (!newfs) {
    4872 fuse_put_module(m);
    4873 return -1;
    4874 }
    4875 f->fs = newfs;
    4876 return 0;
    4877}
    4878
    4879struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
    4880 void *user_data)
    4881{
    4882 struct fuse_fs *fs;
    4883
    4884 if (sizeof(struct fuse_operations) < op_size) {
    4885 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not not work\n");
    4886 op_size = sizeof(struct fuse_operations);
    4887 }
    4888
    4889 fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
    4890 if (!fs) {
    4891 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse_fs object\n");
    4892 return NULL;
    4893 }
    4894
    4895 fs->user_data = user_data;
    4896 if (op)
    4897 memcpy(&fs->op, op, op_size);
    4898 return fs;
    4899}
    4900
    4901static int node_table_init(struct node_table *t)
    4902{
    4903 t->size = NODE_TABLE_MIN_SIZE;
    4904 t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
    4905 if (t->array == NULL) {
    4906 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
    4907 return -1;
    4908 }
    4909 t->use = 0;
    4910 t->split = 0;
    4911
    4912 return 0;
    4913}
    4914
    4915static void *fuse_prune_nodes(void *fuse)
    4916{
    4917 struct fuse *f = fuse;
    4918 int sleep_time;
    4919
    4920 pthread_setname_np(pthread_self(), "fuse_prune_nodes");
    4921
    4922 while(1) {
    4923 sleep_time = fuse_clean_cache(f);
    4924 sleep(sleep_time);
    4925 }
    4926 return NULL;
    4927}
    4928
    4929int fuse_start_cleanup_thread(struct fuse *f)
    4930{
    4931 if (lru_enabled(f))
    4932 return fuse_start_thread(&f->prune_thread, fuse_prune_nodes, f);
    4933
    4934 return 0;
    4935}
    4936
    4937void fuse_stop_cleanup_thread(struct fuse *f)
    4938{
    4939 if (lru_enabled(f)) {
    4940 pthread_mutex_lock(&f->lock);
    4941 pthread_cancel(f->prune_thread);
    4942 pthread_mutex_unlock(&f->lock);
    4943 pthread_join(f->prune_thread, NULL);
    4944 }
    4945}
    4946
    4947/*
    4948 * Not supposed to be called directly, but supposed to be called
    4949 * through the fuse_new macro
    4950 */
    4951struct fuse *_fuse_new_31(struct fuse_args *args,
    4952 const struct fuse_operations *op, size_t op_size,
    4953 struct libfuse_version *version, void *user_data)
    4954{
    4955 struct fuse *f;
    4956 struct node *root;
    4957 struct fuse_fs *fs;
    4958 struct fuse_lowlevel_ops llop = fuse_path_ops;
    4959
    4960 f = (struct fuse *) calloc(1, sizeof(struct fuse));
    4961 if (f == NULL) {
    4962 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
    4963 goto out;
    4964 }
    4965
    4966 f->conf.entry_timeout = 1.0;
    4967 f->conf.attr_timeout = 1.0;
    4968 f->conf.negative_timeout = 0.0;
    4969 f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
    4970
    4971 /* Parse options */
    4972 if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
    4973 fuse_lib_opt_proc) == -1)
    4974 goto out_free;
    4975
    4976 pthread_mutex_lock(&fuse_context_lock);
    4977 static int builtin_modules_registered = 0;
    4978 /* Have the builtin modules already been registered? */
    4979 if (builtin_modules_registered == 0) {
    4980 /* If not, register them. */
    4981 fuse_register_module("subdir", fuse_module_subdir_factory, NULL);
    4982#ifdef HAVE_ICONV
    4983 fuse_register_module("iconv", fuse_module_iconv_factory, NULL);
    4984#endif
    4985 builtin_modules_registered= 1;
    4986 }
    4987 pthread_mutex_unlock(&fuse_context_lock);
    4988
    4989 if (fuse_create_context_key() == -1)
    4990 goto out_free;
    4991
    4992 fs = fuse_fs_new(op, op_size, user_data);
    4993 if (!fs)
    4994 goto out_delete_context_key;
    4995
    4996 f->fs = fs;
    4997
    4998 /* Oh f**k, this is ugly! */
    4999 if (!fs->op.lock) {
    5000 llop.getlk = NULL;
    5001 llop.setlk = NULL;
    5002 }
    5003
    5004 f->pagesize = getpagesize();
    5005 init_list_head(&f->partial_slabs);
    5006 init_list_head(&f->full_slabs);
    5007 init_list_head(&f->lru_table);
    5008
    5009 if (f->conf.modules) {
    5010 char *module;
    5011 char *next;
    5012
    5013 for (module = f->conf.modules; module; module = next) {
    5014 char *p;
    5015 for (p = module; *p && *p != ':'; p++);
    5016 next = *p ? p + 1 : NULL;
    5017 *p = '\0';
    5018 if (module[0] &&
    5019 fuse_push_module(f, module, args) == -1)
    5020 goto out_free_fs;
    5021 }
    5022 }
    5023
    5024 if (!f->conf.ac_attr_timeout_set)
    5025 f->conf.ac_attr_timeout = f->conf.attr_timeout;
    5026
    5027#if defined(__FreeBSD__) || defined(__NetBSD__)
    5028 /*
    5029 * In FreeBSD, we always use these settings as inode numbers
    5030 * are needed to make getcwd(3) work.
    5031 */
    5032 f->conf.readdir_ino = 1;
    5033#endif
    5034
    5035 /* not declared globally, to restrict usage of this function */
    5036 struct fuse_session *fuse_session_new_versioned(
    5037 struct fuse_args *args, const struct fuse_lowlevel_ops *op,
    5038 size_t op_size, struct libfuse_version *version,
    5039 void *userdata);
    5040 f->se = fuse_session_new_versioned(args, &llop, sizeof(llop), version,
    5041 f);
    5042 if (f->se == NULL)
    5043 goto out_free_fs;
    5044
    5045 if (f->conf.debug) {
    5046 fuse_log(FUSE_LOG_DEBUG, "nullpath_ok: %i\n", f->conf.nullpath_ok);
    5047 }
    5048
    5049 /* Trace topmost layer by default */
    5050 f->fs->debug = f->conf.debug;
    5051 f->ctr = 0;
    5052 f->generation = 0;
    5053 if (node_table_init(&f->name_table) == -1)
    5054 goto out_free_session;
    5055
    5056 if (node_table_init(&f->id_table) == -1)
    5057 goto out_free_name_table;
    5058
    5059 pthread_mutex_init(&f->lock, NULL);
    5060
    5061 root = alloc_node(f);
    5062 if (root == NULL) {
    5063 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
    5064 goto out_free_id_table;
    5065 }
    5066 if (lru_enabled(f)) {
    5067 struct node_lru *lnode = node_lru(root);
    5068 init_list_head(&lnode->lru);
    5069 }
    5070
    5071 strcpy(root->inline_name, "/");
    5072 root->name = root->inline_name;
    5073 root->parent = NULL;
    5074 root->nodeid = FUSE_ROOT_ID;
    5075 inc_nlookup(root);
    5076 hash_id(f, root);
    5077
    5078 return f;
    5079
    5080out_free_id_table:
    5081 free(f->id_table.array);
    5082out_free_name_table:
    5083 free(f->name_table.array);
    5084out_free_session:
    5085 fuse_session_destroy(f->se);
    5086out_free_fs:
    5087 free(f->fs);
    5088 free(f->conf.modules);
    5089out_delete_context_key:
    5090 fuse_delete_context_key();
    5091out_free:
    5092 free(f);
    5093out:
    5094 return NULL;
    5095}
    5096
    5097/* Emulates 3.0-style fuse_new(), which processes --help */
    5098FUSE_SYMVER("_fuse_new_30", "_fuse_new@FUSE_3.0")
    5099struct fuse *_fuse_new_30(struct fuse_args *args,
    5100 const struct fuse_operations *op,
    5101 size_t op_size,
    5102 struct libfuse_version *version,
    5103 void *user_data)
    5104{
    5105 struct fuse_config conf = {0};
    5106
    5107 const struct fuse_opt opts[] = {
    5108 FUSE_LIB_OPT("-h", show_help, 1),
    5109 FUSE_LIB_OPT("--help", show_help, 1),
    5111 };
    5112
    5113 if (fuse_opt_parse(args, &conf, opts,
    5114 fuse_lib_opt_proc) == -1)
    5115 return NULL;
    5116
    5117 if (conf.show_help) {
    5118 fuse_lib_help(args);
    5119 return NULL;
    5120 } else
    5121 return _fuse_new_31(args, op, op_size, version, user_data);
    5122}
    5123
    5124/* ABI compat version */
    5125struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
    5126 size_t op_size, void *user_data);
    5127FUSE_SYMVER("fuse_new_31", "fuse_new@FUSE_3.1")
    5128struct fuse *fuse_new_31(struct fuse_args *args,
    5129 const struct fuse_operations *op,
    5130 size_t op_size, void *user_data)
    5131{
    5132 /* unknown version */
    5133 struct libfuse_version version = { 0 };
    5134
    5135 return _fuse_new_31(args, op, op_size, &version, user_data);
    5136}
    5137
    5138/*
    5139 * ABI compat version
    5140 * Emulates 3.0-style fuse_new(), which processes --help
    5141 */
    5142struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op,
    5143 size_t op_size, void *user_data);
    5144FUSE_SYMVER("fuse_new_30", "fuse_new@FUSE_3.0")
    5145struct fuse *fuse_new_30(struct fuse_args *args,
    5146 const struct fuse_operations *op,
    5147 size_t op_size, void *user_data)
    5148{
    5149 struct fuse_config conf = {0};
    5150
    5151 const struct fuse_opt opts[] = {
    5152 FUSE_LIB_OPT("-h", show_help, 1),
    5153 FUSE_LIB_OPT("--help", show_help, 1),
    5155 };
    5156
    5157 if (fuse_opt_parse(args, &conf, opts,
    5158 fuse_lib_opt_proc) == -1)
    5159 return NULL;
    5160
    5161 if (conf.show_help) {
    5162 fuse_lib_help(args);
    5163 return NULL;
    5164 } else
    5165 return fuse_new_31(args, op, op_size, user_data);
    5166}
    5167
    5168
    5169void fuse_destroy(struct fuse *f)
    5170{
    5171 size_t i;
    5172
    5173 if (f->conf.intr && f->intr_installed)
    5174 fuse_restore_intr_signal(f->conf.intr_signal);
    5175
    5176 if (f->fs) {
    5177 fuse_create_context(f);
    5178
    5179 for (i = 0; i < f->id_table.size; i++) {
    5180 struct node *node;
    5181
    5182 for (node = f->id_table.array[i]; node != NULL;
    5183 node = node->id_next) {
    5184 if (node->is_hidden) {
    5185 char *path;
    5186 if (try_get_path(f, node->nodeid, NULL, &path, NULL, false) == 0) {
    5187 fuse_fs_unlink(f->fs, path);
    5188 free(path);
    5189 }
    5190 }
    5191 }
    5192 }
    5193 }
    5194 for (i = 0; i < f->id_table.size; i++) {
    5195 struct node *node;
    5196 struct node *next;
    5197
    5198 for (node = f->id_table.array[i]; node != NULL; node = next) {
    5199 next = node->id_next;
    5200 free_node(f, node);
    5201 f->id_table.use--;
    5202 }
    5203 }
    5204 assert(list_empty(&f->partial_slabs));
    5205 assert(list_empty(&f->full_slabs));
    5206
    5207 while (fuse_modules) {
    5208 fuse_put_module(fuse_modules);
    5209 }
    5210 free(f->id_table.array);
    5211 free(f->name_table.array);
    5212 pthread_mutex_destroy(&f->lock);
    5213 fuse_session_destroy(f->se);
    5214 free(f->fs);
    5215 free(f->conf.modules);
    5216 free(f);
    5217 fuse_delete_context_key();
    5218}
    5219
    5220int fuse_mount(struct fuse *f, const char *mountpoint) {
    5221 return fuse_session_mount(fuse_get_session(f), mountpoint);
    5222}
    5223
    5224
    5225void fuse_unmount(struct fuse *f) {
    5227}
    5228
    5230{
    5231 return FUSE_VERSION;
    5232}
    5233
    5234const char *fuse_pkgversion(void)
    5235{
    5236 return PACKAGE_VERSION;
    5237}
    int fuse_getgroups(int size, gid_t list[])
    Definition fuse.c:4679
    int fuse_mount(struct fuse *f, const char *mountpoint)
    Definition fuse.c:5220
    int fuse_interrupted(void)
    Definition fuse.c:4688
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5169
    int fuse_start_cleanup_thread(struct fuse *fuse)
    Definition fuse.c:4929
    int fuse_invalidate_path(struct fuse *f, const char *path)
    Definition fuse.c:4698
    struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
    Definition fuse.h:1386
    struct fuse_context * fuse_get_context(void)
    Definition fuse.c:4669
    int fuse_loop(struct fuse *f)
    Definition fuse.c:4602
    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:4664
    int fuse_clean_cache(struct fuse *fuse)
    Definition fuse.c:4458
    void fuse_lib_help(struct fuse_args *args)
    Definition fuse.c:4769
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4545
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5225
    struct fuse_fs * fuse_fs_new(const struct fuse_operations *op, size_t op_size, void *private_data)
    Definition fuse.c:4879
    void fuse_stop_cleanup_thread(struct fuse *fuse)
    Definition fuse.c:4937
    fuse_fill_dir_flags
    Definition fuse.h:58
    fuse_readdir_flags
    Definition fuse.h:42
    #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:5234
    int fuse_version(void)
    Definition fuse.c:5229
    @ 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:4669
    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
    uint64_t want_ext
    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:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t parallel_direct_writes
    uint32_t noflush
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2fuse__i_8h_source.html0000644000175000017500000012531315002273247023523 0ustar berndbernd libfuse: fuse-3.17.1.dir/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
    17#define MIN(a, b) \
    18({ \
    19 typeof(a) _a = (a); \
    20 typeof(b) _b = (b); \
    21 _a < _b ? _a : _b; \
    22})
    23
    24struct mount_opts;
    25
    26struct fuse_req {
    27 struct fuse_session *se;
    28 uint64_t unique;
    29 _Atomic int ref_cnt;
    30 pthread_mutex_t lock;
    31 struct fuse_ctx ctx;
    32 struct fuse_chan *ch;
    33 int interrupted;
    34 unsigned int ioctl_64bit : 1;
    35 union {
    36 struct {
    37 uint64_t unique;
    38 } i;
    39 struct {
    41 void *data;
    42 } ni;
    43 } u;
    44 struct fuse_req *next;
    45 struct fuse_req *prev;
    46};
    47
    48struct fuse_notify_req {
    49 uint64_t unique;
    50 void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
    51 const void *, const struct fuse_buf *);
    52 struct fuse_notify_req *next;
    53 struct fuse_notify_req *prev;
    54};
    55
    56struct fuse_session {
    57 char *mountpoint;
    58 volatile int exited;
    59 int fd;
    60 struct fuse_custom_io *io;
    61 struct mount_opts *mo;
    62 int debug;
    63 int deny_others;
    64 struct fuse_lowlevel_ops op;
    65 int got_init;
    66 struct cuse_data *cuse_data;
    67 void *userdata;
    68 uid_t owner;
    69 struct fuse_conn_info conn;
    70 struct fuse_req list;
    71 struct fuse_req interrupts;
    72 pthread_mutex_t lock;
    73 int got_destroy;
    74 pthread_key_t pipe_key;
    75 int broken_splice_nonblock;
    76 uint64_t notify_ctr;
    77 struct fuse_notify_req notify_list;
    78 size_t bufsize;
    79 int error;
    80
    81 /* This is useful if any kind of ABI incompatibility is found at
    82 * a later version, to 'fix' it at run time.
    83 */
    84 struct libfuse_version version;
    85 bool buf_reallocable;
    86};
    87
    88struct fuse_chan {
    89 pthread_mutex_t lock;
    90 int ctr;
    91 int fd;
    92};
    93
    102 char *name;
    103 fuse_module_factory_t factory;
    104 struct fuse_module *next;
    105 struct fusemod_so *so;
    106 int ctr;
    107};
    108
    117#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
    118struct fuse_loop_config
    119{
    120 /* verififier that a correct struct was was passed. This is especially
    121 * needed, as versions below (3, 12) were using a public struct
    122 * (now called fuse_loop_config_v1), which was hard to extend with
    123 * additional parameters, without risking that file system implementations
    124 * would not have noticed and might either pass uninitialized members
    125 * or even too small structs.
    126 * fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
    127 * or 1. v2 or even higher version just need to set a value here
    128 * which not conflicting and very unlikely as having been set by
    129 * file system implementation.
    130 */
    131 int version_id;
    132
    137 int clone_fd;
    150
    156 unsigned int max_threads;
    157};
    158#endif
    159
    160/* ----------------------------------------------------------- *
    161 * Channel interface (when using -o clone_fd) *
    162 * ----------------------------------------------------------- */
    163
    170struct fuse_chan *fuse_chan_get(struct fuse_chan *ch);
    171
    177void fuse_chan_put(struct fuse_chan *ch);
    178
    179struct mount_opts *parse_mount_opts(struct fuse_args *args);
    180void destroy_mount_opts(struct mount_opts *mo);
    181void fuse_mount_version(void);
    182unsigned get_max_read(struct mount_opts *o);
    183void fuse_kern_unmount(const char *mountpoint, int fd);
    184int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo);
    185
    186int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
    187 int count);
    188void fuse_free_req(fuse_req_t req);
    189
    190void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
    191
    192int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
    193
    194void fuse_buf_free(struct fuse_buf *buf);
    195
    196int fuse_session_receive_buf_internal(struct fuse_session *se,
    197 struct fuse_buf *buf,
    198 struct fuse_chan *ch);
    199void fuse_session_process_buf_internal(struct fuse_session *se,
    200 const struct fuse_buf *buf,
    201 struct fuse_chan *ch);
    202
    203struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
    204 size_t op_size, void *private_data);
    205int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
    206int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
    207
    213int fuse_loop_cfg_verify(struct fuse_loop_config *config);
    214
    215
    216/*
    217 * This can be changed dynamically on recent kernels through the
    218 * /proc/sys/fs/fuse/max_pages_limit interface.
    219 *
    220 * Older kernels will always use the default value.
    221 */
    222#define FUSE_DEFAULT_MAX_PAGES_LIMIT 256
    223#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
    224
    225/* room needed in buffer to accommodate header */
    226#define FUSE_BUFFER_HEADER_SIZE 0x1000
    227
    231static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn,
    232 uint64_t want_ext_default,
    233 uint32_t want_default)
    234{
    235 /*
    236 * Convert want to want_ext if necessary.
    237 * For the high level interface this function might be called
    238 * twice, once from the high level interface and once from the
    239 * low level interface. Both, with different want_ext_default and
    240 * want_default values. In order to suppress a failure for the
    241 * second call, we check if the lower 32 bits of want_ext are
    242 * already set to the value of want.
    243 */
    244 if (conn->want != want_default &&
    245 fuse_lower_32_bits(conn->want_ext) != conn->want) {
    246 if (conn->want_ext != want_ext_default) {
    247 fuse_log(FUSE_LOG_ERR,
    248 "fuse: both 'want' and 'want_ext' are set\n");
    249 return -EINVAL;
    250 }
    251
    252 /* high bits from want_ext, low bits from want */
    253 conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
    254 conn->want;
    255 }
    256
    257 return 0;
    258}
    struct fuse_fs *(* fuse_module_factory_t)(struct fuse_args *args, struct fuse_fs *fs[])
    Definition fuse.h:1386
    void fuse_log(enum fuse_log_level level, const char *fmt,...)
    Definition fuse_log.c:77
    void(* fuse_interrupt_func_t)(fuse_req_t req, void *data)
    struct fuse_req * fuse_req_t
    uint64_t fuse_ino_t
    uint64_t want_ext
    unsigned int max_threads
    Definition fuse_i.h:156
    int max_idle_threads
    Definition fuse_i.h:149
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2lib_2fuse__log_8c_source.html0000644000175000017500000004720615002273247024053 0ustar berndbernd libfuse: fuse-3.17.1.dir/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}
    76
    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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2fuse__loop_8c_source.html0000644000175000017500000002701715002273247024241 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2fuse__loop__mt_8c_source.html0000644000175000017500000024324715002273247025105 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 pthread_setname_np(pthread_self(), "fuse_worker");
    136
    137 while (!fuse_session_exited(mt->se)) {
    138 int isforget = 0;
    139 int res;
    140
    141 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    142 res = fuse_session_receive_buf_internal(mt->se, &w->fbuf,
    143 w->ch);
    144 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    145 if (res == -EINTR)
    146 continue;
    147 if (res <= 0) {
    148 if (res < 0) {
    149 fuse_session_exit(mt->se);
    150 mt->error = res;
    151 }
    152 break;
    153 }
    154
    155 pthread_mutex_lock(&mt->lock);
    156 if (mt->exit) {
    157 pthread_mutex_unlock(&mt->lock);
    158 return NULL;
    159 }
    160
    161 /*
    162 * This disgusting hack is needed so that zillions of threads
    163 * are not created on a burst of FORGET messages
    164 */
    165 if (!(w->fbuf.flags & FUSE_BUF_IS_FD)) {
    166 struct fuse_in_header *in = w->fbuf.mem;
    167
    168 if (in->opcode == FUSE_FORGET ||
    169 in->opcode == FUSE_BATCH_FORGET)
    170 isforget = 1;
    171 }
    172
    173 if (!isforget)
    174 mt->numavail--;
    175 if (mt->numavail == 0 && mt->numworker < mt->max_threads)
    176 fuse_loop_start_thread(mt);
    177 pthread_mutex_unlock(&mt->lock);
    178
    179 fuse_session_process_buf_internal(mt->se, &w->fbuf, w->ch);
    180
    181 pthread_mutex_lock(&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 (mt->exit) {
    193 pthread_mutex_unlock(&mt->lock);
    194 return NULL;
    195 }
    196 list_del_worker(w);
    197 mt->numavail--;
    198 mt->numworker--;
    199 pthread_mutex_unlock(&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(&mt->lock);
    208 }
    209
    210 sem_post(&mt->finish);
    211
    212 return NULL;
    213}
    214
    215int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
    216{
    217 sigset_t oldset;
    218 sigset_t newset;
    219 int res;
    220 pthread_attr_t attr;
    221 char *stack_size;
    222
    223 /* Override default stack size
    224 * XXX: This should ideally be a parameter option. It is rather
    225 * well hidden here.
    226 */
    227 pthread_attr_init(&attr);
    228 stack_size = getenv(ENVNAME_THREAD_STACK);
    229 if (stack_size) {
    230 long size;
    231
    232 res = libfuse_strtol(stack_size, &size);
    233 if (res)
    234 fuse_log(FUSE_LOG_ERR, "fuse: invalid stack size: %s\n",
    235 stack_size);
    236 else if (pthread_attr_setstacksize(&attr, size))
    237 fuse_log(FUSE_LOG_ERR, "fuse: could not set stack size: %ld\n",
    238 size);
    239 }
    240
    241 /* Disallow signal reception in worker threads */
    242 sigemptyset(&newset);
    243 sigaddset(&newset, SIGTERM);
    244 sigaddset(&newset, SIGINT);
    245 sigaddset(&newset, SIGHUP);
    246 sigaddset(&newset, SIGQUIT);
    247 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
    248 res = pthread_create(thread_id, &attr, func, arg);
    249 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
    250 pthread_attr_destroy(&attr);
    251 if (res != 0) {
    252 fuse_log(FUSE_LOG_ERR, "fuse: error creating thread: %s\n",
    253 strerror(res));
    254 return -1;
    255 }
    256
    257 return 0;
    258}
    259
    260static int fuse_clone_chan_fd_default(struct fuse_session *se)
    261{
    262 int res;
    263 int clonefd;
    264 uint32_t masterfd;
    265 const char *devname = "/dev/fuse";
    266
    267#ifndef O_CLOEXEC
    268#define O_CLOEXEC 0
    269#endif
    270 clonefd = open(devname, O_RDWR | O_CLOEXEC);
    271 if (clonefd == -1) {
    272 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname,
    273 strerror(errno));
    274 return -1;
    275 }
    276#ifndef O_CLOEXEC
    277 fcntl(clonefd, F_SETFD, FD_CLOEXEC);
    278#endif
    279
    280 masterfd = se->fd;
    281 res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
    282 if (res == -1) {
    283 fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
    284 strerror(errno));
    285 close(clonefd);
    286 return -1;
    287 }
    288 return clonefd;
    289}
    290
    291static struct fuse_chan *fuse_clone_chan(struct fuse_mt *mt)
    292{
    293 int clonefd;
    294 struct fuse_session *se = mt->se;
    295 struct fuse_chan *newch;
    296
    297 if (se->io != NULL) {
    298 if (se->io->clone_fd != NULL)
    299 clonefd = se->io->clone_fd(se->fd);
    300 else
    301 return NULL;
    302 } else {
    303 clonefd = fuse_clone_chan_fd_default(se);
    304 }
    305 if (clonefd < 0)
    306 return NULL;
    307
    308 newch = fuse_chan_new(clonefd);
    309 if (newch == NULL)
    310 close(clonefd);
    311
    312 return newch;
    313}
    314
    315static int fuse_loop_start_thread(struct fuse_mt *mt)
    316{
    317 int res;
    318
    319 struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
    320 if (!w) {
    321 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
    322 return -1;
    323 }
    324 memset(w, 0, sizeof(struct fuse_worker));
    325 w->fbuf.mem = NULL;
    326 w->mt = mt;
    327
    328 w->ch = NULL;
    329 if (mt->clone_fd) {
    330 w->ch = fuse_clone_chan(mt);
    331 if(!w->ch) {
    332 /* Don't attempt this again */
    333 fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
    334 "without -o clone_fd.\n");
    335 mt->clone_fd = 0;
    336 }
    337 }
    338
    339 res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
    340 if (res == -1) {
    341 fuse_chan_put(w->ch);
    342 free(w);
    343 return -1;
    344 }
    345 list_add_worker(w, &mt->main);
    346 mt->numavail ++;
    347 mt->numworker ++;
    348
    349 return 0;
    350}
    351
    352static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
    353{
    354 pthread_join(w->thread_id, NULL);
    355 pthread_mutex_lock(&mt->lock);
    356 list_del_worker(w);
    357 pthread_mutex_unlock(&mt->lock);
    358 fuse_buf_free(&w->fbuf);
    359 fuse_chan_put(w->ch);
    360 free(w);
    361}
    362
    363int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
    364FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
    365int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
    366{
    367int err;
    368 struct fuse_mt mt;
    369 struct fuse_worker *w;
    370 int created_config = 0;
    371
    372 if (config) {
    373 err = fuse_loop_cfg_verify(config);
    374 if (err)
    375 return err;
    376 } else {
    377 /* The caller does not care about parameters - use the default */
    378 config = fuse_loop_cfg_create();
    379 created_config = 1;
    380 }
    381
    382
    383 memset(&mt, 0, sizeof(struct fuse_mt));
    384 mt.se = se;
    385 mt.clone_fd = config->clone_fd;
    386 mt.error = 0;
    387 mt.numworker = 0;
    388 mt.numavail = 0;
    389 mt.max_idle = config->max_idle_threads;
    390 mt.max_threads = config->max_threads;
    391 mt.main.thread_id = pthread_self();
    392 mt.main.prev = mt.main.next = &mt.main;
    393 sem_init(&mt.finish, 0, 0);
    394 pthread_mutex_init(&mt.lock, NULL);
    395
    396 pthread_mutex_lock(&mt.lock);
    397 err = fuse_loop_start_thread(&mt);
    398 pthread_mutex_unlock(&mt.lock);
    399 if (!err) {
    400 /* sem_wait() is interruptible */
    401 while (!fuse_session_exited(se))
    402 sem_wait(&mt.finish);
    403
    404 pthread_mutex_lock(&mt.lock);
    405 for (w = mt.main.next; w != &mt.main; w = w->next)
    406 pthread_cancel(w->thread_id);
    407 mt.exit = 1;
    408 pthread_mutex_unlock(&mt.lock);
    409
    410 while (mt.main.next != &mt.main)
    411 fuse_join_worker(&mt, mt.main.next);
    412
    413 err = mt.error;
    414 }
    415
    416 pthread_mutex_destroy(&mt.lock);
    417 sem_destroy(&mt.finish);
    418 if(se->error != 0)
    419 err = se->error;
    421
    422 if (created_config) {
    423 fuse_loop_cfg_destroy(config);
    424 config = NULL;
    425 }
    426
    427 return err;
    428}
    429
    430int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
    431FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
    432int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
    433{
    434 int err;
    435 struct fuse_loop_config *config = NULL;
    436
    437 if (config_v1 != NULL) {
    438 /* convert the given v1 config */
    439 config = fuse_loop_cfg_create();
    440 if (config == NULL)
    441 return ENOMEM;
    442
    443 fuse_loop_cfg_convert(config, config_v1);
    444 }
    445
    446 err = fuse_session_loop_mt_312(se, config);
    447
    448 fuse_loop_cfg_destroy(config);
    449
    450 return err;
    451}
    452
    453
    454int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
    455FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
    456int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
    457{
    458 int err;
    459 struct fuse_loop_config *config = fuse_loop_cfg_create();
    460 if (clone_fd > 0)
    461 fuse_loop_cfg_set_clone_fd(config, clone_fd);
    462 err = fuse_session_loop_mt_312(se, config);
    463
    464 fuse_loop_cfg_destroy(config);
    465
    466 return err;
    467}
    468
    469struct fuse_loop_config *fuse_loop_cfg_create(void)
    470{
    471 struct fuse_loop_config *config = calloc(1, sizeof(*config));
    472 if (config == NULL)
    473 return NULL;
    474
    475 config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
    476 config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
    477 config->max_threads = FUSE_LOOP_MT_DEF_MAX_THREADS;
    478 config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
    479
    480 return config;
    481}
    482
    483void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
    484{
    485 free(config);
    486}
    487
    488int fuse_loop_cfg_verify(struct fuse_loop_config *config)
    489{
    490 if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
    491 return -EINVAL;
    492
    493 return 0;
    494}
    495
    496void fuse_loop_cfg_convert(struct fuse_loop_config *config,
    497 struct fuse_loop_config_v1 *v1_conf)
    498{
    499 fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
    500
    501 fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
    502}
    503
    504void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
    505 unsigned int value)
    506{
    507 if (value > FUSE_LOOP_MT_MAX_THREADS) {
    508 if (value != UINT_MAX)
    509 fuse_log(FUSE_LOG_ERR,
    510 "Ignoring invalid max threads value "
    511 "%u > max (%u).\n", value,
    512 FUSE_LOOP_MT_MAX_THREADS);
    513 return;
    514 }
    515 config->max_idle_threads = value;
    516}
    517
    518void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
    519 unsigned int value)
    520{
    521 config->max_threads = value;
    522}
    523
    524void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
    525 unsigned int value)
    526{
    527 config->clone_fd = value;
    528}
    529
    @ 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:156
    unsigned int max_idle_threads
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2lib_2fuse__lowlevel_8c_source.html0000644000175000017500000240572415002273247025130 0ustar berndbernd libfuse: fuse-3.17.1.dir/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
    47#define container_of(ptr, type, member) ({ \
    48 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    49 (type *)( (char *)__mptr - offsetof(type,member) );})
    50
    51struct fuse_pollhandle {
    52 uint64_t kh;
    53 struct fuse_session *se;
    54};
    55
    56static size_t pagesize;
    57
    58static __attribute__((constructor)) void fuse_ll_init_pagesize(void)
    59{
    60 pagesize = getpagesize();
    61}
    62
    63static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr)
    64{
    65 attr->ino = stbuf->st_ino;
    66 attr->mode = stbuf->st_mode;
    67 attr->nlink = stbuf->st_nlink;
    68 attr->uid = stbuf->st_uid;
    69 attr->gid = stbuf->st_gid;
    70 attr->rdev = stbuf->st_rdev;
    71 attr->size = stbuf->st_size;
    72 attr->blksize = stbuf->st_blksize;
    73 attr->blocks = stbuf->st_blocks;
    74 attr->atime = stbuf->st_atime;
    75 attr->mtime = stbuf->st_mtime;
    76 attr->ctime = stbuf->st_ctime;
    77 attr->atimensec = ST_ATIM_NSEC(stbuf);
    78 attr->mtimensec = ST_MTIM_NSEC(stbuf);
    79 attr->ctimensec = ST_CTIM_NSEC(stbuf);
    80}
    81
    82static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf)
    83{
    84 stbuf->st_mode = attr->mode;
    85 stbuf->st_uid = attr->uid;
    86 stbuf->st_gid = attr->gid;
    87 stbuf->st_size = attr->size;
    88 stbuf->st_atime = attr->atime;
    89 stbuf->st_mtime = attr->mtime;
    90 stbuf->st_ctime = attr->ctime;
    91 ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
    92 ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
    93 ST_CTIM_NSEC_SET(stbuf, attr->ctimensec);
    94}
    95
    96static size_t iov_length(const struct iovec *iov, size_t count)
    97{
    98 size_t seg;
    99 size_t ret = 0;
    100
    101 for (seg = 0; seg < count; seg++)
    102 ret += iov[seg].iov_len;
    103 return ret;
    104}
    105
    106static void list_init_req(struct fuse_req *req)
    107{
    108 req->next = req;
    109 req->prev = req;
    110}
    111
    112static void list_del_req(struct fuse_req *req)
    113{
    114 struct fuse_req *prev = req->prev;
    115 struct fuse_req *next = req->next;
    116 prev->next = next;
    117 next->prev = prev;
    118}
    119
    120static void list_add_req(struct fuse_req *req, struct fuse_req *next)
    121{
    122 struct fuse_req *prev = next->prev;
    123 req->next = next;
    124 req->prev = prev;
    125 prev->next = req;
    126 next->prev = req;
    127}
    128
    129static void destroy_req(fuse_req_t req)
    130{
    131 assert(req->ch == NULL);
    132 pthread_mutex_destroy(&req->lock);
    133 free(req);
    134}
    135
    136void fuse_free_req(fuse_req_t req)
    137{
    138 int ctr;
    139 struct fuse_session *se = req->se;
    140
    141 if (se->conn.no_interrupt) {
    142 ctr = --req->ref_cnt;
    143 fuse_chan_put(req->ch);
    144 req->ch = NULL;
    145 } else {
    146 pthread_mutex_lock(&se->lock);
    147 req->u.ni.func = NULL;
    148 req->u.ni.data = NULL;
    149 list_del_req(req);
    150 ctr = --req->ref_cnt;
    151 fuse_chan_put(req->ch);
    152 req->ch = NULL;
    153 pthread_mutex_unlock(&se->lock);
    154 }
    155 if (!ctr)
    156 destroy_req(req);
    157}
    158
    159static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se)
    160{
    161 struct fuse_req *req;
    162
    163 req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
    164 if (req == NULL) {
    165 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
    166 } else {
    167 req->se = se;
    168 req->ref_cnt = 1;
    169 list_init_req(req);
    170 pthread_mutex_init(&req->lock, NULL);
    171 }
    172
    173 return req;
    174}
    175
    176/* Send data. If *ch* is NULL, send via session master fd */
    177static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
    178 struct iovec *iov, int count)
    179{
    180 struct fuse_out_header *out = iov[0].iov_base;
    181
    182 assert(se != NULL);
    183 out->len = iov_length(iov, count);
    184 if (se->debug) {
    185 if (out->unique == 0) {
    186 fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
    187 out->error, out->len);
    188 } else if (out->error) {
    189 fuse_log(FUSE_LOG_DEBUG,
    190 " unique: %llu, error: %i (%s), outsize: %i\n",
    191 (unsigned long long) out->unique, out->error,
    192 strerror(-out->error), out->len);
    193 } else {
    194 fuse_log(FUSE_LOG_DEBUG,
    195 " unique: %llu, success, outsize: %i\n",
    196 (unsigned long long) out->unique, out->len);
    197 }
    198 }
    199
    200 ssize_t res;
    201 if (se->io != NULL)
    202 /* se->io->writev is never NULL if se->io is not NULL as
    203 specified by fuse_session_custom_io()*/
    204 res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
    205 se->userdata);
    206 else
    207 res = writev(ch ? ch->fd : se->fd, iov, count);
    208
    209 int err = errno;
    210
    211 if (res == -1) {
    212 /* ENOENT means the operation was interrupted */
    213 if (!fuse_session_exited(se) && err != ENOENT)
    214 perror("fuse: writing device");
    215 return -err;
    216 }
    217
    218 return 0;
    219}
    220
    221
    222int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
    223 int count)
    224{
    225 struct fuse_out_header out;
    226
    227#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
    228 const char *str = strerrordesc_np(error * -1);
    229 if ((str == NULL && error != 0) || error > 0) {
    230#else
    231 if (error <= -1000 || error > 0) {
    232#endif
    233 fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
    234 error = -ERANGE;
    235 }
    236
    237 out.unique = req->unique;
    238 out.error = error;
    239
    240 iov[0].iov_base = &out;
    241 iov[0].iov_len = sizeof(struct fuse_out_header);
    242
    243 return fuse_send_msg(req->se, req->ch, iov, count);
    244}
    245
    246static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
    247 int count)
    248{
    249 int res;
    250
    251 res = fuse_send_reply_iov_nofree(req, error, iov, count);
    252 fuse_free_req(req);
    253 return res;
    254}
    255
    256static int send_reply(fuse_req_t req, int error, const void *arg,
    257 size_t argsize)
    258{
    259 struct iovec iov[2];
    260 int count = 1;
    261 if (argsize) {
    262 iov[1].iov_base = (void *) arg;
    263 iov[1].iov_len = argsize;
    264 count++;
    265 }
    266 return send_reply_iov(req, error, iov, count);
    267}
    268
    269int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count)
    270{
    271 int res;
    272 struct iovec *padded_iov;
    273
    274 padded_iov = malloc((count + 1) * sizeof(struct iovec));
    275 if (padded_iov == NULL)
    276 return fuse_reply_err(req, ENOMEM);
    277
    278 memcpy(padded_iov + 1, iov, count * sizeof(struct iovec));
    279 count++;
    280
    281 res = send_reply_iov(req, 0, padded_iov, count);
    282 free(padded_iov);
    283
    284 return res;
    285}
    286
    287
    288/* `buf` is allowed to be empty so that the proper size may be
    289 allocated by the caller */
    290size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
    291 const char *name, const struct stat *stbuf, off_t off)
    292{
    293 (void)req;
    294 size_t namelen;
    295 size_t entlen;
    296 size_t entlen_padded;
    297 struct fuse_dirent *dirent;
    298
    299 namelen = strlen(name);
    300 entlen = FUSE_NAME_OFFSET + namelen;
    301 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
    302
    303 if ((buf == NULL) || (entlen_padded > bufsize))
    304 return entlen_padded;
    305
    306 dirent = (struct fuse_dirent*) buf;
    307 dirent->ino = stbuf->st_ino;
    308 dirent->off = off;
    309 dirent->namelen = namelen;
    310 dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
    311 memcpy(dirent->name, name, namelen);
    312 memset(dirent->name + namelen, 0, entlen_padded - entlen);
    313
    314 return entlen_padded;
    315}
    316
    317static void convert_statfs(const struct statvfs *stbuf,
    318 struct fuse_kstatfs *kstatfs)
    319{
    320 kstatfs->bsize = stbuf->f_bsize;
    321 kstatfs->frsize = stbuf->f_frsize;
    322 kstatfs->blocks = stbuf->f_blocks;
    323 kstatfs->bfree = stbuf->f_bfree;
    324 kstatfs->bavail = stbuf->f_bavail;
    325 kstatfs->files = stbuf->f_files;
    326 kstatfs->ffree = stbuf->f_ffree;
    327 kstatfs->namelen = stbuf->f_namemax;
    328}
    329
    330static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize)
    331{
    332 return send_reply(req, 0, arg, argsize);
    333}
    334
    335int fuse_reply_err(fuse_req_t req, int err)
    336{
    337 return send_reply(req, -err, NULL, 0);
    338}
    339
    341{
    342 fuse_free_req(req);
    343}
    344
    345static unsigned long calc_timeout_sec(double t)
    346{
    347 if (t > (double) ULONG_MAX)
    348 return ULONG_MAX;
    349 else if (t < 0.0)
    350 return 0;
    351 else
    352 return (unsigned long) t;
    353}
    354
    355static unsigned int calc_timeout_nsec(double t)
    356{
    357 double f = t - (double) calc_timeout_sec(t);
    358 if (f < 0.0)
    359 return 0;
    360 else if (f >= 0.999999999)
    361 return 999999999;
    362 else
    363 return (unsigned int) (f * 1.0e9);
    364}
    365
    366static void fill_entry(struct fuse_entry_out *arg,
    367 const struct fuse_entry_param *e)
    368{
    369 arg->nodeid = e->ino;
    370 arg->generation = e->generation;
    371 arg->entry_valid = calc_timeout_sec(e->entry_timeout);
    372 arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
    373 arg->attr_valid = calc_timeout_sec(e->attr_timeout);
    374 arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
    375 convert_stat(&e->attr, &arg->attr);
    376}
    377
    378/* `buf` is allowed to be empty so that the proper size may be
    379 allocated by the caller */
    380size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
    381 const char *name,
    382 const struct fuse_entry_param *e, off_t off)
    383{
    384 (void)req;
    385 size_t namelen;
    386 size_t entlen;
    387 size_t entlen_padded;
    388
    389 namelen = strlen(name);
    390 entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen;
    391 entlen_padded = FUSE_DIRENT_ALIGN(entlen);
    392 if ((buf == NULL) || (entlen_padded > bufsize))
    393 return entlen_padded;
    394
    395 struct fuse_direntplus *dp = (struct fuse_direntplus *) buf;
    396 memset(&dp->entry_out, 0, sizeof(dp->entry_out));
    397 fill_entry(&dp->entry_out, e);
    398
    399 struct fuse_dirent *dirent = &dp->dirent;
    400 dirent->ino = e->attr.st_ino;
    401 dirent->off = off;
    402 dirent->namelen = namelen;
    403 dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
    404 memcpy(dirent->name, name, namelen);
    405 memset(dirent->name + namelen, 0, entlen_padded - entlen);
    406
    407 return entlen_padded;
    408}
    409
    410static void fill_open(struct fuse_open_out *arg,
    411 const struct fuse_file_info *f)
    412{
    413 arg->fh = f->fh;
    414 if (f->backing_id > 0) {
    415 arg->backing_id = f->backing_id;
    416 arg->open_flags |= FOPEN_PASSTHROUGH;
    417 }
    418 if (f->direct_io)
    419 arg->open_flags |= FOPEN_DIRECT_IO;
    420 if (f->keep_cache)
    421 arg->open_flags |= FOPEN_KEEP_CACHE;
    422 if (f->cache_readdir)
    423 arg->open_flags |= FOPEN_CACHE_DIR;
    424 if (f->nonseekable)
    425 arg->open_flags |= FOPEN_NONSEEKABLE;
    426 if (f->noflush)
    427 arg->open_flags |= FOPEN_NOFLUSH;
    429 arg->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
    430}
    431
    433{
    434 struct fuse_entry_out arg;
    435 size_t size = req->se->conn.proto_minor < 9 ?
    436 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
    437
    438 /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
    439 negative entry */
    440 if (!e->ino && req->se->conn.proto_minor < 4)
    441 return fuse_reply_err(req, ENOENT);
    442
    443 memset(&arg, 0, sizeof(arg));
    444 fill_entry(&arg, e);
    445 return send_reply_ok(req, &arg, size);
    446}
    447
    449 const struct fuse_file_info *f)
    450{
    451 alignas(uint64_t) char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
    452 size_t entrysize = req->se->conn.proto_minor < 9 ?
    453 FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
    454 struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
    455 struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
    456
    457 memset(buf, 0, sizeof(buf));
    458 fill_entry(earg, e);
    459 fill_open(oarg, f);
    460 return send_reply_ok(req, buf,
    461 entrysize + sizeof(struct fuse_open_out));
    462}
    463
    464int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
    465 double attr_timeout)
    466{
    467 struct fuse_attr_out arg;
    468 size_t size = req->se->conn.proto_minor < 9 ?
    469 FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
    470
    471 memset(&arg, 0, sizeof(arg));
    472 arg.attr_valid = calc_timeout_sec(attr_timeout);
    473 arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
    474 convert_stat(attr, &arg.attr);
    475
    476 return send_reply_ok(req, &arg, size);
    477}
    478
    479int fuse_reply_readlink(fuse_req_t req, const char *linkname)
    480{
    481 return send_reply_ok(req, linkname, strlen(linkname));
    482}
    483
    485{
    486 struct fuse_backing_map map = { .fd = fd };
    487 int ret;
    488
    489 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map);
    490 if (ret <= 0) {
    491 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_open: %s\n", strerror(errno));
    492 return 0;
    493 }
    494
    495 return ret;
    496}
    497
    498int fuse_passthrough_close(fuse_req_t req, int backing_id)
    499{
    500 int ret;
    501
    502 ret = ioctl(req->se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id);
    503 if (ret < 0)
    504 fuse_log(FUSE_LOG_ERR, "fuse: passthrough_close: %s\n", strerror(errno));
    505
    506 return ret;
    507}
    508
    510{
    511 struct fuse_open_out arg;
    512
    513 memset(&arg, 0, sizeof(arg));
    514 fill_open(&arg, f);
    515 return send_reply_ok(req, &arg, sizeof(arg));
    516}
    517
    518int fuse_reply_write(fuse_req_t req, size_t count)
    519{
    520 struct fuse_write_out arg;
    521
    522 memset(&arg, 0, sizeof(arg));
    523 arg.size = count;
    524
    525 return send_reply_ok(req, &arg, sizeof(arg));
    526}
    527
    528int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
    529{
    530 return send_reply_ok(req, buf, size);
    531}
    532
    533static int fuse_send_data_iov_fallback(struct fuse_session *se,
    534 struct fuse_chan *ch,
    535 struct iovec *iov, int iov_count,
    536 struct fuse_bufvec *buf,
    537 size_t len)
    538{
    539 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
    540 void *mbuf;
    541 int res;
    542
    543 /* Optimize common case */
    544 if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
    545 !(buf->buf[0].flags & FUSE_BUF_IS_FD)) {
    546 /* FIXME: also avoid memory copy if there are multiple buffers
    547 but none of them contain an fd */
    548
    549 iov[iov_count].iov_base = buf->buf[0].mem;
    550 iov[iov_count].iov_len = len;
    551 iov_count++;
    552 return fuse_send_msg(se, ch, iov, iov_count);
    553 }
    554
    555 res = posix_memalign(&mbuf, pagesize, len);
    556 if (res != 0)
    557 return res;
    558
    559 mem_buf.buf[0].mem = mbuf;
    560 res = fuse_buf_copy(&mem_buf, buf, 0);
    561 if (res < 0) {
    562 free(mbuf);
    563 return -res;
    564 }
    565 len = res;
    566
    567 iov[iov_count].iov_base = mbuf;
    568 iov[iov_count].iov_len = len;
    569 iov_count++;
    570 res = fuse_send_msg(se, ch, iov, iov_count);
    571 free(mbuf);
    572
    573 return res;
    574}
    575
    576struct fuse_ll_pipe {
    577 size_t size;
    578 int can_grow;
    579 int pipe[2];
    580};
    581
    582static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
    583{
    584 close(llp->pipe[0]);
    585 close(llp->pipe[1]);
    586 free(llp);
    587}
    588
    589#ifdef HAVE_SPLICE
    590#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC)
    591static int fuse_pipe(int fds[2])
    592{
    593 int rv = pipe(fds);
    594
    595 if (rv == -1)
    596 return rv;
    597
    598 if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 ||
    599 fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 ||
    600 fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
    601 fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
    602 close(fds[0]);
    603 close(fds[1]);
    604 rv = -1;
    605 }
    606 return rv;
    607}
    608#else
    609static int fuse_pipe(int fds[2])
    610{
    611 return pipe2(fds, O_CLOEXEC | O_NONBLOCK);
    612}
    613#endif
    614
    615static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se)
    616{
    617 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
    618 if (llp == NULL) {
    619 int res;
    620
    621 llp = malloc(sizeof(struct fuse_ll_pipe));
    622 if (llp == NULL)
    623 return NULL;
    624
    625 res = fuse_pipe(llp->pipe);
    626 if (res == -1) {
    627 free(llp);
    628 return NULL;
    629 }
    630
    631 /*
    632 *the default size is 16 pages on linux
    633 */
    634 llp->size = pagesize * 16;
    635 llp->can_grow = 1;
    636
    637 pthread_setspecific(se->pipe_key, llp);
    638 }
    639
    640 return llp;
    641}
    642#endif
    643
    644static void fuse_ll_clear_pipe(struct fuse_session *se)
    645{
    646 struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);
    647 if (llp) {
    648 pthread_setspecific(se->pipe_key, NULL);
    649 fuse_ll_pipe_free(llp);
    650 }
    651}
    652
    653#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
    654static int read_back(int fd, char *buf, size_t len)
    655{
    656 int res;
    657
    658 res = read(fd, buf, len);
    659 if (res == -1) {
    660 fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
    661 return -EIO;
    662 }
    663 if (res != len) {
    664 fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
    665 return -EIO;
    666 }
    667 return 0;
    668}
    669
    670static int grow_pipe_to_max(int pipefd)
    671{
    672 int res;
    673 long max;
    674 long maxfd;
    675 char buf[32];
    676
    677 maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY);
    678 if (maxfd < 0)
    679 return -errno;
    680
    681 res = read(maxfd, buf, sizeof(buf) - 1);
    682 if (res < 0) {
    683 int saved_errno;
    684
    685 saved_errno = errno;
    686 close(maxfd);
    687 return -saved_errno;
    688 }
    689 close(maxfd);
    690 buf[res] = '\0';
    691
    692 res = libfuse_strtol(buf, &max);
    693 if (res)
    694 return res;
    695 res = fcntl(pipefd, F_SETPIPE_SZ, max);
    696 if (res < 0)
    697 return -errno;
    698 return max;
    699}
    700
    701static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
    702 struct iovec *iov, int iov_count,
    703 struct fuse_bufvec *buf, unsigned int flags)
    704{
    705 int res;
    706 size_t len = fuse_buf_size(buf);
    707 struct fuse_out_header *out = iov[0].iov_base;
    708 struct fuse_ll_pipe *llp;
    709 int splice_flags;
    710 size_t pipesize;
    711 size_t total_buf_size;
    712 size_t idx;
    713 size_t headerlen;
    714 struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
    715
    716 if (se->broken_splice_nonblock)
    717 goto fallback;
    718
    719 if (flags & FUSE_BUF_NO_SPLICE)
    720 goto fallback;
    721
    722 total_buf_size = 0;
    723 for (idx = buf->idx; idx < buf->count; idx++) {
    724 total_buf_size += buf->buf[idx].size;
    725 if (idx == buf->idx)
    726 total_buf_size -= buf->off;
    727 }
    728 if (total_buf_size < 2 * pagesize)
    729 goto fallback;
    730
    731 if (se->conn.proto_minor < 14 ||
    732 !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE))
    733 goto fallback;
    734
    735 llp = fuse_ll_get_pipe(se);
    736 if (llp == NULL)
    737 goto fallback;
    738
    739
    740 headerlen = iov_length(iov, iov_count);
    741
    742 out->len = headerlen + len;
    743
    744 /*
    745 * Heuristic for the required pipe size, does not work if the
    746 * source contains less than page size fragments
    747 */
    748 pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
    749
    750 if (llp->size < pipesize) {
    751 if (llp->can_grow) {
    752 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
    753 if (res == -1) {
    754 res = grow_pipe_to_max(llp->pipe[0]);
    755 if (res > 0)
    756 llp->size = res;
    757 llp->can_grow = 0;
    758 goto fallback;
    759 }
    760 llp->size = res;
    761 }
    762 if (llp->size < pipesize)
    763 goto fallback;
    764 }
    765
    766
    767 res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
    768 if (res == -1)
    769 goto fallback;
    770
    771 if (res != headerlen) {
    772 res = -EIO;
    773 fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
    774 headerlen);
    775 goto clear_pipe;
    776 }
    777
    778 pipe_buf.buf[0].flags = FUSE_BUF_IS_FD;
    779 pipe_buf.buf[0].fd = llp->pipe[1];
    780
    781 res = fuse_buf_copy(&pipe_buf, buf,
    783 if (res < 0) {
    784 if (res == -EAGAIN || res == -EINVAL) {
    785 /*
    786 * Should only get EAGAIN on kernels with
    787 * broken SPLICE_F_NONBLOCK support (<=
    788 * 2.6.35) where this error or a short read is
    789 * returned even if the pipe itself is not
    790 * full
    791 *
    792 * EINVAL might mean that splice can't handle
    793 * this combination of input and output.
    794 */
    795 if (res == -EAGAIN)
    796 se->broken_splice_nonblock = 1;
    797
    798 pthread_setspecific(se->pipe_key, NULL);
    799 fuse_ll_pipe_free(llp);
    800 goto fallback;
    801 }
    802 res = -res;
    803 goto clear_pipe;
    804 }
    805
    806 if (res != 0 && res < len) {
    807 struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
    808 void *mbuf;
    809 size_t now_len = res;
    810 /*
    811 * For regular files a short count is either
    812 * 1) due to EOF, or
    813 * 2) because of broken SPLICE_F_NONBLOCK (see above)
    814 *
    815 * For other inputs it's possible that we overflowed
    816 * the pipe because of small buffer fragments.
    817 */
    818
    819 res = posix_memalign(&mbuf, pagesize, len);
    820 if (res != 0)
    821 goto clear_pipe;
    822
    823 mem_buf.buf[0].mem = mbuf;
    824 mem_buf.off = now_len;
    825 res = fuse_buf_copy(&mem_buf, buf, 0);
    826 if (res > 0) {
    827 char *tmpbuf;
    828 size_t extra_len = res;
    829 /*
    830 * Trickiest case: got more data. Need to get
    831 * back the data from the pipe and then fall
    832 * back to regular write.
    833 */
    834 tmpbuf = malloc(headerlen);
    835 if (tmpbuf == NULL) {
    836 free(mbuf);
    837 res = ENOMEM;
    838 goto clear_pipe;
    839 }
    840 res = read_back(llp->pipe[0], tmpbuf, headerlen);
    841 free(tmpbuf);
    842 if (res != 0) {
    843 free(mbuf);
    844 goto clear_pipe;
    845 }
    846 res = read_back(llp->pipe[0], mbuf, now_len);
    847 if (res != 0) {
    848 free(mbuf);
    849 goto clear_pipe;
    850 }
    851 len = now_len + extra_len;
    852 iov[iov_count].iov_base = mbuf;
    853 iov[iov_count].iov_len = len;
    854 iov_count++;
    855 res = fuse_send_msg(se, ch, iov, iov_count);
    856 free(mbuf);
    857 return res;
    858 }
    859 free(mbuf);
    860 res = now_len;
    861 }
    862 len = res;
    863 out->len = headerlen + len;
    864
    865 if (se->debug) {
    866 fuse_log(FUSE_LOG_DEBUG,
    867 " unique: %llu, success, outsize: %i (splice)\n",
    868 (unsigned long long) out->unique, out->len);
    869 }
    870
    871 splice_flags = 0;
    872 if ((flags & FUSE_BUF_SPLICE_MOVE) &&
    873 (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE))
    874 splice_flags |= SPLICE_F_MOVE;
    875
    876 if (se->io != NULL && se->io->splice_send != NULL) {
    877 res = se->io->splice_send(llp->pipe[0], NULL,
    878 ch ? ch->fd : se->fd, NULL, out->len,
    879 splice_flags, se->userdata);
    880 } else {
    881 res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
    882 out->len, splice_flags);
    883 }
    884 if (res == -1) {
    885 res = -errno;
    886 perror("fuse: splice from pipe");
    887 goto clear_pipe;
    888 }
    889 if (res != out->len) {
    890 res = -EIO;
    891 fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
    892 res, out->len);
    893 goto clear_pipe;
    894 }
    895 return 0;
    896
    897clear_pipe:
    898 fuse_ll_clear_pipe(se);
    899 return res;
    900
    901fallback:
    902 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
    903}
    904#else
    905static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
    906 struct iovec *iov, int iov_count,
    907 struct fuse_bufvec *buf, unsigned int flags)
    908{
    909 size_t len = fuse_buf_size(buf);
    910 (void) flags;
    911
    912 return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len);
    913}
    914#endif
    915
    917 enum fuse_buf_copy_flags flags)
    918{
    919 struct iovec iov[2];
    920 struct fuse_out_header out;
    921 int res;
    922
    923 iov[0].iov_base = &out;
    924 iov[0].iov_len = sizeof(struct fuse_out_header);
    925
    926 out.unique = req->unique;
    927 out.error = 0;
    928
    929 res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags);
    930 if (res <= 0) {
    931 fuse_free_req(req);
    932 return res;
    933 } else {
    934 return fuse_reply_err(req, res);
    935 }
    936}
    937
    938int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf)
    939{
    940 struct fuse_statfs_out arg;
    941 size_t size = req->se->conn.proto_minor < 4 ?
    942 FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
    943
    944 memset(&arg, 0, sizeof(arg));
    945 convert_statfs(stbuf, &arg.st);
    946
    947 return send_reply_ok(req, &arg, size);
    948}
    949
    950int fuse_reply_xattr(fuse_req_t req, size_t count)
    951{
    952 struct fuse_getxattr_out arg;
    953
    954 memset(&arg, 0, sizeof(arg));
    955 arg.size = count;
    956
    957 return send_reply_ok(req, &arg, sizeof(arg));
    958}
    959
    960int fuse_reply_lock(fuse_req_t req, const struct flock *lock)
    961{
    962 struct fuse_lk_out arg;
    963
    964 memset(&arg, 0, sizeof(arg));
    965 arg.lk.type = lock->l_type;
    966 if (lock->l_type != F_UNLCK) {
    967 arg.lk.start = lock->l_start;
    968 if (lock->l_len == 0)
    969 arg.lk.end = OFFSET_MAX;
    970 else
    971 arg.lk.end = lock->l_start + lock->l_len - 1;
    972 }
    973 arg.lk.pid = lock->l_pid;
    974 return send_reply_ok(req, &arg, sizeof(arg));
    975}
    976
    977int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
    978{
    979 struct fuse_bmap_out arg;
    980
    981 memset(&arg, 0, sizeof(arg));
    982 arg.block = idx;
    983
    984 return send_reply_ok(req, &arg, sizeof(arg));
    985}
    986
    987static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
    988 size_t count)
    989{
    990 struct fuse_ioctl_iovec *fiov;
    991 size_t i;
    992
    993 fiov = malloc(sizeof(fiov[0]) * count);
    994 if (!fiov)
    995 return NULL;
    996
    997 for (i = 0; i < count; i++) {
    998 fiov[i].base = (uintptr_t) iov[i].iov_base;
    999 fiov[i].len = iov[i].iov_len;
    1000 }
    1001
    1002 return fiov;
    1003}
    1004
    1006 const struct iovec *in_iov, size_t in_count,
    1007 const struct iovec *out_iov, size_t out_count)
    1008{
    1009 struct fuse_ioctl_out arg;
    1010 struct fuse_ioctl_iovec *in_fiov = NULL;
    1011 struct fuse_ioctl_iovec *out_fiov = NULL;
    1012 struct iovec iov[4];
    1013 size_t count = 1;
    1014 int res;
    1015
    1016 memset(&arg, 0, sizeof(arg));
    1017 arg.flags |= FUSE_IOCTL_RETRY;
    1018 arg.in_iovs = in_count;
    1019 arg.out_iovs = out_count;
    1020 iov[count].iov_base = &arg;
    1021 iov[count].iov_len = sizeof(arg);
    1022 count++;
    1023
    1024 if (req->se->conn.proto_minor < 16) {
    1025 if (in_count) {
    1026 iov[count].iov_base = (void *)in_iov;
    1027 iov[count].iov_len = sizeof(in_iov[0]) * in_count;
    1028 count++;
    1029 }
    1030
    1031 if (out_count) {
    1032 iov[count].iov_base = (void *)out_iov;
    1033 iov[count].iov_len = sizeof(out_iov[0]) * out_count;
    1034 count++;
    1035 }
    1036 } else {
    1037 /* Can't handle non-compat 64bit ioctls on 32bit */
    1038 if (sizeof(void *) == 4 && req->ioctl_64bit) {
    1039 res = fuse_reply_err(req, EINVAL);
    1040 goto out;
    1041 }
    1042
    1043 if (in_count) {
    1044 in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
    1045 if (!in_fiov)
    1046 goto enomem;
    1047
    1048 iov[count].iov_base = (void *)in_fiov;
    1049 iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
    1050 count++;
    1051 }
    1052 if (out_count) {
    1053 out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
    1054 if (!out_fiov)
    1055 goto enomem;
    1056
    1057 iov[count].iov_base = (void *)out_fiov;
    1058 iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
    1059 count++;
    1060 }
    1061 }
    1062
    1063 res = send_reply_iov(req, 0, iov, count);
    1064out:
    1065 free(in_fiov);
    1066 free(out_fiov);
    1067
    1068 return res;
    1069
    1070enomem:
    1071 res = fuse_reply_err(req, ENOMEM);
    1072 goto out;
    1073}
    1074
    1075int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
    1076{
    1077 struct fuse_ioctl_out arg;
    1078 struct iovec iov[3];
    1079 size_t count = 1;
    1080
    1081 memset(&arg, 0, sizeof(arg));
    1082 arg.result = result;
    1083 iov[count].iov_base = &arg;
    1084 iov[count].iov_len = sizeof(arg);
    1085 count++;
    1086
    1087 if (size) {
    1088 iov[count].iov_base = (char *) buf;
    1089 iov[count].iov_len = size;
    1090 count++;
    1091 }
    1092
    1093 return send_reply_iov(req, 0, iov, count);
    1094}
    1095
    1096int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
    1097 int count)
    1098{
    1099 struct iovec *padded_iov;
    1100 struct fuse_ioctl_out arg;
    1101 int res;
    1102
    1103 padded_iov = malloc((count + 2) * sizeof(struct iovec));
    1104 if (padded_iov == NULL)
    1105 return fuse_reply_err(req, ENOMEM);
    1106
    1107 memset(&arg, 0, sizeof(arg));
    1108 arg.result = result;
    1109 padded_iov[1].iov_base = &arg;
    1110 padded_iov[1].iov_len = sizeof(arg);
    1111
    1112 memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
    1113
    1114 res = send_reply_iov(req, 0, padded_iov, count + 2);
    1115 free(padded_iov);
    1116
    1117 return res;
    1118}
    1119
    1120int fuse_reply_poll(fuse_req_t req, unsigned revents)
    1121{
    1122 struct fuse_poll_out arg;
    1123
    1124 memset(&arg, 0, sizeof(arg));
    1125 arg.revents = revents;
    1126
    1127 return send_reply_ok(req, &arg, sizeof(arg));
    1128}
    1129
    1130int fuse_reply_lseek(fuse_req_t req, off_t off)
    1131{
    1132 struct fuse_lseek_out arg;
    1133
    1134 memset(&arg, 0, sizeof(arg));
    1135 arg.offset = off;
    1136
    1137 return send_reply_ok(req, &arg, sizeof(arg));
    1138}
    1139
    1140static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1141{
    1142 char *name = (char *) inarg;
    1143
    1144 if (req->se->op.lookup)
    1145 req->se->op.lookup(req, nodeid, name);
    1146 else
    1147 fuse_reply_err(req, ENOSYS);
    1148}
    1149
    1150static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1151{
    1152 struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
    1153
    1154 if (req->se->op.forget)
    1155 req->se->op.forget(req, nodeid, arg->nlookup);
    1156 else
    1157 fuse_reply_none(req);
    1158}
    1159
    1160static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
    1161 const void *inarg)
    1162{
    1163 struct fuse_batch_forget_in *arg = (void *) inarg;
    1164 struct fuse_forget_one *param = (void *) PARAM(arg);
    1165 unsigned int i;
    1166
    1167 (void) nodeid;
    1168
    1169 if (req->se->op.forget_multi) {
    1170 req->se->op.forget_multi(req, arg->count,
    1171 (struct fuse_forget_data *) param);
    1172 } else if (req->se->op.forget) {
    1173 for (i = 0; i < arg->count; i++) {
    1174 struct fuse_forget_one *forget = &param[i];
    1175 struct fuse_req *dummy_req;
    1176
    1177 dummy_req = fuse_ll_alloc_req(req->se);
    1178 if (dummy_req == NULL)
    1179 break;
    1180
    1181 dummy_req->unique = req->unique;
    1182 dummy_req->ctx = req->ctx;
    1183 dummy_req->ch = NULL;
    1184
    1185 req->se->op.forget(dummy_req, forget->nodeid,
    1186 forget->nlookup);
    1187 }
    1188 fuse_reply_none(req);
    1189 } else {
    1190 fuse_reply_none(req);
    1191 }
    1192}
    1193
    1194static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1195{
    1196 struct fuse_file_info *fip = NULL;
    1197 struct fuse_file_info fi;
    1198
    1199 if (req->se->conn.proto_minor >= 9) {
    1200 struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
    1201
    1202 if (arg->getattr_flags & FUSE_GETATTR_FH) {
    1203 memset(&fi, 0, sizeof(fi));
    1204 fi.fh = arg->fh;
    1205 fip = &fi;
    1206 }
    1207 }
    1208
    1209 if (req->se->op.getattr)
    1210 req->se->op.getattr(req, nodeid, fip);
    1211 else
    1212 fuse_reply_err(req, ENOSYS);
    1213}
    1214
    1215static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1216{
    1217 struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
    1218
    1219 if (req->se->op.setattr) {
    1220 struct fuse_file_info *fi = NULL;
    1221 struct fuse_file_info fi_store;
    1222 struct stat stbuf;
    1223 memset(&stbuf, 0, sizeof(stbuf));
    1224 convert_attr(arg, &stbuf);
    1225 if (arg->valid & FATTR_FH) {
    1226 arg->valid &= ~FATTR_FH;
    1227 memset(&fi_store, 0, sizeof(fi_store));
    1228 fi = &fi_store;
    1229 fi->fh = arg->fh;
    1230 }
    1231 arg->valid &=
    1232 FUSE_SET_ATTR_MODE |
    1233 FUSE_SET_ATTR_UID |
    1234 FUSE_SET_ATTR_GID |
    1235 FUSE_SET_ATTR_SIZE |
    1236 FUSE_SET_ATTR_ATIME |
    1237 FUSE_SET_ATTR_MTIME |
    1238 FUSE_SET_ATTR_KILL_SUID |
    1239 FUSE_SET_ATTR_KILL_SGID |
    1240 FUSE_SET_ATTR_ATIME_NOW |
    1241 FUSE_SET_ATTR_MTIME_NOW |
    1242 FUSE_SET_ATTR_CTIME;
    1243
    1244 req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
    1245 } else
    1246 fuse_reply_err(req, ENOSYS);
    1247}
    1248
    1249static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1250{
    1251 struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
    1252
    1253 if (req->se->op.access)
    1254 req->se->op.access(req, nodeid, arg->mask);
    1255 else
    1256 fuse_reply_err(req, ENOSYS);
    1257}
    1258
    1259static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1260{
    1261 (void) inarg;
    1262
    1263 if (req->se->op.readlink)
    1264 req->se->op.readlink(req, nodeid);
    1265 else
    1266 fuse_reply_err(req, ENOSYS);
    1267}
    1268
    1269static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1270{
    1271 struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
    1272 char *name = PARAM(arg);
    1273
    1274 if (req->se->conn.proto_minor >= 12)
    1275 req->ctx.umask = arg->umask;
    1276 else
    1277 name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
    1278
    1279 if (req->se->op.mknod)
    1280 req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
    1281 else
    1282 fuse_reply_err(req, ENOSYS);
    1283}
    1284
    1285static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1286{
    1287 struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
    1288
    1289 if (req->se->conn.proto_minor >= 12)
    1290 req->ctx.umask = arg->umask;
    1291
    1292 if (req->se->op.mkdir)
    1293 req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
    1294 else
    1295 fuse_reply_err(req, ENOSYS);
    1296}
    1297
    1298static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1299{
    1300 char *name = (char *) inarg;
    1301
    1302 if (req->se->op.unlink)
    1303 req->se->op.unlink(req, nodeid, name);
    1304 else
    1305 fuse_reply_err(req, ENOSYS);
    1306}
    1307
    1308static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1309{
    1310 char *name = (char *) inarg;
    1311
    1312 if (req->se->op.rmdir)
    1313 req->se->op.rmdir(req, nodeid, name);
    1314 else
    1315 fuse_reply_err(req, ENOSYS);
    1316}
    1317
    1318static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1319{
    1320 char *name = (char *) inarg;
    1321 char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
    1322
    1323 if (req->se->op.symlink)
    1324 req->se->op.symlink(req, linkname, nodeid, name);
    1325 else
    1326 fuse_reply_err(req, ENOSYS);
    1327}
    1328
    1329static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1330{
    1331 struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
    1332 char *oldname = PARAM(arg);
    1333 char *newname = oldname + strlen(oldname) + 1;
    1334
    1335 if (req->se->op.rename)
    1336 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
    1337 0);
    1338 else
    1339 fuse_reply_err(req, ENOSYS);
    1340}
    1341
    1342static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1343{
    1344 struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg;
    1345 char *oldname = PARAM(arg);
    1346 char *newname = oldname + strlen(oldname) + 1;
    1347
    1348 if (req->se->op.rename)
    1349 req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
    1350 arg->flags);
    1351 else
    1352 fuse_reply_err(req, ENOSYS);
    1353}
    1354
    1355static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1356{
    1357 struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
    1358
    1359 if (req->se->op.link)
    1360 req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
    1361 else
    1362 fuse_reply_err(req, ENOSYS);
    1363}
    1364
    1365static void do_tmpfile(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1366{
    1367 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
    1368
    1369 if (req->se->op.tmpfile) {
    1370 struct fuse_file_info fi;
    1371
    1372 memset(&fi, 0, sizeof(fi));
    1373 fi.flags = arg->flags;
    1374
    1375 if (req->se->conn.proto_minor >= 12)
    1376 req->ctx.umask = arg->umask;
    1377
    1378 req->se->op.tmpfile(req, nodeid, arg->mode, &fi);
    1379 } else
    1380 fuse_reply_err(req, ENOSYS);
    1381}
    1382
    1383static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1384{
    1385 struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
    1386
    1387 if (req->se->op.create) {
    1388 struct fuse_file_info fi;
    1389 char *name = PARAM(arg);
    1390
    1391 memset(&fi, 0, sizeof(fi));
    1392 fi.flags = arg->flags;
    1393
    1394 if (req->se->conn.proto_minor >= 12)
    1395 req->ctx.umask = arg->umask;
    1396 else
    1397 name = (char *) inarg + sizeof(struct fuse_open_in);
    1398
    1399 req->se->op.create(req, nodeid, name, arg->mode, &fi);
    1400 } else
    1401 fuse_reply_err(req, ENOSYS);
    1402}
    1403
    1404static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1405{
    1406 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
    1407 struct fuse_file_info fi;
    1408
    1409 memset(&fi, 0, sizeof(fi));
    1410 fi.flags = arg->flags;
    1411
    1412 if (req->se->op.open)
    1413 req->se->op.open(req, nodeid, &fi);
    1414 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT)
    1415 fuse_reply_err(req, ENOSYS);
    1416 else
    1417 fuse_reply_open(req, &fi);
    1418}
    1419
    1420static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1421{
    1422 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1423
    1424 if (req->se->op.read) {
    1425 struct fuse_file_info fi;
    1426
    1427 memset(&fi, 0, sizeof(fi));
    1428 fi.fh = arg->fh;
    1429 if (req->se->conn.proto_minor >= 9) {
    1430 fi.lock_owner = arg->lock_owner;
    1431 fi.flags = arg->flags;
    1432 }
    1433 req->se->op.read(req, nodeid, arg->size, arg->offset, &fi);
    1434 } else
    1435 fuse_reply_err(req, ENOSYS);
    1436}
    1437
    1438static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1439{
    1440 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
    1441 struct fuse_file_info fi;
    1442 char *param;
    1443
    1444 memset(&fi, 0, sizeof(fi));
    1445 fi.fh = arg->fh;
    1446 fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
    1447
    1448 if (req->se->conn.proto_minor < 9) {
    1449 param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
    1450 } else {
    1451 fi.lock_owner = arg->lock_owner;
    1452 fi.flags = arg->flags;
    1453 param = PARAM(arg);
    1454 }
    1455
    1456 if (req->se->op.write)
    1457 req->se->op.write(req, nodeid, param, arg->size,
    1458 arg->offset, &fi);
    1459 else
    1460 fuse_reply_err(req, ENOSYS);
    1461}
    1462
    1463static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg,
    1464 const struct fuse_buf *ibuf)
    1465{
    1466 struct fuse_session *se = req->se;
    1467 struct fuse_bufvec bufv = {
    1468 .buf[0] = *ibuf,
    1469 .count = 1,
    1470 };
    1471 struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
    1472 struct fuse_file_info fi;
    1473
    1474 memset(&fi, 0, sizeof(fi));
    1475 fi.fh = arg->fh;
    1476 fi.writepage = arg->write_flags & FUSE_WRITE_CACHE;
    1477
    1478 if (se->conn.proto_minor < 9) {
    1479 bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
    1480 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    1481 FUSE_COMPAT_WRITE_IN_SIZE;
    1482 assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
    1483 } else {
    1484 fi.lock_owner = arg->lock_owner;
    1485 fi.flags = arg->flags;
    1486 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
    1487 bufv.buf[0].mem = PARAM(arg);
    1488
    1489 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    1490 sizeof(struct fuse_write_in);
    1491 }
    1492 if (bufv.buf[0].size < arg->size) {
    1493 fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n");
    1494 fuse_reply_err(req, EIO);
    1495 goto out;
    1496 }
    1497 bufv.buf[0].size = arg->size;
    1498
    1499 se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
    1500
    1501out:
    1502 /* Need to reset the pipe if ->write_buf() didn't consume all data */
    1503 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
    1504 fuse_ll_clear_pipe(se);
    1505}
    1506
    1507static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1508{
    1509 struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
    1510 struct fuse_file_info fi;
    1511
    1512 memset(&fi, 0, sizeof(fi));
    1513 fi.fh = arg->fh;
    1514 fi.flush = 1;
    1515 if (req->se->conn.proto_minor >= 7)
    1516 fi.lock_owner = arg->lock_owner;
    1517
    1518 if (req->se->op.flush)
    1519 req->se->op.flush(req, nodeid, &fi);
    1520 else
    1521 fuse_reply_err(req, ENOSYS);
    1522}
    1523
    1524static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1525{
    1526 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
    1527 struct fuse_file_info fi;
    1528
    1529 memset(&fi, 0, sizeof(fi));
    1530 fi.flags = arg->flags;
    1531 fi.fh = arg->fh;
    1532 if (req->se->conn.proto_minor >= 8) {
    1533 fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
    1534 fi.lock_owner = arg->lock_owner;
    1535 }
    1536 if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
    1537 fi.flock_release = 1;
    1538 fi.lock_owner = arg->lock_owner;
    1539 }
    1540
    1541 if (req->se->op.release)
    1542 req->se->op.release(req, nodeid, &fi);
    1543 else
    1544 fuse_reply_err(req, 0);
    1545}
    1546
    1547static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1548{
    1549 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
    1550 struct fuse_file_info fi;
    1551 int datasync = arg->fsync_flags & 1;
    1552
    1553 memset(&fi, 0, sizeof(fi));
    1554 fi.fh = arg->fh;
    1555
    1556 if (req->se->op.fsync)
    1557 req->se->op.fsync(req, nodeid, datasync, &fi);
    1558 else
    1559 fuse_reply_err(req, ENOSYS);
    1560}
    1561
    1562static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1563{
    1564 struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
    1565 struct fuse_file_info fi;
    1566
    1567 memset(&fi, 0, sizeof(fi));
    1568 fi.flags = arg->flags;
    1569
    1570 if (req->se->op.opendir)
    1571 req->se->op.opendir(req, nodeid, &fi);
    1572 else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT)
    1573 fuse_reply_err(req, ENOSYS);
    1574 else
    1575 fuse_reply_open(req, &fi);
    1576}
    1577
    1578static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1579{
    1580 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1581 struct fuse_file_info fi;
    1582
    1583 memset(&fi, 0, sizeof(fi));
    1584 fi.fh = arg->fh;
    1585
    1586 if (req->se->op.readdir)
    1587 req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi);
    1588 else
    1589 fuse_reply_err(req, ENOSYS);
    1590}
    1591
    1592static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1593{
    1594 struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
    1595 struct fuse_file_info fi;
    1596
    1597 memset(&fi, 0, sizeof(fi));
    1598 fi.fh = arg->fh;
    1599
    1600 if (req->se->op.readdirplus)
    1601 req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
    1602 else
    1603 fuse_reply_err(req, ENOSYS);
    1604}
    1605
    1606static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1607{
    1608 struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
    1609 struct fuse_file_info fi;
    1610
    1611 memset(&fi, 0, sizeof(fi));
    1612 fi.flags = arg->flags;
    1613 fi.fh = arg->fh;
    1614
    1615 if (req->se->op.releasedir)
    1616 req->se->op.releasedir(req, nodeid, &fi);
    1617 else
    1618 fuse_reply_err(req, 0);
    1619}
    1620
    1621static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1622{
    1623 struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
    1624 struct fuse_file_info fi;
    1625 int datasync = arg->fsync_flags & 1;
    1626
    1627 memset(&fi, 0, sizeof(fi));
    1628 fi.fh = arg->fh;
    1629
    1630 if (req->se->op.fsyncdir)
    1631 req->se->op.fsyncdir(req, nodeid, datasync, &fi);
    1632 else
    1633 fuse_reply_err(req, ENOSYS);
    1634}
    1635
    1636static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1637{
    1638 (void) nodeid;
    1639 (void) inarg;
    1640
    1641 if (req->se->op.statfs)
    1642 req->se->op.statfs(req, nodeid);
    1643 else {
    1644 struct statvfs buf = {
    1645 .f_namemax = 255,
    1646 .f_bsize = 512,
    1647 };
    1648 fuse_reply_statfs(req, &buf);
    1649 }
    1650}
    1651
    1652static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1653{
    1654 struct fuse_session *se = req->se;
    1655 unsigned int xattr_ext = !!(se->conn.want_ext & FUSE_CAP_SETXATTR_EXT);
    1656 struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
    1657 char *name = xattr_ext ? PARAM(arg) :
    1658 (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
    1659 char *value = name + strlen(name) + 1;
    1660
    1661 /* XXX:The API should be extended to support extra_flags/setxattr_flags */
    1662 if (req->se->op.setxattr)
    1663 req->se->op.setxattr(req, nodeid, name, value, arg->size,
    1664 arg->flags);
    1665 else
    1666 fuse_reply_err(req, ENOSYS);
    1667}
    1668
    1669static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1670{
    1671 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
    1672
    1673 if (req->se->op.getxattr)
    1674 req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size);
    1675 else
    1676 fuse_reply_err(req, ENOSYS);
    1677}
    1678
    1679static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1680{
    1681 struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
    1682
    1683 if (req->se->op.listxattr)
    1684 req->se->op.listxattr(req, nodeid, arg->size);
    1685 else
    1686 fuse_reply_err(req, ENOSYS);
    1687}
    1688
    1689static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1690{
    1691 char *name = (char *) inarg;
    1692
    1693 if (req->se->op.removexattr)
    1694 req->se->op.removexattr(req, nodeid, name);
    1695 else
    1696 fuse_reply_err(req, ENOSYS);
    1697}
    1698
    1699static void convert_fuse_file_lock(struct fuse_file_lock *fl,
    1700 struct flock *flock)
    1701{
    1702 memset(flock, 0, sizeof(struct flock));
    1703 flock->l_type = fl->type;
    1704 flock->l_whence = SEEK_SET;
    1705 flock->l_start = fl->start;
    1706 if (fl->end == OFFSET_MAX)
    1707 flock->l_len = 0;
    1708 else
    1709 flock->l_len = fl->end - fl->start + 1;
    1710 flock->l_pid = fl->pid;
    1711}
    1712
    1713static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1714{
    1715 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
    1716 struct fuse_file_info fi;
    1717 struct flock flock;
    1718
    1719 memset(&fi, 0, sizeof(fi));
    1720 fi.fh = arg->fh;
    1721 fi.lock_owner = arg->owner;
    1722
    1723 convert_fuse_file_lock(&arg->lk, &flock);
    1724 if (req->se->op.getlk)
    1725 req->se->op.getlk(req, nodeid, &fi, &flock);
    1726 else
    1727 fuse_reply_err(req, ENOSYS);
    1728}
    1729
    1730static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
    1731 const void *inarg, int sleep)
    1732{
    1733 struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
    1734 struct fuse_file_info fi;
    1735 struct flock flock;
    1736
    1737 memset(&fi, 0, sizeof(fi));
    1738 fi.fh = arg->fh;
    1739 fi.lock_owner = arg->owner;
    1740
    1741 if (arg->lk_flags & FUSE_LK_FLOCK) {
    1742 int op = 0;
    1743
    1744 switch (arg->lk.type) {
    1745 case F_RDLCK:
    1746 op = LOCK_SH;
    1747 break;
    1748 case F_WRLCK:
    1749 op = LOCK_EX;
    1750 break;
    1751 case F_UNLCK:
    1752 op = LOCK_UN;
    1753 break;
    1754 }
    1755 if (!sleep)
    1756 op |= LOCK_NB;
    1757
    1758 if (req->se->op.flock)
    1759 req->se->op.flock(req, nodeid, &fi, op);
    1760 else
    1761 fuse_reply_err(req, ENOSYS);
    1762 } else {
    1763 convert_fuse_file_lock(&arg->lk, &flock);
    1764 if (req->se->op.setlk)
    1765 req->se->op.setlk(req, nodeid, &fi, &flock, sleep);
    1766 else
    1767 fuse_reply_err(req, ENOSYS);
    1768 }
    1769}
    1770
    1771static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1772{
    1773 do_setlk_common(req, nodeid, inarg, 0);
    1774}
    1775
    1776static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1777{
    1778 do_setlk_common(req, nodeid, inarg, 1);
    1779}
    1780
    1781static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
    1782{
    1783 struct fuse_req *curr;
    1784
    1785 for (curr = se->list.next; curr != &se->list; curr = curr->next) {
    1786 if (curr->unique == req->u.i.unique) {
    1788 void *data;
    1789
    1790 curr->ref_cnt++;
    1791 pthread_mutex_unlock(&se->lock);
    1792
    1793 /* Ugh, ugly locking */
    1794 pthread_mutex_lock(&curr->lock);
    1795 pthread_mutex_lock(&se->lock);
    1796 curr->interrupted = 1;
    1797 func = curr->u.ni.func;
    1798 data = curr->u.ni.data;
    1799 pthread_mutex_unlock(&se->lock);
    1800 if (func)
    1801 func(curr, data);
    1802 pthread_mutex_unlock(&curr->lock);
    1803
    1804 pthread_mutex_lock(&se->lock);
    1805 curr->ref_cnt--;
    1806 if (!curr->ref_cnt) {
    1807 destroy_req(curr);
    1808 }
    1809
    1810 return 1;
    1811 }
    1812 }
    1813 for (curr = se->interrupts.next; curr != &se->interrupts;
    1814 curr = curr->next) {
    1815 if (curr->u.i.unique == req->u.i.unique)
    1816 return 1;
    1817 }
    1818 return 0;
    1819}
    1820
    1821static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1822{
    1823 struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
    1824 struct fuse_session *se = req->se;
    1825
    1826 (void) nodeid;
    1827 if (se->debug)
    1828 fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
    1829 (unsigned long long) arg->unique);
    1830
    1831 req->u.i.unique = arg->unique;
    1832
    1833 pthread_mutex_lock(&se->lock);
    1834 if (find_interrupted(se, req)) {
    1835 fuse_chan_put(req->ch);
    1836 req->ch = NULL;
    1837 destroy_req(req);
    1838 } else
    1839 list_add_req(req, &se->interrupts);
    1840 pthread_mutex_unlock(&se->lock);
    1841}
    1842
    1843static struct fuse_req *check_interrupt(struct fuse_session *se,
    1844 struct fuse_req *req)
    1845{
    1846 struct fuse_req *curr;
    1847
    1848 for (curr = se->interrupts.next; curr != &se->interrupts;
    1849 curr = curr->next) {
    1850 if (curr->u.i.unique == req->unique) {
    1851 req->interrupted = 1;
    1852 list_del_req(curr);
    1853 fuse_chan_put(curr->ch);
    1854 curr->ch = NULL;
    1855 destroy_req(curr);
    1856 return NULL;
    1857 }
    1858 }
    1859 curr = se->interrupts.next;
    1860 if (curr != &se->interrupts) {
    1861 list_del_req(curr);
    1862 list_init_req(curr);
    1863 return curr;
    1864 } else
    1865 return NULL;
    1866}
    1867
    1868static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1869{
    1870 struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
    1871
    1872 if (req->se->op.bmap)
    1873 req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
    1874 else
    1875 fuse_reply_err(req, ENOSYS);
    1876}
    1877
    1878static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1879{
    1880 struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
    1881 unsigned int flags = arg->flags;
    1882 void *in_buf = arg->in_size ? PARAM(arg) : NULL;
    1883 struct fuse_file_info fi;
    1884
    1885 if (flags & FUSE_IOCTL_DIR &&
    1886 !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) {
    1887 fuse_reply_err(req, ENOTTY);
    1888 return;
    1889 }
    1890
    1891 memset(&fi, 0, sizeof(fi));
    1892 fi.fh = arg->fh;
    1893
    1894 if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 &&
    1895 !(flags & FUSE_IOCTL_32BIT)) {
    1896 req->ioctl_64bit = 1;
    1897 }
    1898
    1899 if (req->se->op.ioctl)
    1900 req->se->op.ioctl(req, nodeid, arg->cmd,
    1901 (void *)(uintptr_t)arg->arg, &fi, flags,
    1902 in_buf, arg->in_size, arg->out_size);
    1903 else
    1904 fuse_reply_err(req, ENOSYS);
    1905}
    1906
    1907void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
    1908{
    1909 free(ph);
    1910}
    1911
    1912static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1913{
    1914 struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
    1915 struct fuse_file_info fi;
    1916
    1917 memset(&fi, 0, sizeof(fi));
    1918 fi.fh = arg->fh;
    1919 fi.poll_events = arg->events;
    1920
    1921 if (req->se->op.poll) {
    1922 struct fuse_pollhandle *ph = NULL;
    1923
    1924 if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) {
    1925 ph = malloc(sizeof(struct fuse_pollhandle));
    1926 if (ph == NULL) {
    1927 fuse_reply_err(req, ENOMEM);
    1928 return;
    1929 }
    1930 ph->kh = arg->kh;
    1931 ph->se = req->se;
    1932 }
    1933
    1934 req->se->op.poll(req, nodeid, &fi, ph);
    1935 } else {
    1936 fuse_reply_err(req, ENOSYS);
    1937 }
    1938}
    1939
    1940static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1941{
    1942 struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
    1943 struct fuse_file_info fi;
    1944
    1945 memset(&fi, 0, sizeof(fi));
    1946 fi.fh = arg->fh;
    1947
    1948 if (req->se->op.fallocate)
    1949 req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
    1950 else
    1951 fuse_reply_err(req, ENOSYS);
    1952}
    1953
    1954static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg)
    1955{
    1956 struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg;
    1957 struct fuse_file_info fi_in, fi_out;
    1958
    1959 memset(&fi_in, 0, sizeof(fi_in));
    1960 fi_in.fh = arg->fh_in;
    1961
    1962 memset(&fi_out, 0, sizeof(fi_out));
    1963 fi_out.fh = arg->fh_out;
    1964
    1965
    1966 if (req->se->op.copy_file_range)
    1967 req->se->op.copy_file_range(req, nodeid_in, arg->off_in,
    1968 &fi_in, arg->nodeid_out,
    1969 arg->off_out, &fi_out, arg->len,
    1970 arg->flags);
    1971 else
    1972 fuse_reply_err(req, ENOSYS);
    1973}
    1974
    1975static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    1976{
    1977 struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg;
    1978 struct fuse_file_info fi;
    1979
    1980 memset(&fi, 0, sizeof(fi));
    1981 fi.fh = arg->fh;
    1982
    1983 if (req->se->op.lseek)
    1984 req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
    1985 else
    1986 fuse_reply_err(req, ENOSYS);
    1987}
    1988
    1989static bool want_flags_valid(uint64_t capable, uint64_t want)
    1990{
    1991 uint64_t unknown_flags = want & (~capable);
    1992 if (unknown_flags != 0) {
    1993 fuse_log(FUSE_LOG_ERR,
    1994 "fuse: unknown connection 'want' flags: 0x%08lx\n",
    1995 unknown_flags);
    1996 return false;
    1997 }
    1998 return true;
    1999}
    2000
    2001/* Prevent bogus data races (bogus since "init" is called before
    2002 * multi-threading becomes relevant */
    2003static __attribute__((no_sanitize("thread")))
    2004void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    2005{
    2006 struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
    2007 struct fuse_init_out outarg;
    2008 struct fuse_session *se = req->se;
    2009 size_t bufsize = se->bufsize;
    2010 size_t outargsize = sizeof(outarg);
    2011 uint64_t inargflags = 0;
    2012 uint64_t outargflags = 0;
    2013 bool buf_reallocable = se->buf_reallocable;
    2014 (void) nodeid;
    2015 if (se->debug) {
    2016 fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
    2017 if (arg->major == 7 && arg->minor >= 6) {
    2018 fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
    2019 fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
    2020 arg->max_readahead);
    2021 }
    2022 }
    2023 se->conn.proto_major = arg->major;
    2024 se->conn.proto_minor = arg->minor;
    2025 se->conn.capable_ext = 0;
    2026 se->conn.want_ext = 0;
    2027
    2028 memset(&outarg, 0, sizeof(outarg));
    2029 outarg.major = FUSE_KERNEL_VERSION;
    2030 outarg.minor = FUSE_KERNEL_MINOR_VERSION;
    2031
    2032 if (arg->major < 7) {
    2033 fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
    2034 arg->major, arg->minor);
    2035 fuse_reply_err(req, EPROTO);
    2036 return;
    2037 }
    2038
    2039 if (arg->major > 7) {
    2040 /* Wait for a second INIT request with a 7.X version */
    2041 send_reply_ok(req, &outarg, sizeof(outarg));
    2042 return;
    2043 }
    2044
    2045 if (arg->minor >= 6) {
    2046 if (arg->max_readahead < se->conn.max_readahead)
    2047 se->conn.max_readahead = arg->max_readahead;
    2048 inargflags = arg->flags;
    2049 if (inargflags & FUSE_INIT_EXT)
    2050 inargflags = inargflags | (uint64_t) arg->flags2 << 32;
    2051 if (inargflags & FUSE_ASYNC_READ)
    2052 se->conn.capable_ext |= FUSE_CAP_ASYNC_READ;
    2053 if (inargflags & FUSE_POSIX_LOCKS)
    2054 se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS;
    2055 if (inargflags & FUSE_ATOMIC_O_TRUNC)
    2056 se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC;
    2057 if (inargflags & FUSE_EXPORT_SUPPORT)
    2058 se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT;
    2059 if (inargflags & FUSE_DONT_MASK)
    2060 se->conn.capable_ext |= FUSE_CAP_DONT_MASK;
    2061 if (inargflags & FUSE_FLOCK_LOCKS)
    2062 se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS;
    2063 if (inargflags & FUSE_AUTO_INVAL_DATA)
    2064 se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA;
    2065 if (inargflags & FUSE_DO_READDIRPLUS)
    2066 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS;
    2067 if (inargflags & FUSE_READDIRPLUS_AUTO)
    2068 se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO;
    2069 if (inargflags & FUSE_ASYNC_DIO)
    2070 se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO;
    2071 if (inargflags & FUSE_WRITEBACK_CACHE)
    2072 se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE;
    2073 if (inargflags & FUSE_NO_OPEN_SUPPORT)
    2074 se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT;
    2075 if (inargflags & FUSE_PARALLEL_DIROPS)
    2076 se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS;
    2077 if (inargflags & FUSE_POSIX_ACL)
    2078 se->conn.capable_ext |= FUSE_CAP_POSIX_ACL;
    2079 if (inargflags & FUSE_HANDLE_KILLPRIV)
    2080 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV;
    2081 if (inargflags & FUSE_HANDLE_KILLPRIV_V2)
    2082 se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2;
    2083 if (inargflags & FUSE_CACHE_SYMLINKS)
    2084 se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS;
    2085 if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
    2086 se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT;
    2087 if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
    2088 se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA;
    2089 if (inargflags & FUSE_SETXATTR_EXT)
    2090 se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT;
    2091 if (!(inargflags & FUSE_MAX_PAGES)) {
    2092 size_t max_bufsize =
    2093 FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
    2094 + FUSE_BUFFER_HEADER_SIZE;
    2095 if (bufsize > max_bufsize) {
    2096 bufsize = max_bufsize;
    2097 }
    2098 buf_reallocable = false;
    2099 }
    2100 if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP)
    2101 se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
    2102 if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY))
    2103 se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY;
    2104 if (inargflags & FUSE_PASSTHROUGH)
    2105 se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH;
    2106 if (inargflags & FUSE_NO_EXPORT_SUPPORT)
    2107 se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
    2108 } else {
    2109 se->conn.max_readahead = 0;
    2110 }
    2111
    2112 if (se->conn.proto_minor >= 14) {
    2113#ifdef HAVE_SPLICE
    2114#ifdef HAVE_VMSPLICE
    2115 if ((se->io == NULL) || (se->io->splice_send != NULL)) {
    2116 se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE |
    2118 }
    2119#endif
    2120 if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
    2121 se->conn.capable_ext |= FUSE_CAP_SPLICE_READ;
    2122 }
    2123#endif
    2124 }
    2125 if (se->conn.proto_minor >= 18)
    2126 se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR;
    2127
    2128 /* Default settings for modern filesystems.
    2129 *
    2130 * Most of these capabilities were disabled by default in
    2131 * libfuse2 for backwards compatibility reasons. In libfuse3,
    2132 * we can finally enable them by default (as long as they're
    2133 * supported by the kernel).
    2134 */
    2135#define LL_SET_DEFAULT(cond, cap) \
    2136 if ((cond)) \
    2137 fuse_set_feature_flag(&se->conn, cap)
    2138
    2139 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ);
    2140 LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA);
    2141 LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
    2142 LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
    2143 LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
    2144 LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
    2145 LL_SET_DEFAULT(se->op.getlk && se->op.setlk,
    2147 LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
    2148 LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
    2149 LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
    2151
    2152 /* This could safely become default, but libfuse needs an API extension
    2153 * to support it
    2154 * LL_SET_DEFAULT(1, FUSE_CAP_SETXATTR_EXT);
    2155 */
    2156
    2157 se->conn.time_gran = 1;
    2158
    2159 se->got_init = 1;
    2160 if (se->op.init) {
    2161 uint64_t want_ext_default = se->conn.want_ext;
    2162 uint32_t want_default = fuse_lower_32_bits(se->conn.want_ext);
    2163 int rc;
    2164
    2165 // Apply the first 32 bits of capable_ext to capable
    2166 se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext);
    2167 se->conn.want = want_default;
    2168
    2169 se->op.init(se->userdata, &se->conn);
    2170
    2171 /*
    2172 * se->conn.want is 32-bit value and deprecated in favour of
    2173 * se->conn.want_ext
    2174 * Userspace might still use conn.want - we need to convert it
    2175 */
    2176 rc = convert_to_conn_want_ext(&se->conn, want_ext_default,
    2177 want_default);
    2178 if (rc != 0) {
    2179 fuse_reply_err(req, EPROTO);
    2180 se->error = -EPROTO;
    2182 return;
    2183 }
    2184 }
    2185
    2186 if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
    2187 fuse_reply_err(req, EPROTO);
    2188 se->error = -EPROTO;
    2190 return;
    2191 }
    2192
    2193 unsigned max_read_mo = get_max_read(se->mo);
    2194 if (se->conn.max_read != max_read_mo) {
    2195 fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() "
    2196 "requested different maximum read size (%u vs %u)\n",
    2197 se->conn.max_read, max_read_mo);
    2198 fuse_reply_err(req, EPROTO);
    2199 se->error = -EPROTO;
    2201 return;
    2202 }
    2203
    2204 if (bufsize < FUSE_MIN_READ_BUFFER) {
    2205 fuse_log(FUSE_LOG_ERR,
    2206 "fuse: warning: buffer size too small: %zu\n",
    2207 bufsize);
    2208 bufsize = FUSE_MIN_READ_BUFFER;
    2209 }
    2210
    2211 if (buf_reallocable)
    2212 bufsize = UINT_MAX;
    2213 se->conn.max_write = MIN(se->conn.max_write, bufsize - FUSE_BUFFER_HEADER_SIZE);
    2214 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
    2215
    2216 if (arg->flags & FUSE_MAX_PAGES) {
    2217 outarg.flags |= FUSE_MAX_PAGES;
    2218 outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
    2219 }
    2220 outargflags = outarg.flags;
    2221 /* Always enable big writes, this is superseded
    2222 by the max_write option */
    2223 outargflags |= FUSE_BIG_WRITES;
    2224
    2225 if (se->conn.want_ext & FUSE_CAP_ASYNC_READ)
    2226 outargflags |= FUSE_ASYNC_READ;
    2227 if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS)
    2228 outargflags |= FUSE_POSIX_LOCKS;
    2229 if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC)
    2230 outargflags |= FUSE_ATOMIC_O_TRUNC;
    2231 if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT)
    2232 outargflags |= FUSE_EXPORT_SUPPORT;
    2233 if (se->conn.want_ext & FUSE_CAP_DONT_MASK)
    2234 outargflags |= FUSE_DONT_MASK;
    2235 if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS)
    2236 outargflags |= FUSE_FLOCK_LOCKS;
    2237 if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA)
    2238 outargflags |= FUSE_AUTO_INVAL_DATA;
    2239 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS)
    2240 outargflags |= FUSE_DO_READDIRPLUS;
    2241 if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO)
    2242 outargflags |= FUSE_READDIRPLUS_AUTO;
    2243 if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO)
    2244 outargflags |= FUSE_ASYNC_DIO;
    2245 if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE)
    2246 outargflags |= FUSE_WRITEBACK_CACHE;
    2247 if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS)
    2248 outargflags |= FUSE_PARALLEL_DIROPS;
    2249 if (se->conn.want_ext & FUSE_CAP_POSIX_ACL)
    2250 outargflags |= FUSE_POSIX_ACL;
    2251 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV)
    2252 outargflags |= FUSE_HANDLE_KILLPRIV;
    2253 if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2)
    2254 outargflags |= FUSE_HANDLE_KILLPRIV_V2;
    2255 if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS)
    2256 outargflags |= FUSE_CACHE_SYMLINKS;
    2257 if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA)
    2258 outargflags |= FUSE_EXPLICIT_INVAL_DATA;
    2259 if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT)
    2260 outargflags |= FUSE_SETXATTR_EXT;
    2261 if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
    2262 outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
    2263 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) {
    2264 outargflags |= FUSE_PASSTHROUGH;
    2265 /*
    2266 * outarg.max_stack_depth includes the fuse stack layer,
    2267 * so it is one more than max_backing_stack_depth.
    2268 */
    2269 outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1;
    2270 }
    2271 if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT)
    2272 outargflags |= FUSE_NO_EXPORT_SUPPORT;
    2273
    2274 if (inargflags & FUSE_INIT_EXT) {
    2275 outargflags |= FUSE_INIT_EXT;
    2276 outarg.flags2 = outargflags >> 32;
    2277 }
    2278
    2279 outarg.flags = outargflags;
    2280
    2281 outarg.max_readahead = se->conn.max_readahead;
    2282 outarg.max_write = se->conn.max_write;
    2283 if (se->conn.proto_minor >= 13) {
    2284 if (se->conn.max_background >= (1 << 16))
    2285 se->conn.max_background = (1 << 16) - 1;
    2286 if (se->conn.congestion_threshold > se->conn.max_background)
    2287 se->conn.congestion_threshold = se->conn.max_background;
    2288 if (!se->conn.congestion_threshold) {
    2289 se->conn.congestion_threshold =
    2290 se->conn.max_background * 3 / 4;
    2291 }
    2292
    2293 outarg.max_background = se->conn.max_background;
    2294 outarg.congestion_threshold = se->conn.congestion_threshold;
    2295 }
    2296 if (se->conn.proto_minor >= 23)
    2297 outarg.time_gran = se->conn.time_gran;
    2298
    2299 if (se->debug) {
    2300 fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
    2301 fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
    2302 fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n",
    2303 outarg.max_readahead);
    2304 fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write);
    2305 fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n",
    2306 outarg.max_background);
    2307 fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n",
    2308 outarg.congestion_threshold);
    2309 fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n",
    2310 outarg.time_gran);
    2311 if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH)
    2312 fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n",
    2313 outarg.max_stack_depth);
    2314 }
    2315 if (arg->minor < 5)
    2316 outargsize = FUSE_COMPAT_INIT_OUT_SIZE;
    2317 else if (arg->minor < 23)
    2318 outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE;
    2319
    2320 send_reply_ok(req, &outarg, outargsize);
    2321}
    2322
    2323static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
    2324{
    2325 struct fuse_session *se = req->se;
    2326
    2327 (void) nodeid;
    2328 (void) inarg;
    2329
    2330 se->got_destroy = 1;
    2331 se->got_init = 0;
    2332 if (se->op.destroy)
    2333 se->op.destroy(se->userdata);
    2334
    2335 send_reply_ok(req, NULL, 0);
    2336}
    2337
    2338static void list_del_nreq(struct fuse_notify_req *nreq)
    2339{
    2340 struct fuse_notify_req *prev = nreq->prev;
    2341 struct fuse_notify_req *next = nreq->next;
    2342 prev->next = next;
    2343 next->prev = prev;
    2344}
    2345
    2346static void list_add_nreq(struct fuse_notify_req *nreq,
    2347 struct fuse_notify_req *next)
    2348{
    2349 struct fuse_notify_req *prev = next->prev;
    2350 nreq->next = next;
    2351 nreq->prev = prev;
    2352 prev->next = nreq;
    2353 next->prev = nreq;
    2354}
    2355
    2356static void list_init_nreq(struct fuse_notify_req *nreq)
    2357{
    2358 nreq->next = nreq;
    2359 nreq->prev = nreq;
    2360}
    2361
    2362static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid,
    2363 const void *inarg, const struct fuse_buf *buf)
    2364{
    2365 struct fuse_session *se = req->se;
    2366 struct fuse_notify_req *nreq;
    2367 struct fuse_notify_req *head;
    2368
    2369 pthread_mutex_lock(&se->lock);
    2370 head = &se->notify_list;
    2371 for (nreq = head->next; nreq != head; nreq = nreq->next) {
    2372 if (nreq->unique == req->unique) {
    2373 list_del_nreq(nreq);
    2374 break;
    2375 }
    2376 }
    2377 pthread_mutex_unlock(&se->lock);
    2378
    2379 if (nreq != head)
    2380 nreq->reply(nreq, req, nodeid, inarg, buf);
    2381}
    2382
    2383static int send_notify_iov(struct fuse_session *se, int notify_code,
    2384 struct iovec *iov, int count)
    2385{
    2386 struct fuse_out_header out;
    2387
    2388 if (!se->got_init)
    2389 return -ENOTCONN;
    2390
    2391 out.unique = 0;
    2392 out.error = notify_code;
    2393 iov[0].iov_base = &out;
    2394 iov[0].iov_len = sizeof(struct fuse_out_header);
    2395
    2396 return fuse_send_msg(se, NULL, iov, count);
    2397}
    2398
    2399int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph)
    2400{
    2401 if (ph != NULL) {
    2402 struct fuse_notify_poll_wakeup_out outarg;
    2403 struct iovec iov[2];
    2404
    2405 outarg.kh = ph->kh;
    2406
    2407 iov[1].iov_base = &outarg;
    2408 iov[1].iov_len = sizeof(outarg);
    2409
    2410 return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2);
    2411 } else {
    2412 return 0;
    2413 }
    2414}
    2415
    2416int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
    2417 off_t off, off_t len)
    2418{
    2419 struct fuse_notify_inval_inode_out outarg;
    2420 struct iovec iov[2];
    2421
    2422 if (!se)
    2423 return -EINVAL;
    2424
    2425 if (se->conn.proto_minor < 12)
    2426 return -ENOSYS;
    2427
    2428 outarg.ino = ino;
    2429 outarg.off = off;
    2430 outarg.len = len;
    2431
    2432 iov[1].iov_base = &outarg;
    2433 iov[1].iov_len = sizeof(outarg);
    2434
    2435 return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
    2436}
    2437
    2457static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
    2458 const char *name, size_t namelen,
    2459 enum fuse_notify_entry_flags flags)
    2460{
    2461 struct fuse_notify_inval_entry_out outarg;
    2462 struct iovec iov[3];
    2463
    2464 if (!se)
    2465 return -EINVAL;
    2466
    2467 if (se->conn.proto_minor < 12)
    2468 return -ENOSYS;
    2469
    2470 outarg.parent = parent;
    2471 outarg.namelen = namelen;
    2472 outarg.flags = 0;
    2473 if (flags & FUSE_LL_EXPIRE_ONLY)
    2474 outarg.flags |= FUSE_EXPIRE_ONLY;
    2475
    2476 iov[1].iov_base = &outarg;
    2477 iov[1].iov_len = sizeof(outarg);
    2478 iov[2].iov_base = (void *)name;
    2479 iov[2].iov_len = namelen + 1;
    2480
    2481 return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
    2482}
    2483
    2484int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
    2485 const char *name, size_t namelen)
    2486{
    2487 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
    2488}
    2489
    2490int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
    2491 const char *name, size_t namelen)
    2492{
    2493 if (!se)
    2494 return -EINVAL;
    2495
    2496 if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY))
    2497 return -ENOSYS;
    2498
    2499 return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
    2500}
    2501
    2502
    2503int fuse_lowlevel_notify_delete(struct fuse_session *se,
    2504 fuse_ino_t parent, fuse_ino_t child,
    2505 const char *name, size_t namelen)
    2506{
    2507 struct fuse_notify_delete_out outarg;
    2508 struct iovec iov[3];
    2509
    2510 if (!se)
    2511 return -EINVAL;
    2512
    2513 if (se->conn.proto_minor < 18)
    2514 return -ENOSYS;
    2515
    2516 outarg.parent = parent;
    2517 outarg.child = child;
    2518 outarg.namelen = namelen;
    2519 outarg.padding = 0;
    2520
    2521 iov[1].iov_base = &outarg;
    2522 iov[1].iov_len = sizeof(outarg);
    2523 iov[2].iov_base = (void *)name;
    2524 iov[2].iov_len = namelen + 1;
    2525
    2526 return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3);
    2527}
    2528
    2529int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
    2530 off_t offset, struct fuse_bufvec *bufv,
    2531 enum fuse_buf_copy_flags flags)
    2532{
    2533 struct fuse_out_header out;
    2534 struct fuse_notify_store_out outarg;
    2535 struct iovec iov[3];
    2536 size_t size = fuse_buf_size(bufv);
    2537 int res;
    2538
    2539 if (!se)
    2540 return -EINVAL;
    2541
    2542 if (se->conn.proto_minor < 15)
    2543 return -ENOSYS;
    2544
    2545 out.unique = 0;
    2546 out.error = FUSE_NOTIFY_STORE;
    2547
    2548 outarg.nodeid = ino;
    2549 outarg.offset = offset;
    2550 outarg.size = size;
    2551 outarg.padding = 0;
    2552
    2553 iov[0].iov_base = &out;
    2554 iov[0].iov_len = sizeof(out);
    2555 iov[1].iov_base = &outarg;
    2556 iov[1].iov_len = sizeof(outarg);
    2557
    2558 res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags);
    2559 if (res > 0)
    2560 res = -res;
    2561
    2562 return res;
    2563}
    2564
    2565struct fuse_retrieve_req {
    2566 struct fuse_notify_req nreq;
    2567 void *cookie;
    2568};
    2569
    2570static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
    2571 fuse_req_t req, fuse_ino_t ino,
    2572 const void *inarg,
    2573 const struct fuse_buf *ibuf)
    2574{
    2575 struct fuse_session *se = req->se;
    2576 struct fuse_retrieve_req *rreq =
    2577 container_of(nreq, struct fuse_retrieve_req, nreq);
    2578 const struct fuse_notify_retrieve_in *arg = inarg;
    2579 struct fuse_bufvec bufv = {
    2580 .buf[0] = *ibuf,
    2581 .count = 1,
    2582 };
    2583
    2584 if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
    2585 bufv.buf[0].mem = PARAM(arg);
    2586
    2587 bufv.buf[0].size -= sizeof(struct fuse_in_header) +
    2588 sizeof(struct fuse_notify_retrieve_in);
    2589
    2590 if (bufv.buf[0].size < arg->size) {
    2591 fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
    2592 fuse_reply_none(req);
    2593 goto out;
    2594 }
    2595 bufv.buf[0].size = arg->size;
    2596
    2597 if (se->op.retrieve_reply) {
    2598 se->op.retrieve_reply(req, rreq->cookie, ino,
    2599 arg->offset, &bufv);
    2600 } else {
    2601 fuse_reply_none(req);
    2602 }
    2603out:
    2604 free(rreq);
    2605 if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
    2606 fuse_ll_clear_pipe(se);
    2607}
    2608
    2609int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
    2610 size_t size, off_t offset, void *cookie)
    2611{
    2612 struct fuse_notify_retrieve_out outarg;
    2613 struct iovec iov[2];
    2614 struct fuse_retrieve_req *rreq;
    2615 int err;
    2616
    2617 if (!se)
    2618 return -EINVAL;
    2619
    2620 if (se->conn.proto_minor < 15)
    2621 return -ENOSYS;
    2622
    2623 rreq = malloc(sizeof(*rreq));
    2624 if (rreq == NULL)
    2625 return -ENOMEM;
    2626
    2627 pthread_mutex_lock(&se->lock);
    2628 rreq->cookie = cookie;
    2629 rreq->nreq.unique = se->notify_ctr++;
    2630 rreq->nreq.reply = fuse_ll_retrieve_reply;
    2631 list_add_nreq(&rreq->nreq, &se->notify_list);
    2632 pthread_mutex_unlock(&se->lock);
    2633
    2634 outarg.notify_unique = rreq->nreq.unique;
    2635 outarg.nodeid = ino;
    2636 outarg.offset = offset;
    2637 outarg.size = size;
    2638 outarg.padding = 0;
    2639
    2640 iov[1].iov_base = &outarg;
    2641 iov[1].iov_len = sizeof(outarg);
    2642
    2643 err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2);
    2644 if (err) {
    2645 pthread_mutex_lock(&se->lock);
    2646 list_del_nreq(&rreq->nreq);
    2647 pthread_mutex_unlock(&se->lock);
    2648 free(rreq);
    2649 }
    2650
    2651 return err;
    2652}
    2653
    2655{
    2656 return req->se->userdata;
    2657}
    2658
    2660{
    2661 return &req->ctx;
    2662}
    2663
    2665 void *data)
    2666{
    2667 pthread_mutex_lock(&req->lock);
    2668 pthread_mutex_lock(&req->se->lock);
    2669 req->u.ni.func = func;
    2670 req->u.ni.data = data;
    2671 pthread_mutex_unlock(&req->se->lock);
    2672 if (req->interrupted && func)
    2673 func(req, data);
    2674 pthread_mutex_unlock(&req->lock);
    2675}
    2676
    2678{
    2679 int interrupted;
    2680
    2681 pthread_mutex_lock(&req->se->lock);
    2682 interrupted = req->interrupted;
    2683 pthread_mutex_unlock(&req->se->lock);
    2684
    2685 return interrupted;
    2686}
    2687
    2688static struct {
    2689 void (*func)(fuse_req_t, fuse_ino_t, const void *);
    2690 const char *name;
    2691} fuse_ll_ops[] = {
    2692 [FUSE_LOOKUP] = { do_lookup, "LOOKUP" },
    2693 [FUSE_FORGET] = { do_forget, "FORGET" },
    2694 [FUSE_GETATTR] = { do_getattr, "GETATTR" },
    2695 [FUSE_SETATTR] = { do_setattr, "SETATTR" },
    2696 [FUSE_READLINK] = { do_readlink, "READLINK" },
    2697 [FUSE_SYMLINK] = { do_symlink, "SYMLINK" },
    2698 [FUSE_MKNOD] = { do_mknod, "MKNOD" },
    2699 [FUSE_MKDIR] = { do_mkdir, "MKDIR" },
    2700 [FUSE_UNLINK] = { do_unlink, "UNLINK" },
    2701 [FUSE_RMDIR] = { do_rmdir, "RMDIR" },
    2702 [FUSE_RENAME] = { do_rename, "RENAME" },
    2703 [FUSE_LINK] = { do_link, "LINK" },
    2704 [FUSE_OPEN] = { do_open, "OPEN" },
    2705 [FUSE_READ] = { do_read, "READ" },
    2706 [FUSE_WRITE] = { do_write, "WRITE" },
    2707 [FUSE_STATFS] = { do_statfs, "STATFS" },
    2708 [FUSE_RELEASE] = { do_release, "RELEASE" },
    2709 [FUSE_FSYNC] = { do_fsync, "FSYNC" },
    2710 [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" },
    2711 [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" },
    2712 [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" },
    2713 [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" },
    2714 [FUSE_FLUSH] = { do_flush, "FLUSH" },
    2715 [FUSE_INIT] = { do_init, "INIT" },
    2716 [FUSE_OPENDIR] = { do_opendir, "OPENDIR" },
    2717 [FUSE_READDIR] = { do_readdir, "READDIR" },
    2718 [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" },
    2719 [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" },
    2720 [FUSE_GETLK] = { do_getlk, "GETLK" },
    2721 [FUSE_SETLK] = { do_setlk, "SETLK" },
    2722 [FUSE_SETLKW] = { do_setlkw, "SETLKW" },
    2723 [FUSE_ACCESS] = { do_access, "ACCESS" },
    2724 [FUSE_CREATE] = { do_create, "CREATE" },
    2725 [FUSE_TMPFILE] = { do_tmpfile, "TMPFILE" },
    2726 [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" },
    2727 [FUSE_BMAP] = { do_bmap, "BMAP" },
    2728 [FUSE_IOCTL] = { do_ioctl, "IOCTL" },
    2729 [FUSE_POLL] = { do_poll, "POLL" },
    2730 [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" },
    2731 [FUSE_DESTROY] = { do_destroy, "DESTROY" },
    2732 [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
    2733 [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
    2734 [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
    2735 [FUSE_RENAME2] = { do_rename2, "RENAME2" },
    2736 [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
    2737 [FUSE_LSEEK] = { do_lseek, "LSEEK" },
    2738 [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
    2739};
    2740
    2741/*
    2742 * For ABI compatibility we cannot allow higher values than CUSE_INIT.
    2743 * Without ABI compatibility we could use the size of the array.
    2744 * #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
    2745 */
    2746#define FUSE_MAXOP (CUSE_INIT + 1)
    2747
    2748static const char *opname(enum fuse_opcode opcode)
    2749{
    2750 if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
    2751 return "???";
    2752 else
    2753 return fuse_ll_ops[opcode].name;
    2754}
    2755
    2756static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
    2757 struct fuse_bufvec *src)
    2758{
    2759 ssize_t res = fuse_buf_copy(dst, src, 0);
    2760 if (res < 0) {
    2761 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res));
    2762 return res;
    2763 }
    2764 if ((size_t)res < fuse_buf_size(dst)) {
    2765 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
    2766 return -1;
    2767 }
    2768 return 0;
    2769}
    2770
    2771void fuse_session_process_buf(struct fuse_session *se,
    2772 const struct fuse_buf *buf)
    2773{
    2774 fuse_session_process_buf_internal(se, buf, NULL);
    2775}
    2776
    2777/* libfuse internal handler */
    2778void fuse_session_process_buf_internal(struct fuse_session *se,
    2779 const struct fuse_buf *buf, struct fuse_chan *ch)
    2780{
    2781 const size_t write_header_size = sizeof(struct fuse_in_header) +
    2782 sizeof(struct fuse_write_in);
    2783 struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
    2784 struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
    2785 struct fuse_in_header *in;
    2786 const void *inarg;
    2787 struct fuse_req *req;
    2788 void *mbuf = NULL;
    2789 int err;
    2790 int res;
    2791
    2792 if (buf->flags & FUSE_BUF_IS_FD) {
    2793 if (buf->size < tmpbuf.buf[0].size)
    2794 tmpbuf.buf[0].size = buf->size;
    2795
    2796 mbuf = malloc(tmpbuf.buf[0].size);
    2797 if (mbuf == NULL) {
    2798 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n");
    2799 goto clear_pipe;
    2800 }
    2801 tmpbuf.buf[0].mem = mbuf;
    2802
    2803 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
    2804 if (res < 0)
    2805 goto clear_pipe;
    2806
    2807 in = mbuf;
    2808 } else {
    2809 in = buf->mem;
    2810 }
    2811
    2812 if (se->debug) {
    2813 fuse_log(FUSE_LOG_DEBUG,
    2814 "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
    2815 (unsigned long long) in->unique,
    2816 opname((enum fuse_opcode) in->opcode), in->opcode,
    2817 (unsigned long long) in->nodeid, buf->size, in->pid);
    2818 }
    2819
    2820 req = fuse_ll_alloc_req(se);
    2821 if (req == NULL) {
    2822 struct fuse_out_header out = {
    2823 .unique = in->unique,
    2824 .error = -ENOMEM,
    2825 };
    2826 struct iovec iov = {
    2827 .iov_base = &out,
    2828 .iov_len = sizeof(struct fuse_out_header),
    2829 };
    2830
    2831 fuse_send_msg(se, ch, &iov, 1);
    2832 goto clear_pipe;
    2833 }
    2834
    2835 req->unique = in->unique;
    2836 req->ctx.uid = in->uid;
    2837 req->ctx.gid = in->gid;
    2838 req->ctx.pid = in->pid;
    2839 req->ch = ch ? fuse_chan_get(ch) : NULL;
    2840
    2841 err = EIO;
    2842 if (!se->got_init) {
    2843 enum fuse_opcode expected;
    2844
    2845 expected = se->cuse_data ? CUSE_INIT : FUSE_INIT;
    2846 if (in->opcode != expected)
    2847 goto reply_err;
    2848 } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT)
    2849 goto reply_err;
    2850
    2851 err = EACCES;
    2852 /* Implement -o allow_root */
    2853 if (se->deny_others && in->uid != se->owner && in->uid != 0 &&
    2854 in->opcode != FUSE_INIT && in->opcode != FUSE_READ &&
    2855 in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
    2856 in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
    2857 in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
    2858 in->opcode != FUSE_NOTIFY_REPLY &&
    2859 in->opcode != FUSE_READDIRPLUS)
    2860 goto reply_err;
    2861
    2862 err = ENOSYS;
    2863 if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
    2864 goto reply_err;
    2865 /* Do not process interrupt request */
    2866 if (se->conn.no_interrupt && in->opcode == FUSE_INTERRUPT) {
    2867 if (se->debug)
    2868 fuse_log(FUSE_LOG_DEBUG, "FUSE_INTERRUPT: reply to kernel to disable interrupt\n");
    2869 goto reply_err;
    2870 }
    2871 if (!se->conn.no_interrupt && in->opcode != FUSE_INTERRUPT) {
    2872 struct fuse_req *intr;
    2873 pthread_mutex_lock(&se->lock);
    2874 intr = check_interrupt(se, req);
    2875 list_add_req(req, &se->list);
    2876 pthread_mutex_unlock(&se->lock);
    2877 if (intr)
    2878 fuse_reply_err(intr, EAGAIN);
    2879 }
    2880
    2881 if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
    2882 (in->opcode != FUSE_WRITE || !se->op.write_buf) &&
    2883 in->opcode != FUSE_NOTIFY_REPLY) {
    2884 void *newmbuf;
    2885
    2886 err = ENOMEM;
    2887 newmbuf = realloc(mbuf, buf->size);
    2888 if (newmbuf == NULL)
    2889 goto reply_err;
    2890 mbuf = newmbuf;
    2891
    2892 tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
    2893 tmpbuf.buf[0].mem = (char *)mbuf + write_header_size;
    2894
    2895 res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
    2896 err = -res;
    2897 if (res < 0)
    2898 goto reply_err;
    2899
    2900 in = mbuf;
    2901 }
    2902
    2903 inarg = (void *) &in[1];
    2904 if (in->opcode == FUSE_WRITE && se->op.write_buf)
    2905 do_write_buf(req, in->nodeid, inarg, buf);
    2906 else if (in->opcode == FUSE_NOTIFY_REPLY)
    2907 do_notify_reply(req, in->nodeid, inarg, buf);
    2908 else
    2909 fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
    2910
    2911out_free:
    2912 free(mbuf);
    2913 return;
    2914
    2915reply_err:
    2916 fuse_reply_err(req, err);
    2917clear_pipe:
    2918 if (buf->flags & FUSE_BUF_IS_FD)
    2919 fuse_ll_clear_pipe(se);
    2920 goto out_free;
    2921}
    2922
    2923#define LL_OPTION(n,o,v) \
    2924 { n, offsetof(struct fuse_session, o), v }
    2925
    2926static const struct fuse_opt fuse_ll_opts[] = {
    2927 LL_OPTION("debug", debug, 1),
    2928 LL_OPTION("-d", debug, 1),
    2929 LL_OPTION("--debug", debug, 1),
    2930 LL_OPTION("allow_root", deny_others, 1),
    2932};
    2933
    2935{
    2936 printf("using FUSE kernel interface version %i.%i\n",
    2937 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
    2938 fuse_mount_version();
    2939}
    2940
    2942{
    2943 /* These are not all options, but the ones that are
    2944 potentially of interest to an end-user */
    2945 printf(
    2946" -o allow_other allow access by all users\n"
    2947" -o allow_root allow access by root\n"
    2948" -o auto_unmount auto unmount on process termination\n");
    2949}
    2950
    2951void fuse_session_destroy(struct fuse_session *se)
    2952{
    2953 struct fuse_ll_pipe *llp;
    2954
    2955 if (se->got_init && !se->got_destroy) {
    2956 if (se->op.destroy)
    2957 se->op.destroy(se->userdata);
    2958 }
    2959 llp = pthread_getspecific(se->pipe_key);
    2960 if (llp != NULL)
    2961 fuse_ll_pipe_free(llp);
    2962 pthread_key_delete(se->pipe_key);
    2963 pthread_mutex_destroy(&se->lock);
    2964 free(se->cuse_data);
    2965 if (se->fd != -1)
    2966 close(se->fd);
    2967 if (se->io != NULL)
    2968 free(se->io);
    2969 destroy_mount_opts(se->mo);
    2970 free(se);
    2971}
    2972
    2973
    2974static void fuse_ll_pipe_destructor(void *data)
    2975{
    2976 struct fuse_ll_pipe *llp = data;
    2977 fuse_ll_pipe_free(llp);
    2978}
    2979
    2980void fuse_buf_free(struct fuse_buf *buf)
    2981{
    2982 if (buf->mem == NULL)
    2983 return;
    2984
    2985 size_t write_header_sz =
    2986 sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in);
    2987
    2988 char *ptr = (char *)buf->mem - pagesize + write_header_sz;
    2989 free(ptr);
    2990 buf->mem = NULL;
    2991}
    2992
    2993/*
    2994 * This is used to allocate buffers that hold fuse requests
    2995 */
    2996static void *buf_alloc(size_t size, bool internal)
    2997{
    2998 /*
    2999 * For libfuse internal caller add in alignment. That cannot be done
    3000 * for an external caller, as it is not guaranteed that the external
    3001 * caller frees the raw pointer.
    3002 */
    3003 if (internal) {
    3004 size_t write_header_sz = sizeof(struct fuse_in_header) +
    3005 sizeof(struct fuse_write_in);
    3006 size_t new_size = ROUND_UP(size + write_header_sz, pagesize);
    3007
    3008 char *buf = aligned_alloc(pagesize, new_size);
    3009 if (buf == NULL)
    3010 return NULL;
    3011
    3012 buf += pagesize - write_header_sz;
    3013
    3014 return buf;
    3015 } else {
    3016 return malloc(size);
    3017 }
    3018}
    3019
    3020/*
    3021 *@param internal true if called from libfuse internal code
    3022 */
    3023static int _fuse_session_receive_buf(struct fuse_session *se,
    3024 struct fuse_buf *buf, struct fuse_chan *ch,
    3025 bool internal)
    3026{
    3027 int err;
    3028 ssize_t res;
    3029 size_t bufsize = se->bufsize;
    3030#ifdef HAVE_SPLICE
    3031 struct fuse_ll_pipe *llp;
    3032 struct fuse_buf tmpbuf;
    3033
    3034 if (se->conn.proto_minor < 14 ||
    3035 !(se->conn.want_ext & FUSE_CAP_SPLICE_READ))
    3036 goto fallback;
    3037
    3038 llp = fuse_ll_get_pipe(se);
    3039 if (llp == NULL)
    3040 goto fallback;
    3041
    3042 if (llp->size < bufsize) {
    3043 if (llp->can_grow) {
    3044 res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
    3045 if (res == -1) {
    3046 llp->can_grow = 0;
    3047 res = grow_pipe_to_max(llp->pipe[0]);
    3048 if (res > 0)
    3049 llp->size = res;
    3050 goto fallback;
    3051 }
    3052 llp->size = res;
    3053 }
    3054 if (llp->size < bufsize)
    3055 goto fallback;
    3056 }
    3057
    3058 if (se->io != NULL && se->io->splice_receive != NULL) {
    3059 res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
    3060 llp->pipe[1], NULL, bufsize, 0,
    3061 se->userdata);
    3062 } else {
    3063 res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
    3064 bufsize, 0);
    3065 }
    3066 err = errno;
    3067
    3068 if (fuse_session_exited(se))
    3069 return 0;
    3070
    3071 if (res == -1) {
    3072 if (err == ENODEV) {
    3073 /* Filesystem was unmounted, or connection was aborted
    3074 via /sys/fs/fuse/connections */
    3076 return 0;
    3077 }
    3078 if (err != EINTR && err != EAGAIN)
    3079 perror("fuse: splice from device");
    3080 return -err;
    3081 }
    3082
    3083 if (res < sizeof(struct fuse_in_header)) {
    3084 fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n");
    3085 return -EIO;
    3086 }
    3087
    3088 tmpbuf = (struct fuse_buf){
    3089 .size = res,
    3090 .flags = FUSE_BUF_IS_FD,
    3091 .fd = llp->pipe[0],
    3092 };
    3093
    3094 /*
    3095 * Don't bother with zero copy for small requests.
    3096 * fuse_loop_mt() needs to check for FORGET so this more than
    3097 * just an optimization.
    3098 */
    3099 if (res < sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) +
    3100 pagesize) {
    3101 struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
    3102 struct fuse_bufvec dst = { .count = 1 };
    3103
    3104 if (!buf->mem) {
    3105 buf->mem = buf_alloc(se->bufsize, internal);
    3106 if (!buf->mem) {
    3107 fuse_log(
    3108 FUSE_LOG_ERR,
    3109 "fuse: failed to allocate read buffer\n");
    3110 return -ENOMEM;
    3111 }
    3112 buf->mem_size = se->bufsize;
    3113 if (internal)
    3114 se->buf_reallocable = true;
    3115 }
    3116 buf->size = se->bufsize;
    3117 buf->flags = 0;
    3118 dst.buf[0] = *buf;
    3119
    3120 res = fuse_buf_copy(&dst, &src, 0);
    3121 if (res < 0) {
    3122 fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n",
    3123 strerror(-res));
    3124 fuse_ll_clear_pipe(se);
    3125 return res;
    3126 }
    3127 if (res < tmpbuf.size) {
    3128 fuse_log(FUSE_LOG_ERR,
    3129 "fuse: copy from pipe: short read\n");
    3130 fuse_ll_clear_pipe(se);
    3131 return -EIO;
    3132 }
    3133 assert(res == tmpbuf.size);
    3134
    3135 } else {
    3136 /* Don't overwrite buf->mem, as that would cause a leak */
    3137 buf->fd = tmpbuf.fd;
    3138 buf->flags = tmpbuf.flags;
    3139 }
    3140 buf->size = tmpbuf.size;
    3141
    3142 return res;
    3143
    3144fallback:
    3145#endif
    3146 if (!buf->mem) {
    3147 buf->mem = buf_alloc(se->bufsize, internal);
    3148 if (!buf->mem) {
    3149 fuse_log(FUSE_LOG_ERR,
    3150 "fuse: failed to allocate read buffer\n");
    3151 return -ENOMEM;
    3152 }
    3153 buf->mem_size = se->bufsize;
    3154 if (internal)
    3155 se->buf_reallocable = true;
    3156 }
    3157
    3158restart:
    3159 if (se->buf_reallocable)
    3160 bufsize = buf->mem_size;
    3161 if (se->io != NULL) {
    3162 /* se->io->read is never NULL if se->io is not NULL as
    3163 specified by fuse_session_custom_io()*/
    3164 res = se->io->read(ch ? ch->fd : se->fd, buf->mem, bufsize,
    3165 se->userdata);
    3166 } else {
    3167 res = read(ch ? ch->fd : se->fd, buf->mem, bufsize);
    3168 }
    3169 err = errno;
    3170
    3171 if (fuse_session_exited(se))
    3172 return 0;
    3173 if (res == -1) {
    3174 if (err == EINVAL && se->buf_reallocable &&
    3175 se->bufsize > buf->mem_size) {
    3176 void *newbuf = buf_alloc(se->bufsize, internal);
    3177 if (!newbuf) {
    3178 fuse_log(
    3179 FUSE_LOG_ERR,
    3180 "fuse: failed to (re)allocate read buffer\n");
    3181 return -ENOMEM;
    3182 }
    3183 fuse_buf_free(buf);
    3184 buf->mem = newbuf;
    3185 buf->mem_size = se->bufsize;
    3186 se->buf_reallocable = true;
    3187 goto restart;
    3188 }
    3189
    3190 /* ENOENT means the operation was interrupted, it's safe
    3191 to restart */
    3192 if (err == ENOENT)
    3193 goto restart;
    3194
    3195 if (err == ENODEV) {
    3196 /* Filesystem was unmounted, or connection was aborted
    3197 via /sys/fs/fuse/connections */
    3199 return 0;
    3200 }
    3201 /* Errors occurring during normal operation: EINTR (read
    3202 interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
    3203 umounted) */
    3204 if (err != EINTR && err != EAGAIN)
    3205 perror("fuse: reading device");
    3206 return -err;
    3207 }
    3208 if ((size_t)res < sizeof(struct fuse_in_header)) {
    3209 fuse_log(FUSE_LOG_ERR, "short read on fuse device\n");
    3210 return -EIO;
    3211 }
    3212
    3213 buf->size = res;
    3214
    3215 return res;
    3216}
    3217
    3218int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf)
    3219{
    3220 return _fuse_session_receive_buf(se, buf, NULL, false);
    3221}
    3222
    3223/* libfuse internal handler */
    3224int fuse_session_receive_buf_internal(struct fuse_session *se,
    3225 struct fuse_buf *buf,
    3226 struct fuse_chan *ch)
    3227{
    3228 return _fuse_session_receive_buf(se, buf, ch, true);
    3229}
    3230
    3231struct fuse_session *
    3232fuse_session_new_versioned(struct fuse_args *args,
    3233 const struct fuse_lowlevel_ops *op, size_t op_size,
    3234 struct libfuse_version *version, void *userdata)
    3235{
    3236 int err;
    3237 struct fuse_session *se;
    3238 struct mount_opts *mo;
    3239
    3240 if (sizeof(struct fuse_lowlevel_ops) < op_size) {
    3241 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
    3242 op_size = sizeof(struct fuse_lowlevel_ops);
    3243 }
    3244
    3245 if (args->argc == 0) {
    3246 fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
    3247 return NULL;
    3248 }
    3249
    3250 se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
    3251 if (se == NULL) {
    3252 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
    3253 goto out1;
    3254 }
    3255 se->fd = -1;
    3256 se->conn.max_write = FUSE_DEFAULT_MAX_PAGES_LIMIT * getpagesize();
    3257 se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE;
    3258 se->conn.max_readahead = UINT_MAX;
    3259
    3260 /* Parse options */
    3261 if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1)
    3262 goto out2;
    3263 if(se->deny_others) {
    3264 /* Allowing access only by root is done by instructing
    3265 * kernel to allow access by everyone, and then restricting
    3266 * access to root and mountpoint owner in libfuse.
    3267 */
    3268 // We may be adding the option a second time, but
    3269 // that doesn't hurt.
    3270 if(fuse_opt_add_arg(args, "-oallow_other") == -1)
    3271 goto out2;
    3272 }
    3273 mo = parse_mount_opts(args);
    3274 if (mo == NULL)
    3275 goto out3;
    3276
    3277 if(args->argc == 1 &&
    3278 args->argv[0][0] == '-') {
    3279 fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
    3280 "will be ignored\n");
    3281 } else if (args->argc != 1) {
    3282 int i;
    3283 fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
    3284 for(i = 1; i < args->argc-1; i++)
    3285 fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
    3286 fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
    3287 goto out4;
    3288 }
    3289
    3290 if (se->debug)
    3291 fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION);
    3292
    3293 list_init_req(&se->list);
    3294 list_init_req(&se->interrupts);
    3295 list_init_nreq(&se->notify_list);
    3296 se->notify_ctr = 1;
    3297 pthread_mutex_init(&se->lock, NULL);
    3298
    3299 err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
    3300 if (err) {
    3301 fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
    3302 strerror(err));
    3303 goto out5;
    3304 }
    3305
    3306 memcpy(&se->op, op, op_size);
    3307 se->owner = getuid();
    3308 se->userdata = userdata;
    3309
    3310 se->mo = mo;
    3311
    3312 /* Fuse server application should pass the version it was compiled
    3313 * against and pass it. If a libfuse version accidentally introduces an
    3314 * ABI incompatibility, it might be possible to 'fix' that at run time,
    3315 * by checking the version numbers.
    3316 */
    3317 se->version = *version;
    3318
    3319 return se;
    3320
    3321out5:
    3322 pthread_mutex_destroy(&se->lock);
    3323out4:
    3324 fuse_opt_free_args(args);
    3325out3:
    3326 if (mo != NULL)
    3327 destroy_mount_opts(mo);
    3328out2:
    3329 free(se);
    3330out1:
    3331 return NULL;
    3332}
    3333
    3334struct fuse_session *fuse_session_new_30(struct fuse_args *args,
    3335 const struct fuse_lowlevel_ops *op,
    3336 size_t op_size, void *userdata);
    3337struct fuse_session *fuse_session_new_30(struct fuse_args *args,
    3338 const struct fuse_lowlevel_ops *op,
    3339 size_t op_size,
    3340 void *userdata)
    3341{
    3342 /* unknown version */
    3343 struct libfuse_version version = { 0 };
    3344
    3345 return fuse_session_new_versioned(args, op, op_size, &version,
    3346 userdata);
    3347}
    3348
    3349FUSE_SYMVER("fuse_session_custom_io_317", "fuse_session_custom_io@@FUSE_3.17")
    3350int fuse_session_custom_io_317(struct fuse_session *se,
    3351 const struct fuse_custom_io *io, size_t op_size, int fd)
    3352{
    3353 if (sizeof(struct fuse_custom_io) < op_size) {
    3354 fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
    3355 op_size = sizeof(struct fuse_custom_io);
    3356 }
    3357
    3358 if (fd < 0) {
    3359 fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
    3360 "fuse_session_custom_io()\n", fd);
    3361 return -EBADF;
    3362 }
    3363 if (io == NULL) {
    3364 fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
    3365 "fuse_session_custom_io()\n");
    3366 return -EINVAL;
    3367 } else if (io->read == NULL || io->writev == NULL) {
    3368 /* If the user provides their own file descriptor, we can't
    3369 guarantee that the default behavior of the io operations made
    3370 in libfuse will function properly. Therefore, we enforce the
    3371 user to implement these io operations when using custom io. */
    3372 fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
    3373 "implement both io->read() and io->writev\n");
    3374 return -EINVAL;
    3375 }
    3376
    3377 se->io = calloc(1, sizeof(struct fuse_custom_io));
    3378 if (se->io == NULL) {
    3379 fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
    3380 "Error: %s\n", strerror(errno));
    3381 return -errno;
    3382 }
    3383
    3384 se->fd = fd;
    3385 memcpy(se->io, io, op_size);
    3386 return 0;
    3387}
    3388
    3389int fuse_session_custom_io_30(struct fuse_session *se,
    3390 const struct fuse_custom_io *io, int fd);
    3391FUSE_SYMVER("fuse_session_custom_io_30", "fuse_session_custom_io@FUSE_3.0")
    3392int fuse_session_custom_io_30(struct fuse_session *se,
    3393 const struct fuse_custom_io *io, int fd)
    3394{
    3395 return fuse_session_custom_io_317(se, io,
    3396 offsetof(struct fuse_custom_io, clone_fd), fd);
    3397}
    3398
    3399int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
    3400{
    3401 int fd;
    3402
    3403 if (mountpoint == NULL) {
    3404 fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n");
    3405 return -1;
    3406 }
    3407
    3408 /*
    3409 * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
    3410 * would ensue.
    3411 */
    3412 do {
    3413 fd = open("/dev/null", O_RDWR);
    3414 if (fd > 2)
    3415 close(fd);
    3416 } while (fd >= 0 && fd <= 2);
    3417
    3418 /*
    3419 * To allow FUSE daemons to run without privileges, the caller may open
    3420 * /dev/fuse before launching the file system and pass on the file
    3421 * descriptor by specifying /dev/fd/N as the mount point. Note that the
    3422 * parent process takes care of performing the mount in this case.
    3423 */
    3424 fd = fuse_mnt_parse_fuse_fd(mountpoint);
    3425 if (fd != -1) {
    3426 if (fcntl(fd, F_GETFD) == -1) {
    3427 fuse_log(FUSE_LOG_ERR,
    3428 "fuse: Invalid file descriptor /dev/fd/%u\n",
    3429 fd);
    3430 return -1;
    3431 }
    3432 se->fd = fd;
    3433 return 0;
    3434 }
    3435
    3436 /* Open channel */
    3437 fd = fuse_kern_mount(mountpoint, se->mo);
    3438 if (fd == -1)
    3439 return -1;
    3440 se->fd = fd;
    3441
    3442 /* Save mountpoint */
    3443 se->mountpoint = strdup(mountpoint);
    3444 if (se->mountpoint == NULL)
    3445 goto error_out;
    3446
    3447 return 0;
    3448
    3449error_out:
    3450 fuse_kern_unmount(mountpoint, fd);
    3451 return -1;
    3452}
    3453
    3454int fuse_session_fd(struct fuse_session *se)
    3455{
    3456 return se->fd;
    3457}
    3458
    3459void fuse_session_unmount(struct fuse_session *se)
    3460{
    3461 if (se->mountpoint != NULL) {
    3462 fuse_kern_unmount(se->mountpoint, se->fd);
    3463 se->fd = -1;
    3464 free(se->mountpoint);
    3465 se->mountpoint = NULL;
    3466 }
    3467}
    3468
    3469#ifdef linux
    3470int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
    3471{
    3472 char *buf;
    3473 size_t bufsize = 1024;
    3474 char path[128];
    3475 int ret;
    3476 int fd;
    3477 unsigned long pid = req->ctx.pid;
    3478 char *s;
    3479
    3480 sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
    3481
    3482retry:
    3483 buf = malloc(bufsize);
    3484 if (buf == NULL)
    3485 return -ENOMEM;
    3486
    3487 ret = -EIO;
    3488 fd = open(path, O_RDONLY);
    3489 if (fd == -1)
    3490 goto out_free;
    3491
    3492 ret = read(fd, buf, bufsize);
    3493 close(fd);
    3494 if (ret < 0) {
    3495 ret = -EIO;
    3496 goto out_free;
    3497 }
    3498
    3499 if ((size_t)ret == bufsize) {
    3500 free(buf);
    3501 bufsize *= 4;
    3502 goto retry;
    3503 }
    3504
    3505 buf[ret] = '\0';
    3506 ret = -EIO;
    3507 s = strstr(buf, "\nGroups:");
    3508 if (s == NULL)
    3509 goto out_free;
    3510
    3511 s += 8;
    3512 ret = 0;
    3513 while (1) {
    3514 char *end;
    3515 unsigned long val = strtoul(s, &end, 0);
    3516 if (end == s)
    3517 break;
    3518
    3519 s = end;
    3520 if (ret < size)
    3521 list[ret] = val;
    3522 ret++;
    3523 }
    3524
    3525out_free:
    3526 free(buf);
    3527 return ret;
    3528}
    3529#else /* linux */
    3530/*
    3531 * This is currently not implemented on other than Linux...
    3532 */
    3533int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
    3534{
    3535 (void) req; (void) size; (void) list;
    3536 return -ENOSYS;
    3537}
    3538#endif
    3539
    3540/* Prevent spurious data race warning - we don't care
    3541 * about races for this flag */
    3542__attribute__((no_sanitize_thread))
    3543void fuse_session_exit(struct fuse_session *se)
    3544{
    3545 se->exited = 1;
    3546}
    3547
    3548__attribute__((no_sanitize_thread))
    3549void fuse_session_reset(struct fuse_session *se)
    3550{
    3551 se->exited = 0;
    3552 se->error = 0;
    3553}
    3554
    3555__attribute__((no_sanitize_thread))
    3556int fuse_session_exited(struct fuse_session *se)
    3557{
    3558 return se->exited;
    3559}
    #define FUSE_CAP_IOCTL_DIR
    #define FUSE_CAP_DONT_MASK
    #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
    #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
    @ 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]
    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:68
    uint32_t poll_events
    uint32_t cache_readdir
    Definition fuse_common.h:97
    uint32_t nonseekable
    Definition fuse_common.h:86
    int32_t backing_id
    uint32_t parallel_direct_writes
    uint32_t noflush
    uint32_t flush
    Definition fuse_common.h:82
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2lib_2fuse__misc_8h_source.html0000644000175000017500000002435415002273247024231 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2fuse__opt_8c_source.html0000644000175000017500000024010315002273247024063 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2fuse__signals_8c_source.html0000644000175000017500000010411415002273247024722 0ustar berndbernd libfuse: fuse-3.17.1.dir/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#define BT_STACK_SZ (1024 * 1024)
    31static void *backtrace_buffer[BT_STACK_SZ];
    32
    33static void dump_stack(void)
    34{
    35#ifdef HAVE_BACKTRACE
    36 char **strings;
    37
    38 int nptrs = backtrace(backtrace_buffer, BT_STACK_SZ);
    39 strings = backtrace_symbols(backtrace_buffer, nptrs);
    40
    41 if (strings == NULL) {
    42 fuse_log(FUSE_LOG_ERR, "Failed to get backtrace symbols: %s\n",
    43 strerror(errno));
    44 return;
    45 }
    46
    47 for (int idx = 0; idx < nptrs; idx++)
    48 fuse_log(FUSE_LOG_ERR, "%s\n", strings[idx]);
    49
    50 free(strings);
    51#endif
    52}
    53
    54static void exit_handler(int sig)
    55{
    56 if (fuse_instance == NULL)
    57 return;
    58
    59 fuse_session_exit(fuse_instance);
    60
    61 if (sig < 0) {
    62 fuse_log(FUSE_LOG_ERR,
    63 "assertion error: signal value <= 0\n");
    64 dump_stack();
    65 abort();
    66 fuse_instance->error = sig;
    67 }
    68
    69 fuse_instance->error = sig;
    70}
    71
    72static void exit_backtrace(int sig)
    73{
    74 if (fuse_instance == NULL)
    75 return;
    76
    77 fuse_session_exit(fuse_instance);
    78
    79 fuse_remove_signal_handlers(fuse_instance);
    80 fuse_log(FUSE_LOG_ERR, "Got signal: %d\n", sig);
    81 dump_stack();
    82 abort();
    83}
    84
    85
    86static void do_nothing(int sig)
    87{
    88 (void) sig;
    89}
    90
    91static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
    92{
    93 struct sigaction sa;
    94 struct sigaction old_sa;
    95
    96 memset(&sa, 0, sizeof(struct sigaction));
    97 sa.sa_handler = remove ? SIG_DFL : handler;
    98 sigemptyset(&(sa.sa_mask));
    99 sa.sa_flags = 0;
    100
    101 if (sigaction(sig, NULL, &old_sa) == -1) {
    102 perror("fuse: cannot get old signal handler");
    103 return -1;
    104 }
    105
    106 if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
    107 sigaction(sig, &sa, NULL) == -1) {
    108 perror("fuse: cannot set signal handler");
    109 return -1;
    110 }
    111 return 0;
    112}
    113
    114static int _fuse_set_signal_handlers(int signals[], int nr_signals,
    115 void (*handler)(int))
    116{
    117 for (int idx = 0; idx < nr_signals; idx++) {
    118 int signal = signals[idx];
    119
    120 /*
    121 * If we used SIG_IGN instead of the do_nothing function,
    122 * then we would be unable to tell if we set SIG_IGN (and
    123 * thus should reset to SIG_DFL in fuse_remove_signal_handlers)
    124 * or if it was already set to SIG_IGN (and should be left
    125 * untouched.
    126 */
    127 if (set_one_signal_handler(signal, handler, 0) == -1) {
    128 fuse_log(FUSE_LOG_ERR,
    129 "Failed to install signal handler for sig %d\n",
    130 signal);
    131 return -1;
    132 }
    133 }
    134
    135 return 0;
    136}
    137
    138int fuse_set_signal_handlers(struct fuse_session *se)
    139{
    140 size_t nr_signals;
    141 int rc;
    142
    143 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
    144 rc = _fuse_set_signal_handlers(teardown_sigs, nr_signals, exit_handler);
    145 if (rc < 0)
    146 return rc;
    147
    148 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
    149 rc = _fuse_set_signal_handlers(ignore_sigs, nr_signals, do_nothing);
    150 if (rc < 0)
    151 return rc;
    152
    153 if (fuse_instance == NULL)
    154 fuse_instance = se;
    155 return 0;
    156}
    157
    158int fuse_set_fail_signal_handlers(struct fuse_session *se)
    159{
    160 size_t nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
    161
    162 int rc = _fuse_set_signal_handlers(fail_sigs, nr_signals,
    163 exit_backtrace);
    164 if (rc < 0)
    165 return rc;
    166
    167 if (fuse_instance == NULL)
    168 fuse_instance = se;
    169
    170 return 0;
    171}
    172
    173static void _fuse_remove_signal_handlers(int signals[], int nr_signals,
    174 void (*handler)(int))
    175{
    176 for (int idx = 0; idx < nr_signals; idx++)
    177 set_one_signal_handler(signals[idx], handler, 1);
    178}
    179
    180void fuse_remove_signal_handlers(struct fuse_session *se)
    181{
    182 size_t nr_signals;
    183
    184 if (fuse_instance != se)
    185 fuse_log(FUSE_LOG_ERR,
    186 "fuse: fuse_remove_signal_handlers: unknown session\n");
    187 else
    188 fuse_instance = NULL;
    189
    190 nr_signals = sizeof(teardown_sigs) / sizeof(teardown_sigs[0]);
    191 _fuse_remove_signal_handlers(teardown_sigs, nr_signals, exit_handler);
    192
    193 nr_signals = sizeof(ignore_sigs) / sizeof(ignore_sigs[0]);
    194 _fuse_remove_signal_handlers(ignore_sigs, nr_signals, do_nothing);
    195
    196 nr_signals = sizeof(fail_sigs) / sizeof(fail_sigs[0]);
    197 _fuse_remove_signal_handlers(fail_sigs, nr_signals, exit_backtrace);
    198}
    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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2helper_8c_source.html0000644000175000017500000032645715002273247023400 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 if (cond) conn->want_ext |= (cap)
    428#define LL_DISABLE(cond,cap) \
    429 if (cond) conn->want_ext &= ~(cap)
    430
    431 LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
    432 LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
    433
    434 LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE);
    435 LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE);
    436
    437 LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE);
    438 LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE);
    439
    440 LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
    441 LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA);
    442
    443 LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS);
    444 LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO);
    445
    446 LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO);
    447 LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO);
    448
    449 LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
    450 LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE);
    451
    452 LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ);
    453 LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ);
    454
    455 LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS);
    456 LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS);
    457}
    458
    459struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args)
    460{
    461 struct fuse_conn_info_opts *opts;
    462
    463 opts = calloc(1, sizeof(struct fuse_conn_info_opts));
    464 if(opts == NULL) {
    465 fuse_log(FUSE_LOG_ERR, "calloc failed\n");
    466 return NULL;
    467 }
    468 if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
    469 free(opts);
    470 return NULL;
    471 }
    472 return opts;
    473}
    474
    475int fuse_open_channel(const char *mountpoint, const char* options)
    476{
    477 struct mount_opts *opts = NULL;
    478 int fd = -1;
    479 const char *argv[] = { "", "-o", options };
    480 int argc = sizeof(argv) / sizeof(argv[0]);
    481 struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv);
    482
    483 opts = parse_mount_opts(&args);
    484 if (opts == NULL)
    485 return -1;
    486
    487 fd = fuse_kern_mount(mountpoint, opts);
    488 destroy_mount_opts(opts);
    489
    490 return fd;
    491}
    int fuse_mount(struct fuse *f, const char *mountpoint)
    Definition fuse.c:5220
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5169
    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:4602
    void fuse_lib_help(struct fuse_args *args)
    Definition fuse.c:4769
    int fuse_open_channel(const char *mountpoint, const char *options)
    Definition helper.c:475
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4545
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5225
    #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:459
    #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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2modules_2iconv_8c_source.html0000644000175000017500000035037215002273247025042 0ustar berndbernd libfuse: fuse-3.17.1.dir/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:4669
    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:4879
    fuse_fill_dir_flags
    Definition fuse.h:58
    fuse_readdir_flags
    Definition fuse.h:42
    #define FUSE_REGISTER_MODULE(name_, factory_)
    Definition fuse.h:1398
    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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2modules_2subdir_8c_source.html0000644000175000017500000033375715002273247025224 0ustar berndbernd libfuse: fuse-3.17.1.dir/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:4669
    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:4879
    fuse_readdir_flags
    Definition fuse.h:42
    #define FUSE_REGISTER_MODULE(name_, factory_)
    Definition fuse.h:1398
    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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2mount_8c_source.html0000644000175000017500000035425515002273247023260 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2mount__bsd_8c_source.html0000644000175000017500000013153115002273247024235 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2mount__util_8c_source.html0000644000175000017500000015714215002273247024450 0ustar berndbernd libfuse: fuse-3.17.1.dir/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
    80 return 1;
    81}
    82#endif /* IGNORE_MTAB */
    83
    84static int add_mount(const char *progname, const char *fsname,
    85 const char *mnt, const char *type, const char *opts)
    86{
    87 int res;
    88 int status;
    89 sigset_t blockmask;
    90 sigset_t oldmask;
    91
    92 sigemptyset(&blockmask);
    93 sigaddset(&blockmask, SIGCHLD);
    94 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    95 if (res == -1) {
    96 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    97 return -1;
    98 }
    99
    100 res = fork();
    101 if (res == -1) {
    102 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    103 goto out_restore;
    104 }
    105 if (res == 0) {
    106 char *env = NULL;
    107
    108 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    109
    110 if(setuid(geteuid()) == -1) {
    111 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    112 res = -1;
    113 goto out_restore;
    114 }
    115
    116 execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
    117 "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
    118 fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
    119 progname, strerror(errno));
    120 exit(1);
    121 }
    122 res = waitpid(res, &status, 0);
    123 if (res == -1)
    124 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    125
    126 if (status != 0)
    127 res = -1;
    128
    129 out_restore:
    130 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    131
    132 return res;
    133}
    134
    135int fuse_mnt_add_mount(const char *progname, const char *fsname,
    136 const char *mnt, const char *type, const char *opts)
    137{
    138 if (!mtab_needs_update(mnt))
    139 return 0;
    140
    141 return add_mount(progname, fsname, mnt, type, opts);
    142}
    143
    144static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
    145{
    146 int res;
    147 int status;
    148 sigset_t blockmask;
    149 sigset_t oldmask;
    150
    151 sigemptyset(&blockmask);
    152 sigaddset(&blockmask, SIGCHLD);
    153 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    154 if (res == -1) {
    155 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    156 return -1;
    157 }
    158
    159 res = fork();
    160 if (res == -1) {
    161 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    162 goto out_restore;
    163 }
    164 if (res == 0) {
    165 char *env = NULL;
    166
    167 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    168
    169 if(setuid(geteuid()) == -1) {
    170 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    171 res = -1;
    172 goto out_restore;
    173 }
    174
    175 if (lazy) {
    176 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
    177 "-l", NULL, &env);
    178 } else {
    179 execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
    180 NULL, &env);
    181 }
    182 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
    183 progname, strerror(errno));
    184 exit(1);
    185 }
    186 res = waitpid(res, &status, 0);
    187 if (res == -1)
    188 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    189
    190 if (status != 0) {
    191 res = -1;
    192 }
    193
    194 out_restore:
    195 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    196 return res;
    197
    198}
    199
    200int fuse_mnt_umount(const char *progname, const char *abs_mnt,
    201 const char *rel_mnt, int lazy)
    202{
    203 int res;
    204
    205 if (!mtab_needs_update(abs_mnt)) {
    206 res = umount2(rel_mnt, lazy ? 2 : 0);
    207 if (res == -1)
    208 fprintf(stderr, "%s: failed to unmount %s: %s\n",
    209 progname, abs_mnt, strerror(errno));
    210 return res;
    211 }
    212
    213 return exec_umount(progname, rel_mnt, lazy);
    214}
    215
    216static int remove_mount(const char *progname, const char *mnt)
    217{
    218 int res;
    219 int status;
    220 sigset_t blockmask;
    221 sigset_t oldmask;
    222
    223 sigemptyset(&blockmask);
    224 sigaddset(&blockmask, SIGCHLD);
    225 res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
    226 if (res == -1) {
    227 fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
    228 return -1;
    229 }
    230
    231 res = fork();
    232 if (res == -1) {
    233 fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
    234 goto out_restore;
    235 }
    236 if (res == 0) {
    237 char *env = NULL;
    238
    239 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    240
    241 if(setuid(geteuid()) == -1) {
    242 fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
    243 res = -1;
    244 goto out_restore;
    245 }
    246
    247 execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
    248 "--fake", mnt, NULL, &env);
    249 fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
    250 progname, strerror(errno));
    251 exit(1);
    252 }
    253 res = waitpid(res, &status, 0);
    254 if (res == -1)
    255 fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
    256
    257 if (status != 0)
    258 res = -1;
    259
    260 out_restore:
    261 sigprocmask(SIG_SETMASK, &oldmask, NULL);
    262 return res;
    263}
    264
    265int fuse_mnt_remove_mount(const char *progname, const char *mnt)
    266{
    267 if (!mtab_needs_update(mnt))
    268 return 0;
    269
    270 return remove_mount(progname, mnt);
    271}
    272
    273char *fuse_mnt_resolve_path(const char *progname, const char *orig)
    274{
    275 char buf[PATH_MAX];
    276 char *copy;
    277 char *dst;
    278 char *end;
    279 char *lastcomp;
    280 const char *toresolv;
    281
    282 if (!orig[0]) {
    283 fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
    284 orig);
    285 return NULL;
    286 }
    287
    288 copy = strdup(orig);
    289 if (copy == NULL) {
    290 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    291 return NULL;
    292 }
    293
    294 toresolv = copy;
    295 lastcomp = NULL;
    296 for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
    297 if (end[0] != '/') {
    298 char *tmp;
    299 end[1] = '\0';
    300 tmp = strrchr(copy, '/');
    301 if (tmp == NULL) {
    302 lastcomp = copy;
    303 toresolv = ".";
    304 } else {
    305 lastcomp = tmp + 1;
    306 if (tmp == copy)
    307 toresolv = "/";
    308 }
    309 if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
    310 lastcomp = NULL;
    311 toresolv = copy;
    312 }
    313 else if (tmp)
    314 tmp[0] = '\0';
    315 }
    316 if (realpath(toresolv, buf) == NULL) {
    317 fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
    318 strerror(errno));
    319 free(copy);
    320 return NULL;
    321 }
    322 if (lastcomp == NULL)
    323 dst = strdup(buf);
    324 else {
    325 dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
    326 if (dst) {
    327 unsigned buflen = strlen(buf);
    328 if (buflen && buf[buflen-1] == '/')
    329 sprintf(dst, "%s%s", buf, lastcomp);
    330 else
    331 sprintf(dst, "%s/%s", buf, lastcomp);
    332 }
    333 }
    334 free(copy);
    335 if (dst == NULL)
    336 fprintf(stderr, "%s: failed to allocate memory\n", progname);
    337 return dst;
    338}
    339
    340int fuse_mnt_check_fuseblk(void)
    341{
    342 char buf[256];
    343 FILE *f = fopen("/proc/filesystems", "r");
    344 if (!f)
    345 return 1;
    346
    347 while (fgets(buf, sizeof(buf), f))
    348 if (strstr(buf, "fuseblk\n")) {
    349 fclose(f);
    350 return 1;
    351 }
    352
    353 fclose(f);
    354 return 0;
    355}
    356
    357int fuse_mnt_parse_fuse_fd(const char *mountpoint)
    358{
    359 int fd = -1;
    360 int len = 0;
    361
    362 if (mountpoint == NULL) {
    363 fprintf(stderr, "Invalid null-ptr mount-point!\n");
    364 return -1;
    365 }
    366
    367 if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 &&
    368 len == strlen(mountpoint)) {
    369 return fd;
    370 }
    371
    372 return -1;
    373}
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2lib_2mount__util_8h_source.html0000644000175000017500000001364415002273247024453 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2lib_2util_8c_source.html0000644000175000017500000001343515002273247023063 0ustar berndbernd libfuse: fuse-3.17.1.dir/lib/util.c Source File
    libfuse
    util.c
    1#include <stdlib.h>
    2#include <errno.h>
    3
    4#include "util.h"
    5
    6int libfuse_strtol(const char *str, long *res)
    7{
    8 char *endptr;
    9 int base = 10;
    10 long val;
    11
    12 errno = 0;
    13
    14 if (!str)
    15 return -EINVAL;
    16
    17 val = strtol(str, &endptr, base);
    18
    19 if (errno)
    20 return -errno;
    21
    22 if (endptr == str || *endptr != '\0')
    23 return -EINVAL;
    24
    25 *res = val;
    26 return 0;
    27}
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2lib_2util_8h_source.html0000644000175000017500000001302615002273247023064 0ustar berndbernd libfuse: fuse-3.17.1.dir/lib/util.h Source File
    libfuse
    util.h
    1#ifndef FUSE_UTIL_H_
    2#define FUSE_UTIL_H_
    3
    4#include <stdint.h>
    5
    6#define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
    7
    8int libfuse_strtol(const char *str, long *res);
    9
    13static inline uint32_t fuse_lower_32_bits(uint64_t nr)
    14{
    15 return (uint32_t)(nr & 0xffffffff);
    16}
    17
    21static inline uint64_t fuse_higher_32_bits(uint64_t nr)
    22{
    23 return nr & ~0xffffffffULL;
    24}
    25
    26#ifndef FUSE_VAR_UNUSED
    27#define FUSE_VAR_UNUSED(var) (__attribute__((unused)) var)
    28#endif
    29
    30#endif
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2test_2readdir__inode_8c_source.html0000644000175000017500000002532415002273247025246 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2test_2release__unlink__race_8c_source.html0000644000175000017500000005430015002273247026603 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2test_2stracedecode_8c_source.html0000644000175000017500000010623215002273247024742 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2test_2test__setattr_8c_source.html0000644000175000017500000012177115002273247025206 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2test_2test__syscalls_8c_source.html0000644000175000017500000114274215002273247025357 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2test_2test__want__conversion_8c_source.html0000644000175000017500000011072015002273247027065 0ustar berndbernd libfuse: fuse-3.17.1.dir/test/test_want_conversion.c Source File
    libfuse
    test_want_conversion.c
    1#include "util.h"
    2#include <string.h>
    3#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
    4
    5#include "fuse_i.h"
    6#include <stdio.h>
    7#include <assert.h>
    8#include <inttypes.h>
    9
    10static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
    11{
    12 printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64 "\n", prefix,
    13 conn->want, conn->want_ext);
    14}
    15
    16static void application_init(struct fuse_conn_info *conn)
    17{
    18 /* Simulate application init */
    20 conn->want &= ~FUSE_CAP_SPLICE_READ;
    21}
    22
    23static void test_fuse_fs_init(struct fuse_conn_info *conn)
    24{
    25 uint64_t want_ext_default = conn->want_ext;
    26 uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
    27 int rc;
    28
    29 /* High-level init */
    30 fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
    31
    32 conn->want = want_default;
    33
    34 application_init(conn);
    35
    36 rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
    37 assert(rc == 0);
    38}
    39
    40static void test_do_init(struct fuse_conn_info *conn)
    41{
    42 /* Initial setup */
    46 conn->capable = fuse_lower_32_bits(conn->capable_ext);
    47 conn->want_ext = conn->capable_ext;
    48
    49 print_conn_info("Initial state", conn);
    50
    51 uint64_t want_ext_default = conn->want_ext;
    52 uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
    53 int rc;
    54
    55 conn->want = want_default;
    56 conn->capable = fuse_lower_32_bits(conn->capable_ext);
    57
    58 test_fuse_fs_init(conn);
    59
    60 rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
    61 assert(rc == 0);
    62
    63 /* Verify all expected flags are set */
    64 assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ));
    65 assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE);
    66 assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE);
    67 assert(conn->want_ext & FUSE_CAP_POSIX_LOCKS);
    68 assert(conn->want_ext & FUSE_CAP_FLOCK_LOCKS);
    69 assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
    70 assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
    71 /* Verify no other flags are set */
    72 assert(conn->want_ext ==
    76
    77 print_conn_info("After init", conn);
    78}
    79
    80static void test_want_conversion_basic(void)
    81{
    82 struct fuse_conn_info conn = { 0 };
    83
    84 printf("\nTesting basic want conversion:\n");
    85 test_do_init(&conn);
    86 print_conn_info("After init", &conn);
    87}
    88
    89static void test_want_conversion_conflict(void)
    90{
    91 struct fuse_conn_info conn = { 0 };
    92 int rc;
    93
    94 printf("\nTesting want conversion conflict:\n");
    95
    96 /* Test conflicting values */
    97 /* Initialize like fuse_lowlevel.c does */
    101 conn.capable = fuse_lower_32_bits(conn.capable_ext);
    102 conn.want_ext = conn.capable_ext;
    103 conn.want = fuse_lower_32_bits(conn.want_ext);
    104 print_conn_info("Test conflict initial", &conn);
    105
    106 /* Initialize default values like in basic test */
    107 uint64_t want_ext_default_ll = conn.want_ext;
    108 uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);
    109
    110 /* Simulate application init modifying capabilities */
    111 conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */
    112 conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */
    113
    114 rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
    115 want_default_ll);
    116 assert(rc == -EINVAL);
    117 print_conn_info("Test conflict after", &conn);
    118
    119 printf("Want conversion conflict test passed\n");
    120}
    121
    122static void test_want_conversion_high_bits(void)
    123{
    124 struct fuse_conn_info conn = { 0 };
    125 int rc;
    126
    127 printf("\nTesting want conversion high bits preservation:\n");
    128
    129 /* Test high bits preservation */
    130 conn.want_ext = (1ULL << 33) | FUSE_CAP_ASYNC_READ;
    131 conn.want = fuse_lower_32_bits(conn.want_ext);
    132 print_conn_info("Test high bits initial", &conn);
    133
    134 uint64_t want_ext_default_ll = conn.want_ext;
    135 uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);
    136
    137 rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
    138 want_default_ll);
    139 assert(rc == 0);
    140 assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ));
    141 print_conn_info("Test high bits after", &conn);
    142
    143 printf("Want conversion high bits test passed\n");
    144}
    145
    146int main(void)
    147{
    148 test_want_conversion_basic();
    149 test_want_conversion_conflict();
    150 test_want_conversion_high_bits();
    151 return 0;
    152}
    #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
    uint64_t capable_ext
    uint32_t capable
    uint64_t want_ext
    fuse-3.17.2/doc/html/test_2test__want__conversion_8c_source.html0000644000175000017500000011516215002273247023771 0ustar berndbernd libfuse: test/test_want_conversion.c Source File
    libfuse
    test_want_conversion.c
    1#include "util.h"
    2#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
    3
    4#include "fuse_i.h"
    5#include <stdio.h>
    6#include <assert.h>
    7#include <inttypes.h>
    8#include <stdbool.h>
    9
    10static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
    11{
    12 printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64 "\n", prefix,
    13 conn->want, conn->want_ext);
    14}
    15
    16static void application_init_old_style(struct fuse_conn_info *conn)
    17{
    18 /* Simulate application init the old style */
    20 conn->want &= ~FUSE_CAP_SPLICE_READ;
    21}
    22
    23static void application_init_new_style(struct fuse_conn_info *conn)
    24{
    25 /* Simulate application init the new style */
    26 fuse_set_feature_flag(conn, FUSE_CAP_ASYNC_READ);
    27 fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ);
    28}
    29
    30static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style)
    31{
    32 uint64_t want_ext_default = conn->want_ext;
    33 uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
    34 int rc;
    35
    36 /* High-level init */
    37 fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
    38
    39 conn->want = want_default;
    40
    41 if (new_style)
    42 application_init_new_style(conn);
    43 else
    44 application_init_old_style(conn);
    45
    46 rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
    47 assert(rc == 0);
    48}
    49
    50static void test_do_init(struct fuse_conn_info *conn, bool new_style)
    51{
    52 /* Initial setup */
    57 conn->capable = fuse_lower_32_bits(conn->capable_ext);
    58 conn->want_ext = conn->capable_ext;
    59
    60 print_conn_info("Initial state", conn);
    61
    62 uint64_t want_ext_default = conn->want_ext;
    63 uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
    64 int rc;
    65
    66 conn->want = want_default;
    67 conn->capable = fuse_lower_32_bits(conn->capable_ext);
    68
    69 test_fuse_fs_init(conn, new_style);
    70
    71 rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
    72 assert(rc == 0);
    73
    74 /* Verify all expected flags are set */
    75 assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ));
    76 assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE);
    77 assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE);
    78 assert(conn->want_ext & FUSE_CAP_POSIX_LOCKS);
    79 assert(conn->want_ext & FUSE_CAP_FLOCK_LOCKS);
    80 assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
    81 assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
    82 /* Verify no other flags are set */
    83 assert(conn->want_ext ==
    87
    88 print_conn_info("After init", conn);
    89}
    90
    91static void test_want_conversion_basic(void)
    92{
    93 struct fuse_conn_info conn = { 0 };
    94
    95 printf("\nTesting basic want conversion:\n");
    96 test_do_init(&conn, false);
    97 test_do_init(&conn, true);
    98 print_conn_info("After init", &conn);
    99}
    100
    101static void test_want_conversion_conflict(void)
    102{
    103 struct fuse_conn_info conn = { 0 };
    104 int rc;
    105
    106 printf("\nTesting want conversion conflict:\n");
    107
    108 /* Test conflicting values */
    109 /* Initialize like fuse_lowlevel.c does */
    113 conn.capable = fuse_lower_32_bits(conn.capable_ext);
    114 conn.want_ext = conn.capable_ext;
    115 conn.want = fuse_lower_32_bits(conn.want_ext);
    116 print_conn_info("Test conflict initial", &conn);
    117
    118 /* Initialize default values like in basic test */
    119 uint64_t want_ext_default_ll = conn.want_ext;
    120 uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);
    121
    122 /* Simulate application init modifying capabilities */
    123 conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */
    124 conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */
    125
    126 rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
    127 want_default_ll);
    128 assert(rc == -EINVAL);
    129 print_conn_info("Test conflict after", &conn);
    130
    131 printf("Want conversion conflict test passed\n");
    132}
    133
    134static void test_want_conversion_high_bits(void)
    135{
    136 struct fuse_conn_info conn = { 0 };
    137 int rc;
    138
    139 printf("\nTesting want conversion high bits preservation:\n");
    140
    141 /* Test high bits preservation */
    142 conn.want_ext = (1ULL << 33) | FUSE_CAP_ASYNC_READ;
    143 conn.want = fuse_lower_32_bits(conn.want_ext);
    144 print_conn_info("Test high bits initial", &conn);
    145
    146 uint64_t want_ext_default_ll = conn.want_ext;
    147 uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);
    148
    149 rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
    150 want_default_ll);
    151 assert(rc == 0);
    152 assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ));
    153 print_conn_info("Test high bits after", &conn);
    154
    155 printf("Want conversion high bits test passed\n");
    156}
    157
    158int main(void)
    159{
    160 test_want_conversion_basic();
    161 test_want_conversion_conflict();
    162 test_want_conversion_high_bits();
    163 return 0;
    164}
    #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
    uint64_t capable_ext
    uint32_t capable
    uint64_t want_ext
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2test_2test__write__cache_8c_source.html0000644000175000017500000017316115002273247026134 0ustar berndbernd libfuse: fuse-3.17.1.dir/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) {
    71 assert(fuse_get_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE));
    72 fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE);
    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
    int fuse_set_signal_handlers(struct fuse_session *se)
    #define FUSE_CAP_WRITEBACK_CACHE
    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
    void(* init)(void *userdata, struct fuse_conn_info *conn)
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2test_2wrong__command_8c_source.html0000644000175000017500000001204215002273247025301 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2util_2fusermount_8c_source.html0000644000175000017500000077626015002273247024540 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2util_2mount_8fuse_8c_source.html0000644000175000017500000021170315002273247024567 0ustar berndbernd libfuse: fuse-3.17.1.dir/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:475
    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2cuse_8c.html0000644000175000017500000012061515002273247022351 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81_8dir_2example_2cuse__client_8c.html0000644000175000017500000003227415002273247024051 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81_8dir_2example_2hello_8c.html0000644000175000017500000006751015002273247022521 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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 */
    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] <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
    #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.17.2/doc/html/test_2hello_8c.html0000644000175000017500000006463115002273413016740 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 COPYING.
    */
    #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 */
    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] <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
    #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.17.2/doc/html/fuse-3_817_81_8dir_2example_2hello__ll_8c.html0000644000175000017500000013223515002273247023344 0ustar berndbernd libfuse: fuse-3.17.1.dir/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
    

    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 COPYING.
    */
    #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:5234
    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.17.2/doc/html/fuse-3_817_81_8dir_2example_2hello__ll__uds_8c.html0000644000175000017500000013265215002273247024361 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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:5234
    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.17.2/doc/html/fuse-3_817_81_8dir_2example_2invalidate__path_8c.html0000644000175000017500000012504515002273247024707 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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:5220
    void fuse_destroy(struct fuse *f)
    Definition fuse.c:5169
    int fuse_invalidate_path(struct fuse *f, const char *path)
    Definition fuse.c:4698
    int fuse_loop(struct fuse *f)
    Definition fuse.c:4602
    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:4769
    struct fuse_session * fuse_get_session(struct fuse *f)
    Definition fuse.c:4545
    void fuse_unmount(struct fuse *f)
    Definition fuse.c:5225
    @ 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:5234
    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:77
    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.17.2/doc/html/fuse-3_817_81_8dir_2example_2ioctl_8c.html0000644000175000017500000005524215002273247022527 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81_8dir_2example_2ioctl_8h.html0000644000175000017500000001256515002273247022535 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81_8dir_2example_2ioctl__client_8c.html0000644000175000017500000001776315002273247024232 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 tests the ioctl.c example file systsem.
    This program can be distributed under the terms of the GNU GPLv2.
    See the file COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81_8dir_2example_2notify__inval__entry_8c.html0000644000175000017500000015045415002273247025636 0ustar berndbernd libfuse: fuse-3.17.1.dir/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

    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 COPYING.
    */
    #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;
    };
    static struct options options = {
    .timeout = 5,
    .no_notify = 0,
    .update_interval = 1,
    .only_expire = 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),
    };
    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;
    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");
    printf("Exiting...\n");
    // Make sure to exit now, rather than on next request from userspace
    pthread_kill(main_thread, SIGPIPE);
    break;
    }
    // 1) ret == 0: successful expire of an existing entry
    // 2) ret == -ENOENT: kernel has already expired the entry /
    // entry does not exist anymore in the kernel
    assert(ret == 0 || ret == -ENOENT);
    } else { // invalidate entry
    (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
    }
    }
    free(old_name);
    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"
    " --timeout=<secs> Timeout for kernel caches\n"
    " --update-interval=<secs> Update-rate of file system contents\n"
    " --no-notify Disable kernel notifications\n"
    " --only-expire Expire entries instead of invalidating them\n"
    "\n");
    }
    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;
    }
    /* 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:5234
    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
    #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.17.2/doc/html/fuse-3_817_81_8dir_2example_2notify__inval__inode_8c.html0000644000175000017500000015167415002273247025600 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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:5234
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file notify_inval_inode.c.

    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2notify__store__retrieve_8c.html0000644000175000017500000017165115002273247026347 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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:5234
    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:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file notify_store_retrieve.c.

    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2null_8c.html0000644000175000017500000021650715002273247022372 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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) */
    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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    static int xmp_fallocate(const char *path, int mode,
    off_t offset, off_t length, struct fuse_file_info *fi)
    {
    (void) path;
    if (mode)
    return -EOPNOTSUPP;
    return -posix_fallocate(fi->fh, offset, length);
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file null.c.

    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2passthrough_8c.html0000644000175000017500000015237315002273247023767 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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>
    #ifdef __FreeBSD__
    #include <sys/socket.h>
    #include <sys/un.h>
    #endif
    #include <sys/time.h>
    #ifdef HAVE_SETXATTR
    #include <sys/xattr.h>
    #endif
    #include "passthrough_helpers.h"
    static int fill_dir_plus = 0;
    static void *xmp_init(struct fuse_conn_info *conn,
    struct fuse_config *cfg)
    {
    (void) conn;
    cfg->use_ino = 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. */
    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 (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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    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 (mode)
    return -EOPNOTSUPP;
    if(fi == NULL)
    fd = open(path, O_WRONLY);
    else
    fd = fi->fh;
    if (fd == -1)
    return -errno;
    res = -posix_fallocate(fd, offset, length);
    if(fi == NULL)
    close(fd);
    return res;
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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 {
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file passthrough.c.

    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2passthrough__fh_8c.html0000644000175000017500000021726315002273247024603 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #define FUSE_USE_VERSION 31
    #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) */
    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;
    }
    #ifdef HAVE_POSIX_FALLOCATE
    static int xmp_fallocate(const char *path, int mode,
    off_t offset, off_t length, struct fuse_file_info *fi)
    {
    (void) path;
    if (mode)
    return -EOPNOTSUPP;
    return -posix_fallocate(fi->fh, offset, length);
    }
    #endif
    #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;
    }
    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,
    #ifdef HAVE_POSIX_FALLOCATE
    .fallocate = xmp_fallocate,
    #endif
    #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,
    };
    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
    uint32_t direct_io
    Definition fuse_common.h:71
    void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
    Definition fuse.h:641

    Definition in file passthrough_fh.c.

    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2passthrough__ll_8c.html0000644000175000017500000052443115002273247024613 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #define _GNU_SOURCE
    #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
    #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) {
    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/%lli -> %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=%lu)\n",
    ino, out_buf.buf[0].size, (unsigned long) 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 = EOPNOTSUPP;
    (void) ino;
    #ifdef HAVE_FALLOCATE
    err = fallocate(fi->fh, mode, offset, length);
    if (err < 0)
    err = errno;
    #elif defined(HAVE_POSIX_FALLOCATE)
    if (mode) {
    fuse_reply_err(req, EOPNOTSUPP);
    return;
    }
    err = posix_fallocate(fi->fh, offset, length);
    #endif
    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, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, "
    "off=%lu, ino=%" PRIu64 "/fd=%lu, "
    "off=%lu, size=%zd, flags=0x%x)\n",
    ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, 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);
    }
    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,
    };
    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;
    }
    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:5234
    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:97
    uint32_t parallel_direct_writes
    uint32_t direct_io
    Definition fuse_common.h:71
    uint32_t keep_cache
    Definition fuse_common.h:77
    void(* init)(void *userdata, struct fuse_conn_info *conn)

    Definition in file passthrough_ll.c.

    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2poll_8c.html0000644000175000017500000010024415002273247022354 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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:4669
    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:86
    uint32_t direct_io
    Definition fuse_common.h:71
    void(* destroy)(void *private_data)
    Definition fuse.h:649

    Definition in file poll.c.

    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2example_2poll__client_8c.html0000644000175000017500000002134615002273247024056 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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.17.2/doc/html/fuse-3_817_81_8dir_2example_2printcap_8c.html0000644000175000017500000011531215002273247023230 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 COPYING.
    */
    #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
    #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:5234
    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.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse_8h.html0000644000175000017500000014354615002273247022361 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 1398 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 1369 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 4458 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 5169 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 4664 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 4879 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 4669 of file fuse.c.

    ◆ fuse_get_session()

    struct fuse_session * fuse_get_session ( struct fuse *  f)

    Get session from fuse object

    Definition at line 4545 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 4679 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 4688 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 4698 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 4769 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 4602 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()

    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

    Parameters
    argcthe argument counter passed to the main() function
    argvthe argument vector passed to the main() function
    opthe file system operation
    private_dataInitial value for the private_data field of struct fuse_context. May be overridden by the struct fuse_operations.init handler.
    Returns
    0 on success, nonzero on failure

    Example usage, see hello.c

    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 5220 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 475 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 4929 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 4937 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 5225 of file fuse.c.

    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse__common_8h.html0000644000175000017500000024674615002273247024076 0ustar berndbernd libfuse: fuse-3.17.1.dir/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)
     

    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 675 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 338 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 187 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 204 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 291 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 428 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 498 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 224 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 482 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 466 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 216 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 262 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 398 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 415 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 269 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 518 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 362 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 443 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 370 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 510 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 389 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 195 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 299 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 327 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 489 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 240 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 249 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 232 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 347 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 530 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 841 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 810 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_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_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 459 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 5234 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 1907 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 180 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 158 of file fuse_signals.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 138 of file fuse_signals.c.

    ◆ fuse_version()

    int fuse_version ( void  )

    Get the version of the library

    Returns
    the version

    Definition at line 5229 of file fuse.c.

    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse__log_8h.html0000644000175000017500000003450715002273247023355 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse__lowlevel_8h.html0000644000175000017500000041454515002273247024431 0ustar berndbernd libfuse: fuse-3.17.1.dir/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 290 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 380 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 2941 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 2503 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 2490 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 2484 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 2416 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 2399 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 2609 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 2529 of file fuse_lowlevel.c.

    ◆ fuse_lowlevel_version()

    void fuse_lowlevel_version ( void  )

    Print low-level version information to stdout.

    Definition at line 2934 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 484 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 464 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 977 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 528 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 448 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 916 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 432 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 335 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 1075 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 1096 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 1005 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 269 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 960 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 1130 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 340 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 509 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 1120 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 479 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 938 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 518 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 950 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 2659 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 3533 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 2664 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 2677 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 2654 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 2951 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 3454 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 3399 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 2771 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 3218 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 3459 of file fuse_lowlevel.c.

    fuse-3.17.2/doc/html/fuse-3_817_81_8dir_2include_2fuse__opt_8h.html0000644000175000017500000007756515002273247023411 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/dir_afd1a5f89d59cae8c3bf3e89c0f7b45e.html0000644000175000017500000000661115002273247021741 0ustar berndbernd libfuse: fuse-3.17.1.dir/build Directory Reference
    libfuse
    build Directory Reference

    Directories

     meson-private
     

    Files

     fuse_config.h
     
     libfuse_config.h
     
    fuse-3.17.2/doc/html/dir_9e890e4e051b7ad89260eb605ee3a3c0.html0000644000175000017500000000415715002273247021341 0ustar berndbernd libfuse: fuse-3.17.1.dir/doc Directory Reference
    libfuse
    doc Directory Reference
    fuse-3.17.2/doc/html/dir_8e18dfdf7be85b959157088a64d07c40.html0000644000175000017500000002377215002273247021315 0ustar berndbernd libfuse: fuse-3.17.1.dir/example Directory Reference
    libfuse
    example Directory Reference
    fuse-3.17.2/doc/html/dir_d726b7599710abf1b6039c9dcf7cf687.html0000644000175000017500000001004615002273247021362 0ustar berndbernd libfuse: fuse-3.17.1.dir Directory Reference
    libfuse
    fuse-3.17.1.dir Directory Reference

    Directories

     build
     
     example
     
     include
     
     lib
     
     test
     
     util
     
    fuse-3.17.2/doc/html/dir_cb081b49593532e6f6dadd15ccd82ab4.html0000644000175000017500000001226315002273247021473 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/dir_398a6172cfdb51678e55b54be73d5fa5.html0000644000175000017500000002130715002273247021354 0ustar berndbernd libfuse: fuse-3.17.1.dir/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.17.2/doc/html/dir_a58186fda3c587e14871c11a193bc78f.html0000644000175000017500000000534415002273247021271 0ustar berndbernd libfuse: fuse-3.17.1.dir/build/meson-private Directory Reference
    libfuse
    meson-private Directory Reference

    Files

     sanitycheckc.c
     
    fuse-3.17.2/doc/html/dir_71fd611d49b15b397aa830250b7fdc25.html0000644000175000017500000000577615002273247021262 0ustar berndbernd libfuse: fuse-3.17.1.dir/lib/modules Directory Reference
    libfuse
    modules Directory Reference

    Files

     iconv.c
     
     subdir.c
     
    fuse-3.17.2/doc/html/dir_db9b1b9045937e68e72f268df7b71cec.html0000644000175000017500000001161615002273247021445 0ustar berndbernd libfuse: fuse-3.17.1.dir/test Directory Reference
    libfuse
    test Directory Reference

    Files

     readdir_inode.c
     
     release_unlink_race.c
     
     stracedecode.c
     
     test_setattr.c
     
     test_syscalls.c
     
     test_want_conversion.c
     
     test_write_cache.c
     
     wrong_command.c
     
    fuse-3.17.2/doc/html/dir_a320cda2ad42f6d84fc7a0968b7fa58c.html0000644000175000017500000000562515002273247021561 0ustar berndbernd libfuse: fuse-3.17.1.dir/util Directory Reference
    libfuse
    util Directory Reference

    Files

     fusermount.c
     
     mount.fuse.c
     
    fuse-3.17.2/example/0000755000175000017500000000000015002273413013144 5ustar berndberndfuse-3.17.2/example/cuse.c0000644000175000017500000001665215002272303014256 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 COPYING. */ /** @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.17.2/example/cuse_client.c0000644000175000017500000000604615002272303015610 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 COPYING. */ /** @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.17.2/example/cxxopts.hpp0000644000175000017500000012322315002272303015365 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.17.2/example/hello.c0000644000175000017500000001046715002272303014420 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 COPYING. */ /** @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.17.2/example/hello_ll.c0000644000175000017500000001456515002272303015112 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 COPYING. */ /** @file * * minimal example filesystem using low-level API * * Compile with: * * gcc -Wall hello_ll.c `pkg-config fuse3 --cflags --libs` -o hello_ll * * ## 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.17.2/example/hello_ll_uds.c0000644000175000017500000002011215002272303015746 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 COPYING. */ /** @file * * minimal example filesystem using low-level API and a custom io. This custom * io is implemented using UNIX domain sockets (of type SOCK_STREAM) * * Compile with: * * gcc -Wall hello_ll_uds.c `pkg-config fuse3 --cflags --libs` -o hello_ll_uds * * ## Source code ## * \include hello_ll.c */ #define FUSE_USE_VERSION 34 #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #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.17.2/example/invalidate_path.c0000644000175000017500000001554615002272303016454 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 COPYING. */ /** @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.17.2/example/ioctl.c0000644000175000017500000001047215002272303014423 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 COPYING. */ /** @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.17.2/example/ioctl.h0000644000175000017500000000163715002272303014433 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 COPYING. */ /** @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.17.2/example/ioctl_client.c0000644000175000017500000000250415002272303015756 0ustar berndbernd/* FUSE fioclient: FUSE ioctl example client Copyright (C) 2008 SUSE Linux Products GmbH Copyright (C) 2008 Tejun Heo This program tests the ioctl.c example file systsem. This program can be distributed under the terms of the GNU GPLv2. See the file COPYING. */ /** @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.17.2/example/memfs_ll.cc0000644000175000017500000006046115002272303015255 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 COPYING. */ #include #define FUSE_USE_VERSION 317 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 lock(mutex); 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 (flags & (RENAME_EXCHANGE | RENAME_NOREPLACE)) { fuse_reply_err(req, EINVAL); return; } 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); } static const struct fuse_lowlevel_ops memfs_oper = { .init = nullptr, .destroy = nullptr, .lookup = memfs_lookup, .forget = memfs_forget, .getattr = memfs_getattr, .setattr = memfs_setattr, .readlink = nullptr, .mknod = nullptr, .mkdir = memfs_mkdir, .unlink = memfs_unlink, .rmdir = memfs_rmdir, .symlink = nullptr, .rename = memfs_rename, .link = memfs_link, .open = memfs_open, .read = memfs_read, .write = memfs_write, .flush = nullptr, .release = memfs_release, .fsync = nullptr, .opendir = memfs_opendir, .readdir = memfs_readdir, .releasedir = memfs_releasedir, .fsyncdir = nullptr, .statfs = memfs_statfs, .setxattr = nullptr, .getxattr = nullptr, .listxattr = nullptr, .removexattr = nullptr, .access = nullptr, .create = memfs_create, .getlk = nullptr, .setlk = nullptr, .bmap = nullptr, .ioctl = nullptr, .poll = nullptr, .write_buf = nullptr, .retrieve_reply = nullptr, .forget_multi = memfs_forget_multi, .flock = nullptr, .fallocate = nullptr, .readdirplus = nullptr, .copy_file_range = nullptr, .lseek = nullptr, .tmpfile = nullptr, }; 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.17.2/example/meson.build0000644000175000017500000000273015002272303015305 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 not platform.endswith('bsd') and 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++17', install: false) endif # TODO: Link passthrough_fh with ulockmgr if available fuse-3.17.2/example/notify_inval_entry.c0000644000175000017500000002650615002272303017240 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 COPYING. */ /** @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 * * ## 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; }; static struct options options = { .timeout = 5, .no_notify = 0, .update_interval = 1, .only_expire = 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), 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; while(!fuse_session_exited(se)) { old_name = strdup(file_name); update_fs(); if (!options.no_notify && lookup_cnt) { if(options.only_expire) { // expire entry int ret = fuse_lowlevel_notify_expire_entry (se, FUSE_ROOT_ID, old_name, strlen(old_name)); // no kernel support if (ret == -ENOSYS) { printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n"); printf("Exiting...\n"); fuse_session_exit(se); // Make sure to exit now, rather than on next request from userspace pthread_kill(main_thread, SIGPIPE); break; } // 1) ret == 0: successful expire of an existing entry // 2) ret == -ENOENT: kernel has already expired the entry / // entry does not exist anymore in the kernel assert(ret == 0 || ret == -ENOENT); } else { // invalidate entry assert(fuse_lowlevel_notify_inval_entry (se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0); } } free(old_name); sleep(options.update_interval); } 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" "\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; } /* 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.17.2/example/notify_inval_inode.c0000644000175000017500000002503315002272303017167 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 COPYING. */ /** @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.17.2/example/notify_store_retrieve.c0000644000175000017500000003123215002272303017737 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 COPYING. */ /** @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.17.2/example/null.c0000644000175000017500000000542715002272303014267 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 COPYING. */ /** @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.17.2/example/passthrough.c0000644000175000017500000002512115002272303015655 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 COPYING. */ /** @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 31 #define _GNU_SOURCE #ifdef linux /* For pread()/pwrite()/utimensat() */ #define _XOPEN_SOURCE 700 #endif #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #include #endif #include #ifdef HAVE_SETXATTR #include #endif #include "passthrough_helpers.h" static int fill_dir_plus = 0; static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { (void) conn; cfg->use_ino = 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. */ 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 (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; } #ifdef HAVE_POSIX_FALLOCATE 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 (mode) return -EOPNOTSUPP; if(fi == NULL) fd = open(path, O_WRONLY); else fd = fi->fh; if (fd == -1) return -errno; res = -posix_fallocate(fd, offset, length); if(fi == NULL) close(fd); return res; } #endif #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; } 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, #ifdef HAVE_POSIX_FALLOCATE .fallocate = xmp_fallocate, #endif #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, }; 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 COPYING. */ /** @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 31 #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) */ 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; } #ifdef HAVE_POSIX_FALLOCATE static int xmp_fallocate(const char *path, int mode, off_t offset, off_t length, struct fuse_file_info *fi) { (void) path; if (mode) return -EOPNOTSUPP; return -posix_fallocate(fi->fh, offset, length); } #endif #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; } 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, #ifdef HAVE_POSIX_FALLOCATE .fallocate = xmp_fallocate, #endif #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, }; int main(int argc, char *argv[]) { umask(0); return fuse_main(argc, argv, &xmp_oper, NULL); } fuse-3.17.2/example/passthrough_helpers.h0000644000175000017500000000475515002272303017416 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 */ /* * Creates files on the underlying file system in response to a FUSE_MKNOD * operation */ static 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; } fuse-3.17.2/example/passthrough_hp.cc0000644000175000017500000013350615002272303016516 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 COPYING. */ /** @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 #include #include #include #include #include // C++ includes #include #include #include #include "cxxopts.hpp" #include #include 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}; uint64_t 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 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 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 { char procname[64]; 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 : 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 { char procname[64]; 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 { #ifdef HAVE_UTIMENSAT char procname[64]; sprintf(procname, "/proc/self/fd/%i", ifd); res = 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 fs_lock.unlock(); if (fs.debug) cerr << "DEBUG: lookup(): inode " << e->attr.st_ino << " (userspace) already known; fd = " << inode.fd << endl; lock_guard g {inode.m}; inode.nlookup++; if (fs.debug) cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " " << "inode " << inode.src_ino << " count " << inode.nlookup << endl; close(newfd); } else { // no existing inode /* This is just here to make Helgrind happy. It violates the 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); { lock_guard g {inode.m}; inode.nlookup++; if (fs.debug) cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " " << "inode " << inode.src_ino << " count " << inode.nlookup << endl; } fuse_reply_entry(req, &e); 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) { if (fs.debug) cerr << "DEBUG: forget: cleaning up inode " << inode.src_ino << endl; { lock_guard g_fs {fs.mutex}; l.unlock(); 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); } 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); } 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). */ char buf[64]; sprintf(buf, "/proc/self/fd/%i", inode.fd); auto fd = 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); 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); } #ifdef HAVE_POSIX_FALLOCATE 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; if (mode) { fuse_reply_err(req, EOPNOTSUPP); return; } auto err = posix_fallocate(fi->fh, offset, length); fuse_reply_err(req, err); } #endif 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; sfs_oper.tmpfile = sfs_tmpfile; 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; #ifdef HAVE_POSIX_FALLOCATE sfs_oper.fallocate = sfs_fallocate; #endif 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); 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"); 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.17.2/example/passthrough_ll.c0000644000175000017500000007745115002272303016361 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 COPYING. */ /** @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, 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 ## * \include passthrough_ll.c */ #define _GNU_SOURCE #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) #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/%lli -> %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=%lu)\n", ino, out_buf.buf[0].size, (unsigned long) 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 = EOPNOTSUPP; (void) ino; #ifdef HAVE_FALLOCATE err = fallocate(fi->fh, mode, offset, length); if (err < 0) err = errno; #elif defined(HAVE_POSIX_FALLOCATE) if (mode) { fuse_reply_err(req, EOPNOTSUPP); return; } err = posix_fallocate(fi->fh, offset, length); #endif 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, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " "off=%lu, ino=%" PRIu64 "/fd=%lu, " "off=%lu, size=%zd, flags=0x%x)\n", ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, 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); } 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, }; 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.17.2/example/poll.c0000644000175000017500000001502115002272303014252 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 COPYING. */ /** @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.17.2/example/poll_client.c0000644000175000017500000000302515002272303015611 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 COPYING. */ /** @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.17.2/example/printcap.c0000644000175000017500000000745115002272303015134 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 COPYING. */ /** @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.17.2/include/0000755000175000017500000000000015002272303013131 5ustar berndberndfuse-3.17.2/include/cuse_lowlevel.h0000644000175000017500000000501515002272303016153 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 COPYING.LIB. Read example/cusexmp.c for usages. */ #ifndef CUSE_LOWLEVEL_H_ #define CUSE_LOWLEVEL_H_ #ifndef FUSE_USE_VERSION #define FUSE_USE_VERSION 29 #endif #include "fuse_lowlevel.h" #include #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.17.2/include/fuse.h0000644000175000017500000013756715002272303014267 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 COPYING.LIB. */ #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 * * ----------------------------------------------------------- */ /** 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: 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. */ 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 *); }; /** 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); 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.17.2/include/fuse_common.h0000644000175000017500000010763715002272303015632 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 COPYING.LIB. */ /** @file */ #include #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 #define FUSE_MAKE_VERSION(maj, min) ((maj) * 100 + (min)) #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) #ifdef HAVE_STATIC_ASSERT #define fuse_static_assert(condition, message) static_assert(condition, message) #else #define fuse_static_assert(condition, message) #endif #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]; }; fuse_static_assert(sizeof(struct fuse_file_info) == 64, "fuse_file_info size mismatch"); /** * 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 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 (1 << 25) /** * Indicates support that dentries can be expired. * * Expiring dentries, instead of invalidating them, makes a difference for * overmounted dentries, where plain invalidation would detach all submounts * before dropping the dentry from the cache. If only expiry is set on the * dentry, then any overmounts are left alone and until ->d_revalidate() * is called. * * Note: ->d_revalidate() is not called for the case of following a submount, * so invalidation will only be triggered for the non-overmounted case. * The dentry could also be mounted in a different mount instance, in which case * any submounts will still be detached. */ #define FUSE_CAP_EXPIRE_ONLY (1 << 26) /** * Indicates that an extended 'struct fuse_setxattr' is used by the kernel * side - extra_flags are passed, which are used (as of now by acl) processing. * For example FUSE_SETXATTR_ACL_KILL_SGID might be set. */ #define FUSE_CAP_SETXATTR_EXT (1 << 27) /** * 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 (1 << 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 (1 << 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 (1 << 30) /** * 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; /** * For future use. */ uint32_t reserved[16]; }; fuse_static_assert(sizeof(struct fuse_conn_info) == 128, "Size of struct fuse_conn_info must be 128 bytes"); 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 static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag) { if (conn->capable_ext & flag) { conn->want_ext |= flag; return true; } return false; } static inline void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag) { conn->want_ext &= ~flag; } static inline bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag) { return conn->capable_ext & flag ? true : false; } /* ----------------------------------------------------------- * * 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.17.2/include/fuse_kernel.h0000644000175000017500000007036015002272303015612 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 */ #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 40 /** 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 */ #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 /** * 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, /* 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_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; uint32_t unused[6]; }; #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) 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; }; #define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0) #define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1) struct fuse_setupmapping_in { /* An already open handle */ uint64_t fh; /* Offset into the file to start the mapping */ uint64_t foffset; /* Length of mapping required */ uint64_t len; /* Flags, FUSE_SETUPMAPPING_FLAG_* */ uint64_t flags; /* Offset in Memory Window */ uint64_t moffset; }; struct fuse_removemapping_in { /* number of fuse_removemapping_one follows */ uint32_t count; }; struct fuse_removemapping_one { /* Offset into the dax window start the unmapping */ uint64_t moffset; /* Length of mapping required */ uint64_t len; }; #define FUSE_REMOVEMAPPING_MAX_ENTRY \ (PAGE_SIZE / sizeof(struct fuse_removemapping_one)) struct fuse_syncfs_in { uint64_t padding; }; /* * For each security context, send fuse_secctx with size of security context * fuse_secctx will be followed by security context name and this in turn * will be followed by actual context label. * fuse_secctx, name, context */ struct fuse_secctx { uint32_t size; uint32_t padding; }; /* * Contains the information about how many fuse_secctx structures are being * sent and what's the total size of all security contexts (including * size of fuse_secctx_header). * */ struct fuse_secctx_header { uint32_t size; uint32_t nr_secctx; }; /** * struct fuse_ext_header - extension header * @size: total size of this extension including this header * @type: type of extension * * This is made compatible with fuse_secctx_header by using type values > * FUSE_MAX_NR_SECCTX */ struct fuse_ext_header { uint32_t size; uint32_t type; }; /** * struct fuse_supp_groups - Supplementary group extension * @nr_groups: number of supplementary groups * @groups: flexible array of group IDs */ struct fuse_supp_groups { uint32_t nr_groups; uint32_t groups[]; }; #endif /* _LINUX_FUSE_H */ fuse-3.17.2/include/fuse_log.h0000644000175000017500000000430415002272303015106 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 COPYING.LIB. */ #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, ...); /** * 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.17.2/include/fuse_lowlevel.h0000644000175000017500000022570315002272303016166 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 COPYING.LIB. */ #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; /** * 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. * * 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 */ 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 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 * * @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); }; /** * 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); /* ----------------------------------------------------------- * * 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 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. * * 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); #ifdef __cplusplus } #endif #endif /* FUSE_LOWLEVEL_H_ */ fuse-3.17.2/include/fuse_mount_compat.h0000644000175000017500000000204615002272303017033 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.17.2/include/fuse_opt.h0000644000175000017500000001657515002272303015144 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 COPYING.LIB. */ #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.17.2/include/meson.build0000644000175000017500000000026215002272303015273 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.17.2/lib/0000755000175000017500000000000015002272303012254 5ustar berndberndfuse-3.17.2/lib/buffer.c0000644000175000017500000001471615002272303013702 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 COPYING.LIB */ #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.17.2/lib/compat.c0000644000175000017500000000534015002272303013705 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 COPYING.LIB. */ /* 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.17.2/lib/cuse_lowlevel.c0000644000175000017500000002145415002272303015276 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 COPYING.LIB. */ #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, fuse_ino_t nodeid, const void *inarg) { struct fuse_init_in *arg = (struct fuse_init_in *) inarg; 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); } 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.17.2/lib/fuse.c0000644000175000017500000035230715002272303013374 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 COPYING.LIB */ #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 container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #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; } 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) { 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); } else { return -ENOSYS; } } 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) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "rename %s %s 0x%x\n", oldpath, newpath, flags); return fs->op.rename(oldpath, newpath, flags); } else { return -ENOSYS; } } int fuse_fs_unlink(struct fuse_fs *fs, const char *path) { fuse_get_context()->private_data = fs->user_data; if (fs->op.unlink) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "unlink %s\n", path); return fs->op.unlink(path); } else { return -ENOSYS; } } int fuse_fs_rmdir(struct fuse_fs *fs, const char *path) { fuse_get_context()->private_data = fs->user_data; if (fs->op.rmdir) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path); return fs->op.rmdir(path); } else { return -ENOSYS; } } 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) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "symlink %s %s\n", linkname, path); return fs->op.symlink(linkname, path); } else { return -ENOSYS; } } 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) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "link %s %s\n", oldpath, newpath); return fs->op.link(oldpath, newpath); } else { return -ENOSYS; } } 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) { 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); } else { return 0; } } int fuse_fs_opendir(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (fs->op.opendir) { int err; 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; } else { return 0; } } int fuse_fs_open(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (fs->op.open) { int err; 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; } else { return 0; } } 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) { fuse_get_context()->private_data = fs->user_data; if (fs->op.read || fs->op.read_buf) { int res; 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; } else { return -ENOSYS; } } int fuse_fs_read(struct fuse_fs *fs, const char *path, char *mem, size_t size, off_t off, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (fs->op.read || fs->op.read_buf) { int res; 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; } else { return -ENOSYS; } } int fuse_fs_write_buf(struct fuse_fs *fs, const char *path, struct fuse_bufvec *buf, off_t off, struct fuse_file_info *fi) { fuse_get_context()->private_data = fs->user_data; if (fs->op.write_buf || fs->op.write) { int res; size_t 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; } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "flush[%llu]\n", (unsigned long long) fi->fh); return fs->op.flush(path, fi); } else { return -ENOSYS; } } 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) { 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); } else { return 0; } } 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) { 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); } else { return -ENOSYS; } } int fuse_fs_create(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.create) { int err; 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; } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { if (fs->debug) { char buf[10]; fuse_log(FUSE_LOG_DEBUG, "utimens[%s] %s %li.%09lu %li.%09lu\n", file_info_string(fi, buf, sizeof(buf)), path, tv[0].tv_sec, tv[0].tv_nsec, tv[1].tv_sec, tv[1].tv_nsec); } return fs->op.utimens(path, tv, fi); } else { return -ENOSYS; } } 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) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "access %s 0%o\n", path, mask); return fs->op.access(path, mask); } else { return -ENOSYS; } } 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) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "readlink %s %lu\n", path, (unsigned long) len); return fs->op.readlink(path, buf, len); } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "listxattr %s %lu\n", path, (unsigned long) size); return fs->op.listxattr(path, list, size); } else { return -ENOSYS; } } 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) { 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); } else { return -ENOSYS; } } 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) { if (fs->debug) fuse_log(FUSE_LOG_DEBUG, "removexattr %s %s\n", path, name); return fs->op.removexattr(path, name); } else { return -ENOSYS; } } 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) { 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); } else return -ENOSYS; } int fuse_fs_poll(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp) { fuse_get_context()->private_data = fs->user_data; if (fs->op.poll) { int res; 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; } else return -ENOSYS; } 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) { 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); } else return -ENOSYS; } 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) { 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); } else return -ENOSYS; } 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) { 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); } else { return -ENOSYS; } } 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) { uint64_t want_ext_default = conn->want_ext; uint32_t want_default = fuse_lower_32_bits(conn->want_ext); int rc; conn->want = want_default; fs->user_data = fs->op.init(conn, cfg); rc = convert_to_conn_want_ext(conn, want_ext_default, want_default); if (rc != 0) { /* * This is a grave developer error, but * we cannot return an error here, as the function * signature does not allow it. */ fuse_log( FUSE_LOG_ERR, "fuse: Aborting due to invalid conn want flags.\n"); _exit(EXIT_FAILURE); } } } 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; 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) { 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); } else return -ENOSYS; } 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; } else { 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); } 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, }; 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; } } free(fbuf.mem); fuse_session_reset(se); 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; #ifdef HAVE_PTHREAD_SETNAME_NP pthread_setname_np(pthread_self(), "fuse_prune_nodes"); #endif 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; if (f->conf.debug) { fuse_log(FUSE_LOG_DEBUG, "nullpath_ok: %i\n", f->conf.nullpath_ok); } /* 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.17.2/lib/fuse_i.h0000644000175000017500000001603415002272303013703 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 COPYING.LIB */ #include "fuse.h" #include "fuse_lowlevel.h" #include "util.h" #include #include #include #define MIN(a, b) \ ({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a < _b ? _a : _b; \ }) struct mount_opts; 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; unsigned int ioctl_64bit : 1; union { struct { uint64_t unique; } i; struct { fuse_interrupt_func_t func; void *data; } ni; } u; struct fuse_req *next; struct fuse_req *prev; }; struct fuse_notify_req { uint64_t unique; void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, const void *, const struct fuse_buf *); struct fuse_notify_req *next; struct fuse_notify_req *prev; }; struct fuse_session { char *mountpoint; volatile int exited; 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; /* true if reading requests from /dev/fuse are handled internally */ bool buf_reallocable; }; 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 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 /** * Get the wanted capability flags, converting from old format if necessary */ static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn, uint64_t want_ext_default, uint32_t want_default) { /* * 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 != want_default && fuse_lower_32_bits(conn->want_ext) != conn->want) { if (conn->want_ext != want_ext_default) { fuse_log(FUSE_LOG_ERR, "fuse: both 'want' and 'want_ext' are set\n"); 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; } fuse-3.17.2/lib/fuse_log.c0000644000175000017500000000345515002272303014232 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 COPYING.LIB */ #include "fuse_log.h" #include #include #include #include #define MAX_SYSLOG_LINE_LEN 512 static bool to_syslog = false; static void default_log_func(__attribute__((unused)) enum fuse_log_level level, const char *fmt, va_list ap) { if (to_syslog) { int sys_log_level = LOG_ERR; /* * with glibc fuse_log_level has identical values as * syslog levels, but we also support BSD - better we convert to * be sure. */ switch (level) { case FUSE_LOG_DEBUG: sys_log_level = LOG_DEBUG; break; case FUSE_LOG_INFO: sys_log_level = LOG_INFO; break; case FUSE_LOG_NOTICE: sys_log_level = LOG_NOTICE; break; case FUSE_LOG_WARNING: sys_log_level = LOG_WARNING; break; case FUSE_LOG_ERR: sys_log_level = LOG_ERR; break; case FUSE_LOG_CRIT: sys_log_level = LOG_CRIT; break; case FUSE_LOG_ALERT: sys_log_level = LOG_ALERT; break; case FUSE_LOG_EMERG: sys_log_level = LOG_EMERG; } char log[MAX_SYSLOG_LINE_LEN]; vsnprintf(log, MAX_SYSLOG_LINE_LEN, fmt, ap); syslog(sys_log_level, "%s", log); } 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.17.2/lib/fuse_loop.c0000644000175000017500000000160315002272303014413 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 COPYING.LIB */ #include "fuse_config.h" #include "fuse_lowlevel.h" #include "fuse_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; fuse_session_reset(se); return res; } fuse-3.17.2/lib/fuse_loop_mt.c0000644000175000017500000003002015002272303015106 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 COPYING.LIB. */ #define _GNU_SOURCE #include "fuse_config.h" #include "fuse_lowlevel.h" #include "fuse_misc.h" #include "fuse_kernel.h" #include "fuse_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; }; struct fuse_mt { pthread_mutex_t lock; int numworker; int numavail; struct fuse_session *se; struct fuse_worker main; sem_t finish; int exit; 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; #ifdef HAVE_PTHREAD_SETNAME_NP pthread_setname_np(pthread_self(), "fuse_worker"); #endif while (!fuse_session_exited(mt->se)) { int isforget = 0; int res; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); res = fuse_session_receive_buf_internal(mt->se, &w->fbuf, w->ch); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); if (res == -EINTR) continue; if (res <= 0) { if (res < 0) { fuse_session_exit(mt->se); mt->error = res; } break; } pthread_mutex_lock(&mt->lock); if (mt->exit) { pthread_mutex_unlock(&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) fuse_loop_start_thread(mt); pthread_mutex_unlock(&mt->lock); fuse_session_process_buf_internal(mt->se, &w->fbuf, w->ch); pthread_mutex_lock(&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 (mt->exit) { pthread_mutex_unlock(&mt->lock); return NULL; } list_del_worker(w); mt->numavail--; mt->numworker--; pthread_mutex_unlock(&mt->lock); pthread_detach(w->thread_id); fuse_buf_free(&w->fbuf); fuse_chan_put(w->ch); free(w); return NULL; } pthread_mutex_unlock(&mt->lock); } sem_post(&mt->finish); return NULL; } int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg) { sigset_t oldset; sigset_t newset; int res; pthread_attr_t attr; char *stack_size; /* Override default stack size * 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; } #ifndef O_CLOEXEC fcntl(clonefd, F_SETFD, FD_CLOEXEC); #endif 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->lock); list_del_worker(w); pthread_mutex_unlock(&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; sem_init(&mt.finish, 0, 0); pthread_mutex_init(&mt.lock, NULL); pthread_mutex_lock(&mt.lock); err = fuse_loop_start_thread(&mt); pthread_mutex_unlock(&mt.lock); if (!err) { /* sem_wait() is interruptible */ while (!fuse_session_exited(se)) sem_wait(&mt.finish); pthread_mutex_lock(&mt.lock); for (w = mt.main.next; w != &mt.main; w = w->next) pthread_cancel(w->thread_id); mt.exit = 1; pthread_mutex_unlock(&mt.lock); while (mt.main.next != &mt.main) fuse_join_worker(&mt, mt.main.next); err = mt.error; } pthread_mutex_destroy(&mt.lock); sem_destroy(&mt.finish); if(se->error != 0) err = se->error; fuse_session_reset(se); if (created_config) { fuse_loop_cfg_destroy(config); config = NULL; } return err; } int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1); FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2") int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1) { int err; struct fuse_loop_config *config = NULL; if (config_v1 != NULL) { /* convert the given v1 config */ config = fuse_loop_cfg_create(); if (config == NULL) return ENOMEM; fuse_loop_cfg_convert(config, config_v1); } err = fuse_session_loop_mt_312(se, config); fuse_loop_cfg_destroy(config); return err; } int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd); FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0") int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd) { 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.17.2/lib/fuse_lowlevel.c0000644000175000017500000026012115002272303015275 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 COPYING.LIB */ #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 #include #include #include #include #include #include #include #include #include #include #include #include #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 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) 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(); } 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; } static 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) { 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; if (se->conn.no_interrupt) { 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. If *ch* is NULL, send via session master fd */ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, struct iovec *iov, int count) { struct fuse_out_header *out = iov[0].iov_base; 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); } } ssize_t res; if (se->io != NULL) /* se->io->writev is never NULL if se->io is not NULL as specified by fuse_session_custom_io()*/ res = se->io->writev(ch ? ch->fd : se->fd, iov, count, se->userdata); else res = writev(ch ? ch->fd : se->fd, iov, count); int err = errno; if (res == -1) { /* ENOENT means the operation was interrupted */ if (!fuse_session_exited(se) && err != ENOENT) perror("fuse: writing device"); return -err; } return 0; } 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); } 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) { 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)); } int 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)); } 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) { 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); } 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); 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 %zi\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) { 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); 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); } #else 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) { size_t len = fuse_buf_size(buf); (void) flags; return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); } #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; 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); 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->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)); } static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { char *name = (char *) inarg; if (req->se->op.lookup) req->se->op.lookup(req, nodeid, name); else fuse_reply_err(req, ENOSYS); } static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg; if (req->se->op.forget) req->se->op.forget(req, nodeid, arg->nlookup); else fuse_reply_none(req); } static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_batch_forget_in *arg = (void *) inarg; struct fuse_forget_one *param = (void *) PARAM(arg); unsigned int i; (void) nodeid; if (req->se->op.forget_multi) { req->se->op.forget_multi(req, arg->count, (struct fuse_forget_data *) param); } else if (req->se->op.forget) { for (i = 0; i < arg->count; i++) { struct fuse_forget_one *forget = ¶m[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_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_file_info *fip = NULL; struct fuse_file_info fi; if (req->se->conn.proto_minor >= 9) { struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg; 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_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg; 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) { arg->valid &= ~FATTR_FH; memset(&fi_store, 0, sizeof(fi_store)); fi = &fi_store; fi->fh = arg->fh; } arg->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, arg->valid, fi); } else fuse_reply_err(req, ENOSYS); } static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_access_in *arg = (struct fuse_access_in *) inarg; if (req->se->op.access) req->se->op.access(req, nodeid, arg->mask); else fuse_reply_err(req, ENOSYS); } static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { (void) inarg; if (req->se->op.readlink) req->se->op.readlink(req, nodeid); else fuse_reply_err(req, ENOSYS); } static void do_mknod(fuse_req_t req, 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) req->ctx.umask = arg->umask; else name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE; 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_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg; if (req->se->conn.proto_minor >= 12) req->ctx.umask = arg->umask; if (req->se->op.mkdir) req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); else fuse_reply_err(req, ENOSYS); } static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { char *name = (char *) inarg; if (req->se->op.unlink) req->se->op.unlink(req, nodeid, name); else fuse_reply_err(req, ENOSYS); } static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { char *name = (char *) inarg; if (req->se->op.rmdir) req->se->op.rmdir(req, nodeid, name); else fuse_reply_err(req, ENOSYS); } static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { char *name = (char *) inarg; char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1; if (req->se->op.symlink) req->se->op.symlink(req, linkname, nodeid, name); else fuse_reply_err(req, ENOSYS); } static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg; char *oldname = PARAM(arg); 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_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg; char *oldname = PARAM(arg); 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_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_link_in *arg = (struct fuse_link_in *) inarg; if (req->se->op.link) req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); 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; 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_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_create_in *arg = (struct fuse_create_in *) inarg; if (req->se->op.create) { struct fuse_file_info fi; char *name = PARAM(arg); memset(&fi, 0, sizeof(fi)); fi.flags = arg->flags; if (req->se->conn.proto_minor >= 12) req->ctx.umask = arg->umask; else name = (char *) inarg + sizeof(struct fuse_open_in); req->se->op.create(req, nodeid, name, arg->mode, &fi); } else fuse_reply_err(req, ENOSYS); } static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_open_in *arg = (struct fuse_open_in *) inarg; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = arg->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_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_read_in *arg = (struct fuse_read_in *) inarg; 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_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_write_in *arg = (struct fuse_write_in *) inarg; struct fuse_file_info fi; char *param; 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) { param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; } else { fi.lock_owner = arg->lock_owner; fi.flags = arg->flags; param = PARAM(arg); } if (req->se->op.write) req->se->op.write(req, nodeid, param, arg->size, arg->offset, &fi); else fuse_reply_err(req, ENOSYS); } static void do_write_buf(fuse_req_t req, 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; 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) { 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 { fi.lock_owner = arg->lock_owner; fi.flags = arg->flags; 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: do_write_buf: buffer size too small\n"); fuse_reply_err(req, EIO); goto out; } bufv.buf[0].size = arg->size; se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); 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, fuse_ino_t nodeid, const void *inarg) { struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg; 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_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_release_in *arg = (struct fuse_release_in *) inarg; 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_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; 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_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_open_in *arg = (struct fuse_open_in *) inarg; struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = arg->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_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_read_in *arg = (struct fuse_read_in *) inarg; 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_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_read_in *arg = (struct fuse_read_in *) inarg; 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_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_release_in *arg = (struct fuse_release_in *) inarg; 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_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; 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_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { (void) nodeid; (void) inarg; 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_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_session *se = req->se; unsigned int xattr_ext = !!(se->conn.want_ext & FUSE_CAP_SETXATTR_EXT); struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; char *name = xattr_ext ? PARAM(arg) : (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE; char *value = name + strlen(name) + 1; /* XXX:The API should be extended to support extra_flags/setxattr_flags */ if (req->se->op.setxattr) req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags); else fuse_reply_err(req, ENOSYS); } static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; if (req->se->op.getxattr) req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size); else fuse_reply_err(req, ENOSYS); } static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; if (req->se->op.listxattr) req->se->op.listxattr(req, nodeid, arg->size); else fuse_reply_err(req, ENOSYS); } static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { char *name = (char *) inarg; if (req->se->op.removexattr) req->se->op.removexattr(req, nodeid, name); else fuse_reply_err(req, ENOSYS); } static void convert_fuse_file_lock(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, fuse_ino_t nodeid, const void *inarg) { struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; 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_setlk_common(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, int sleep) { struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; 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, fuse_ino_t nodeid, const void *inarg) { do_setlk_common(req, nodeid, inarg, 0); } static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { do_setlk_common(req, nodeid, inarg, 1); } 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, fuse_ino_t nodeid, const void *inarg) { struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg; 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 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, fuse_ino_t nodeid, const void *inarg) { struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg; if (req->se->op.bmap) req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); else fuse_reply_err(req, ENOSYS); } static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg; unsigned int flags = arg->flags; void *in_buf = arg->in_size ? PARAM(arg) : NULL; 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->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); } void fuse_pollhandle_destroy(struct fuse_pollhandle *ph) { free(ph); } static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg; 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_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg; 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_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg) { struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg; 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_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg; 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 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%08lx\n", unknown_flags); return false; } return true; } /* 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, fuse_ino_t nodeid, const void *inarg) { struct fuse_init_in *arg = (struct fuse_init_in *) inarg; 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; 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; } 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); /* 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; se->got_init = 1; if (se->op.init) { uint64_t want_ext_default = se->conn.want_ext; uint32_t want_default = fuse_lower_32_bits(se->conn.want_ext); int rc; // Apply the first 32 bits of capable_ext to capable se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext); se->conn.want = want_default; 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 */ rc = convert_to_conn_want_ext(&se->conn, want_ext_default, want_default); if (rc != 0) { fuse_reply_err(req, EPROTO); se->error = -EPROTO; fuse_session_exit(se); return; } } 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 (inargflags & FUSE_INIT_EXT) { outargflags |= FUSE_INIT_EXT; outarg.flags2 = outargflags >> 32; } outarg.flags = outargflags; outarg.max_readahead = se->conn.max_readahead; outarg.max_write = se->conn.max_write; if (se->conn.proto_minor >= 13) { 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; send_reply_ok(req, &outarg, outargsize); } static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_session *se = req->se; (void) nodeid; (void) inarg; 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 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; 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); } 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); } /** * 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; 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); 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; } static struct { void (*func)(fuse_req_t, fuse_ino_t, const void *); 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_LSEEK] = { do_lseek, "LSEEK" }, [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) 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; } if (se->debug) { fuse_log(FUSE_LOG_DEBUG, "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); goto clear_pipe; } req->unique = in->unique; req->ctx.uid = in->uid; req->ctx.gid = in->gid; req->ctx.pid = in->pid; req->ch = ch ? fuse_chan_get(ch) : NULL; err = EIO; if (!se->got_init) { enum fuse_opcode expected; expected = se->cuse_data ? CUSE_INIT : FUSE_INIT; if (in->opcode != expected) goto reply_err; } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) goto reply_err; err = EACCES; /* Implement -o allow_root */ if (se->deny_others && in->uid != se->owner && in->uid != 0 && in->opcode != FUSE_INIT && in->opcode != FUSE_READ && in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR && in->opcode != FUSE_NOTIFY_REPLY && in->opcode != FUSE_READDIRPLUS) 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; } #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), 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"); } 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); 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; 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; 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 (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->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; /* 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); 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: 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) { /* unknown version */ struct libfuse_version version = { 0 }; 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; if (mountpoint == NULL) { fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n"); 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); return -1; } se->fd = fd; return 0; } /* Open channel */ fd = fuse_kern_mount(mountpoint, se->mo); if (fd == -1) return -1; se->fd = fd; /* Save mountpoint */ se->mountpoint = strdup(mountpoint); if (se->mountpoint == NULL) goto error_out; return 0; error_out: fuse_kern_unmount(mountpoint, fd); 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) { fuse_kern_unmount(se->mountpoint, se->fd); se->fd = -1; free(se->mountpoint); se->mountpoint = NULL; } } #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) { se->exited = 1; } __attribute__((no_sanitize_thread)) void fuse_session_reset(struct fuse_session *se) { se->exited = 0; se->error = 0; } __attribute__((no_sanitize_thread)) int fuse_session_exited(struct fuse_session *se) { return se->exited; } fuse-3.17.2/lib/fuse_misc.h0000644000175000017500000000345115002272303014405 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 COPYING.LIB */ #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.17.2/lib/fuse_opt.c0000644000175000017500000002135715002272303014254 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 COPYING.LIB */ #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.17.2/lib/fuse_signals.c0000644000175000017500000001120115002272303015075 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 COPYING.LIB */ #include "fuse_config.h" #include "fuse_lowlevel.h" #include "fuse_i.h" #include #include #include #include #include #ifdef HAVE_BACKTRACE #include #endif 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; #define BT_STACK_SZ (1024 * 1024) static void *backtrace_buffer[BT_STACK_SZ]; 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) return; 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.17.2/lib/fuse_versionscript0000644000175000017500000000756015002272303016143 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; # Local Variables: # indent-tabs-mode: t # End: fuse-3.17.2/lib/helper.c0000644000175000017500000003307115002272303013703 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 COPYING.LIB. */ #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) \ if (cond) conn->want_ext |= (cap) #define LL_DISABLE(cond,cap) \ if (cond) conn->want_ext &= ~(cap) 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.17.2/lib/meson.build0000644000175000017500000000405315002272303014420 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 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.17.2/lib/modules/0000755000175000017500000000000015002272303013724 5ustar berndberndfuse-3.17.2/lib/modules/iconv.c0000644000175000017500000003772215002272303015221 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 COPYING.LIB */ #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; } 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, }; 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.17.2/lib/modules/subdir.c0000644000175000017500000003544215002272303015370 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 COPYING.LIB */ #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; } 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, }; 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.17.2/lib/mount.c0000644000175000017500000004234115002272303013566 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 COPYING.LIB. */ /* 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" #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, "On calling fusermount posix_spawn failed: %s\n", 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-unmountsocketpair() failed", 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 = "/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 not found, try 'modprobe fuse' first\n"); 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.17.2/lib/mount_bsd.c0000644000175000017500000001362115002272303014415 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 COPYING.LIB. */ #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); } _exit(EXIT_SUCCESS); } 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.17.2/lib/mount_util.c0000644000175000017500000001747415002272303014634 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 COPYING.LIB. */ #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; } 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.17.2/lib/mount_util.h0000644000175000017500000000124115002272303014622 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 COPYING.LIB. */ #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.17.2/lib/util.c0000644000175000017500000000053615002272303013401 0ustar berndbernd#include #include #include "util.h" 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; } fuse-3.17.2/lib/util.h0000644000175000017500000000122015002272303013375 0ustar berndbernd#ifndef FUSE_UTIL_H_ #define FUSE_UTIL_H_ #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) int libfuse_strtol(const char *str, long *res); /** * 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(var) (__attribute__((unused)) var) #endif #endif fuse-3.17.2/meson.build0000644000175000017500000002062415002272303013654 0ustar berndberndproject('libfuse3', ['c'], version: '3.17.2', meson_version: '>= 0.51.0', default_options: [ 'buildtype=debugoptimized', 'c_std=gnu11', 'cpp_std=c++17', '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 ''' 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', 'static_assert', 'pthread_setname_np' ] foreach func : test_funcs private_cfg.set('HAVE_' + func.to_upper(), cc.has_function(func, prefix: include_default, args: args_default)) endforeach 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 ')) # Test if headers exist private_cfg.set('HAVE_LINUX_CLOSE_RANGE_H', cc.check_header('#include ')) # Test if structs have specific member private_cfg.set('HAVE_STRUCT_STAT_ST_ATIM', cc.has_member('struct stat', 'st_atim', prefix: include_default, args: args_default)) private_cfg.set('HAVE_STRUCT_STAT_ST_ATIMESPEC', cc.has_member('struct stat', 'st_atimespec', prefix: include_default, args: args_default)) # # 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.17.2/meson_options.txt0000644000175000017500000000176215002272303015151 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') fuse-3.17.2/requirements.txt0000644000175000017500000000024315002272303014771 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.17.2/signify/0000755000175000017500000000000015002273413013161 5ustar berndberndfuse-3.17.2/signify/fuse-3.15.pub0000644000175000017500000000013715002272303015215 0ustar berndbernduntrusted comment: signify public key RWRsNRJIAGSHE0C6WuVmusQlvudAPlmOOkCw1LbuOLuu+25DuAmPCpDf fuse-3.17.2/signify/fuse-3.16.pub0000644000175000017500000000013715002272303015216 0ustar berndbernduntrusted comment: signify public key RWQtnc3WSoYwHGAdfvtTTVX8RsAXrNwMb8xqVwlY8lYY2Fxn2QUDiPYK fuse-3.17.2/signify/fuse-3.17.pub0000644000175000017500000000013715002272303015217 0ustar berndbernduntrusted comment: signify public key RWQqzcI/bjQ4/nouPgwUbs0WorZncrBxJHmiCb2N+GrMs9L6YAcGSFY/ fuse-3.17.2/signify/fuse-3.18.pub0000644000175000017500000000013715002272303015220 0ustar berndbernduntrusted comment: signify public key RWS6gMnNrKp/zRSYWv13J+KwXE26vCUsbC/hVZmjQ8PA3xjixGLjodz3 fuse-3.17.2/test/0000755000175000017500000000000015002273413012470 5ustar berndberndfuse-3.17.2/test/ci-build.sh0000755000175000017500000000772315002272303014525 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} ) # 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" non_sanitized_build # Documentation. (cd "${SOURCE_DIR}"; doxygen doc/Doxyfile) # Clean up. rm -rf "${TEST_DIR}" fuse-3.17.2/test/conftest.py0000644000175000017500000000575715002272303014702 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: raise AssertionError('Suspicious output to stderr (matched "%s")' % 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.17.2/test/hello.c0000644000175000017500000001036215002272303013736 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 COPYING. */ /** @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.17.2/test/lsan_suppress.txt0000644000175000017500000000005215002272303016124 0ustar berndbernd# Suppression file for address sanitizer. fuse-3.17.2/test/meson.build0000644000175000017500000000321415002272303014627 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) 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.17.2/test/pytest.ini0000644000175000017500000000024315002272303014515 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.17.2/test/readdir_inode.c0000644000175000017500000000310015002272303015413 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.17.2/test/release_unlink_race.c0000644000175000017500000000337315002272303016631 0ustar berndbernd/* This program can be distributed under the terms of the GNU GPLv2. See the file COPYING. */ #define FUSE_USE_VERSION 31 #define _GNU_SOURCE #include #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.17.2/test/stracedecode.c0000644000175000017500000001215115002272303015256 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.17.2/test/test_ctests.py0000644000175000017500000001312515002272303015405 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() @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") fuse-3.17.2/test/test_custom_io.py0000644000175000017500000000422115002272303016076 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.17.2/test/test_examples.py0000755000175000017500000007455215002272303015734 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 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')) @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')) if name == 'passthrough_plus': cmdline = base_cmdline + \ [ pjoin(basename, 'example', 'passthrough'), '--plus', '-f', mnt_dir ] else: cmdline = base_cmdline + \ [ pjoin(basename, 'example', name), '-f', mnt_dir ] 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') 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) tst_readdir_big(src_dir, work_dir) tst_open_read(src_dir, work_dir) tst_open_write(src_dir, work_dir) tst_create(work_dir) tst_passthrough(src_dir, work_dir) 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) 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")) @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') 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_readdir(src_dir, mnt_dir): 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) inodes_should = readdir_inode(src_newdir) assert inodes_is == inodes_should os.unlink(file_) os.unlink(subfile) os.rmdir(subdir) os.rmdir(src_newdir) def tst_readdir_big(src_dir, mnt_dir): # 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) inodes_should = readdir_inode(src_dir) assert inodes_is == inodes_should for fname in fnames: stat_src = os.stat(pjoin(src_dir, fname)) stat_mnt = os.stat(pjoin(mnt_dir, fname)) assert stat_src.st_ino == stat_mnt.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): name = name_generator() src_name = pjoin(src_dir, name) mnt_name = pjoin(src_dir, name) 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') assert name in os.listdir(src_dir) assert name in os.listdir(mnt_dir) assert os.stat(src_name) == os.stat(mnt_name) name = name_generator() src_name = pjoin(src_dir, name) mnt_name = pjoin(src_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) assert os.stat(src_name) == os.stat(mnt_name) 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.17.2/test/test_setattr.c0000644000175000017500000001054515002272303015363 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 COPYING. */ #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.17.2/test/test_syscalls.c0000644000175000017500000012327315002272303015535 0ustar berndbernd#define _GNU_SOURCE #include "fuse_config.h" #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 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(); #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.17.2/test/test_want_conversion.c0000644000175000017500000001144515002272303017113 0ustar berndbernd#include "util.h" #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17) #include "fuse_i.h" #include #include #include #include static void print_conn_info(const char *prefix, struct fuse_conn_info *conn) { printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64 "\n", prefix, conn->want, 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; } 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_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ); } static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style) { uint64_t want_ext_default = conn->want_ext; uint32_t want_default = fuse_lower_32_bits(conn->want_ext); int rc; /* High-level init */ fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT); conn->want = want_default; if (new_style) application_init_new_style(conn); else application_init_old_style(conn); rc = convert_to_conn_want_ext(conn, want_ext_default, want_default); assert(rc == 0); } 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; conn->capable = fuse_lower_32_bits(conn->capable_ext); conn->want_ext = conn->capable_ext; print_conn_info("Initial state", conn); uint64_t want_ext_default = conn->want_ext; uint32_t want_default = fuse_lower_32_bits(conn->want_ext); int rc; conn->want = want_default; conn->capable = fuse_lower_32_bits(conn->capable_ext); test_fuse_fs_init(conn, new_style); rc = convert_to_conn_want_ext(conn, want_ext_default, want_default); 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_POSIX_LOCKS); assert(conn->want_ext & FUSE_CAP_FLOCK_LOCKS); assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT); assert(conn->want_ext & FUSE_CAP_ASYNC_READ); /* Verify no other flags are set */ assert(conn->want_ext == (FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE | FUSE_CAP_POSIX_LOCKS | FUSE_CAP_FLOCK_LOCKS | FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_ASYNC_READ)); print_conn_info("After init", conn); } static void test_want_conversion_basic(void) { struct fuse_conn_info conn = { 0 }; printf("\nTesting basic want conversion:\n"); test_do_init(&conn, false); test_do_init(&conn, true); print_conn_info("After init", &conn); } 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); /* Initialize default values like in basic test */ uint64_t want_ext_default_ll = conn.want_ext; uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll); /* 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 = convert_to_conn_want_ext(&conn, want_ext_default_ll, want_default_ll); 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); uint64_t want_ext_default_ll = conn.want_ext; uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll); rc = convert_to_conn_want_ext(&conn, want_ext_default_ll, want_default_ll); 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.17.2/test/test_write_cache.c0000644000175000017500000001776715002272303016167 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 COPYING. */ #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.17.2/test/util.py0000644000175000017500000001515115002272303014017 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: 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.17.2/test/wrong_command.c0000644000175000017500000000100415002272303015456 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.17.2/tsan_suppressions.txt0000644000175000017500000000017115002272303016050 0ustar berndbernd# Use with # TSAN_OPTIONS="suppressions=tsan_suppressions.txt" ./myprogram # False positive race:pthread_setcancelstate fuse-3.17.2/util/0000755000175000017500000000000015002273413012466 5ustar berndberndfuse-3.17.2/util/fuse.conf0000644000175000017500000000132515002272303014275 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.17.2/util/fusermount.c0000644000175000017500000011616315002272303015046 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 COPYING. */ /* 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 #ifdef HAVE_LINUX_CLOSE_RANGE_H #include #endif #define FUSE_COMMFD_ENV "_FUSE_COMMFD" #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(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; } #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 */, #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 try_open(const char *dev, char **devp, int silent) { int fd = open(dev, O_RDWR); if (fd != -1) { *devp = strdup(dev); if (*devp == NULL) { fprintf(stderr, "%s: failed to allocate memory\n", progname); close(fd); fd = -1; } } else if (errno == ENODEV || errno == ENOENT)/* check for ENOENT too, for the udev case */ return -2; else if (!silent) { fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev, strerror(errno)); } return fd; } static int try_open_fuse_device(char **devp) { int fd; drop_privs(); fd = try_open(FUSE_DEV, devp, 0); restore_privs(); return fd; } static int open_fuse_device(char **devp) { int fd = try_open_fuse_device(devp); if (fd >= -1) return fd; fprintf(stderr, "%s: fuse device not found, try 'modprobe fuse' first\n", progname); return -1; } static int mount_fuse(const char *mnt, const char *opts, const char **type) { int res; int fd; char *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(dev); 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_LINUX_CLOSE_RANGE_H 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.17.2/util/init_script0000755000175000017500000000363615002272303014750 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.17.2/util/install_helper.sh0000755000175000017500000000277315002272303016040 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.17.2/util/meson.build0000644000175000017500000000241715002272303014631 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.17.2/util/mount.fuse.c0000644000175000017500000002500115002272303014730 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 COPYING. */ #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.17.2/util/parse-backtrace.sh0000755000175000017500000000535215002272303016056 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.17.2/util/udev.rules0000644000175000017500000000003415002272303014477 0ustar berndberndKERNEL=="fuse", MODE="0666" fuse-3.17.2/xfstests/0000755000175000017500000000000015002272303013371 5ustar berndberndfuse-3.17.2/xfstests/README.md0000644000175000017500000000070215002272303014647 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.17.2/xfstests/local.config0000644000175000017500000000131515002272303015652 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.17.2/xfstests/mount.fuse.passthrough0000755000175000017500000000211315002272303017765 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" "$@"