summaryrefslogtreecommitdiffstats
path: root/fuse
diff options
context:
space:
mode:
Diffstat (limited to 'fuse')
-rw-r--r--fuse/AUTHORS9
-rw-r--r--fuse/Android.mk51
-rw-r--r--fuse/COPYING339
-rw-r--r--fuse/COPYING.LIB502
-rw-r--r--fuse/ChangeLog3535
-rw-r--r--fuse/README380
-rw-r--r--fuse/android/config.h87
-rw-r--r--fuse/android/statvfs.c48
-rw-r--r--fuse/android/sys/statvfs.h39
-rw-r--r--fuse/buffer.c318
-rw-r--r--fuse/cuse_lowlevel.c371
-rw-r--r--fuse/fuse.c4942
-rw-r--r--fuse/fuse_i.h130
-rw-r--r--fuse/fuse_kern_chan.c98
-rw-r--r--fuse/fuse_loop.c46
-rw-r--r--fuse/fuse_loop_mt.c291
-rw-r--r--fuse/fuse_lowlevel.c2968
-rw-r--r--fuse/fuse_misc.h57
-rw-r--r--fuse/fuse_mt.c122
-rw-r--r--fuse/fuse_opt.c426
-rw-r--r--fuse/fuse_session.c243
-rw-r--r--fuse/fuse_signals.c72
-rw-r--r--fuse/fuse_versionscript210
-rw-r--r--fuse/helper.c480
-rw-r--r--fuse/include/Makefile.am17
-rw-r--r--fuse/include/cuse_lowlevel.h87
-rw-r--r--fuse/include/fuse.h1064
-rw-r--r--fuse/include/fuse_common.h505
-rw-r--r--fuse/include/fuse_common_compat.h26
-rw-r--r--fuse/include/fuse_compat.h203
-rw-r--r--fuse/include/fuse_kernel.h691
-rw-r--r--fuse/include/fuse_lowlevel.h1845
-rw-r--r--fuse/include/fuse_lowlevel_compat.h157
-rw-r--r--fuse/include/fuse_opt.h270
-rw-r--r--fuse/include/old/fuse.h9
-rw-r--r--fuse/include/ulockmgr.h26
-rw-r--r--fuse/modules/iconv.c739
-rw-r--r--fuse/modules/subdir.c697
-rw-r--r--fuse/mount.c644
-rw-r--r--fuse/mount_bsd.c391
-rw-r--r--fuse/mount_util.c368
-rw-r--r--fuse/mount_util.h19
-rw-r--r--fuse/ulockmgr.c444
43 files changed, 23966 insertions, 0 deletions
diff --git a/fuse/AUTHORS b/fuse/AUTHORS
new file mode 100644
index 000000000..8c1e88f2c
--- /dev/null
+++ b/fuse/AUTHORS
@@ -0,0 +1,9 @@
+FUSE
+----
+
+Miklos Szeredi <miklos@szeredi.hu>
+
+CUSE
+----
+
+Tejun Heo <teheo@suse.de>
diff --git a/fuse/Android.mk b/fuse/Android.mk
new file mode 100644
index 000000000..5260e146b
--- /dev/null
+++ b/fuse/Android.mk
@@ -0,0 +1,51 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ android/statvfs.c \
+ buffer.c \
+ cuse_lowlevel.c \
+ fuse.c \
+ fuse_kern_chan.c \
+ fuse_loop.c \
+ fuse_loop_mt.c \
+ fuse_lowlevel.c \
+ fuse_mt.c fuse_opt.c \
+ fuse_session.c \
+ fuse_signals.c \
+ helper.c \
+ mount.c \
+ mount_util.c \
+ ulockmgr.c
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/android \
+ $(LOCAL_PATH)/include
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils
+
+LOCAL_CFLAGS := \
+ -D_FILE_OFFSET_BITS=64 \
+ -DFUSE_USE_VERSION=26 \
+ -fno-strict-aliasing
+
+LOCAL_MODULE := libfusetwrp
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/fuse/COPYING b/fuse/COPYING
new file mode 100644
index 000000000..d159169d1
--- /dev/null
+++ b/fuse/COPYING
@@ -0,0 +1,339 @@
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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.
diff --git a/fuse/COPYING.LIB b/fuse/COPYING.LIB
new file mode 100644
index 000000000..4362b4915
--- /dev/null
+++ b/fuse/COPYING.LIB
@@ -0,0 +1,502 @@
+ 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/fuse/ChangeLog b/fuse/ChangeLog
new file mode 100644
index 000000000..f2e5d024f
--- /dev/null
+++ b/fuse/ChangeLog
@@ -0,0 +1,3535 @@
+2015-05-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.9.4
+
+2015-05-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: fix exec environment for mount and umount. Found by
+ Tavis Ormandy (CVE-2015-3202).
+
+2015-02-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: fix fuse_remove_signal_handlers() to properly restore
+ the default signal handler. Reported by: Chris Johnson
+
+2014-07-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: highlevel API: fix directory file handle passed to
+ ioctl() method. Reported by Eric Biggers
+
+2014-07-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * 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.
+
+2014-03-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Initilaize stat buffer passed to ->getattr() and ->fgetattr() to
+ zero in all cases. Reported by Daniel Iwan
+
+2013-08-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: Add missing includes. This allows compiling fuse with
+ musl. Patch by Daniel Thau
+
+2013-07-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.9.3
+
+2013-06-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: fix multiple close of device fd. Reported by Dan
+ Greenfield
+
+2013-03-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: fix thread cancel race. Exiting a worker my race with
+ cancelling that same worker. This caused a segmenation
+ fault. Reported and tested by Anatol Pomozov
+
+2013-02-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: fix crash in unlock_path(). Patch by Ratna Manoj
+
+ * libfuse: fix the 'remember' option. The lru list was not
+ initialized for the "/" path. This resulted in remove_node_lru()
+ crashing on LOOKUP-DOTDOT. Patch by Madan Valluri
+
+ * libfuse: configure: detect new util-linux
+
+ * libfuse: Use AC_CONFIG_HEADERS instead of AM_CONFIG_HEADER.
+ Patch by Anatol Pomozov
+
+ * libfuse: rename ./configure.in to ./configure.ac. Patch by
+ Anatol Pomozov
+
+2012-10-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.9.2
+
+2012-10-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix deadlock in libfuse. Running "svn update" on a fuse
+ filesystem could deadlock because of a bug in the way the paths
+ are locked. Reported by Kazuaki Anami
+
+2012-08-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix missing config.h in buffer.c. Reported by Matthew Gabeler-Lee
+
+2012-08-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Not unhashing the name in forget (commit on 2011-12-09) broke
+ the forget logic in a subtle way, resulting in "fuse internal
+ error: node NNN not found" and causing the filesystem daemon to
+ abort. Fix by incrementing the node refcount if nlookup goes from
+ zero to one. Reported by Kyle Lippincott
+
+2012-08-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix linking against GNU libiconv. Patch by Natanael Copa
+
+2012-07-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.9.1
+
+2012-07-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix crash caused by freeing a stack address. Reported by Itay
+ Perl
+
+2012-07-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix install of mount.fuse from out-of-tree build. Patch by
+ Olivier Blin
+
+ * Fix build with automake >= 1.12.1. Patch by Olivier Blin
+
+2012-04-24 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add fallocate operation. Only works on linux kernels 3.5 or
+ later. Patch by Anatol Pomozov
+
+2012-05-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Linking to a library that uses threads requires the application
+ to be linked with -pthreads otherwise some pthread functions will
+ be linked to stubs in glibc. So move -pthread from Libs.private
+ to Libs in fuse.pc. Reported by Werner Fink
+
+ * Fix the compile command in the examples. Reported by Luciano
+ Dalle Ore
+
+2012-04-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.9.0
+
+2012-04-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add missing fuse_fs_flock to fuse_versionscript
+
+2012-04-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check protocol version before sending notifications and return
+ -ENOSYS if a particular notification is not supported.
+
+ * Add 'flag_utime_omit_ok' flag to fuse_operations. If the
+ filesystem sets this flag then ->utimens() will receive UTIME_OMIT
+ and UTIME_NOW values as specified in utimensat(2).
+
+2012-01-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Interpret octal escape codes in options. Requested by Jan
+ Engelhardt
+
+2012-01-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add man pages for fusermount, mount.fuse and ulockmgr_server.
+ Lifted from the Debian package. The man pages were written by
+ Daniel Baumann and Bastien Roucaries
+
+2012-01-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Disable symbol versions on MacOSX. Patch by Anatol Pomozov
+
+2012-01-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Remove unnecessary mutex unlock at the end of multithreaded
+ event loop.
+
+2011-12-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix hang in wait_on_path(). Reported by Ville Silventoinen
+
+ * Don't unhash name in FORGET. This resulted in ENOENT being
+ returned for unlinked but still open files if the kernel sent a
+ FORGET request for the parent directory.
+
+ * Free request in fuse_reply_data().
+
+2011-12-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix build if FUSE_NODE_SLAB is not defined. Patch by Emmanuel
+ Dreyfus
+
+ * Check for availability of utimensat() function. Patch by
+ Emmanuel Dreyfus
+
+2011-12-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add fuse_lowlevel_notify_delete() which tells the kernel that a
+ file or directory is deleted. Patch by John Muir
+
+2011-12-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Update retrieve_reply() method
+
+2011-12-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Low level API: lock argument of fuse_reply_lock should have a
+ 'const' qualifier. Reported by Shachar Sharon
+
+ * Add support for ioctl on directories. Reported by Antonio SJ
+ Musumeci
+
+2011-10-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Reply to request with ENOMEM in case of failure to allocate
+ request structure. Otherwise the task issuing the request will
+ just freeze up until the filesystem daemon is killed. Reported by
+ Stephan Kulow
+
+2011-09-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Replace daemon() function with fork(). Patch by Anatol Pomozov
+
+2011-08-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * If configured with --disable-mtab then don't call mount(8) from
+ libfuse to update the mtab. Reported by: James Sierp
+
+2011-08-24 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Use LRU list for cleaning up the cache if the "remember=T"
+ option was given. Patch by therealneworld@gmail.com
+
+2011-07-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add ->flock() operation to low and high level interfaces. This
+ fixes problems with emulating flock() with POSIX locking.
+ Reported by Sebastian Pipping. As with lock/setlk/getlk most
+ filesystems don't need to implement this, as the kernel takes care
+ of file locking. The only reason to implement locking operations
+ is for network filesystems which want file locking to work between
+ clients.
+
+2011-07-02 Sebastian Pipping <sebastian@pipping.org>
+
+ * Make xmp_utimens of examples "fusexmp" and "fusexmp_fh"
+ not follow symlinks as other layers do that already.
+
+2011-06-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add "remember" option. This works similar to "noforget" except
+ that eventually the node will be allowed to expire from the cache.
+ Patch by therealneworld@gmail.com
+
+2011-05-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check if splice/vmsplice are supported
+
+2011-05-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Remove -lrt -ldl from fuse.pc for dynamic linking since
+ libfuse.so is already linked with these libraries. Reported by:
+ Nikolaus Rath
+
+2011-05-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Cleaner build output. Patch by Reuben Hawkins
+
+2011-05-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Disable splice by default, add "splice_read", "splice_write" and
+ "splice_move" options. Keep the "no_splice_*" variants, which can
+ disable splice even if the filesystem explicitly enables it.
+
+2011-04-15 Max Krasnyansky <maxk@kernel.org>
+ * Added support for "auto_unmount" option which unmounts the
+ filesystem automatically on process exit (or crash).
+
+2011-03-30 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Patches by Laszlo Papp fixing various issues found by the
+ Coverity checker
+
+2011-03-11 Miklos Szeredi <miklos@szeredi.hu>
+
+ * In case of failure to add to /etc/mtab don't umount. Reported
+ by Marc Deslauriers
+
+2011-02-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: In fuse_session_loop_mt() don't pause when exiting the
+ worker threads. The pause() was added in 2.2.1 to prevent
+ segfault on pthread_cancel() on an exited, detached thread. Now
+ worker threads are not detached and pthread_cancel() should work
+ fine even after the thread exited. Reported by Boris Protopopov
+
+2011-01-31 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: chdir to / before performing mount/umount
+
+ * fusermount: only allow mount and umount if util-linux supports
+ --no-canonicalize
+
+2010-12-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Highlevel lib: allow hash tables to shrink
+
+ * Highlevel lib: add slab allocation for node cache. This will
+ allow the memory used by the filesystem to grow and shrink
+ depending on how many inodes are currently cached.
+
+2010-12-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Highlevel lib: use dynamically resized hash table for looking up
+ by name and node ID.
+
+2010-12-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Allow batching of forget requests. This allows forget requests
+ to be processed faster and doesn't require a modification to fuse
+ filesystems. Reported by Terje Malmedal
+
+ * Add ->forget_multi() operation to the lowlevel API. The
+ filesystem may implement this to process multiple forget requests
+ in one call
+
+ * Fix the ambiguity of ioctl ABI on the kernel/userspace boundary
+ for 32bit vs. 64bit userspace
+
+2010-11-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add new write_buf() method to the highlevel API. Similarly to
+ the lowlevel write_buf() method, this allows implementing zero
+ copy writes.
+
+ * Add a new read_buf() method to the highlevel API. This allows
+ returning a generic buffer from the read method, which in turn
+ allows zero copy reads.
+
+ * In fusexmp_fh implement the ->read_buf() and ->write_buf()
+ methods. Leave the ->read() and ->write() implementations for
+ reference, even though they are not necessary.
+
+2010-11-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix check for read-only fs in mtab update
+
+ * Open /dev/null for write instead of read for redirecting stdout
+ and stderr
+
+ * If umount(8) supports --fake and --no-canonicalize (util-linux-ng
+ version 2.18 or later), and umount(2) supports the
+ UMOUNT_NOFOLLOW flag (linux kernel version 2.6.35 or later) then,
+ "fusermount -u" will call the umount(2) system call and use
+ "umount --fake ..." to update /etc/mtab
+
+ * Added --disable-legacy-umount option to configure. This
+ disables the runtime checking of umount(8) version. When built
+ with this option then "fusermount -u" will fail if umount(8)
+ doesn't support the --fake and --no-canonicalize options.
+
+ * Fix fuse_buf_copy() if already at the end of the buffers
+
+ * Add new ->write_buf() method to low level interface. This
+ allows passig a generic buffer, either containing a memory buffer
+ or a file descriptor. This allows implementing zero copy writes.
+
+ * Add fuse_session_receive_buf() and fuse_session_process_buf()
+ which may be used in event loop implementations to replace
+ fuse_chan_recv() and fuse_session_process() respectively.
+
+ * Remove unnecessary restoring of current working directory in
+ "fusermount -u"
+
+ * Add ctx->pid to debug output
+
+ * Fix st_nlink value in high level lib if file is unlinked but
+ still open
+
+ * libfuse: add store request. Request data to be stored in the
+ kernel buffers for a given inode.
+
+ * libfuse: add retrieve request. Retrieve data stored in the
+ kernel buffers for a given inode.
+
+2010-10-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Use LTLIBICONV when linking libfuse. This fixes building against
+ uclibc + libiconv. Patch by Natanael Copa
+
+2010-10-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add missing argument check in ulockmgr.c to prevent calling
+ ulockmgr_server with illegal arguments. This would cause an ever
+ growing list of ulockmgr_server processes with an endless list of
+ open files which finally exceeds the open file handle limit.
+ Patch by Markus Ammer
+
+2010-09-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix ambiguous symbol version for fuse_chan_new.
+ fuse_versionscript included fuse_chan_new in both FUSE_2.4 and
+ FUSE_2.6. Remove the FUSE_2.4, which is invalid.
+
+2010-09-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix option escaping for fusermount. If the "fsname=" option
+ contained a comma then the option parser in fusermount was
+ confused (Novell bugzilla #641480). Fix by escaping commas when
+ passing them over to fusermount. Reported by Jan Engelhardt
+
+2010-08-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add NetBSD support. Patch from Emmanuel Dreyfus
+
+2010-07-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: add buffer interface. Add a generic buffer interface
+ for use with I/O. Buffer vectors are supplied and each buffer in
+ the vector may be a memory pointer or a file descriptor.
+
+ * The fuse_reply_fd() interface is converted to using buffers.
+
+2010-06-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Make the number of max background requests and congestion
+ threshold tunable. New options are "max_background" and
+ "congestion_threshold". Only effective on linux kernel versions
+ 2.6.32 or greater. Patch by Csaba Henk
+
+2010-06-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add fuse_reply_fd() reply function to the low level interface.
+ On linux version 2.6.35 or greater this will use splice() to move
+ data directly from a file descriptor to the fuse device without
+ needing to go though a userspace buffer. With the
+ FUSE_REPLY_FD_MOVE flag the kernel will attempt to move the data
+ directly into the filesystem's cache. On earlier kernels it will
+ fall back to an intermediate buffer. The options
+ "no_splice_write" and "no_splice_move" can be used to disable
+ splicing and moving respectively.
+
+2010-06-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix out-of-source build. Patch by Jörg Faschingbauer
+
+ * Add a "nopath" option and flag, indicating that path argument
+ need not be calculated for the following operations: read, write,
+ flush, release, fsync, readdir, releasedir, fsyncdir, ftruncate,
+ fgetattr, lock, ioctl and poll.
+
+2010-05-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Remove "chmod root" from install of fusermount. Reported by
+ Lucas C. Villa Real
+
+2010-04-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.8.4
+
+2010-04-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix checking for symlinks in umount from /tmp. Reported by Al
+ Viro
+
+ * Fix umounting if /tmp is a symlink. Reported by Franco Broi
+
+2010-02-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix definition of FUSE_OPT_END for C++. Reported by Tim
+ Bruylants
+
+2010-02-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix stack alignment for clone()
+
+2010-02-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.8.3
+
+2010-02-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Using "--no-canonicalize" with umount(8) conflicts with the race
+ fix, sinceit assumes the supplied path is absolute, while the race
+ fix relies on the path being relative to the current directory.
+ Reported by Tom Rindborg
+
+2010-01-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.8.2
+
+2010-01-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix race if two "fusermount -u" instances are run in parallel.
+ Reported by Dan Rosenberg
+
+ * Make sure that the path to be unmounted doesn't refer to a
+ symlink
+
+2010-01-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix compile error on FreeBSD. Patch by Jay Sullivan
+
+2009-12-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Use '--no-canonicalize' option of mount(8) (available in
+ util-linux-ng version 2.17 or greater) to avoid calling
+ readling(2) on the newly mounted filesystem before the mount
+ procedure is finished. This has caused a deadlock if "audit" was
+ enabled in the kernel. Also use '--no-canonicalize' for umount to
+ avoid touching the mounted filesystem.
+
+2009-09-11 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.8.1
+
+2009-08-25 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix missing versioned symbol fuse_get_context@FUSE_2.2
+
+2009-08-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.8.0
+
+2009-08-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add missing fuse_session_data to versionscript
+
+ * Make sure all global symbols are prefixed with "fuse_" or "cuse_"
+
+2009-07-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Clarify how the protocol version should be negotiated between
+ kernel and userspace. Notably libfuse didn't correctly handle the
+ case when the supported major versions didn't match
+
+ * Add missing pthread link for libulockmgr. Patch by Petr Salinger
+
+2009-07-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * The context is extended with a 'umask' field. The umask is sent
+ for mknod, mkdir and create requests by linux kernel version
+ 2.6.31 or later, otherwise the umask is set to zero. Also
+ introduce a new feature flag: FUSE_CAP_DONT_MASK. If the kernel
+ supports this feature, then this flag will be set in conn->capable
+ in the ->init() method. If the filesystem sets this flag in in
+ conn->want, then the create modes will not be masked.
+
+ * Add low level interfaces for lookup cache and attribute
+ invalidation. This feature is available in linux kernels 2.6.31
+ or later. Patch by John Muir
+
+ * Kernel interface version is now 7.12
+
+ * fusermount: Do not silently ignore command line arguments.
+ Patch by Sebastian Harl
+
+2009-06-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.8.0-pre3
+
+2009-06-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add fuse_getgroups (high level lib) and fuse_req_getgroups (low
+ level lib) functions to query the supplementary group IDs for the
+ current request. Currently this is implemented on Linux by
+ reading from the /proc filesystem.
+
+2009-06-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add "noforget" option to high level lib to prevent ESTALE errors
+ on NFS exported filesystems. This result in paths being cached
+ forever, resulting in ever growing memory usage. Use with care.
+
+ * Add "no_remote_lock" option to disable remote file locking even
+ if the filesystem implements it. With this option locking
+ primitives (flock, lockf, fcntl(F_SETLK)) will still work, but
+ will ignore remotely locked files.
+
+ * CUSE patches from Tejun Heo:
+
+ * Unrestricted ioctl support left some debris. Clean them up:
+ o No reason to pass around pointer to flags. Pass flags directly.
+ o Clean up comment and prototype parameter names.
+ o fuse_lib_ioctl() didn't reply when get_path() failed. Fix it.
+ o Remove unused variables {in|out}_iov from fuse_lib_ioctl().
+
+ * Add fuse_reply_ioctl_iov()
+
+ * Move fuse_session, fuse_req and fuse_ll definitions to fuse_i.h
+ and make send_reply_iov() and fuse_setup_common() global (also in
+ fuse_i.h). These will be used by CUSE support.
+
+ * Restructure fuse_ll_process()
+
+ * Implement libfuse side of CUSE support. CUSE uses subset of FUSE
+ operations as dir operations don't make sense for CUSE where one
+ instance implements single character device.
+
+ CUSE support comes with its own cuse_lowevel_ops and related
+ initialization and helper functions. Except for initialization, it
+ usage is basically identical to FUSE.
+
+ This patch also adds example/cusexmp.c which can create a character
+ device with name and device number specified on command line. The
+ created device itself is pretty boring. It's a bit bucket supporting
+ read, write and access via ioctl.
+
+2009-06-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add missing fuse_reply_bmap to versionscript. Debian
+ Bug#531329. Reported by Goswin Brederlow
+
+2009-05-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Don't call forget_node() if the lookup was negative and write()
+ for the reply returned ENOENT. Reported by John Haxby
+
+2009-05-25 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add FUSE_CAP_EXPORT_SUPPORT to fuse_common.h
+
+2009-05-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix missing newlines in some printfs
+
+ * Fix 'make install-strip'. Reported by Dominick Layfield
+
+2009-01-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.8.0-pre2
+
+2008-12-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Implement poll support. Patch by Tejun Heo
+
+ * Add missing setattr flags to <fuse_lowlevel.h>.
+
+ * Only pass valid flags to ->setattr().
+
+2008-12-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Implement ioctl support. On high level interface only
+ "restricted" ioctls are supported (which are defined with the
+ _IO(), _IOR(), _IOW() or _IOWR() macros). Unrestricted ioctls
+ will only be allwed to CUSE (Character Device in Userspace)
+ servers. Patch by Tejun Heo
+
+2008-11-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * If open sets fi->nonseekable, libfuse will tell the kernel that
+ the file is not seekable. Patch by Tejun Heo
+
+2008-11-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lowlevel lib: fix deadlock if fuse_reply_* is called from the
+ interrupt handling function. Reported by Tero Marttila
+
+2008-10-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Allow commas in options to be escaped with a backslash
+
+ * Add new function: fuse_opt_add_opt_escaped()
+
+ * Add missing fuse_reply_bmap() to the version script
+
+2008-10-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Pass current file flags to read and write operations
+
+2008-07-24 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Clean up debug output in highlevel lib
+
+2008-07-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.8.0-pre1
+
+2008-06-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix handling of (no)suid and (no)dev options if filesystem is
+ mounted from /etc/fstab or via mount(8). Reported by Jan Ondrej.
+
+ * Skip calling mount(8) if /etc/mtab doesn't exist or if it's on a
+ read-only filesystem. This works around issues with certain mount
+ implementations. Reported by Szabolcs Szakacsits.
+
+2008-06-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Remove fuse kernel module sources. Linux 2.6.27 will support
+ NFS exporting.
+
+2008-06-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix theoretical infinite loops in libfuse. Reported by Szabolcs
+ Szakacsits
+
+ * Fix missing <sys/param.h> include for PATH_MAX. Reported by
+ Szabolcs Szakacsits
+
+2008-05-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix mounting over symlink. Reported by Szabolcs Szakacsits
+
+2008-05-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Don't allow bigger than 4kB writes by default on 2.6.26 and
+ later kernels, so that filesystems not expecting this are not
+ broken on a kernel upgrade. Provide a 'big_writes' mount option
+ to enable this feature. In future API revisions this may become
+ the default.
+
+2008-04-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Update warning message for missing newline at end of fuse.conf
+
+ * Update debug message for successful operation to not include the
+ string "error:"
+
+2008-04-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Update error message for missing mountpoint parameter. Reported
+ by Allen Pulsifer
+
+2008-04-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Print library version information to debug output
+
+ * Highlevel lib: don't limit paths to 4095 characters
+
+2008-03-25 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix memory leaks on mount. Patch by Szabolcs Szakacsits
+
+2008-03-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix missing pthread_mutex_destroy in error path of
+ fuse_lib_opendir(). Patch by Szabolcs Szakacsits
+
+2008-03-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add queuing on contention to per-node lock algorithm, to avoid
+ starvation.
+
+ * Only enable cancelation when reading a request, otherwise
+ cancellation could happen with a mutex held, which could hang the
+ process on umount
+
+2008-02-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Block SIGCHLD when executing mount and umount
+
+ * fusexmp_fh: avoid unnecessary seeking in readdir
+
+ * Update kernel interface to 7.9:
+
+ * Support receiving file handle from kernel in GETATTR request
+
+ * Allow operations with a NULL path argument, if the filesystem
+ supports it
+
+ * Add support atomic open(O_TRUNC)
+
+ * Support the st_blksize field in struct stat
+
+ * If the "FUSE_THREAD_STACK" environment is set, initialize the
+ stack size of threads by this value. Patch by Florin Malita
+
+ * Add per-node locking, instead of a global tree lock to protect
+ the path from changing during operations. Original patch by
+ Rodrigo Castro
+
+2008-02-03 Csaba Henk <csaba.henk@creo.hu>
+
+ * lib/mount_bsd.c:
+ - string formatting fixes
+ - exit if mounting has failed
+ (in FreeBSD a mount failure is not critical per se, as the daemon
+ still could be mounted externally, but waiting for such an event
+ is more confusing than fruitful)
+ - ditch the kvm(8) stuff and simply use forced unmount which just
+ won't block
+ - prettify option specifications
+ - add "-onosync_unmount" kernel option
+
+2008-01-07 Csaba Henk <csaba.henk@creo.hu>
+
+ * lib/mount_bsd.c:
+ - refine device closing in a race-free way
+ - add support for "-osubtype" on FreeBSD
+
+ * makeconf.sh: make it work under FreeBSD
+
+2008-01-03 Csaba Henk <csaba.henk@creo.hu>
+
+ * lib/mount_bsd.c: close device before unmount
+ (cf. lib/mount.c rev. 1.43) and fix some warnings
+
+2007-12-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix './configure --disable-static'. Patch from Ismail Dönmez
+
+2007-12-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.7.2
+
+2007-12-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix kernel module compile for 2.6.24
+
+ * Invalidate attributes of parent directory after create(), since
+ the modification time changes. Invalidate attributes on rename,
+ since some filesystems may update st_ctime. Reported by Szabolcs
+ Szakacsits
+
+ * Fix NFS exporting to handle 64bit node IDs
+
+ * Disable old symbol versions if __UCLIBC__ is defined. If a
+ symbol in a library has multiple versions, the runtime linker in
+ uClibc seems to randomly choose between them.
+
+ * Remove erroneous 'fuse_opt_insert_arg@FUSE_2_5' from
+ fuse_version_script. fuse_opt_free_args() was added in fuse-2.6.
+
+ * Close fuse device file descriptor before calling umount(),
+ preventing a deadlock when umount is synchronous. Reported by
+ Szabolcs Szakacsits
+
+2007-11-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * 'fusermount -u' did not umount the filesystem if /etc/mtab was a
+ symlink. This bug was introduced in 2.7.1 by "Don't call
+ /bin/[u]mount if /etc/mtab is a symlink". Found by robertsong.
+
+2007-10-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.7.1
+
+2007-10-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Clarify licence version to be "LGPLv2" for the library
+
+ * kernel fixes:
+
+ * After mount set nlink attribute for the root inode to 1
+
+ * Fix wake up of task waiting for a reserved request
+
+ * Fix allowing setattr, listxattr and statfs for other users
+
+2007-09-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add missing context initialization in fuse_fs_chmod(). Bug
+ found by "iohead"
+
+ * Fix kernel module compilation for 2.6.23. Based on patch by
+ Marian Marinov
+
+2007-09-04 Philippe Elie <phil.el@wanadoo.fr>
+
+ * lib/fuse_lowlevel.c: fix a fuse_req leak in do_forget()
+
+2007-07-31 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Work around hotplug issue, that it calls filesystem with file
+ descriptors 0, 1 and 2 not open. Tracked down by Leif Johnson
+
+2007-07-25 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Don't call /bin/[u]mount if /etc/mtab is a symlink. Reported by
+ Tomas M
+
+ * Also don't touch /etc/mtab if it is within the mounted
+ filesystem. Suggested by Jeffrey Law
+
+2007-07-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Reset args->argc in fuse_opt_free_args(). Patch by Lucas
+ C. Villa Real
+
+2007-07-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.7.0
+
+2007-07-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Accept a NULL "op" for fuse_main(), etc. This is useful if
+ filesystem is only invoking fuse to print a help message, or
+ version. Fixes RedHat bugzilla #217343
+
+2007-06-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: fix locking when loading a filesystem module
+
+2007-06-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add fs subtype support to mount.fuse
+
+2007-06-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add fs subtype support to libfuse and fusermount
+
+2007-06-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: sync with mainline (2.6.22)
+
+2007-06-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Send debug output to stderr instead of stdout. Patch by Jan
+ Engelhardt
+
+2007-06-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libulockmgr: Work around a kernel bug in recv(), causing it to
+ sometimes return zero even if data was available on the socket.
+
+2007-05-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: optimization: store parent pointer in node instead of
+ parent id
+
+2007-05-25 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: don't create new thread for each FORGET request. FORGET
+ messages sometimes caused so many threads to be created, that
+ process virtual memory space ran out. Reported by Chris AtLee
+
+2007-05-24 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: fix memory leak on thread creation failure in multithreaded
+ event loop. Found by Chris AtLee
+
+2007-05-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lowlevel lib: add fuse_reply_iov function, which is similar to
+ fuse_reply_buf, but accepts a vector of buffers. Patch by Roger
+ Willcocks
+
+2007-05-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix Oops or error if a regular file is created with mknod(2) on
+ a fuse filesystem. Kernels 2.6.18 onward are affected. Thanks to
+ J. Cameijo Cerdeira for the report
+
+2007-05-11 Csaba Henk <csaba.henk@creo.hu>
+
+ * libfuse: fix return value of fuse_loop()/fuse_loop_mt().
+ Error reported by Csaba Henk, fix by Miklos Szeredi
+
+ * libfuse: fix unlock in flush
+
+ * libfuse: do unlocking on RELEASE+FLUSH
+
+2007-05-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.7.0-rc1
+
+2007-05-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: sync with mainline:
+
+ * Use invalidate_mapping_pages() if available
+
+ * Fix BUG when invalid file type is supplied in mount. Patch by
+ Timo Savola
+
+2007-04-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: call umount(8) directly instead of fusermount if
+ possible
+
+ * Clean up init script, make it LSB compliant
+
+2007-04-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * In multithreaded loop, use a semaphore instead of SIGHUP to wake
+ up the main thread on umount. This is more elegant, and works
+ even if signals are blocked.
+
+2007-04-25 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Improve mounting support in libfuse:
+ - check non-empty mountpoint
+ - only fall back to fusermount when necessary
+
+2007-04-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Don't chdir to "/" in foreground mode, it causes more trouble
+ than it's worth
+
+2007-04-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Replace utils/mount.fuse "sh" script with a "C" program
+
+2007-04-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add -lulockmgr to compilation comment in fusexmp_fh.c
+
+2007-04-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check for iconv. Patch by Csaba Henk
+
+ * Add direct umounting
+
+ * Use "fusectl" as the device for the fusectl filesystem. Debian
+ Bug#417945. Reported by Laurent Bonnaud
+
+2007-04-01 Csaba Henk <csaba.henk@creo.hu>
+
+ * Fix some FreeBSD related macros.
+
+2007-03-30 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add support for direct mounting by libfuse. Fall back on
+ calling fusermount if it doesn't work
+
+2007-03-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.7.0-pre1
+
+2007-03-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Correctly handle O_APPEND in direct IO mode. Reported by Greg
+ Bruno
+
+ * mount.fuse should use /bin/bash. Debian Bug#413403. Reported
+ by Thomas Weinbrenner
+
+2007-02-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix detection of installed fuse in init script. Reported and
+ fix suggested by Davide Canova
+
+2007-02-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix 2.6.9 RHEL kernels, which have compatibility mutex.h, but
+ don't define mutex_destroy(), bummer. Patch from Phil Schwan
+
+2007-02-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Compile fuseblk for kernels which don't have an option to turn
+ off the block layer (CONFIG_BLOCK). Reported by Szakacsits
+ Szabolcs
+
+2007-02-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add filesystem stacking support to high level API. Filesystem
+ modules can be built into libfuse or loaded from shared object
+ (.so) files
+
+ * Add 'subdir' and 'iconv' built in modules
+
+ * lib/fuse.c: Fix locking for the reply code in create and open
+
+2007-02-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: make it compile on "strange" kernels which have emulated
+ mutexes via <linux/mutex.h> but no i_mutex. Reported by Tomasz
+ Mateja
+
+2007-01-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: fix BUG in control filesystem if it is umounted and
+ mounted again, while some fuse filesystems are present.
+ Bugreport from Florent Mertens
+
+ * kernel: sync with mainline, support 2.6.20
+
+2007-01-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib/Makefile.am: actually link libfuse against libfuse_libs
+
+2007-01-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Build fix for 2.6.16 vanila and 2.6.15 FC5 kernels. Patch from
+ Ian Abbott
+
+2007-01-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix abort in fuse_new() compatibility API for opts == NULL case.
+ Novell bugzilla #233870. Patch from Takashi Iwai.
+
+2007-01-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix option parsing in mount.fuse. Patch from Jens M. Noedler
+
+2007-01-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix unaligned access in file desctriptor passing in libfuse,
+ fusermount and ulockmgr. Debian bug ID: 404904. Reported and
+ tested by Sebastian Fontius
+
+2006-12-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: don't keep unreferenced inodes in the icache.
+
+2006-12-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: Fix detection of fuseblk. Reported by Szakacsits
+ Szabolcs
+
+ * lib: Fix use after free in fuse_flush(). Reported by Ron
+ Lindman
+
+2006-12-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * mount.fuse: add "setuid=USER" option which does a "su - USER"
+ for the filesystem
+
+ * fusermount: use "/bin/mount -f" to add entry to /etc/mtab, and
+ "/bin/umount" to remove entry from /etc/mtab. This gets rid of
+ the ugly code dealing with mtab, as well as a possible race
+ between fusermount and mount trying to modify /etc/mtab at the
+ same time
+
+ * Fix "buffer size too small: 4" warning for users of the
+ fuse_loop_mt_proc() function.
+
+2006-12-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix warnings with gcc-4.1 on 64bit archs. Report from
+ Harshavardhana
+
+ * Add extra warning options, and fix resulting warnings
+
+ * Really fix fuse_teardown problem
+
+2006-12-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add -lrt to fuse.pc (if needed) to fix static linking against
+ libfuse. Reported by Szakacsits Szabolcs
+
+2006-12-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.6.1
+
+2006-11-30 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix API version 21 and 22 compatibility for fuse_teardown.
+ Reported by Bgs
+
+2006-11-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: Print a more helpful message in case the kernel
+ doesn't support the 'fuseblk' filesystem type. This has been
+ biting ntfs-3g users. Reported by Yura Pakhuchiy
+
+ * kernel: fix build problem for "make -C ...". Reported by
+ Stephen Bryant
+
+2006-11-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix bug in certain error paths of lookup routines. The request
+ object was reused for sending FORGET, which is illegal. This bug
+ could cause an Oops in linux-2.6.18 or in fuse-2.6.0, and might
+ silently corrupt memory in earlier versions. Report and test
+ program by Russ Cox
+
+2006-11-11 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Print an error if an incompatible kernel interface version is
+ detected in INIT. This will only show if filesystem is started
+ with -d or -f
+
+ * Fix order of fuse_destroy()/fuse_unmount() in error cleanup of
+ fuse_setup_common(). Reported by Szakacsits Szabolcs
+
+2006-11-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix recursive locking in fuse_create(). Thanks to Takuya
+ Ishibashi for the bug report
+
+2006-10-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix automake problem. Patch from Nix
+
+2006-10-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix mount.fuse to use /bin/sh instead of /bin/bash, which is not
+ always available on embedded systems. Patch from Paul Smith
+
+ * Fix util/Makefile.am, so that failure to run update-rc.d or
+ device creation doesn't cause make to fail. Reported by Paul
+ Smith
+
+2006-10-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.6.0
+
+2006-10-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: don't try to create a lock file if /etc/mtab is a
+ symlink. Report and patch from Alexei Sheplyakov (debian bug
+ #393693)
+
+2006-10-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Minor changes, sync with mainline tree
+
+2006-10-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.6.0-rc3
+
+2006-10-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: cleanups
+
+2006-10-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: Fix compilation on patched 2.6.18 (fc6) and 2.6.19.
+ Report from David Shaw
+
+ * lib: Fix lost error on renaming a file. Report from David Shaw
+
+ * lib: Fix lost error on hiding open files (renaming to
+ .fuse_hiddenXXXX)
+
+ * kernel: Fix a rare hang on SMP/32bit on heavy filesystem
+ activity. The cause of the bug was that some calls to
+ i_size_write() were not protected by a lock, and hence
+ i_size_seqcount could become corrupted. This caused subsequent
+ calls to i_size_read() to spin forever. This is a long standing
+ bug was probably introduced in version 2.2, and thought to be
+ related to NFS exporting (it's not). It was reported by various
+ people, but Dana Henriksen has finally helped me to track it down,
+ so big thanks to him
+
+ * kernel: Protect against truncation of a swapfile
+
+2006-10-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: Check for signature of super_operations->umount_begin().
+ Ubuntu kernel 2.6.17 seems to use the new signature found in
+ 2.6.18. Thanks to Florent Mertens for the report
+
+2006-10-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Make sure inode numers wrap around at 2^32. This is needed on
+ dual 64bit/32bit architectures, because 32bit applications using
+ the non-largefile interface would otherwise break (EOVERFLOW error
+ would be returned by the stat() system call family)
+
+ * ulockmgr: handle the case, when a locking operation fails
+ because no more file desctriptors are available in
+ ulockmgr_server. Also work around a Linux kernel bug (known to
+ exist for all Linux kernel versions <= 2.6.18) which may cause
+ sent file descriptors to be lost in the above case
+
+ * ulockmgr: optimize file descriptor use
+
+ * restore needed cpp flags to util/Makefile.am
+
+ * Install udev rules as 99-fuse.rules instead of 60-fuse.rules
+
+ * Minor clean up of udev rules
+
+ * Add a synchronous DESTROY message to kernel interface. This is
+ invoked from umount, when the final instance of the filesystem is
+ released. It is only sent for filesystems mounted with the
+ 'blkdev' option for security reasons.
+
+ * If the DESTROY message is received, call the filesystem's
+ ->destroy() method. In this case it's not called from session
+ destruction as it would be otherwise.
+
+2006-10-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.6.0-rc2
+
+2006-10-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add support for FLUSH+RELEASE operation for FreeBSD. Original
+ patch by Csaba Henk
+
+ * Add init script to insert fuse module and mount the control
+ filesystem. The script is installed as /etc/init.d/fuse and on
+ debian based systems (where update-rc.d is available) symlinks
+ from /etc/rc*.d/ are also installed.
+
+ * Include '#define FUSE_USE_VERSION=XX' into examples so they
+ become more self contained.
+
+2006-09-30 Miklos Szeredi <miklos@szeredi.hu>
+
+ * API changes:
+
+ * Move lock_owner from a separate argument into fuse_file_info
+
+ * Add a flag to fuse_file_info indicating (1) a highlevel lock
+ operation (unlock all) was initiated by a flush, (2) a lowlevel
+ release operation should perform a flush as well.
+
+ * fusermount: revert modprobe change (2006-08-18) since it
+ doesn't work reliably with udev
+
+ * Add support for block device backed filesystems. This mode is
+ selected with the 'blkdev' option, which is privileged.
+
+ * Add support for the bmap (FIBMAP ioctl) operation on block
+ device backed filesystems. This allows swapon and lilo to work on
+ such filesystems.
+
+ * kernel changes:
+
+ * Drop support for kernels earlier than 2.6.9. Kernel module from
+ previous (2.5.x) release can be used with library from this
+ release
+
+ * In fuse_dentry_revalidate() use dget_parent() instead of
+ dereferencing d_parent, since there's no protection against parent
+ changing and going away
+
+ * Protect nlookup from concurrent updates
+
+ * In lookup if a directory alias exists but is unused,
+ then get rid of it, otherwise return -EBUSY.
+
+ * In mkdir if a directory alias exists, return success, but leave
+ dentry negative. In reality this could happen if a remote rename
+ immediately followed the mkdir.
+
+ * Don't BUG in fuse_iget() if multiple retries are needed to get a
+ good inode. This could happen if several lookups are racing for
+ the same inode.
+
+2006-09-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix compilation on 2.6.9. Report from Troy Ayers
+
+2006-09-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix Oops in fuse_readpages(). Reported by David Shaw
+
+2006-09-24 Csaba Henk <csaba.henk@creo.hu>
+
+ * Add support for nanosec times on FreeBSD
+
+ * Fix FreeBSD compatibility issues
+
+2006-09-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix one more compatibility bug. Thanks to Ricardo Correia
+
+ * Fix utimens compilation with uClibc. Patch from Jamie Guinan
+
+2006-09-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fixed several compatibility bugs in low level interface.
+ Reported by Ricardo Correia
+
+ * Add workaround for ARM caching bug
+
+2006-09-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Rename new utimes() method to more logical utimens()
+
+2006-09-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fuse tried to unlink already unlinked hidden files. Bug
+ reported by Milan Svoboda
+
+2006-09-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.6.0-rc1
+
+2006-09-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: Fix unlock on close for kernels < 2.6.18
+
+ * Add ulockmgr library & server. This can be used for handling
+ file locking requests either directly from libfuse or over a
+ network, etc. This first version is not optimized and the number
+ of file descriptors it uses may get out of hand
+
+2006-09-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: Add interrupt support to high level library, which may be
+ enabled with the 'intr' mount option.
+
+ * When an operation is interrupted the thread handling that
+ operation will receive SIGUSR1 (or other signal specified with the
+ 'intr_signal=N' option). The library installs a no-op signal
+ handler for this signal, unless there's already a handler
+ installed.
+
+ * The filesystem may query interrupt status (regardless of 'intr')
+ with the fuse_interrupted() function.
+
+ * mount.fuse: initialize $HOME if not set. Report from Sven Goldt
+
+2006-09-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: Multithreaded loop now allows unlimited number of threads.
+ This is needed for locking operations which may block
+ indefinitely. Also the kernel now doesn't limit the number of
+ outstanding requests so the library shouldn't do so either.
+
+2006-09-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix recursive lock bug in interrupt handling
+
+ * Add utimes() method to highlevel interface, which supports
+ setting times with nanosecond resolution
+
+2006-08-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: fix page leak if fuse_readpages() failed in it's
+ initialization. Bug found and original patch from Alexander
+ Zarochentsev
+
+ * For linux kernels >=2.6.18 (2.6.19 if using the fuse module from
+ the kernel tree) the statfs method will receive the path within
+ the filesystem on which the stat(v)fs syscall was called
+
+ * fusermount: try to modprobe fuse module if invoked by root and
+ unable to open device. This is needed with udev, since the device
+ node will be created only when the module is inserted, hence
+ module autoloading won't work. Reported by Szakacsits Szabolcs
+
+2006-07-30 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: if selinux is active, restore the original file's
+ security context in unmount_rename(). Redhat bugzilla id 188561.
+ Patch from Yves Perrenoud
+
+ * Add POSIX file locking operation to high level library
+
+ * Initialize context for unlink of hidden files on umount. Bug
+ reported by Tim Stoakes
+
+2006-07-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Multiple release() calls can race with each other, resulting in
+ the hidden file being deleted before the last release finishes.
+ Bug found and patch tested by Mark Huijgen
+
+2006-07-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: if /dev/fuse doesn't exist, suggest modprobing fuse;
+ this makes sense on systems using udev. Reported by Szakacsits
+ Szabolcs
+
+2006-06-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.6.0-pre3
+
+2006-06-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Support in kernel module for file locking and interruption. The
+ same functionality is available in official kernels >= 2.6.18
+
+2006-06-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add POSIX file locking support
+
+ * Add request interruption
+
+2006-06-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add missing pthread_rwlock_destroy(). Patch from Remy Blank
+
+2006-06-05 Remy Blank <remy.blank@pobox.com>
+
+ * lib: canonicalize mount point in fuse_helper_opt_proc() so that
+ unmounting succeeds even if mount point was relative.
+
+2006-06-04 Csaba Henk <csaba.henk@creo.hu>
+
+ * lib: fix emergency umount in helper.c when malloc fails.
+ (The way it was done would end up in a segfault.)
+
+2006-06-01 Csaba Henk <csaba.henk@creo.hu>
+
+ * lib: adjust threading related compiler flags.
+ Switch to "-pthread" from "-lpthread" as that's the preferred
+ one on several platforms. Consulted with Terrence Cole and
+ Miklos Szeredi
+
+2006-05-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: search fusermount in installation directory (bindir) as
+ well as in PATH.
+
+2006-05-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: fix compilation if CLOCK_MONOTONIC is not defined.
+ Reported by Christian Magnusson
+
+2006-04-23 Csaba Henk <csaba.henk@creo.hu>
+
+ * lib: make FreeBSD mount routine recognize if kernel features
+ backgrounded init and if it does, run the mount util in foreground
+ (similarly to Linux)
+
+2006-04-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: fix fput deadlock fix, the lockless solution could lead
+ to "VFS: busy inodes after umount..."
+
+ * kernel: fix race between checking and setting file->private_data
+ for the device. Found by Al Viro
+
+2006-04-11 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: remove request pool, instead allocate requests on
+ demand. Account the number of background requests, and if they go
+ over a limit, block the allocation of new requests.
+
+ * kernel: fix deadlock if backgrounded request holds the last
+ reference to the super block
+
+ * kernel: don't use fuse_reset_request() during direct I/O
+
+2006-04-06 Csaba Henk <csaba.henk@creo.hu>
+
+ * lib: Let FreeBSD mount option parsing routine recognize "no"
+ prefixes for FUSE specific options as well
+
+2006-04-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: Add missing rwlock initialization. Patch by Ryan Bradetich
+
+2006-03-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * API changes:
+
+ * fuse_main(), fuse_setup() and fuse_new() have an additionl
+ user_data parameter
+
+ * fuse_mount() returns a 'struct fuse_chan' pointer instead of a
+ file descriptor
+
+ * fuse_unmount() receives a 'struct fuse_chan' pointer. It
+ destroys the given channel
+
+ * fuse_teardown() no longer has a file descriptor parameter
+
+ * new exported functions: fuse_session_remove_chan(),
+ fuse_get_session(), fuse_daemonize()
+
+ * fuse_chan_recv() may now return a new channel which will be used
+ to send the reply
+
+2006-03-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.6.0-pre2
+
+2006-03-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Don't unmount if already unmounted. This fixes a problem seen
+ in the following situation: Lazy unmount a busy filesystem; Mount
+ a new one in top; When the first finally unmounts, the second also
+ unmounts. Reported by Franco Broi
+
+2006-03-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lowlevel lib: use indirect function calls instead of a
+ switch/case construct. Besides increased efficiency it helps
+ maintainability & readability too. Patch from Florin Malita
+
+2006-03-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: replace global spinlock with a per-connection spinlock
+
+2006-03-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix source compatibility breakage for fuse_unmount(). Report
+ from Yura Pakhuchiy
+
+2006-03-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix O_ASYNC handling in fuse_dev_release(). From Jeff Dike
+
+2006-03-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add O_ASYNC and O_NONBLOCK support to FUSE device. Patch by
+ Jeff Dike
+
+ * Renamed fuse_chan_receive() to fuse_chan_recv() and changed
+ interface to return -errno in case of error.
+
+2006-03-01 Csaba Henk <csaba.henk@creo.hu>
+
+ * libfuse: pass device file descriptor to fuse_unmount(), rewrite
+ FreeBSD implementation so that it uses libc (sysctl backed) instead
+ of an embdedded script (kmem backed). Adjust the control flow of
+ hello_ll so that device doesn't get closed before unmount attempt.
+
+2006-02-25 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Lowlevel lib: return all-zero statvfs data if filesystem doesn't
+ implement method. This is needed on FreeBSD, and nicer on Linux
+ too. Highlevel lib already did this. Reported by Csaba Henk
+
+ * Fix negative entry handling. There was a bug, that negative
+ lookups with timeouts (nodeid == 0) returned -EIO.
+
+2006-02-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix race between RELEASE and UNLINK, which might leave
+ .fuse_hidden* files around
+
+2006-02-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusexmp_fh: implement flush() method and call close() on the
+ open file descriptor. This is needed if used on an NFS
+ filesystem, which buffers data until file is closed. Franco Broi
+ spotted the situation when 'cp -p' failed to set the modification
+ time because of this.
+
+2006-02-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.6.0-pre1
+
+2006-02-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: fix use-after-free bug in interruptred reply_entry().
+ Patch from John Muir
+
+ * libfuse: fix wrong symbol versioning for fuse_mount. Debian bug
+ ID: 352631. Found by Stéphane Rosi
+
+2006-02-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Lowlevel lib: Unify fuse_dirent_size() and fuse_add_dirent()
+ into a single function fuse_add_direntry(). This cleans up the
+ interface and makes it possible to do stacking.
+
+2006-02-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix rare race betweeen abort and release caused by failed iget()
+ in fuse_create_open().
+
+ * Add 'ac_attr_timeout' option e.g. for filesystems which do their
+ own attribute caching.
+
+2006-02-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Work around FreeBSD runtime linker "feature" which binds an old
+ version of a symbol to internal references if the symbol has more
+ than one version. This resulted in infinite recursion in
+ fuse_lowlevel_new_compat25().
+
+2006-02-10 Csaba Henk <csaba.henk@creo.hu>
+
+ * Refine clock_gettime() querying so that linker options
+ shall be set as it's appropriate for the target platform.
+
+2006-02-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix udev rule syntax. Reported by Nix
+
+2006-02-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * In some cases udev rule seems to be ineffective when installed
+ as 40-fuse.rules but work as 60-fuse.rules. Reported by John Hunt
+
+2006-02-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix compilation when build directory is different from source
+ directory. Reported by Frédéric L. W. Meunier
+
+2006-02-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix even bigger bug introduced in fix for request_end() on
+ 2006-01-14. Reported by Gal Rosen
+
+2006-01-30 Miklos Szeredi <miklos@szeredi.hu>
+
+ * highlevel-lib: add 'auto_cache' option. This caches file data
+ based on modification time and size
+
+2006-01-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Sanitize storage type and help message in mount_bsd.c. Patch
+ from Csaba Henk
+
+ * fuse_opt: add new helper constants FUSE_OPT_KEY_KEEP and
+ FUSE_OPT_KEY_DISCARD
+
+ * Add options 'max_readahead', 'sync_read' and 'async_read'
+
+ * Kernel ABI version 7.6:
+
+ * Negotiate the 'max_readahead' value and 'async_read' flags with
+ userspace in the INIT method
+
+ * Add connection info to ->init() methods to both lowlevel and
+ highlevel API
+
+ * Fall back to synchronous read() behavior if either library or
+ userspace filesystem is using the old interface version. This is
+ needed so non-updated filesystems won't be confused by the
+ different read() behavior
+
+2006-01-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: if "fsname=" option was given, pass it to fusermount
+
+ * fuse_opt: add new fuse_opt_insert_arg() function, which is
+ needed by filesystems to implement some argument manipulations
+ correctly
+
+ * fuse_opt: fix memory leak in handling "--" option
+
+2006-01-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: fix detection of case when fuse is not configured into
+ the kernel either as module or built-in
+
+ * fuse_opt.h: fix incompatibility with C++ compilers by renaming
+ 'template' structure member to 'templ'. Reported by Takashi Iwai
+
+ * fuse.h: fix compatibility bugs. Patch by Yura Pakhuchiy
+
+ * kernel: support version 2.6.16 (i_sem -> i_mutex)
+
+2006-01-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added (again) asynchronous readpages support
+
+ * Each connection now shows up under /sys/fs/fuse/connections
+
+ * Connection attributes exported to sysfs: 'waiting' number of
+ waiting requests; 'abort' abort the connection
+
+ * Connection may be aborted through either the sysfs interface or
+ with 'umount -f mountpoint'
+
+2006-01-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.5.0
+
+2006-01-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: fix a couple of bugs
+
+ * Order of request_end() and fuse_copy_finish() was wrong.
+ Posthumous note: Franco Broi managed to exploit this, though it
+ seemed quite impossible
+
+ * request_end() used request pointer after decrementing refcount
+
+ * Clearing ->connected or ->mounted connection flags could race
+ with setting other bitfields not protected with a lock
+
+2006-01-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: add necessary compile flags for 2.4.X/x86_64.
+ Report from Sean Ziegeler
+
+2006-01-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.5.0-pre2
+
+2006-01-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Applied patch from Csaba Henk, to update mount_bsd to new
+ fuse_mount() semantics
+
+ * Ignore auto,noauto,... options in mount.fuse. Reported by Frank
+ Steiner and Don Taber
+
+ * fusermount: add 'dirsync' mount option
+
+2006-01-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Improved help reporting and added version reporting to library
+
+2006-01-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Change working directory to "/" even if running in the
+ foreground. Patch from Jonathan Brandmeyer
+
+ * Changed lots of functions to use 'struct fuse_args' instead of
+ separate argc and argv
+
+ * Added fuse_parse_cmdline(), fuse_set_signal_handlers() and
+ fuse_remove_signal_handlers() functions, so that it's now pretty
+ easy to get all the functionality of fuse_main() with a filesystem
+ using the lowlevel API.
+
+2006-01-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * mount.fuse: the 'user' option should be ignored. Report and
+ solution from Mattd.
+
+ * mount.fuse: export PATH in the right place. Report and patch
+ from Hannes Schweizer
+
+2005-12-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Clean up the option parsing interface slightly, by creating an
+ "argument list" structure, that contains the argument vector and
+ count
+
+2005-12-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: check if /mnt/mtab is a symlink and don't modify it
+ in that case
+
+ * kernel: simplify request size limiting. INIT only contains
+ maximum write size, maximum path component size remains fixed at
+ 1024 bytes, and maximum xattr size depends on read buffer.
+
+2005-12-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix readdir() failure on x86_64, of 32bit programs compiled
+ without largefile support. Bug report and help from Anthony
+ Kolasny
+
+ * If lookup returns invalid mode, return -EIO instead of creating
+ a regular file
+
+ * Add current output argument vector to option processing
+ function
+
+2005-12-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix stale code in ifdef FreeBSD. Patch from Csaba Henk
+
+2005-12-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.5.0-pre1
+
+2005-12-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: added option parsing interface, defined in
+ <fuse_opt.h>.
+
+2005-12-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Return EIO for file operations (read, write, fsync, flush) on
+ open files whose inode has become "bad". Inodes will be marked
+ "bad" if their type changes. Bug report by Csaba Henk
+
+2005-12-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Use bigger request buffer size. write() did not work on archs
+ with > 4k page size, Bug report by Mark Haney
+
+ * ABI version 7.5:
+
+ * Extend INIT reply with data size limits
+
+2005-12-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix memory leak in fuse_read_cmd()/fuse_process_cmd(). Bug
+ reported by Vincenzo Ciancia
+
+ * Handle exit-by-umount in fuse_read_cmd()
+
+2005-11-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check if '-msoft-float' option is supported by compiler when
+ configuring for a 2.4.x kernel. Bug report by Mark Haney
+
+ * In multithreaded loop send a TERM signal to the main thread if
+ one of the other threads exit. Needed on FreeBSD for a clean exit
+ on umount. Should not cause any harm on Linux either
+
+2005-11-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix bug in 32-bit file handle compatibility
+
+2005-11-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Block TERM, INT, HUP and QUIT signals in all but the main
+ thread. According to POSIX it's not specified which thread will
+ receive these signals.
+
+ * Kernel changes:
+
+ * Check for directory aliasing on mkdir, not just on lookup
+
+ * Check for special node ID values in create+open operation
+
+ * Sync with -mm: readv, writev, aio_read and aio_write methods
+ added to file operations
+
+ * Cleanups: lookup code, page offset calculation
+
+ * ABI stepped to 7.4, changes:
+
+ * frsize member added to fuse_kstatfs structure
+
+ * added support for negative entry caching: on lowlevel API if
+ fuse_entry_param::ino is set to zero in reply to a lookup request,
+ the kernel will cache the dentry for the specified amount of time.
+
+ * libfuse: added 'negative_timeout' option: specifies how much
+ negative entries should be cached. Default is zero, to be
+ compatible with prior versions
+
+2005-11-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add detection of mainline FUSE code in running kernel
+
+2005-11-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Don't use async cancelation in multithreaded loop. This makes
+ it more portable to systems where read() is not async cancel safe.
+ Report from Andriy Gapon
+
+2005-11-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Warn if API version 11 compatibility is requested
+
+2005-11-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * More FreeBSD merge
+
+ * fusermount: don't allow mountpoints with '\n', '\t', or '\\' in
+ them, because it corrupts /etc/mtab. Found by Thomas Biege
+ CVE-2005-3531
+
+ * libfuse: don't use system() to invoke 'fusermount -u ...'
+ because it breaks mountpoints with spaces in them into multiple
+ arguments
+
+2005-11-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Merge library part of FreeBSD port. Patch by Csaba Henk
+
+2005-11-11 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Use 64bit type for file handle, so the full range supported by
+ the kernel interface is available to applications
+
+2005-11-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Moved mountpoint argument checking from fuse_parse_cmdline() to
+ fuse_mount() in preparation to FreeBSD merge.
+
+2005-11-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Remove unneeded close() from fuse_teardown(). Spotted by Csaba
+ Henk.
+
+2005-11-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Make the statfs change backwards compatible.
+
+2005-11-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Change ->statfs() method to use 'struct statvfs' instead of
+ 'struct statfs'. This makes the API more portable since statvfs()
+ is defined by POSIX.
+
+2005-10-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add fgetattr() method, which currently will only be called after
+ a successful call to a create() method.
+
+2005-10-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Change kernel ABI version to 7.3
+
+ * Add ACCESS operation. This is called from the access() system
+ call if 'default_permissions' mount option is not given, and is
+ not called on kernels 2.4.*
+
+ * Add atomic CREATE+OPEN operation. This will only work with
+ 2.6.15 (presumably) or later Linux kernels.
+
+ * Add ftruncate() method. This will only work with 2.6.15
+ (presumably) or later Linux kernels.
+
+ * Fix kernel module compile if kernel source and build directories
+ differ. Report and initial patch by John Eastman
+
+2005-10-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: optimize buffer reallocation in fill_dir.
+
+2005-10-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.4.1
+
+2005-10-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: add debug for write result (by Shaun Jackman) and
+ warnings for too large read/write result
+
+2005-10-11 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Spelling fixes, thanks to Ioannis Barkas
+
+2005-10-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fuse_common.h: use extern "C". Thanks to Valient Gough for the
+ patch
+
+2005-10-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * highlevel-lib: init() and destroy() methods didn't have an
+ initialized fuse_context. Bug reported by Tim Stoakes
+
+2005-10-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.4.0
+
+2005-10-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add documentation to fuse_lowlevel.h
+
+ * API cleanups:
+
+ * Remove definitions of unused FATTR_CTIME / FUSE_SET_ATTR_CTIME
+
+ * Move fuse_mount() and fuse_unmount() to fuse_common.h
+
+ * Change the return type of fuse_reply_none() from int to void.
+
+2005-09-30 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: NFS exporting leaked dentries. Bug found and fixed by
+ Akshat Aranya.
+
+2005-09-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: fix error message, when unable to open /dev/fuse.
+ Report by Balázs Pozsár
+
+2005-09-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * UClibc fixes from Christian Magnusson
+
+2005-09-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added NAME="%k" to util/udev.rules. Fix by Mattias Wadman.
+
+2005-09-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.4.0-rc1
+
+2005-09-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: allow user umount in the case when /etc/mtab is a
+ symlink to /proc/mounts. Reported by Balázs Pozsár.
+
+2005-09-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check for special node ID values in lookup and creation
+
+2005-09-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Slight optimization in returning EINVAL error in case of an open
+ with O_DIRECT flag.
+
+2005-09-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Remove '--enable-auto-modprobe' configure flag. Module
+ auto-loading is now handled by the kernel.
+
+2005-09-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Install UDEV rule file, so /dev/fuse is created with mode 0666.
+ Help from Jens M. Noedler.
+
+2005-09-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add memory cleanup on thread exit
+
+2005-09-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Set umask to zero in fusexmp and fusexmp_fh, so that
+ files/directories are created with the requested mode.
+
+2005-09-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Don't ignore read error in multithreaded loop
+
+2005-09-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.4.0-pre2
+
+2005-09-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Revert lock and access operations. Postpone these until 2.5.
+
+2005-09-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix compile warning on 2.6.13 and later
+
+ * Fix compilation on old kernels
+
+2005-08-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: always refresh directory contents after rewinddir() to
+ conform to SUS. Bug found by John Muir.
+
+2005-08-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.4.0-pre1
+
+2005-08-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: cleaned up (or messed up, depending on your POV) the low
+ level library API. Hopefully this is close to the final form.
+
+2005-08-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: don't allow empty mountpoint argument, which defeats
+ automatic umounting in fuse_main(). Bugreport by Václav Jůza
+
+2005-08-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fix warnings in fuse.h and fuse_lowlevel.h if -Wshadow compiler
+ option is used (Paul Alfille).
+
+2005-08-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * highlevel-lib: added mount options "attr_timeout" and
+ "entry_timeout". These options control the length of time file
+ attributes and entries (names) are cached. Both default to 1.0
+ second.
+
+ * kernel: correctly handle zero timeout for attributes and entries
+
+2005-08-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added missing symbols to versionscript (Joshua J. Berry)
+
+ * kernel: implement two flags, open can set: 'direct_io' and
+ 'keep_cache'. These correspond exactly to mount options
+ 'direct_io' and 'kernel_cache', but allow a per-open setting.
+
+ * Move 'direct_io' and 'kernel_cache' mount option handling to
+ userspace. For both mount options, if the option is given, then
+ the respective open flag is set, otherwise the open flag is left
+ unmodified (so the filesystem can set it).
+
+ * lib (highlevel): make open method optional
+
+2005-07-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: invalidate attributes for read/readdir/readlink
+ operations
+
+ * kernel: detect newer UML kernels
+
+2005-07-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Make the installation path of fuse.ko and mount.fuse
+ configurable through INSTALL_MOD_PATH and MOUNT_FUSE_PATH
+ environment variables. Requirement and help from Csaba Henk.
+
+2005-07-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix bug, that causes filesystem requests to hang when unique
+ request counter becomes negative. This happens after
+ 2,147,483,648 operations, so most people won't care. Thanks to
+ Franco Broi for the report and testing.
+
+2005-07-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Don't change mtime/ctime/atime to local time on read/write.
+ Bug reported by Ben Grimm
+
+ * Install fuse_common.h and fuse_lowlevel.h. Report by Christian
+ Magnusson
+
+ * fusermount: use getopt_long() for option parsing. It allows the
+ use of '--' to stop argument scanning, so fusermount can now
+ operate on directories whose names begin with a '-'. Patch by
+ Adam Connell
+
+2005-07-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: add '-v', '--version' and '--help' options
+
+ * add inode based API
+
+2005-07-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: don't block signals in worker threads. Problem noticed by
+ Usarin Heininga
+
+2005-07-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: don't allow both 'allow_other' and 'allow_root' options to
+ be given
+
+2005-07-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: check if mountpoint is empty (only '.' and '..' for
+ directories, and size = 0 for regular files). If "nonempty"
+ option is given, omit this check. This is useful, so users don't
+ accidentally hide data (e.g. from backup programs). Thanks to
+ Frank van Maarseveen for pointing this out.
+
+ * kernel: check if mandatory mount options ('fd', 'rootmode',
+ 'user_id', 'group_id') are all given
+
+ * lib: simplify 'readdir_ino' handling
+
+ * lib: add mount options 'umask=M', 'uid=N', 'gid=N'
+
+2005-07-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: clean up 'direct_io' code
+
+2005-06-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add 'mount.fuse' written by Petr Klima
+
+ * '/dev/fuse' is created by 'make install' if does not yet exist
+
+2005-06-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix UCLIBC compile error. Patch by Christian Magnusson
+
+2005-06-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Enable the auto-loading of the module via access to the
+ corresponding device file. Patch by Takashi Iwai.
+
+ * Allow mounting a regular file (over a regular file) for
+ unprivleged users.
+
+ * Do not create temporary device file. Require "/dev/fuse" to
+ exist, and be readable/writable by the mounting user.
+
+2005-06-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.3.0
+
+2005-06-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix serious information leak: if the filesystem returns a short
+ byte count to a read request, and there are non-zero number of
+ pages which are not filled at all, these pages will not be zeroed.
+ Hence the user can read out previous memory contents. Found by
+ Sven Tantau.
+
+2005-05-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add "readdir_ino" mount option, which tries to fill in the d_ino
+ field in struct dirent. This mount option is ignored if "use_ino"
+ is used. It helps some programs (e.g. 'pwd' used over NFS from a
+ non-Linux OS). Patch by David Shaw.
+
+2005-05-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.3-rc1
+
+2005-05-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * File save in krusader and other editors doesn't work with sshfs,
+ because open() is interrupted by a periodic signal, and open()
+ restarts forever, without any progress. This could just be fixed
+ in open(), but the problem is more generic: if signals are
+ received more often than the filesystem can get the request to
+ userspace, it will never finish. This is probably only a
+ theoretical problem, nevertheless I'm removing the possibility to
+ interrupt requests with anything other than SIGKILL, even before
+ being sent to userspace. Bugreport by Eduard Czimbalmos.
+
+2005-05-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: add "tree_lock" rwlock, that is locked for write in
+ rename, unlink and rmdir, and locked for read in all other
+ operations. This should fix the rename/release race reported by
+ Valient Gough and others. The solution is very coarse, a finer
+ grained locking scheme could be implemented, but it would be much
+ more complex. Let's see whether this is good enough.
+
+2005-05-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.3-pre7
+
+2005-05-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Better fix for out of order FORGET messages. Now the
+ LOOKUP/FORGET messages are balanced exactly (one FORGET can
+ balance many lookups), so the order no longer matters. This
+ changes the kernel ABI slightly, but the library remains backward
+ compatible.
+
+2005-05-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix abort for out of order FORGET messages. Again. Spotted by
+ Franco Broi again. Sorry :)
+
+2005-04-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.3-pre6
+
+2005-04-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Make fusermount work with fuse kernel modules not yet supporting
+ the "group_id" option (added for the purpose of stricter
+ permission checking).
+
+2005-04-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check for hard-linked directories in lookup. This could cause
+ problems in the VFS, which assumes that such objects never exist.
+
+ * Make checking of permission for other users more strict. Now
+ the same privilege is required for the mount owner as for ptrace
+ on the process performing the filesystem operation.
+
+2005-04-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.3-pre5
+
+2005-04-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add -msoft-float to kernel module compile flags for 2.4.X. This
+ is needed on certain architectures. Report from Chris Kirby
+
+ * Fix buggy behavior of open(..., O_CREAT|O_EXCL) if interrupted.
+ Reported by David Shaw
+
+ * Remove "allow_root" option from kernel module, and implement
+ it's functionality in the library
+
+ * Fix Oops caused by premature release of fuse_conn. Clean up
+ related code, to be more readable
+
+ * Sendfile should not use page cache if "direct_io" mount option
+ is given
+
+2005-04-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix Oops in case of nfs export. Spotted by David Shaw
+
+ * Fix another Oops in case of write over nfs with direct_io turned
+ on. Again spotted by David Shaw
+
+2005-04-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.3-pre4
+
+2005-04-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * lib: finalized new readdir() interface, which now supersedes the
+ getdir() method.
+
+2005-04-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.3-pre3
+
+2005-04-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Implement backward compatibility with version 5 kernel ABI
+
+2005-04-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.3-pre2
+
+2005-04-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: fix dirent offset handling
+
+ * lib: add readdir and releasedir methods
+
+ * lib: use fh field of fuse_file_info in opendir, readdir,
+ releasedir and fsyncdir methods
+
+ * lib: check kernel API version and bail out of it's old. This
+ will be properly fixed in the next release
+
+2005-03-31 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.3-pre1
+
+2005-03-31 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel API: add padding to structures, so 64bit and 32bit
+ compiler will return the same size
+
+ * kernel API: add offset field to fuse_dirent. This will allow
+ more sophisticated readdir interface for userspace
+
+ * kernel API: change major number to 6
+
+ * kernel: fix warnings on 64bit archs
+
+ * kernel: in case of API version mismatch, return ECONNREFUSED
+
+2005-03-24 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: trivial cleanups
+
+2005-03-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add fsyncdir() operation
+
+2005-03-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: add locking to background list (fixes previous fix)
+
+2005-03-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: fix bug which could cause leave busy inodes after
+ unmount, and Oops.
+
+2005-03-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * examples: add -lpthread to link flags to work around valgrind
+ quirk
+
+ * lib: don't exit threads, so cancelation doesn't cause segfault
+
+2005-03-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: fix nasty bug which could cause an Oops under certain
+ situations. Found by Magnus Johansson
+
+2005-02-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: added opendir() method. This can be used in case
+ permission checking in getdir() is too late. Thanks to Usarin
+ Heininga for pointing out this deficiency
+
+ * libfuse: added init() and destroy() methods to fuse_operations
+
+ * kernel: llseek() method for files and directories made explicit
+
+ * kernel: fixed inode leak in NFS export in case of nodeid
+ wrapping
+
+2005-02-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse: clean up some unitialized memory found with valgrind
+
+ * Add -lpthread to Libs in fuse.pc. Valgrind seems to need an
+ explicitly linked libpthread for applications
+
+2005-02-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: set umask, otherwise /etc/mtab will have
+ unpredictable permission. Spotted by Jindrich Kolorenc
+
+ * fusermount: set owner and group of /etc/mtab to original values
+ on unmount
+
+ * libfuse: add 'use_ino' option to help. Patch by Valient Gough
+
+2005-02-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Cleaned up directory reading (temporary file is not used)
+
+2005-02-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.2
+
+2005-02-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix possible race when operation is interrupted
+
+2005-01-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix compilation on 2.6.7
+
+2005-01-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.2-pre6
+
+2005-01-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix bug in link() operation which caused the wrong path to be
+ passed as the first argument. Found by Anton Altaparmakov
+
+2005-01-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * LIB: fix double reply in readdir operation
+
+ * fusermount: fix uid checking bug. Patch by Adam Connell
+
+ * KERNEL: fix compile on various RedHat patched 2.4 kernels.
+ Patch by Keshava Gowda
+
+2005-01-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * KERNEL: provide correct llseek semantics for fuse device (fixes
+ a bug on Progeny 2.4.20 kernel). Reported by Valient Gough
+
+2005-01-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.2-pre5 (matches kernel 2.6.11-rc1-mm2)
+
+2005-01-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * KERNEL ABI: remove GETDIR operation, and add OPENDIR, READDIR
+ and RELEASEDIR. This ends the ugly hack of passing a file
+ descriptor to the kernel, and actually makes the code simpler.
+
+2005-01-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.2-pre4
+
+2005-01-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: remove capability setting, which was the cause of
+ problems for some users. It seems that FS related capabilities
+ are removed by setfsuid(), so this isn't even needed.
+
+2005-01-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fix compilation on 2.4 kernels (reported by Valient Gough)
+
+ * fix failure to unmount bug (found by David Shaw)
+
+ * fusermount: improve parsing of /etc/fuse.conf
+
+2005-01-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Remove 'mount_max' and 'user_allow_other' module options. These
+ are now checked by fusermount, and can be set in /etc/fuse.conf
+
+ * KERNEL: change check for fsid == 0 to capable(CAP_DAC_OVERRIDE)
+
+2005-01-11 Miklos Szeredi <miklos@szeredi.hu>
+
+ * KERNEL: fix possible inode allocation problem, where
+ sizeof(struct inode) is not aligned (found by Mike Waychison)
+
+ * KERNEL: use new follow_link/put_link methods
+
+ * KERNEL: cosmetic fixes
+
+2005-01-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.2-pre3
+
+2005-01-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add missing code that was accidently left out
+
+2005-01-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.2-pre2
+
+2005-01-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Change "uid" mount option to "user_id" to avoid confusion with a
+ mount option "uid" commonly used by many filesystems
+
+2005-01-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.2-pre1
+
+2005-01-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * If FUSE is configured in the kernel, don't build it by default
+
+2005-01-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Compile fix by Christian Magnusson
+
+2005-01-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix compilation for 2.6.{0-5} kernels
+
+2005-01-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * KERNEL: if request is interrupted, still keep reference to used
+ inode(s) and file, so that FORGET and RELEASE are not sent until
+ userspace finishes the request.
+
+ * remove /{sys,proc}/fs/fuse/version, and instead add an INIT
+ request with the same information, which is more flexible,
+ simpler, works on embedded systems.
+
+2004-12-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * KERNEL ABI: update interface to make it independent of type
+ sizes. This will help on 64 bit architectures which can run
+ legacy 32 bit applications.
+
+ * KERNEL ABI: add "len" field to request headers. This will allow
+ sending/receiving requests in multiple chunks.
+
+ * KERNEL: handle file type change more intelligently
+
+ * LIB: "-o debug" option should disable backgrounding (fix by
+ Fabien Reygrobellet)
+
+2004-12-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * KERNEL: invalidate dentry/attributes if interrupted request
+ could leave filesystem in an unknown state.
+
+2004-12-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * KERNEL: lots of cleanups related to avoiding possible deadlocks.
+ These will cause some regressions, but stability is considered
+ more important. If any of these features turns out to be
+ important, it can be readded with the deadlock problems addressed.
+
+ * Make all requests interruptible (only with SIGKILL currently).
+ This can be used to break any deadlock produced by the userspace
+ filesystem accessing it's own exported files. The RELEASE request
+ is special, because if it's interrupted before sending it to
+ userspace it is still sent, but the reply is not awaited.
+
+ * If request is interrupted before being sent to userspace, and if
+ it hasn't yet got any side effects, it is always restarted,
+ regardless of the SA_RESTART flag. This makes these interruptions
+ transparent to the process.
+
+ * Remove shared-writable mmap support, which was prone to an
+ out-of-memory deadlock situation
+
+ * Remove INVALIDATE userspace initiated request
+
+ * Make readpages() synchronous. Asynchronous requests are
+ deadlock prone, since they cannot be interrupted.
+
+ * Add readv/writev support to fuse device operations
+
+ * Remove some printks, which userspace FS can use for a DoS
+ against syslog
+
+ * Remove 'large_read' mount option from 2.6 in kernel, check it in
+ fusermount instead
+
+ * LIB: improve compatibility with a fuse.h header installed in
+ ${prefix}/include which in turn includes the real header.
+
+ * LIB: improve compatibility by defining fuse_main() (which is now
+ not used), so old configure scripts find it.
+
+2004-12-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * When mounting on a subdirectory of / don't duplicate slashes at
+ the beggining of path (spotted by David Shaw)
+
+2004-12-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix bug causing garbage in mount options (spotted by David Shaw)
+
+2004-12-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add 'writepage' flag to 'fuse_file_info'.
+
+ * More comments in fuse.h
+
+ * Get rid of double underscores
+
+2004-12-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add -D_FILE_OFFSET_BITS=64 to cflags provided by pkg-config
+
+ * helper.c: add -ho option, which only displays the options not
+ the usage header. This can be used by filesystems which have
+ their own options.
+
+2004-12-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add source compatibility to 2.1 and 1.1 APIs. To select betwen
+ versions simply define FUSE_USE_VERSION to 22, 21 or 11 before
+ including the fuse header
+
+ * Add binary compatibility to 2.1 version of library with symbol
+ versioning
+
+2004-12-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.1
+
+2004-12-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: clean up writing functions
+
+ * kernel: no allocation on write in direct_io mode
+
+ * move linux/fuse.h to fuse_kernel.h
+
+2004-11-30 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: clean up reading functions
+
+2004-11-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel: make readpage() uninterruptible
+
+ * kernel: check readonly filesystem flag in fuse_permission
+
+ * lib: don't die if version file not found and new style device
+ exists
+
+ * lib: add '-r' option, which is short for '-o ro'
+
+ * fusermount: simplify device opening
+
+ * kernel: when direct_io is turend on, copy data directly to
+ destination without itermediate buffer. More efficient and safer,
+ since no allocation is done.
+
+ * fusermount: fix warning if fuse module is not loaded
+
+ * kernel: use /dev/fuse on 2.4 too
+
+2004-11-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * libfuse API change: open, read, write, flush, fsync and release
+ are passed a 'struct fuse_file_info' pointer containing the open
+ flags (open and release), and the file handle. Verion changed to
+ 3.0.
+
+2004-11-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * More cleanups in the kernel
+
+ * The 10,229 charater device number has been assigned for FUSE
+
+ * Version file checking fix (reported by Christian Magnusson)
+
+ * fusermount: opening the fuse device now doesn't need /sys.
+
+ * Optimize reading by controlling the maximum readahead based on
+ the 'max_read' mount option
+
+ * fixes for UCLIBC (Christian Magnusson)
+
+2004-11-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Cleaned up kernel in preparation for merge into mainline:
+
+ * Use /sys/fs/fuse/version instead of /proc/fs/fuse/version
+
+ * Use real device (/dev/fuse) instead of /proc/fs/fuse/dev
+
+ * __user annotations for sparse
+
+ * allocate individual pages instead of kmalloc in fuse_readdir,
+ fuse_read and fuse_write.
+
+ * Fix NFS export in case "use_ino" mount option is given
+
+ * Make libfuse and fusermount compatible with future versions
+
+ * fusermount: properly add mount options to /etc/mtab
+
+2004-11-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fusermount: do not resolve last component of mountpoint on if it
+ is '.' or '..'. This new path resolvation is now done on mount as
+ well as unmount. This enables relative paths to work on unmount.
+
+ * fusermount: parse common mount options like "ro", "rw", etc...
+
+ * Allow module params to be changed through sysfs
+
+2004-11-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 2.1-pre1
+
+2004-11-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix bug in fuse_readpages() causing Oops in certain situations.
+ Bug found by Vincenzo Ciancia.
+
+ * Fix compilation with kernels versions > 2.6.9.
+
+2004-11-11 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check kernel interface version in fusermount to prevent
+ strangeness in case of mismatch.
+
+ * No need to allocate fuse_conn until actual mount happens
+
+ * Fix potential race between umount and fuse_invalidate
+
+ * Check superblock of proc file in addition to inode number
+
+ * Fix race between request_send_noreply() and fuse_dev_release()
+
+2004-11-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Separate configure for the kernel directory
+
+ * Don't allow write to return more than 'count'
+
+ * Extend kernel interface for future use
+
+2004-11-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix 'makeconf.sh' to use autoreconf if available
+
+2004-11-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add ino argument to 'fuse_dirfil_t'. NOTE: This breaks source
+ compatibility with earlier versions. To compile earier versions
+ just add '-DFUSE_DIRFIL_COMPAT' compile flag or fix the source.
+ Do not use the "use_ino" mount flag with filesystems compiled with
+ FUSE_DIRFIL_COMPAT.
+
+ * Add pkg-config support. To compile a FUSE based filesystem you
+ can do "gcc -Wall `pkg-config --cflags --libs fuse` myfs.c -o myfs"
+ or similar. Note, that the PKG_CONFIG_PATH environment variable
+ usually needs to be set to "/usr/local/lib/pkgconfig".
+
+ * fuse.h is now installed in ${prefix}/include/fuse/
+
+2004-11-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added "use_ino" mount option. This enables the filesystems to
+ set the st_ino field on files
+
+2004-11-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix compile problems with ancient (<=2.4.18) kernels (reported
+ by Jeremy Smith)
+
+ * Add "allow_root" mount option. Patch by Yaroslav Rastrigin
+
+ * Clear the 'exited' flag when mail loop is finished
+
+2004-10-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Make xattr functions work under 2.6 (bug found by Vincenzo
+ Ciancia)
+
+2004-10-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Reset request in fuse_flush() (bugreport by David Shaw)
+
+2004-10-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fuse_main() now does not exit on error, rather it returns an
+ error code
+
+ * Exported __fuse_setup() and __fuse_teardown() functions, which
+ make it easier to implement a custom event loop.
+
+ * Use daemon() call to background the filesystem after mounting.
+ This function closes the standard input, output and error and
+ changes the current working directory to "/".
+
+2004-10-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 1.9
+
+2004-10-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Don't allow fuse_flush() to be interrupted (bug found by David
+ Shaw)
+
+2004-09-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add PID to fuse_context. Patch by Steven James
+
+ * Change file handle type to 'unsigned long' in kernel interface
+
+2004-09-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * A slight API change: fuse_get_context() doesn't need the "fuse"
+ pointer, but the returned context contains it instead. The
+ fuse_get() function is not needed anymore, so it's removed.
+
+ * Fix mounting and umounting FUSE filesystem under another FUSE
+ filesystem by non-root (bug spotted by Valient Gough)
+
+2004-09-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix deadlock in case of memory allocation failure. Patch by
+ Christian Magnusson
+
+2004-09-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check memory allocation failures in libfuse
+
+2004-09-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check temporary file creation failure in do_getdir(). Bug
+ spotted by Terje Oseberg
+
+2004-09-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Allow "large_read" option for 2.6 kernels but warn of deprecation
+
+ * Make requests non-interruptible so race with FORGET is avoided.
+ This is only a temporary solution
+
+ * Support compiling FUSE kernel module on 2.4.x UML kernels
+
+2004-09-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix bug in case two FORGETs for the same node are executed in
+ the wrong order. Bug spotted and endured for months by Franco
+ Broi, and logfile for solution provided by Terje Oseberg
+
+2004-09-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add -D_REENTRANT to the compile flags
+
+ * Add documentation of fuse internals by Terje Oseberg
+
+2004-08-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Change release method to be non-interruptible. Fixes bug
+ causing missing release() call when program which has opened files
+ is killed (reported by Franco Broi and David Shaw)
+
+2004-07-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add fuse_invalidate() to library API
+
+2004-07-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check permissions in setattr if 'default_permissions' flag is
+ set. Bug spotted by Damjan Lango
+
+2004-07-24 Miklos Szeredi <miklos@szeredi.hu>
+
+ * 'large_read' mount option removed for 2.6 kernels, since the
+ default (dynamic read size) is better
+
+ * Extend kernel API with file handles. A file handle is returned
+ by open, and passed to read, write, flush, fsync and release.
+ This is currently only used for debug output in the library.
+
+ * Security changes:
+
+ * Change the current directory to the mountpoint before checking
+ the permissions and mount filesystem on "."
+
+ * By default don't modprobe the fuse module for non-root. The old
+ behavior can be restored with the '--enable-auto-modprobe' flag of
+ ./configure
+
+ * By default don't allow shared writable mappings for non-root.
+ The old behavior can be restored with the 'user_mmap=1' module
+ parameter
+
+2004-07-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Clean up mount option passing to fusermount and to fuse_new()
+ BEWARE: this changes the userspace API slightly, and the command
+ line usage of programs using fuse_main()
+
+2004-07-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Optimize reading under 2.6 kernels by issuing multiple page
+ asynchronous read requests
+
+2004-07-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Only use redirty_page_for_writepage() for kernels >= 2.6.6
+
+2004-07-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Separate directory entry and inode attribute validity timer
+
+ * New write semaphore to stop page writeback during truncate
+
+ * Fsync now waits for all writes to complete before sending the
+ request
+
+ * Optimization: if a page is completely written by
+ fuse_commit_write(), clear the dirty flag and set the uptodate
+ flag for that page
+
+ * Some memory cleanup at exit
+
+2004-07-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add FUSE_HARD_REMOVE flag, and '-i' option to fuse main, which
+ disable the "hide if open" behavior of unlink/rename.
+
+ * If temporary buffer allocation fails in raw read, fall back to a
+ smaller buffer
+
+2004-07-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix bug in do_open() in libfuse: open count was incremented
+ after the reply is sent so it could race with unlink/forget and
+ cause an abort.
+
+2004-07-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * When performing create or remove operation, refresh the parent's
+ attributes on next revalidate, as i_nlink (and maybe size/time)
+ could be inacurate.
+
+ * Use redirty_page_for_writepage() in fuse_writepage() for skipped
+ pages (2.6 only)
+
+ * Set set_page_dirty address space operation (2.6 only)
+
+2004-07-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Minor fix in read: print debug info even if read size is zero
+
+2004-07-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix race between truncate and writepage (fsx-linux now runs
+ without error)
+
+2004-07-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix kernel hang on mkfifo under 2.4 kernels (spotted and patch
+ by Mattias Wadman)
+
+ * Added option for direct read/write (-r)
+
+ * Fix revalidate time setting for newly created inodes
+
+ * Remove uid==0 check for '-x' option in fusermount (kernel checks
+ this)
+
+ * fuse_main() only installs handlers for signals (out of INT, HUP,
+ TERM, PIPE), for which no handler has yet been installed
+
+ * Add module option 'user_allow_other' which if set to non-zero
+ will allow non root user to specify the 'allow_other' mount option
+ ('-x' option of fusermount)
+
+ * Fix deadlock between page writeback completion and truncate
+ (bug found by Valient Gough with the fsx-linux utility)
+
+2004-07-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Change passing fuse include dir to 2.6 kernel make system more
+ robust (fixes compile problems seen on SuSE 9.1 with updated 2.6
+ kernel)
+
+2004-06-30 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Acquire inode->i_sem before open and release methods to prevent
+ concurrent rename or unlink operations.
+
+ * Make __fuse_read_cmd() read only one command. This allows
+ multiplexing the fuse file descriptor with other event sources
+ using select() or poll() (patch by Jeff Harris)
+
+ * Export 'exited' flag with __fuse_exited() (patch by Jeff Harris)
+
+2004-06-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix file offset wrap around at 4G when doing large reads
+
+2004-06-24 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix memory leak in open (Valient Gough)
+
+2004-06-24 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add "close after delete" support to libfuse (patch by Valient
+ Gough)
+
+ * Cancel all worker threads before exit in multithreaded mode
+
+2004-06-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix locking bugs
+
+ * Don't send reply to RELEASE
+
+ * Work with newer libtool (1.5a)
+
+ * Check for st_atim member of struct stat
+
+2004-06-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * No request allocation needed on inode and file release
+
+2004-06-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix possible inode leak in userspace in case of unfinished
+ lookup/mknod/mkdir/symlink/link operation.
+
+2004-06-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix some races and cleanups in fuse_read_super()
+
+2004-06-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Requests are allocated at open time
+
+2004-06-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Build shared library as well as static (using libtool)
+
+ * Change FUSE_MINOR_VERSION from 1 to 0. I know it's illegal but
+ there has not been a release with the previous minor number, and I
+ hope nobody is using it for anything.
+
+ * Change fuse_main(), so that default behavior is to go into
+ background if mount is successful. '-f' and '-d' options disable
+ backgrounding. This fixes the "Why does my FUSE daemon hang?"
+ newbie complaint.
+
+ * Cache ENOSYS (function not implemented) errors on *xattr, flush
+ and fsync
+
+ * Don't call getdir method from open() only from first readdir().
+ Open is sometimes just used to store the current directory
+ (e.g. find)
+
+2004-05-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added flush() call
+
+2004-05-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Extended attributes support for 2.4 (patch by Cody Pisto)
+
+2004-04-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fixed parser with modversions (Mattias Wadman)
+
+2004-04-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added mount option parser to 2.4 build
+
+2004-04-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Replaced binary mount data with text options
+
+ * Show FUSE specific mount options in /proc/mounts
+
+ * Check in fuse.h whether _FILE_OFFSET_BITS is set to 64
+
+2004-04-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check some limits so userspace won't get too big requests
+
+2004-04-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Kill compile warning
+
+ * Upgraded user-mount patch for 2.6.5
+
+2004-04-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add detection of user-mode-linux to configure
+
+2004-03-31 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fixed zero size case for getxattr and listxattr
+
+2004-03-30 Miklos Szeredi <miklos@szeredi.hu>
+
+ * new fusermount flag '-z': lazy unmount, default is not lazy
+
+ * Extended attributes operations added (getxattr, setxattr,
+ listxattr, removexattr)
+
+2004-03-25 Miklos Szeredi <miklos@szeredi.hu>
+
+ * If filesystem doesn't define a statfs operation, then an
+ all-zero default statfs is returned instead of ENOSYS
+
+2004-03-24 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add FS_BINARY_MOUNTDATA filesystem flag for kernels > 2.6.4
+
+2004-03-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix for uClinux (Christian Magnusson)
+
+2004-03-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fuse_main() adds "-n progname" to the fusermount command line
+
+ * More kernel interface changes:
+
+ * Lookup/getattr return cache timeout values
+
+2004-02-25 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Clean up option parsing in fuse_main()
+
+ * Added fuse_get() function which returns the fuse object created
+ by fuse_main()
+
+2004-02-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * removed old way of mounting (fusermount mountpoint program)
+
+ * more kernel interface changes:
+
+ * added nanosecond precision to file times
+
+ * removed interface version from mount data
+
+ * added /proc/fs/fuse/version which contains MAJOR.MINOR
+
+2004-02-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * statfs library API changed to match other methods. Since this
+ is not backward compatible FUSE_MAJOR_VERSION is changed to 2
+
+ * kernel interface changes follow:
+
+ * statfs changed to 64 bits, added 'bavail' field
+
+ * add generation number to lookup result
+
+ * optimized mknod/mkdir/symlink/link (no separate lookup is
+ needed)
+
+ * rdev size increased to 32 bits for mknod
+
+ * kernel interface version changed to 3.1
+
+2004-02-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * user-mount upgraded for 2.6.3 kernel
+
+2004-02-17 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added user-mount.2.6.2-rc3.patch
+
+ * Add FS_SAFE flag to fuse filesystem
+
+ * fusermount should allow (un)mounting for non-root even if not
+ suid-root
+
+2004-02-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Remove MS_PERMISSION mount flag (that means something else now)
+
+2004-02-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added check for i_size_read/write functions to configure.in
+ (patch by Valient Gough)
+
+2004-02-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fixed writing >= 2G files
+
+ * Check file size on open (with generic_file_open())
+
+ * Readpage calls flush_dcache_page() after storing data
+
+ * Use i_size_read/write for accessing inode->i_size
+
+ * Make loopback mount of a fuse file work
+
+2004-02-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 1.1
+
+2004-01-29 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Properly check if the inode exists in fuse_invalidate
+
+2004-01-27 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added -q option for fusermount
+
+ * fuse_unmount() now uses -q option of fusermount, so no error is
+ printed if the cause of the program exit is that the filesystem
+ has already been unmounted
+
+ * Fix i_nlink correctness after rmdir/unlink
+
+2004-01-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 1.1-pre2
+
+2004-01-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix typo (thanks Marcos Dione)
+
+ * Compile fixes for 2.4 kernels
+
+2004-01-23 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix CONFIG_MODVERSIONS compile on 2.6
+
+2004-01-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Write all pending data before a RELEASE operation
+
+ * Suppress 'Bad file descriptor' warning on exit
+
+ * Replaced fusermount option '-d xxx' with '-n xxx' so it doesn't
+ get confused with '-d' of fuse_main() (sorry about this change)
+
+ * New fusermount option '-l' which enables big reads. Big reads
+ are now disabled by default.
+
+ * fuse_main() can accept fusermount arguments after a '--'
+
+2004-01-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Support for exporting filesystem over NFS (see README.NFS)
+
+2004-01-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Support non-blocking writepage on 2.6. This makes FUSE behave
+ much more nicely in low-memory situations
+
+ * Fix 32-bit dev handling in getattr and mknod for 2.6 kernels.
+ (Note: the mknod method does not yet use 32bit device number)
+
+2004-01-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Code cleanups
+
+2004-01-07 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Released 1.1-pre1
+
+2004-01-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Integrated 2.6 kernel support patch by Michael Grigoriev
+
+ * Improvements and cleanups for 2.6 kernels
+
+2004-01-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added -d option to fusermount
+
+2003-12-15 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added major+minor version to library API, and minor version to
+ kernel API
+
+2003-12-13 David McNab <david@rebirthing.co.nz>
+
+ * Implemented fsync support in examples/example.py
+
+ * Implemented 'fsync' and 'statfs' methods in python
+ interface
+
+2003-12-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Make it compile on 2.4.19.
+
+ * Add fsync operation (write file failed on xemacs & vi)
+
+2003-12-12 David McNab <david@rebirthing.co.nz>
+
+ * Added distutils support to the python module, as per standard
+ python development practice
+
+2003-12-11 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Add file locking for mount/unmount (based on patch by Valient
+ Gough)
+
+2003-12-11 David McNab <david@rebirthing.co.nz>
+
+ * Python filesystem - was broken with python2.3, now fixed:
+ - changed PyTuple_* calls to PySequence_*, because os.lstat
+ is no longer returning a pure tuple
+ - changed PyInt_Check() calls to also call PyLong_Check,
+ to cover for cases (eg os.lstat) where longs are returned
+ - Added support for file 'release' handling, which IMO is
+ essential since this signals to a FS that writes to a file
+ are complete (and therefore the file can now be disposed of
+ meaningfully at the python filesystem's discretion)
+ - Added '__init__' handler to base Fuse class, which allows
+ your Python class to know the mountpoint and mount args,
+ as attributes myfs.mountpoint, myfs.optlist, myfs.optdict
+
+ * General:
+ - added 'mount.fuse' script (in util/ dir), which is meant to be
+ symlinked from /sbin, and which allows FUSE filesystems to
+ be mounted with the 'mount' command, and listed in fstab;
+ also, mount arguments get passed to your filesystem
+
+
+2003-11-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix kernel version detection (again). Bugreport by Peter Levart
+
+2003-11-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Applied read combining patch by Michael Grigoriev (tested by
+ Valient Gough and Vincent Wagelaar)
+
+2003-10-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Mtab handling fix in fusermount by "Valient Gough" (SF patch
+ #766443)
+
+2003-10-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Error code fixes in kernel module
+
+2003-10-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * kernel version detection fix
+
+ * fusermount now uses "lazy" umount option
+
+ * fusermount can use modprobe with module-init-tools
+
+2003-09-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Integrated caching patch by Michael Grigoriev
+
+ * Added "Filesystems" file with descriptions of projects using
+ FUSE
+
+ * Added patch by Michael Grigoriev to allow compliation of FUSE
+ kernel module for 2.6 kernels
+
+2003-06-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * And another spec-file fix by Achim Settelmeier
+
+2003-05-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Spec-file fix by Achim Settelmeier
+
+2003-03-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fix umount oops (found by Samuli Kärkkäinen)
+
+2003-03-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Merge of fuse_redhat.spec and fuse.spec by Achim Settelmeier
+
+2003-03-04 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Updated fuse.spec file (Achim Settelmeier)
+
+2003-02-19 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Version 1.0 released
+
+2003-02-12 Miklos Szeredi <miklos@szeredi.hu>
+
+ * SuSE compilation fix by Juan-Mariano de Goyeneche
+
+2002-12-10 Miklos Szeredi <miklos@szeredi.hu>
+
+ * The release() VFS call is now exported to the FUSE interface
+
+2002-12-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * 64 bit file offset fixes in the fuse kernel module
+
+ * Added function 'fuse_exit()' which can be used to exit the main
+ loop
+
+2002-12-03 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added _FILE_OFFSET_BITS=64 define to fuse.h. Note, that this is
+ an incompatible interface change.
+
+2002-10-28 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Portablility fix (bug reported by C. Chris Erway)
+
+2002-10-25 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Use Mark Glines' fd passing method for default operation instead
+ of old reexec
+
+2002-10-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fix "Stale NFS file handle" bug caused by changes in 2.4.19
+
+2002-10-22 Miklos Szeredi <miklos@szeredi.hu>
+
+ * fix incompatiblity with Red Hat kernels, with help from Nathan
+ Thompson-Amato.
+
+2002-04-18 Mark Glines <mark@glines.org>
+
+ * added an alternative to fuse_mount(), called
+ fuse_mount_ioslave(), which does not need to reexec the
+ FUSE program.
+ * added a small helper util needed by fuse_mount_ioslave().
+
+2002-03-16 Mark Glines <mark@glines.org>
+
+ * use struct fuse_statfs everywhere possible to avoid problems
+ with the headerfiles changing struct statfs member sizes
+
+2002-03-01 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Another RPM spec file for RedHat >= 7 by Ian Pilcher
+
+2002-01-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * RPM support by Achim Settelmeier
+
+2002-01-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Version 0.95 released
+
+2002-01-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Revaidate all path components not just the last, this means a
+ very small performance penalty for being more up-to-date.
+
+2002-01-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Update and fix python interface
+
+2002-01-07 Mark Glines <mark@glines.org>
+
+ * Added statfs() support to kernel, lib, examples, and perl!
+
+2001-12-26 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Better cross compilation support
+
+ * Ported to Compaq IPAQ
+
+2001-12-20 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added function fuse_get_context() to library API (inspired by
+ patch from Matt Ryan)
+
+ * Added flags to fusermount and to kernel interface to control
+ permission checking
+
+ * Integrated fuse_set_operations() into fuse_new()
+
+2001-12-08 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Applied header protection + extern "C" patch by Roland
+ Bauerschmidt
+
+2001-12-02 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Added perl bindings by Mark Glines
+
+2001-11-21 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Cleaned up way of mounting simple filesystems.
+
+ * fuse_main() helper function added
+
+2001-11-18 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Optimized read/write operations, so that minimal copying of data
+ is done
+
+2001-11-14 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Python bindings by Jeff Epler added
+
+2001-11-13 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Fixed vfsmount reference leak in fuse_follow_link
+
+ * FS blocksize is set to PAGE_CACHE_SIZE, blksize attribute from
+ userspace is ignored
+
+2001-11-09 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Started ChangeLog
diff --git a/fuse/README b/fuse/README
new file mode 100644
index 000000000..398dd6557
--- /dev/null
+++ b/fuse/README
@@ -0,0 +1,380 @@
+General Information
+===================
+
+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.
+
+You can download the source code releases from
+
+ http://sourceforge.net/projects/fuse
+
+or alternatively you can use CVS to get the very latest development
+version:
+
+ cvs -d :pserver:anonymous@fuse.cvs.sourceforge.net:/cvsroot/fuse co fuse
+
+
+Dependencies
+============
+
+Linux kernel version 2.6.X where X >= 9.
+
+Alternatively a kernel module from FUSE release 2.5.* can be used with
+this release, which supports kernels >= 2.4.21.
+
+Installation
+============
+
+./configure
+make
+make install
+modprobe fuse
+
+You may also need to add '/usr/local/lib' to '/etc/ld.so.conf' and/or
+run ldconfig.
+
+You'll also need a fuse kernel module, Linux kernels 2.6.14 or later
+contain FUSE support.
+
+For more details see the file 'INSTALL'
+
+How To Use
+==========
+
+FUSE is made up of three main parts:
+
+ - A kernel filesystem module
+
+ - A userspace library
+
+ - A mount/unmount program
+
+
+Here's how to create your very own virtual filesystem in five easy
+steps (after installing FUSE):
+
+ 1) Edit the file example/fusexmp.c to do whatever you want...
+
+ 2) Build the fusexmp program
+
+ 3) run 'example/fusexmp /mnt/fuse -d'
+
+ 4) ls -al /mnt/fuse
+
+ 5) Be glad
+
+If it doesn't work out, please ask! Also see the file 'include/fuse.h' for
+detailed documentation of the library interface.
+
+Security
+========
+
+If you run 'make install', the fusermount program is installed
+set-user-id to root. This is done to allow normal users 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:
+
+ - The user can only mount on a mountpoint, for which it has write
+ permission
+
+ - The mountpoint is not 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.
+
+Configuration
+=============
+
+Some options regarding mount policy can be set in the file
+'/etc/fuse.conf'
+
+Currently these options are:
+
+mount_max = NNN
+
+ Set the maximum number of FUSE mounts allowed to non-root users.
+ The default is 1000.
+
+user_allow_other
+
+ Allow non-root users to specify the 'allow_other' or 'allow_root'
+ mount options.
+
+
+Mount options
+=============
+
+Most of the generic mount options described in 'man mount' are
+supported (ro, rw, suid, nosuid, dev, nodev, exec, noexec, atime,
+noatime, sync async, dirsync). Filesystems are mounted with
+'-onodev,nosuid' by default, which can only be overridden by a
+privileged user.
+
+These are FUSE specific mount options that can be specified for all
+filesystems:
+
+default_permissions
+
+ By default FUSE doesn't check file access permissions, the
+ filesystem is free to implement it's access policy or leave it to
+ the underlying file access mechanism (e.g. in case of network
+ filesystems). This option enables permission checking, restricting
+ access based on file mode. This is option is usually useful
+ together with the 'allow_other' mount option.
+
+allow_other
+
+ This option overrides the security measure restricting file access
+ to the user mounting the filesystem. So all users (including root)
+ can access the files. This option is by default only allowed to
+ root, but this restriction can be removed with a configuration
+ option described in the previous section.
+
+allow_root
+
+ This option is similar to 'allow_other' but file access is limited
+ to the user mounting the filesystem and root. This option and
+ 'allow_other' are mutually exclusive.
+
+kernel_cache
+
+ This option disables flushing the cache of the file contents on
+ every open(). 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(), so a read() system call will not
+ always initiate a read operation.
+
+auto_cache
+
+ This option enables automatic flushing of the data cache on open().
+ The cache will only be flushed if the modification time or the size
+ of the file has changed.
+
+large_read
+
+ Issue large read requests. This can improve performance for some
+ filesystems, but can also degrade performance. This option is only
+ useful on 2.4.X kernels, as on 2.6 kernels requests size is
+ automatically determined for optimum performance.
+
+direct_io
+
+ This option disables the use of page cache (file content cache) in
+ the kernel for this filesystem. This has several affects:
+
+ - Each read() or write() system call will initiate one or more
+ read or write operations, data will not be cached in the
+ kernel.
+
+ - 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).
+
+max_read=N
+
+ With this option the maximum size of read operations can be set.
+ The default is infinite. Note that the size of read requests is
+ limited anyway to 32 pages (which is 128kbyte on i386).
+
+max_readahead=N
+
+ Set the maximum number of bytes to read-ahead. The default is
+ determined by the kernel. On linux-2.6.22 or earlier it's 131072
+ (128kbytes)
+
+max_write=N
+
+ Set the maximum number of bytes in a single write operation. The
+ default is 128kbytes. Note, that due to various limitations, the
+ size of write requests can be much smaller (4kbytes). This
+ limitation will be removed in the future.
+
+async_read
+
+ Perform reads asynchronously. This is the default
+
+sync_read
+
+ Perform all reads (even read-ahead) synchronously.
+
+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()
+ - write()
+ - fsync()
+ - close()
+ - f*xattr()
+ - ftruncate()
+ - fstat()
+ - fchmod()
+ - fchown()
+
+debug
+
+ Turns on debug information printing by the library.
+
+fsname=NAME
+
+ Sets the filesystem source (first field in /etc/mtab). The default
+ is the program name.
+
+subtype=TYPE
+
+ Sets the filesystem type (third field in /etc/mtab). The default is
+ the program name.
+
+ If the kernel suppports it, /etc/mtab and /proc/mounts will show the
+ filesystem type as "fuse.TYPE"
+
+ If the kernel doesn't support subtypes, the source filed will be
+ "TYPE#NAME", or if fsname option is not specified, just "TYPE".
+
+use_ino
+
+ Honor the 'st_ino' field in getattr() and fill_dir(). This value is
+ used to fill in the 'st_ino' field in the stat()/lstat()/fstat()
+ functions and the 'd_ino' field in the readdir() function. The
+ filesystem does not have to guarantee uniqueness, however some
+ applications rely on this value being unique for the whole
+ filesystem.
+
+readdir_ino
+
+ If 'use_ino' option is not given, still try to fill in the 'd_ino'
+ field in readdir(). 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.
+
+nonempty
+
+ Allows mounts over a non-empty file or directory. By default these
+ mounts are rejected (from version 2.3.1) to prevent accidental
+ covering up of data, which could for example prevent automatic
+ backup.
+
+umask=M
+
+ Override the permission bits in 'st_mode' set by the filesystem.
+ The resulting permission bits are the ones missing from the given
+ umask value. The value is given in octal representation.
+
+uid=N
+
+ Override the 'st_uid' field set by the filesystem.
+
+gid=N
+
+ Override the 'st_gid' field set by the filesystem.
+
+blkdev
+
+ Mount a filesystem backed by a block device. This is a privileged
+ option. The device must be specified with the 'fsname=NAME' option.
+
+entry_timeout=T
+
+ 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. "-oentry_timeout=2.8")
+
+negative_timeout=T
+
+ The timeout in seconds for which a negative lookup will be cached.
+ This means, that if file did not exist (lookup retuned ENOENT), 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.
+
+attr_timeout=T
+
+ The timeout in seconds for which file/directory attributes are
+ cached. The default is 1.0 second.
+
+ac_attr_timeout=T
+
+ 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. The default is the value of 'attr_timeout'
+
+intr
+
+ Allow requests to be interrupted. Turning on this option may result
+ in unexpected behavior, if the filesystem does not support request
+ interruption.
+
+intr_signal=NUM
+
+ Specify which signal number to send to the filesystem when a request
+ is interrupted. The default is 10 (USR1).
+
+modules=M1[:M2...]
+
+ 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.
+
+
+Modules distributed with fuse
+-----------------------------
+
+iconv
+`````
+Perform file name character set conversion. Options are:
+
+from_code=CHARSET
+
+ Character set to convert from (see iconv -l for a list of possible
+ values). Default is UTF-8.
+
+to_code=CHARSET
+
+ Character set to convert to. Default is determined by the current
+ locale.
+
+
+subdir
+``````
+Prepend a given directory to each path. Options are:
+
+subdir=DIR
+
+ Directory to prepend to all paths. This option is mandatory.
+
+rellinks
+
+ Transform absolute symlinks into relative
+
+norellinks
+
+ Do not transform absolute symlinks into relative. This is the default.
+
+
+Reporting bugs
+==============
+
+Please send bug reports to the <fuse-devel@lists.sourceforge.net>
+mailing list.
+
+The list is open, you need not be subscribed to post.
diff --git a/fuse/android/config.h b/fuse/android/config.h
new file mode 100644
index 000000000..dac8c1671
--- /dev/null
+++ b/fuse/android/config.h
@@ -0,0 +1,87 @@
+/* include/config.h. Generated from config.h.in by configure. */
+/* include/config.h.in. Generated from configure.in by autoheader. */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `fdatasync' function. */
+#define HAVE_FDATASYNC 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define if you have the iconv() function and it works. */
+#define HAVE_ICONV 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `setxattr' function. */
+#define HAVE_SETXATTR 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if `st_atim' is member of `struct stat'. */
+//#define HAVE_STRUCT_STAT_ST_ATIM 0
+
+/* Define to 1 if `st_atimespec' is member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define as const if the declaration of iconv() needs const. */
+#define ICONV_CONST
+
+/* Don't update /etc/mtab */
+/* #undef IGNORE_MTAB */
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "fuse"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "fuse"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "fuse 2.9.3"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "fuse"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.9.3"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "2.9.3"
diff --git a/fuse/android/statvfs.c b/fuse/android/statvfs.c
new file mode 100644
index 000000000..7cec574da
--- /dev/null
+++ b/fuse/android/statvfs.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "sys/statvfs.h"
+#include <sys/statfs.h>
+
+#define MAP(to,from) vfs->to = sfs.from
+
+int statvfs(const char *path, struct statvfs *vfs) {
+ struct statfs sfs;
+ int ret;
+ int *fsid;
+ if ((ret = statfs(path, &sfs)) != 0)
+ return ret;
+
+ MAP(f_bsize, f_bsize);
+ MAP(f_frsize, f_frsize);
+ MAP(f_blocks, f_blocks);
+ MAP(f_bfree, f_bfree);
+ MAP(f_bavail, f_bavail);
+ MAP(f_files, f_files);
+ MAP(f_ffree, f_ffree);
+ MAP(f_namemax, f_namelen);
+
+ vfs->f_favail = 0;
+ vfs->f_flag = 0;
+
+ fsid = (int *)&sfs.f_fsid;
+ vfs->f_fsid = (fsid[0] << sizeof(fsid[0])) | fsid[1];
+
+ return ret;
+}
diff --git a/fuse/android/sys/statvfs.h b/fuse/android/sys/statvfs.h
new file mode 100644
index 000000000..0d770dd72
--- /dev/null
+++ b/fuse/android/sys/statvfs.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _SYS_STATVFS_H_
+#define _SYS_STATVFS_H_
+#include <sys/types.h>
+
+struct statvfs {
+ unsigned long f_bsize; /* file system block size */
+ unsigned long f_frsize; /* fragment size */
+ fsblkcnt_t f_blocks; /* size of fs in f_frsize units */
+ fsblkcnt_t f_bfree; /* # free blocks */
+ fsblkcnt_t f_bavail; /* # free blocks for non-root */
+ fsfilcnt_t f_files; /* # inodes */
+ fsfilcnt_t f_ffree; /* # free inodes */
+ fsfilcnt_t f_favail; /* # free inodes for non-root */
+ unsigned long f_fsid; /* file system ID */
+ unsigned long f_flag; /* mount flags */
+ unsigned long f_namemax; /* maximum filename length */
+};
+
+int statvfs(const char *, struct statvfs *);
+#endif
diff --git a/fuse/buffer.c b/fuse/buffer.c
new file mode 100644
index 000000000..6fa55c979
--- /dev/null
+++ b/fuse/buffer.c
@@ -0,0 +1,318 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#define _GNU_SOURCE
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_lowlevel.h"
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+
+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 = SIZE_MAX;
+ else
+ 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 = pwrite64(dst->fd, src->mem + src_off, len,
+ dst->pos + dst_off);
+ } else {
+ res = write(dst->fd, src->mem + src_off, len);
+ }
+ if (res == -1) {
+ if (!copied)
+ return -errno;
+ break;
+ }
+ if (res == 0)
+ break;
+
+ copied += res;
+ if (!(dst->flags & FUSE_BUF_FD_RETRY))
+ break;
+
+ 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, dst->mem + dst_off, len,
+ src->pos + src_off);
+ } else {
+ res = read(src->fd, dst->mem + dst_off, len);
+ }
+ if (res == -1) {
+ if (!copied)
+ return -errno;
+ break;
+ }
+ if (res == 0)
+ break;
+
+ copied += res;
+ if (!(src->flags & FUSE_BUF_FD_RETRY))
+ break;
+
+ 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;
+ loff_t *srcpos = NULL;
+ loff_t *dstpos = NULL;
+ loff_t srcpos_val;
+ loff_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) {
+ void *dstmem = dst->mem + dst_off;
+ void *srcmem = 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);
+
+ 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;
+}
diff --git a/fuse/cuse_lowlevel.c b/fuse/cuse_lowlevel.c
new file mode 100644
index 000000000..ae08ed485
--- /dev/null
+++ b/fuse/cuse_lowlevel.c
@@ -0,0 +1,371 @@
+/*
+ CUSE: Character device in Userspace
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "cuse_lowlevel.h"
+#include "fuse_kernel.h"
+#include "fuse_i.h"
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+
+struct cuse_data {
+ struct cuse_lowlevel_ops clop;
+ unsigned max_read;
+ unsigned dev_major;
+ unsigned dev_minor;
+ unsigned flags;
+ unsigned dev_info_len;
+ char dev_info[];
+};
+
+static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
+{
+ return &req->f->cuse_data->clop;
+}
+
+static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ (void)ino;
+ req_clop(req)->open(req, fi);
+}
+
+static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+ loff_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, loff_t off, struct fuse_file_info *fi)
+{
+ (void)ino;
+ req_clop(req)->write(req, buf, size, off, fi);
+}
+
+static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ (void)ino;
+ req_clop(req)->flush(req, fi);
+}
+
+static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi)
+{
+ (void)ino;
+ req_clop(req)->release(req, fi);
+}
+
+static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info *fi)
+{
+ (void)ino;
+ req_clop(req)->fsync(req, datasync, fi);
+}
+
+static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
+ struct fuse_file_info *fi, unsigned int flags,
+ const void *in_buf, size_t in_bufsz, size_t out_bufsz)
+{
+ (void)ino;
+ req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
+ out_bufsz);
+}
+
+static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi, struct fuse_pollhandle *ph)
+{
+ (void)ino;
+ req_clop(req)->poll(req, fi, ph);
+}
+
+static size_t cuse_pack_info(int argc, const char **argv, char *buf)
+{
+ size_t size = 0;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ size_t len;
+
+ len = strlen(argv[i]) + 1;
+ size += len;
+ if (buf) {
+ memcpy(buf, argv[i], len);
+ buf += len;
+ }
+ }
+
+ return size;
+}
+
+static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
+ const struct cuse_lowlevel_ops *clop)
+{
+ struct cuse_data *cd;
+ size_t dev_info_len;
+
+ dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
+ NULL);
+
+ if (dev_info_len > CUSE_INIT_INFO_MAX) {
+ fprintf(stderr, "cuse: dev_info (%zu) too large, limit=%u\n",
+ dev_info_len, CUSE_INIT_INFO_MAX);
+ return NULL;
+ }
+
+ cd = calloc(1, sizeof(*cd) + dev_info_len);
+ if (!cd) {
+ fprintf(stderr, "cuse: failed to allocate cuse_data\n");
+ return NULL;
+ }
+
+ memcpy(&cd->clop, clop, sizeof(cd->clop));
+ cd->max_read = 131072;
+ cd->dev_major = ci->dev_major;
+ cd->dev_minor = ci->dev_minor;
+ cd->dev_info_len = dev_info_len;
+ cd->flags = ci->flags;
+ cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
+
+ return cd;
+}
+
+struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
+ const struct cuse_info *ci,
+ const struct cuse_lowlevel_ops *clop,
+ void *userdata)
+{
+ struct fuse_lowlevel_ops lop;
+ struct cuse_data *cd;
+ struct fuse_session *se;
+ struct fuse_ll *ll;
+
+ cd = cuse_prep_data(ci, clop);
+ if (!cd)
+ return NULL;
+
+ memset(&lop, 0, sizeof(lop));
+ lop.init = clop->init;
+ lop.destroy = clop->destroy;
+ lop.open = clop->open ? cuse_fll_open : NULL;
+ lop.read = clop->read ? cuse_fll_read : NULL;
+ lop.write = clop->write ? cuse_fll_write : NULL;
+ lop.flush = clop->flush ? cuse_fll_flush : NULL;
+ lop.release = clop->release ? cuse_fll_release : NULL;
+ lop.fsync = clop->fsync ? cuse_fll_fsync : NULL;
+ lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL;
+ lop.poll = clop->poll ? cuse_fll_poll : NULL;
+
+ se = fuse_lowlevel_new_common(args, &lop, sizeof(lop), userdata);
+ if (!se) {
+ free(cd);
+ return NULL;
+ }
+ ll = se->data;
+ ll->cuse_data = cd;
+
+ return se;
+}
+
+static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
+ char *dev_info, unsigned dev_info_len)
+{
+ struct iovec iov[3];
+
+ iov[1].iov_base = arg;
+ iov[1].iov_len = sizeof(struct cuse_init_out);
+ iov[2].iov_base = dev_info;
+ iov[2].iov_len = dev_info_len;
+
+ return fuse_send_reply_iov_nofree(req, 0, iov, 3);
+}
+
+void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
+ struct cuse_init_out outarg;
+ struct fuse_ll *f = req->f;
+ struct cuse_data *cd = f->cuse_data;
+ size_t bufsize = fuse_chan_bufsize(req->ch);
+ struct cuse_lowlevel_ops *clop = req_clop(req);
+
+ (void) nodeid;
+ if (f->debug) {
+ fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
+ fprintf(stderr, "flags=0x%08x\n", arg->flags);
+ }
+ f->conn.proto_major = arg->major;
+ f->conn.proto_minor = arg->minor;
+ f->conn.capable = 0;
+ f->conn.want = 0;
+
+ if (arg->major < 7) {
+ fprintf(stderr, "cuse: unsupported protocol version: %u.%u\n",
+ arg->major, arg->minor);
+ fuse_reply_err(req, EPROTO);
+ return;
+ }
+
+ if (bufsize < FUSE_MIN_READ_BUFFER) {
+ fprintf(stderr, "cuse: warning: buffer size too small: %zu\n",
+ bufsize);
+ bufsize = FUSE_MIN_READ_BUFFER;
+ }
+
+ bufsize -= 4096;
+ if (bufsize < f->conn.max_write)
+ f->conn.max_write = bufsize;
+
+ f->got_init = 1;
+ if (f->op.init)
+ f->op.init(f->userdata, &f->conn);
+
+ memset(&outarg, 0, sizeof(outarg));
+ outarg.major = FUSE_KERNEL_VERSION;
+ outarg.minor = FUSE_KERNEL_MINOR_VERSION;
+ outarg.flags = cd->flags;
+ outarg.max_read = cd->max_read;
+ outarg.max_write = f->conn.max_write;
+ outarg.dev_major = cd->dev_major;
+ outarg.dev_minor = cd->dev_minor;
+
+ if (f->debug) {
+ fprintf(stderr, " CUSE_INIT: %u.%u\n",
+ outarg.major, outarg.minor);
+ fprintf(stderr, " flags=0x%08x\n", outarg.flags);
+ fprintf(stderr, " max_read=0x%08x\n", outarg.max_read);
+ fprintf(stderr, " max_write=0x%08x\n", outarg.max_write);
+ fprintf(stderr, " dev_major=%u\n", outarg.dev_major);
+ fprintf(stderr, " dev_minor=%u\n", outarg.dev_minor);
+ fprintf(stderr, " dev_info: %.*s\n", cd->dev_info_len,
+ cd->dev_info);
+ }
+
+ cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
+
+ if (clop->init_done)
+ clop->init_done(f->userdata);
+
+ fuse_free_req(req);
+}
+
+struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
+ const struct cuse_info *ci,
+ const struct cuse_lowlevel_ops *clop,
+ int *multithreaded, void *userdata)
+{
+ const char *devname = "/dev/cuse";
+ static const struct fuse_opt kill_subtype_opts[] = {
+ FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_DISCARD),
+ FUSE_OPT_END
+ };
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+ struct fuse_session *se;
+ struct fuse_chan *ch;
+ int fd;
+ int foreground;
+ int res;
+
+ res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground);
+ if (res == -1)
+ goto err_args;
+
+ res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
+ if (res == -1)
+ goto err_args;
+
+ /*
+ * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
+ * would ensue.
+ */
+ do {
+ fd = open("/dev/null", O_RDWR);
+ if (fd > 2)
+ close(fd);
+ } while (fd >= 0 && fd <= 2);
+
+ se = cuse_lowlevel_new(&args, ci, clop, userdata);
+ fuse_opt_free_args(&args);
+ if (se == NULL)
+ goto err_args;
+
+ fd = open(devname, O_RDWR);
+ if (fd == -1) {
+ if (errno == ENODEV || errno == ENOENT)
+ fprintf(stderr, "cuse: device not found, try 'modprobe cuse' first\n");
+ else
+ fprintf(stderr, "cuse: failed to open %s: %s\n",
+ devname, strerror(errno));
+ goto err_se;
+ }
+
+ ch = fuse_kern_chan_new(fd);
+ if (!ch) {
+ close(fd);
+ goto err_se;
+ }
+
+ fuse_session_add_chan(se, ch);
+
+ res = fuse_set_signal_handlers(se);
+ if (res == -1)
+ goto err_se;
+
+ res = fuse_daemonize(foreground);
+ if (res == -1)
+ goto err_sig;
+
+ return se;
+
+err_sig:
+ fuse_remove_signal_handlers(se);
+err_se:
+ fuse_session_destroy(se);
+err_args:
+ fuse_opt_free_args(&args);
+ return NULL;
+}
+
+void cuse_lowlevel_teardown(struct fuse_session *se)
+{
+ fuse_remove_signal_handlers(se);
+ fuse_session_destroy(se);
+}
+
+int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
+ const struct cuse_lowlevel_ops *clop, void *userdata)
+{
+ struct fuse_session *se;
+ int multithreaded;
+ int res;
+
+ se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
+ userdata);
+ if (se == NULL)
+ return 1;
+
+ if (multithreaded)
+ res = fuse_session_loop_mt(se);
+ else
+ res = fuse_session_loop(se);
+
+ cuse_lowlevel_teardown(se);
+ if (res == -1)
+ return 1;
+
+ return 0;
+}
diff --git a/fuse/fuse.c b/fuse/fuse.c
new file mode 100644
index 000000000..2c1aa17d8
--- /dev/null
+++ b/fuse/fuse.c
@@ -0,0 +1,4942 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+
+/* For pthread_rwlock_t */
+#define _GNU_SOURCE
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_lowlevel.h"
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+#include "fuse_common_compat.h"
+#include "fuse_compat.h"
+#include "fuse_kernel.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <signal.h>
+#include <assert.h>
+#include <poll.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/file.h>
+
+#ifdef USE_MODULES
+#include <dlfcn.h>
+#endif
+
+#define FUSE_NODE_SLAB 1
+
+#ifndef MAP_ANONYMOUS
+#undef FUSE_NODE_SLAB
+#endif
+
+#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
+
+#define FUSE_UNKNOWN_INO 0xffffffff
+#define OFFSET_MAX 0x7fffffffffffffffLL
+
+#define NODE_TABLE_MIN_SIZE 8192
+
+struct fuse_config {
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int umask;
+ double entry_timeout;
+ double negative_timeout;
+ double attr_timeout;
+ double ac_attr_timeout;
+ int ac_attr_timeout_set;
+ int remember;
+ int nopath;
+ int debug;
+ int hard_remove;
+ int use_ino;
+ int readdir_ino;
+ int set_mode;
+ int set_uid;
+ int set_gid;
+ int direct_io;
+ int kernel_cache;
+ int auto_cache;
+ int intr;
+ int intr_signal;
+ int help;
+ char *modules;
+};
+
+struct fuse_fs {
+ struct fuse_operations op;
+ struct fuse_module *m;
+ void *user_data;
+ int compat;
+ 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 first_locked : 1;
+ bool second_locked : 1;
+ 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;
+ int nullpath_ok;
+ int utime_omit_ok;
+ 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;
+ loff_t start;
+ loff_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;
+ loff_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_dh {
+ pthread_mutex_t lock;
+ struct fuse *fuse;
+ fuse_req_t req;
+ char *contents;
+ int allocated;
+ unsigned len;
+ unsigned size;
+ unsigned needlen;
+ int filled;
+ uint64_t fh;
+ int error;
+ fuse_ino_t nodeid;
+};
+
+/* old dir handle */
+struct fuse_dirhandle {
+ fuse_fill_dir_t filler;
+ void *buf;
+};
+
+struct fuse_context_i {
+ struct fuse_context ctx;
+ fuse_req_t req;
+};
+
+static pthread_key_t fuse_context_key;
+static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
+static int fuse_context_ref;
+
+#ifdef USE_MODULES
+static struct fusemod_so *fuse_current_so;
+static struct fuse_module *fuse_modules;
+
+static int fuse_load_so_name(const char *soname)
+{
+ struct fusemod_so *so;
+
+ so = calloc(1, sizeof(struct fusemod_so));
+ if (!so) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+ }
+
+ fuse_current_so = so;
+ so->handle = dlopen(soname, RTLD_NOW);
+ fuse_current_so = NULL;
+ if (!so->handle) {
+ fprintf(stderr, "fuse: %s\n", dlerror());
+ goto err;
+ }
+ if (!so->ctr) {
+ fprintf(stderr, "fuse: %s did not register any modules\n",
+ soname);
+ goto err;
+ }
+ return 0;
+
+err:
+ if (so->handle)
+ dlclose(so->handle);
+ free(so);
+ return -1;
+}
+
+static int fuse_load_so_module(const char *module)
+{
+ int res;
+ char *soname = malloc(strlen(module) + 64);
+ if (!soname) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+ }
+ sprintf(soname, "libfusemod_%s.so", module);
+ res = fuse_load_so_name(soname);
+ free(soname);
+ return res;
+}
+
+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);
+ assert(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)
+ *mp = (*mp)->next;
+ else
+ mp = &(*mp)->next;
+ }
+ dlclose(so->handle);
+ free(so);
+ }
+ }
+ pthread_mutex_unlock(&fuse_context_lock);
+}
+#endif
+
+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)
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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;
+ }
+ fprintf(stderr,
+ "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)
+ fprintf(stderr, "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 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;
+ strncpy(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 = -ENOENT;
+ 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 void queue_element_unlock(struct fuse *f, struct lock_queue_element *qe)
+{
+ struct node *wnode;
+
+ if (qe->first_locked) {
+ wnode = qe->wnode1 ? *qe->wnode1 : NULL;
+ unlock_path(f, qe->nodeid1, wnode, NULL);
+ qe->first_locked = false;
+ }
+ if (qe->second_locked) {
+ wnode = qe->wnode2 ? *qe->wnode2 : NULL;
+ unlock_path(f, qe->nodeid2, wnode, NULL);
+ qe->second_locked = false;
+ }
+}
+
+static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
+{
+ int err;
+ bool first = (qe == f->lockq);
+
+ if (!qe->path1) {
+ /* Just waiting for it to be unlocked */
+ if (get_node(f, qe->nodeid1)->treelock == 0)
+ pthread_cond_signal(&qe->cond);
+
+ return;
+ }
+
+ if (!qe->first_locked) {
+ err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
+ qe->wnode1, true);
+ if (!err)
+ qe->first_locked = true;
+ else if (err != -EAGAIN)
+ goto err_unlock;
+ }
+ if (!qe->second_locked && qe->path2) {
+ err = try_get_path(f, qe->nodeid2, qe->name2, qe->path2,
+ qe->wnode2, true);
+ if (!err)
+ qe->second_locked = true;
+ else if (err != -EAGAIN)
+ goto err_unlock;
+ }
+
+ if (qe->first_locked && (qe->second_locked || !qe->path2)) {
+ err = 0;
+ goto done;
+ }
+
+ /*
+ * Only let the first element be partially locked otherwise there could
+ * be a deadlock.
+ *
+ * But do allow the first element to be partially locked to prevent
+ * starvation.
+ */
+ if (!first)
+ queue_element_unlock(f, qe);
+
+ /* keep trying */
+ return;
+
+err_unlock:
+ queue_element_unlock(f, qe);
+done:
+ qe->err = err;
+ qe->done = true;
+ pthread_cond_signal(&qe->cond);
+}
+
+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)
+ fprintf(stderr, "%s %li (w)\n", msg, wnode->nodeid);
+ else
+ fprintf(stderr, "%s %li\n", msg, nodeid);
+ }
+}
+
+static void queue_path(struct fuse *f, struct lock_queue_element *qe)
+{
+ struct lock_queue_element **qp;
+
+ qe->done = false;
+ qe->first_locked = false;
+ qe->second_locked = false;
+ pthread_cond_init(&qe->cond, NULL);
+ qe->next = NULL;
+ for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
+ *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.nopath) {
+ *path = NULL;
+ } else {
+ err = get_path_common(f, nodeid, NULL, path, NULL);
+ if (err == -ENOENT && f->nullpath_ok)
+ 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);
+}
+
+static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
+ fuse_ino_t nodeid2, const char *name2,
+ char **path1, char **path2,
+ struct node **wnode1, struct node **wnode2)
+{
+ int err;
+
+ /* FIXME: locking two paths needs deadlock checking */
+ err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
+ if (!err) {
+ err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
+ if (err) {
+ struct node *wn1 = wnode1 ? *wnode1 : NULL;
+
+ unlock_path(f, nodeid1, wn1, NULL);
+ free(*path1);
+ }
+ }
+ return err;
+}
+
+static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
+ fuse_ino_t nodeid2, const char *name2,
+ char **path1, char **path2,
+ struct node **wnode1, struct node **wnode2)
+{
+ int err;
+
+ pthread_mutex_lock(&f->lock);
+ 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);
+ }
+ 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) {
+ fprintf(stderr, "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 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)
+ 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);
+}
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+static int fuse_compat_open(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ int err;
+ if (!fs->compat || fs->compat >= 25)
+ err = fs->op.open(path, fi);
+ else if (fs->compat == 22) {
+ struct fuse_file_info_compat tmp;
+ memcpy(&tmp, fi, sizeof(tmp));
+ err = ((struct fuse_operations_compat22 *) &fs->op)->open(path,
+ &tmp);
+ memcpy(fi, &tmp, sizeof(tmp));
+ fi->fh = tmp.fh;
+ } else
+ err = ((struct fuse_operations_compat2 *) &fs->op)
+ ->open(path, fi->flags);
+ return err;
+}
+
+static int fuse_compat_release(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ if (!fs->compat || fs->compat >= 22)
+ return fs->op.release(path, fi);
+ else
+ return ((struct fuse_operations_compat2 *) &fs->op)
+ ->release(path, fi->flags);
+}
+
+static int fuse_compat_opendir(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ if (!fs->compat || fs->compat >= 25)
+ return fs->op.opendir(path, fi);
+ else {
+ int err;
+ struct fuse_file_info_compat tmp;
+ memcpy(&tmp, fi, sizeof(tmp));
+ err = ((struct fuse_operations_compat22 *) &fs->op)
+ ->opendir(path, &tmp);
+ memcpy(fi, &tmp, sizeof(tmp));
+ fi->fh = tmp.fh;
+ return err;
+ }
+}
+
+static void convert_statfs_compat(struct fuse_statfs_compat1 *compatbuf,
+ struct statvfs *stbuf)
+{
+ stbuf->f_bsize = compatbuf->block_size;
+ stbuf->f_blocks = compatbuf->blocks;
+ stbuf->f_bfree = compatbuf->blocks_free;
+ stbuf->f_bavail = compatbuf->blocks_free;
+ stbuf->f_files = compatbuf->files;
+ stbuf->f_ffree = compatbuf->files_free;
+ stbuf->f_namemax = compatbuf->namelen;
+}
+
+static void convert_statfs_old(struct statfs *oldbuf, struct statvfs *stbuf)
+{
+ stbuf->f_bsize = oldbuf->f_bsize;
+ stbuf->f_blocks = oldbuf->f_blocks;
+ stbuf->f_bfree = oldbuf->f_bfree;
+ stbuf->f_bavail = oldbuf->f_bavail;
+ stbuf->f_files = oldbuf->f_files;
+ stbuf->f_ffree = oldbuf->f_ffree;
+ stbuf->f_namemax = oldbuf->f_namelen;
+}
+
+static int fuse_compat_statfs(struct fuse_fs *fs, const char *path,
+ struct statvfs *buf)
+{
+ int err;
+
+ if (!fs->compat || fs->compat >= 25) {
+ err = fs->op.statfs(fs->compat == 25 ? "/" : path, buf);
+ } else if (fs->compat > 11) {
+ struct statfs oldbuf;
+ err = ((struct fuse_operations_compat22 *) &fs->op)
+ ->statfs("/", &oldbuf);
+ if (!err)
+ convert_statfs_old(&oldbuf, buf);
+ } else {
+ struct fuse_statfs_compat1 compatbuf;
+ memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1));
+ err = ((struct fuse_operations_compat1 *) &fs->op)
+ ->statfs(&compatbuf);
+ if (!err)
+ convert_statfs_compat(&compatbuf, buf);
+ }
+ return err;
+}
+
+#else /* __FreeBSD__ || __NetBSD__ */
+
+static inline int fuse_compat_open(struct fuse_fs *fs, char *path,
+ struct fuse_file_info *fi)
+{
+ return fs->op.open(path, fi);
+}
+
+static inline int fuse_compat_release(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ return fs->op.release(path, fi);
+}
+
+static inline int fuse_compat_opendir(struct fuse_fs *fs, const char *path,
+ struct fuse_file_info *fi)
+{
+ return fs->op.opendir(path, fi);
+}
+
+static inline int fuse_compat_statfs(struct fuse_fs *fs, const char *path,
+ struct statvfs *buf)
+{
+ return fs->op.statfs(fs->compat == 25 ? "/" : path, buf);
+}
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.getattr) {
+ if (fs->debug)
+ fprintf(stderr, "getattr %s\n", path);
+
+ return fs->op.getattr(path, buf);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_fgetattr(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.fgetattr) {
+ if (fs->debug)
+ fprintf(stderr, "fgetattr[%llu] %s\n",
+ (unsigned long long) fi->fh, path);
+
+ return fs->op.fgetattr(path, buf, fi);
+ } else if (path && fs->op.getattr) {
+ if (fs->debug)
+ fprintf(stderr, "getattr %s\n", path);
+
+ return fs->op.getattr(path, buf);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
+ const char *newpath)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.rename) {
+ if (fs->debug)
+ fprintf(stderr, "rename %s %s\n", oldpath, newpath);
+
+ return fs->op.rename(oldpath, newpath);
+ } 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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "release%s[%llu] flags: 0x%x\n",
+ fi->flush ? "+flush" : "",
+ (unsigned long long) fi->fh, fi->flags);
+
+ return fuse_compat_release(fs, 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)
+ fprintf(stderr, "opendir flags: 0x%x %s\n", fi->flags,
+ path);
+
+ err = fuse_compat_opendir(fs, path, fi);
+
+ if (fs->debug && !err)
+ fprintf(stderr, " opendir[%lli] 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)
+ fprintf(stderr, "open flags: 0x%x %s\n", fi->flags,
+ path);
+
+ err = fuse_compat_open(fs, path, fi);
+
+ if (fs->debug && !err)
+ fprintf(stderr, " open[%lli] 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++)
+ 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, loff_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)
+ fprintf(stderr,
+ "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)
+ fprintf(stderr, " 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) > (int) size)
+ fprintf(stderr, "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,
+ loff_t off, struct fuse_file_info *fi)
+{
+ int res;
+ struct fuse_bufvec *buf = NULL;
+
+ res = fuse_fs_read_buf(fs, 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);
+
+ return res;
+}
+
+int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
+ struct fuse_bufvec *buf, loff_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)
+ fprintf(stderr,
+ "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)
+ fprintf(stderr, " 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)
+ fprintf(stderr, "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, loff_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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "statfs %s\n", path);
+
+ return fuse_compat_statfs(fs, 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)
+ fprintf(stderr, "releasedir[%llu] flags: 0x%x\n",
+ (unsigned long long) fi->fh, fi->flags);
+
+ return fs->op.releasedir(path, fi);
+ } else {
+ return 0;
+ }
+}
+
+static int fill_dir_old(struct fuse_dirhandle *dh, const char *name, int type,
+ ino_t ino)
+{
+ int res;
+ struct stat stbuf;
+
+ memset(&stbuf, 0, sizeof(stbuf));
+ stbuf.st_mode = type << 12;
+ stbuf.st_ino = ino;
+
+ res = dh->filler(dh->buf, name, &stbuf, 0);
+ return res ? -ENOMEM : 0;
+}
+
+int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
+ fuse_fill_dir_t filler, loff_t off,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.readdir) {
+ if (fs->debug)
+ fprintf(stderr, "readdir[%llu] from %llu\n",
+ (unsigned long long) fi->fh,
+ (unsigned long long) off);
+
+ return fs->op.readdir(path, buf, filler, off, fi);
+ } else if (fs->op.getdir) {
+ struct fuse_dirhandle dh;
+
+ if (fs->debug)
+ fprintf(stderr, "getdir[%llu]\n",
+ (unsigned long long) fi->fh);
+
+ dh.filler = filler;
+ dh.buf = buf;
+ return fs->op.getdir(path, &dh, fill_dir_old);
+ } 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)
+ fprintf(stderr,
+ "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)
+ fprintf(stderr, " 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)
+ fprintf(stderr, "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;
+
+ fprintf(stderr, "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)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.chown) {
+ if (fs->debug)
+ fprintf(stderr, "chown %s %lu %lu\n", path,
+ (unsigned long) uid, (unsigned long) gid);
+
+ return fs->op.chown(path, uid, gid);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_truncate(struct fuse_fs *fs, const char *path, loff_t size)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.truncate) {
+ if (fs->debug)
+ fprintf(stderr, "truncate %s %llu\n", path,
+ (unsigned long long) size);
+
+ return fs->op.truncate(path, size);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, loff_t size,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.ftruncate) {
+ if (fs->debug)
+ fprintf(stderr, "ftruncate[%llu] %llu\n",
+ (unsigned long long) fi->fh,
+ (unsigned long long) size);
+
+ return fs->op.ftruncate(path, size, fi);
+ } else if (path && fs->op.truncate) {
+ if (fs->debug)
+ fprintf(stderr, "truncate %s %llu\n", path,
+ (unsigned long long) size);
+
+ return fs->op.truncate(path, size);
+ } else {
+ return -ENOSYS;
+ }
+}
+
+int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
+ const struct timespec tv[2])
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.utimens) {
+ if (fs->debug)
+ fprintf(stderr, "utimens %s %li.%09lu %li.%09lu\n",
+ path, tv[0].tv_sec, tv[0].tv_nsec,
+ tv[1].tv_sec, tv[1].tv_nsec);
+
+ return fs->op.utimens(path, tv);
+ } else if(fs->op.utime) {
+ struct utimbuf buf;
+
+ if (fs->debug)
+ fprintf(stderr, "utime %s %li %li\n", path,
+ tv[0].tv_sec, tv[1].tv_sec);
+
+ buf.actime = tv[0].tv_sec;
+ buf.modtime = tv[1].tv_sec;
+ return fs->op.utime(path, &buf);
+ } 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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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, 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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "poll[%llu] ph: %p\n",
+ (unsigned long long) fi->fh, ph);
+
+ res = fs->op.poll(path, fi, ph, reventsp);
+
+ if (fs->debug && !res)
+ fprintf(stderr, " 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,
+ loff_t offset, loff_t length, struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.fallocate) {
+ if (fs->debug)
+ fprintf(stderr, "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;
+}
+
+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);
+ 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);
+ 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 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));
+ if (fi)
+ res = fuse_fs_fgetattr(f->fs, path, &e->attr, fi);
+ else
+ res = fuse_fs_getattr(f->fs, path, &e->attr);
+ if (res == 0) {
+ struct node *node;
+
+ node = find_node(f, nodeid, name);
+ if (node == NULL)
+ res = -ENOMEM;
+ else {
+ 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);
+ if (f->conf.debug)
+ fprintf(stderr, " NODEID: %lu\n",
+ (unsigned long) e->ino);
+ }
+ }
+ return res;
+}
+
+static struct fuse_context_i *fuse_get_context_internal(void)
+{
+ struct fuse_context_i *c;
+
+ c = (struct fuse_context_i *) pthread_getspecific(fuse_context_key);
+ 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 */
+ fprintf(stderr, "fuse: failed to allocate thread specific data\n");
+ abort();
+ }
+ pthread_setspecific(fuse_context_key, c);
+ }
+ 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) {
+ fprintf(stderr, "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_get_context_internal();
+ const struct fuse_ctx *ctx = fuse_req_ctx(req);
+ c->req = req;
+ c->ctx.fuse = req_fuse(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)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (!fs->op.write_buf)
+ conn->want &= ~FUSE_CAP_SPLICE_READ;
+ if (!fs->op.lock)
+ conn->want &= ~FUSE_CAP_POSIX_LOCKS;
+ if (!fs->op.flock)
+ conn->want &= ~FUSE_CAP_FLOCK_LOCKS;
+ if (fs->op.init)
+ fs->user_data = fs->op.init(conn);
+}
+
+static void fuse_lib_init(void *data, struct fuse_conn_info *conn)
+{
+ struct fuse *f = (struct fuse *) data;
+ struct fuse_context_i *c = fuse_get_context_internal();
+
+ memset(c, 0, sizeof(*c));
+ c->ctx.fuse = f;
+ conn->want |= FUSE_CAP_EXPORT_SUPPORT;
+ fuse_fs_init(f->fs, conn);
+}
+
+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);
+#ifdef USE_MODULES
+ if (fs->m)
+ fuse_put_module(fs->m);
+#endif
+ free(fs);
+}
+
+static void fuse_lib_destroy(void *data)
+{
+ struct fuse *f = (struct fuse *) data;
+ struct fuse_context_i *c = fuse_get_context_internal();
+
+ memset(c, 0, sizeof(*c));
+ c->ctx.fuse = f;
+ fuse_fs_destroy(f->fs);
+ f->fs = NULL;
+}
+
+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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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,
+ unsigned long 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 && f->fs->op.fgetattr)
+ 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);
+ if (fi)
+ err = fuse_fs_fgetattr(f->fs, path, &buf, fi);
+ else
+ err = fuse_fs_getattr(f->fs, path, &buf);
+ 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)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.chmod)
+ return fs->op.chmod(path, mode);
+ 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 (valid == FUSE_SET_ATTR_SIZE && fi != NULL &&
+ f->fs->op.ftruncate && f->fs->op.fgetattr)
+ 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);
+ 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);
+ }
+ if (!err && (valid & FUSE_SET_ATTR_SIZE)) {
+ if (fi)
+ err = fuse_fs_ftruncate(f->fs, path,
+ attr->st_size, fi);
+ else
+ err = fuse_fs_truncate(f->fs, path,
+ attr->st_size);
+ }
+#ifdef HAVE_UTIMENSAT
+ if (!err && f->utime_omit_ok &&
+ (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);
+ } 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);
+ }
+ if (!err) {
+ if (fi)
+ err = fuse_fs_fgetattr(f->fs, path, &buf, fi);
+ else
+ err = fuse_fs_getattr(f->fs, path, &buf);
+ }
+ 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);
+ } 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)
+{
+ 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 && is_open(f, newdir, newname))
+ err = hide_node(f, newpath, newdir, newname);
+ if (!err) {
+ err = fuse_fs_rename(f->fs, oldpath, newpath);
+ if (!err)
+ 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;
+ const char *compatpath;
+
+ if (path != NULL || f->nullpath_ok || f->conf.nopath)
+ compatpath = path;
+ else
+ compatpath = "-";
+
+ fuse_fs_release(f->fs, compatpath, 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.nopath) {
+ 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;
+
+ }
+ }
+ 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_fgetattr(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);
+ }
+ 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,
+ loff_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, loff_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;
+ fi->fh_old = 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->len = 0;
+ dh->filled = 0;
+ dh->nodeid = ino;
+ fuse_mutex_init(&dh->lock);
+
+ 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;
+ }
+ 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 fill_dir(void *dh_, const char *name, const struct stat *statp,
+ loff_t off)
+{
+ struct fuse_dh *dh = (struct fuse_dh *) dh_;
+ struct stat stbuf;
+ size_t newlen;
+
+ 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) {
+ struct node *node;
+ pthread_mutex_lock(&dh->fuse->lock);
+ node = lookup_node(dh->fuse, dh->nodeid, name);
+ if (node)
+ stbuf.st_ino = (ino_t) node->nodeid;
+ pthread_mutex_unlock(&dh->fuse->lock);
+ }
+ }
+
+ if (off) {
+ if (extend_contents(dh, dh->needlen) == -1)
+ return 1;
+
+ dh->filled = 0;
+ 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;
+ } else {
+ newlen = dh->len +
+ fuse_add_direntry(dh->req, NULL, 0, name, NULL, 0);
+ if (extend_contents(dh, newlen) == -1)
+ return 1;
+
+ fuse_add_direntry(dh->req, dh->contents + dh->len,
+ dh->size - dh->len, name, &stbuf, newlen);
+ }
+ dh->len = newlen;
+ return 0;
+}
+
+static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
+ size_t size, loff_t off, struct fuse_dh *dh,
+ struct fuse_file_info *fi)
+{
+ 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;
+
+ dh->len = 0;
+ dh->error = 0;
+ dh->needlen = size;
+ dh->filled = 1;
+ dh->req = req;
+ fuse_prepare_interrupt(f, req, &d);
+ err = fuse_fs_readdir(f->fs, path, dh, fill_dir, off, fi);
+ 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 void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+ loff_t off, struct fuse_file_info *llfi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_file_info fi;
+ struct fuse_dh *dh = get_dirhandle(llfi, &fi);
+
+ pthread_mutex_lock(&dh->lock);
+ /* According to SUS, directory contents need to be refreshed on
+ rewinddir() */
+ if (!off)
+ dh->filled = 0;
+
+ if (!dh->filled) {
+ int err = readdir_fill(f, req, ino, size, off, dh, &fi);
+ if (err) {
+ reply_err(req, err);
+ goto out;
+ }
+ }
+ if (dh->filled) {
+ if (off < dh->len) {
+ if (off + size > dh->len)
+ size = dh->len - off;
+ } else
+ size = 0;
+ } else {
+ size = dh->len;
+ off = 0;
+ }
+ fuse_reply_buf(req, dh->contents + off, size);
+out:
+ pthread_mutex_unlock(&dh->lock);
+}
+
+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;
+ const char *compatpath;
+
+ get_path_nullok(f, ino, &path);
+ if (path != NULL || f->nullpath_ok || f->conf.nopath)
+ compatpath = path;
+ else
+ compatpath = "-";
+
+ fuse_prepare_interrupt(f, req, &d);
+ fuse_fs_releasedir(f->fs, compatpath, &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(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, 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)
+ 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 ?: (void *)in_buf);
+
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+
+ 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,
+ loff_t offset, loff_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 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,
+ .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,
+};
+
+int fuse_notify_poll(struct fuse_pollhandle *ph)
+{
+ return fuse_lowlevel_notify_poll(ph);
+}
+
+static void free_cmd(struct fuse_cmd *cmd)
+{
+ free(cmd->buf);
+ free(cmd);
+}
+
+void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd)
+{
+ fuse_session_process(f->se, cmd->buf, cmd->buflen, cmd->ch);
+ free_cmd(cmd);
+}
+
+int fuse_exited(struct fuse *f)
+{
+ return fuse_session_exited(f->se);
+}
+
+struct fuse_session *fuse_get_session(struct fuse *f)
+{
+ return f->se;
+}
+
+static struct fuse_cmd *fuse_alloc_cmd(size_t bufsize)
+{
+ struct fuse_cmd *cmd = (struct fuse_cmd *) malloc(sizeof(*cmd));
+ if (cmd == NULL) {
+ fprintf(stderr, "fuse: failed to allocate cmd\n");
+ return NULL;
+ }
+ cmd->buf = (char *) malloc(bufsize);
+ if (cmd->buf == NULL) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ free(cmd);
+ return NULL;
+ }
+ return cmd;
+}
+
+struct fuse_cmd *fuse_read_cmd(struct fuse *f)
+{
+ struct fuse_chan *ch = fuse_session_next_chan(f->se, NULL);
+ size_t bufsize = fuse_chan_bufsize(ch);
+ struct fuse_cmd *cmd = fuse_alloc_cmd(bufsize);
+ if (cmd != NULL) {
+ int res = fuse_chan_recv(&ch, cmd->buf, bufsize);
+ if (res <= 0) {
+ free_cmd(cmd);
+ if (res < 0 && res != -EINTR && res != -EAGAIN)
+ fuse_exit(f);
+ return NULL;
+ }
+ cmd->buflen = res;
+ cmd->ch = ch;
+ }
+ return cmd;
+}
+
+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 fuse_chan *ch = fuse_session_next_chan(se, NULL);
+ size_t bufsize = fuse_chan_bufsize(ch);
+ char *buf = (char *) malloc(bufsize);
+ struct pollfd fds = {
+ .fd = fuse_chan_fd(ch),
+ .events = POLLIN
+ };
+
+ if (!buf) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ return -1;
+ }
+
+ curr_time(&now);
+ next_clean = now.tv_sec;
+ while (!fuse_session_exited(se)) {
+ struct fuse_chan *tmpch = ch;
+ struct fuse_buf fbuf = {
+ .mem = buf,
+ .size = bufsize,
+ };
+ 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(se, &fbuf, &tmpch);
+
+ if (res == -EINTR)
+ continue;
+ if (res <= 0)
+ break;
+
+ fuse_session_process_buf(se, &fbuf, tmpch);
+ } else {
+ timeout = fuse_clean_cache(f);
+ curr_time(&now);
+ next_clean = now.tv_sec + timeout;
+ }
+ }
+
+ free(buf);
+ 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);
+}
+
+int fuse_invalidate(struct fuse *f, const char *path)
+{
+ (void) f;
+ (void) path;
+ return -EINVAL;
+}
+
+void fuse_exit(struct fuse *f)
+{
+ fuse_session_exit(f->se);
+}
+
+struct fuse_context *fuse_get_context(void)
+{
+ return &fuse_get_context_internal()->ctx;
+}
+
+/*
+ * The size of fuse_context got extended, so need to be careful about
+ * incompatibility (i.e. a new binary cannot work with an old
+ * library).
+ */
+struct fuse_context *fuse_get_context_compat22(void);
+struct fuse_context *fuse_get_context_compat22(void)
+{
+ return &fuse_get_context_internal()->ctx;
+}
+FUSE_SYMVER(".symver fuse_get_context_compat22,fuse_get_context@FUSE_2.2");
+
+int fuse_getgroups(int size, gid_t list[])
+{
+ fuse_req_t req = fuse_get_context_internal()->req;
+ return fuse_req_getgroups(req, size, list);
+}
+
+int fuse_interrupted(void)
+{
+ return fuse_req_interrupted(fuse_get_context_internal()->req);
+}
+
+void fuse_set_getcontext_func(struct fuse_context *(*func)(void))
+{
+ (void) func;
+ /* no-op */
+}
+
+enum {
+ KEY_HELP,
+};
+
+#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("-h", KEY_HELP),
+ FUSE_OPT_KEY("--help", KEY_HELP),
+ 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("hard_remove", hard_remove, 1),
+ FUSE_LIB_OPT("use_ino", use_ino, 1),
+ FUSE_LIB_OPT("readdir_ino", readdir_ino, 1),
+ FUSE_LIB_OPT("direct_io", direct_io, 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("umask=", set_mode, 1),
+ FUSE_LIB_OPT("umask=%o", umask, 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("nopath", nopath, 1),
+ FUSE_LIB_OPT("intr", intr, 1),
+ FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0),
+ FUSE_LIB_OPT("modules=%s", modules, 0),
+ FUSE_OPT_END
+};
+
+static void fuse_lib_help(void)
+{
+ fprintf(stderr,
+" -o hard_remove immediate removal (don't hide files)\n"
+" -o use_ino let filesystem set inode numbers\n"
+" -o readdir_ino try to fill in d_ino in readdir\n"
+" -o direct_io use direct I/O\n"
+" -o kernel_cache cache files in kernel\n"
+" -o [no]auto_cache enable caching based on modification times (off)\n"
+" -o umask=M set file 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 nopath don't supply path if not necessary\n"
+" -o intr allow requests to be interrupted\n"
+" -o intr_signal=NUM signal to send on interrupt (%i)\n"
+" -o modules=M1[:M2...] names of modules to push onto filesystem stack\n"
+"\n", FUSE_DEFAULT_INTR_SIGNAL);
+}
+
+#ifdef USE_MODULES
+static void fuse_lib_help_modules(void)
+{
+ struct fuse_module *m;
+ fprintf(stderr, "\nModule options:\n");
+ pthread_mutex_lock(&fuse_context_lock);
+ for (m = fuse_modules; m; m = m->next) {
+ struct fuse_fs *fs = NULL;
+ struct fuse_fs *newfs;
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+ if (fuse_opt_add_arg(&args, "") != -1 &&
+ fuse_opt_add_arg(&args, "-h") != -1) {
+ fprintf(stderr, "\n[%s]\n", m->name);
+ newfs = m->factory(&args, &fs);
+ assert(newfs == NULL);
+ }
+ fuse_opt_free_args(&args);
+ }
+ pthread_mutex_unlock(&fuse_context_lock);
+}
+#endif
+
+static int fuse_lib_opt_proc(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ (void) arg; (void) outargs;
+
+ if (key == KEY_HELP) {
+ struct fuse_config *conf = (struct fuse_config *) data;
+ fuse_lib_help();
+ conf->help = 1;
+ }
+
+ return 1;
+}
+
+int fuse_is_lib_option(const char *opt)
+{
+ return fuse_lowlevel_is_lib_option(opt) ||
+ fuse_opt_match(fuse_lib_opts, opt);
+}
+
+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);
+}
+
+#ifdef USE_MODULES
+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;
+ }
+ newfs->m = m;
+ f->fs = newfs;
+ f->nullpath_ok = newfs->op.flag_nullpath_ok && f->nullpath_ok;
+ f->conf.nopath = newfs->op.flag_nopath && f->conf.nopath;
+ f->utime_omit_ok = newfs->op.flag_utime_omit_ok && f->utime_omit_ok;
+ return 0;
+}
+#endif
+
+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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+ }
+ t->use = 0;
+ t->split = 0;
+
+ return 0;
+}
+
+static void thread_exit_handler(int sig)
+{
+ pthread_exit(0);
+}
+
+static void *fuse_prune_nodes(void *fuse)
+{
+ struct fuse *f = fuse;
+ int sleep_time;
+
+#if defined(__ANDROID__)
+ struct sigaction actions;
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = thread_exit_handler;
+ sigaction(SIGUSR1, &actions, NULL);
+
+ sigset_t setusr1;
+ sigemptyset(&setusr1);
+ sigaddset(&setusr1, SIGUSR1);
+ pthread_sigmask(SIG_BLOCK, &setusr1, NULL);
+#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);
+#if defined(__ANDROID__)
+ pthread_kill(f->prune_thread, SIGUSR1);
+#else
+ pthread_cancel(f->prune_thread);
+#endif
+ pthread_mutex_unlock(&f->lock);
+ pthread_join(f->prune_thread, NULL);
+ }
+}
+
+struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
+ const struct fuse_operations *op,
+ size_t op_size, void *user_data, int compat)
+{
+ struct fuse *f;
+ struct node *root;
+ struct fuse_fs *fs;
+ struct fuse_lowlevel_ops llop = fuse_path_ops;
+
+ if (fuse_create_context_key() == -1)
+ goto out;
+
+ f = (struct fuse *) calloc(1, sizeof(struct fuse));
+ if (f == NULL) {
+ fprintf(stderr, "fuse: failed to allocate fuse object\n");
+ goto out_delete_context_key;
+ }
+
+ fs = fuse_fs_new(op, op_size, user_data);
+ if (!fs)
+ goto out_free;
+
+ fs->compat = compat;
+ f->fs = fs;
+ f->nullpath_ok = fs->op.flag_nullpath_ok;
+ f->conf.nopath = fs->op.flag_nopath;
+ f->utime_omit_ok = fs->op.flag_utime_omit_ok;
+
+ /* Oh f**k, this is ugly! */
+ if (!fs->op.lock) {
+ llop.getlk = NULL;
+ llop.setlk = NULL;
+ }
+
+ 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;
+
+ f->pagesize = getpagesize();
+ init_list_head(&f->partial_slabs);
+ init_list_head(&f->full_slabs);
+ init_list_head(&f->lru_table);
+
+ if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
+ fuse_lib_opt_proc) == -1)
+ goto out_free_fs;
+
+#ifdef USE_MODULES
+ 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;
+ }
+ }
+#endif
+
+ 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
+
+ if (compat && compat <= 25) {
+ if (fuse_sync_compat_args(args) == -1)
+ goto out_free_fs;
+ }
+
+ f->se = fuse_lowlevel_new_common(args, &llop, sizeof(llop), f);
+ if (f->se == NULL) {
+#ifdef USE_MODULES
+ if (f->conf.help)
+ fuse_lib_help_modules();
+#endif
+ goto out_free_fs;
+ }
+
+ fuse_session_add_chan(f->se, ch);
+
+ if (f->conf.debug) {
+ fprintf(stderr, "nullpath_ok: %i\n", f->nullpath_ok);
+ fprintf(stderr, "nopath: %i\n", f->conf.nopath);
+ fprintf(stderr, "utime_omit_ok: %i\n", f->utime_omit_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;
+
+ fuse_mutex_init(&f->lock);
+
+ root = alloc_node(f);
+ if (root == NULL) {
+ fprintf(stderr, "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;
+
+ if (f->conf.intr &&
+ fuse_init_intr_signal(f->conf.intr_signal,
+ &f->intr_installed) == -1)
+ goto out_free_root;
+
+ root->parent = NULL;
+ root->nodeid = FUSE_ROOT_ID;
+ inc_nlookup(root);
+ hash_id(f, root);
+
+ return f;
+
+out_free_root:
+ free(root);
+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:
+ /* Horrible compatibility hack to stop the destructor from being
+ called on the filesystem without init being called first */
+ fs->op.destroy = NULL;
+ fuse_fs_destroy(f->fs);
+ free(f->conf.modules);
+out_free:
+ free(f);
+out_delete_context_key:
+ fuse_delete_context_key();
+out:
+ return NULL;
+}
+
+struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
+ const struct fuse_operations *op, size_t op_size,
+ void *user_data)
+{
+ return fuse_new_common(ch, args, op, op_size, user_data, 0);
+}
+
+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) {
+ struct fuse_context_i *c = fuse_get_context_internal();
+
+ memset(c, 0, sizeof(*c));
+ c->ctx.fuse = 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));
+
+ free(f->id_table.array);
+ free(f->name_table.array);
+ pthread_mutex_destroy(&f->lock);
+ fuse_session_destroy(f->se);
+ free(f->conf.modules);
+ free(f);
+ fuse_delete_context_key();
+}
+
+static struct fuse *fuse_new_common_compat25(int fd, struct fuse_args *args,
+ const struct fuse_operations *op,
+ size_t op_size, int compat)
+{
+ struct fuse *f = NULL;
+ struct fuse_chan *ch = fuse_kern_chan_new(fd);
+
+ if (ch)
+ f = fuse_new_common(ch, args, op, op_size, NULL, compat);
+
+ return f;
+}
+
+#ifdef USE_MODULES
+/* called with fuse_context_lock held or during initialization (before
+ main() has been called) */
+void fuse_register_module(struct fuse_module *mod)
+{
+ mod->ctr = 0;
+ mod->so = fuse_current_so;
+ if (mod->so)
+ mod->so->ctr++;
+ mod->next = fuse_modules;
+ fuse_modules = mod;
+}
+#endif
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+static struct fuse *fuse_new_common_compat(int fd, const char *opts,
+ const struct fuse_operations *op,
+ size_t op_size, int compat)
+{
+ struct fuse *f;
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+
+ if (fuse_opt_add_arg(&args, "") == -1)
+ return NULL;
+ if (opts &&
+ (fuse_opt_add_arg(&args, "-o") == -1 ||
+ fuse_opt_add_arg(&args, opts) == -1)) {
+ fuse_opt_free_args(&args);
+ return NULL;
+ }
+ f = fuse_new_common_compat25(fd, &args, op, op_size, compat);
+ fuse_opt_free_args(&args);
+
+ return f;
+}
+
+struct fuse *fuse_new_compat22(int fd, const char *opts,
+ const struct fuse_operations_compat22 *op,
+ size_t op_size)
+{
+ return fuse_new_common_compat(fd, opts, (struct fuse_operations *) op,
+ op_size, 22);
+}
+
+struct fuse *fuse_new_compat2(int fd, const char *opts,
+ const struct fuse_operations_compat2 *op)
+{
+ return fuse_new_common_compat(fd, opts, (struct fuse_operations *) op,
+ sizeof(struct fuse_operations_compat2),
+ 21);
+}
+
+struct fuse *fuse_new_compat1(int fd, int flags,
+ const struct fuse_operations_compat1 *op)
+{
+ const char *opts = NULL;
+ if (flags & FUSE_DEBUG_COMPAT1)
+ opts = "debug";
+ return fuse_new_common_compat(fd, opts, (struct fuse_operations *) op,
+ sizeof(struct fuse_operations_compat1),
+ 11);
+}
+
+FUSE_SYMVER(".symver fuse_exited,__fuse_exited@FUSE_UNVERSIONED");
+FUSE_SYMVER(".symver fuse_process_cmd,__fuse_process_cmd@FUSE_UNVERSIONED");
+FUSE_SYMVER(".symver fuse_read_cmd,__fuse_read_cmd@FUSE_UNVERSIONED");
+FUSE_SYMVER(".symver fuse_set_getcontext_func,__fuse_set_getcontext_func@FUSE_UNVERSIONED");
+FUSE_SYMVER(".symver fuse_new_compat2,fuse_new@FUSE_UNVERSIONED");
+FUSE_SYMVER(".symver fuse_new_compat22,fuse_new@FUSE_2.2");
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+struct fuse *fuse_new_compat25(int fd, struct fuse_args *args,
+ const struct fuse_operations_compat25 *op,
+ size_t op_size)
+{
+ return fuse_new_common_compat25(fd, args, (struct fuse_operations *) op,
+ op_size, 25);
+}
+
+FUSE_SYMVER(".symver fuse_new_compat25,fuse_new@FUSE_2.5");
diff --git a/fuse/fuse_i.h b/fuse/fuse_i.h
new file mode 100644
index 000000000..fa3715606
--- /dev/null
+++ b/fuse/fuse_i.h
@@ -0,0 +1,130 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse.h"
+#include "fuse_lowlevel.h"
+
+struct fuse_chan;
+struct fuse_ll;
+
+struct fuse_session {
+ struct fuse_session_ops op;
+
+ int (*receive_buf)(struct fuse_session *se, struct fuse_buf *buf,
+ struct fuse_chan **chp);
+
+ void (*process_buf)(void *data, const struct fuse_buf *buf,
+ struct fuse_chan *ch);
+
+ void *data;
+
+ volatile int exited;
+
+ struct fuse_chan *ch;
+};
+
+struct fuse_req {
+ struct fuse_ll *f;
+ uint64_t unique;
+ int ctr;
+ pthread_mutex_t lock;
+ struct fuse_ctx ctx;
+ struct fuse_chan *ch;
+ int interrupted;
+ unsigned int ioctl_64bit : 1;
+ union {
+ struct {
+ uint64_t unique;
+ } i;
+ struct {
+ fuse_interrupt_func_t func;
+ void *data;
+ } ni;
+ } u;
+ struct fuse_req *next;
+ struct fuse_req *prev;
+};
+
+struct fuse_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_ll {
+ int debug;
+ int allow_root;
+ int atomic_o_trunc;
+ int no_remote_posix_lock;
+ int no_remote_flock;
+ int big_writes;
+ int splice_write;
+ int splice_move;
+ int splice_read;
+ int no_splice_write;
+ int no_splice_move;
+ int no_splice_read;
+ struct fuse_lowlevel_ops op;
+ int got_init;
+ struct cuse_data *cuse_data;
+ void *userdata;
+ uid_t owner;
+ struct fuse_conn_info conn;
+ struct fuse_req list;
+ struct fuse_req interrupts;
+ pthread_mutex_t lock;
+ int got_destroy;
+ pthread_key_t pipe_key;
+ int broken_splice_nonblock;
+ uint64_t notify_ctr;
+ struct fuse_notify_req notify_list;
+};
+
+struct fuse_cmd {
+ char *buf;
+ size_t buflen;
+ struct fuse_chan *ch;
+};
+
+struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
+ const struct fuse_operations *op,
+ size_t op_size, void *user_data, int compat);
+
+int fuse_sync_compat_args(struct fuse_args *args);
+
+struct fuse_chan *fuse_kern_chan_new(int fd);
+
+struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
+ const struct fuse_lowlevel_ops *op,
+ size_t op_size, void *userdata);
+
+void fuse_kern_unmount_compat22(const char *mountpoint);
+int fuse_chan_clearfd(struct fuse_chan *ch);
+
+void fuse_kern_unmount(const char *mountpoint, int fd);
+int fuse_kern_mount(const char *mountpoint, struct fuse_args *args);
+
+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);
+
+
+struct fuse *fuse_setup_common(int argc, char *argv[],
+ const struct fuse_operations *op,
+ size_t op_size,
+ char **mountpoint,
+ int *multithreaded,
+ int *fd,
+ void *user_data,
+ int compat);
+
+void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
+
+int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
diff --git a/fuse/fuse_kern_chan.c b/fuse/fuse_kern_chan.c
new file mode 100644
index 000000000..4a9beb8f8
--- /dev/null
+++ b/fuse/fuse_kern_chan.c
@@ -0,0 +1,98 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse_lowlevel.h"
+#include "fuse_kernel.h"
+#include "fuse_i.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+
+static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
+ size_t size)
+{
+ struct fuse_chan *ch = *chp;
+ int err;
+ ssize_t res;
+ struct fuse_session *se = fuse_chan_session(ch);
+ assert(se != NULL);
+
+restart:
+ res = read(fuse_chan_fd(ch), buf, size);
+ err = errno;
+
+ if (fuse_session_exited(se))
+ return 0;
+ if (res == -1) {
+ /* ENOENT means the operation was interrupted, it's safe
+ to restart */
+ if (err == ENOENT)
+ goto restart;
+
+ if (err == ENODEV) {
+ fuse_session_exit(se);
+ return 0;
+ }
+ /* Errors occurring during normal operation: EINTR (read
+ interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
+ umounted) */
+ if (err != EINTR && err != EAGAIN)
+ perror("fuse: reading device");
+ return -err;
+ }
+ if ((size_t) res < sizeof(struct fuse_in_header)) {
+ fprintf(stderr, "short read on fuse device\n");
+ return -EIO;
+ }
+ return res;
+}
+
+static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
+ size_t count)
+{
+ if (iov) {
+ ssize_t res = writev(fuse_chan_fd(ch), iov, count);
+ int err = errno;
+
+ if (res == -1) {
+ struct fuse_session *se = fuse_chan_session(ch);
+
+ assert(se != NULL);
+
+ /* ENOENT means the operation was interrupted */
+ if (!fuse_session_exited(se) && err != ENOENT)
+ perror("fuse: writing device");
+ return -err;
+ }
+ }
+ return 0;
+}
+
+static void fuse_kern_chan_destroy(struct fuse_chan *ch)
+{
+ int fd = fuse_chan_fd(ch);
+
+ if (fd != -1)
+ close(fd);
+}
+
+#define MIN_BUFSIZE 0x21000
+
+struct fuse_chan *fuse_kern_chan_new(int fd)
+{
+ struct fuse_chan_ops op = {
+ .receive = fuse_kern_chan_receive,
+ .send = fuse_kern_chan_send,
+ .destroy = fuse_kern_chan_destroy,
+ };
+ size_t bufsize = getpagesize() + 0x1000;
+ bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize;
+ return fuse_chan_new(&op, fd, bufsize, NULL);
+}
diff --git a/fuse/fuse_loop.c b/fuse/fuse_loop.c
new file mode 100644
index 000000000..b7b4ca4ee
--- /dev/null
+++ b/fuse/fuse_loop.c
@@ -0,0 +1,46 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse_lowlevel.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+int fuse_session_loop(struct fuse_session *se)
+{
+ int res = 0;
+ struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+ size_t bufsize = fuse_chan_bufsize(ch);
+ char *buf = (char *) malloc(bufsize);
+ if (!buf) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ return -1;
+ }
+
+ while (!fuse_session_exited(se)) {
+ struct fuse_chan *tmpch = ch;
+ struct fuse_buf fbuf = {
+ .mem = buf,
+ .size = bufsize,
+ };
+
+ res = fuse_session_receive_buf(se, &fbuf, &tmpch);
+
+ if (res == -EINTR)
+ continue;
+ if (res <= 0)
+ break;
+
+ fuse_session_process_buf(se, &fbuf, tmpch);
+ }
+
+ free(buf);
+ fuse_session_reset(se);
+ return res < 0 ? -1 : 0;
+}
diff --git a/fuse/fuse_loop_mt.c b/fuse/fuse_loop_mt.c
new file mode 100644
index 000000000..90fc1e694
--- /dev/null
+++ b/fuse/fuse_loop_mt.c
@@ -0,0 +1,291 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "fuse_lowlevel.h"
+#include "fuse_misc.h"
+#include "fuse_kernel.h"
+#include "fuse_i.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <sys/time.h>
+
+/* Environment var controlling the thread stack size */
+#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
+
+struct fuse_worker {
+ struct fuse_worker *prev;
+ struct fuse_worker *next;
+ pthread_t thread_id;
+ size_t bufsize;
+ char *buf;
+ struct fuse_mt *mt;
+};
+
+struct fuse_mt {
+ pthread_mutex_t lock;
+ int numworker;
+ int numavail;
+ struct fuse_session *se;
+ struct fuse_chan *prevch;
+ struct fuse_worker main;
+ sem_t finish;
+ int exit;
+ int error;
+};
+
+static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
+{
+ struct fuse_worker *prev = next->prev;
+ w->next = next;
+ w->prev = prev;
+ prev->next = w;
+ next->prev = w;
+}
+
+static void list_del_worker(struct fuse_worker *w)
+{
+ struct fuse_worker *prev = w->prev;
+ struct fuse_worker *next = w->next;
+ prev->next = next;
+ next->prev = prev;
+}
+
+static int fuse_loop_start_thread(struct fuse_mt *mt);
+
+static void thread_exit_handler(int sig)
+{
+ pthread_exit(0);
+}
+
+static void *fuse_do_work(void *data)
+{
+ struct fuse_worker *w = (struct fuse_worker *) data;
+ struct fuse_mt *mt = w->mt;
+
+#if defined(__ANDROID__)
+ struct sigaction actions;
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = thread_exit_handler;
+ sigaction(SIGUSR1, &actions, NULL);
+
+ sigset_t setusr1;
+ sigemptyset(&setusr1);
+ sigaddset(&setusr1, SIGUSR1);
+ pthread_sigmask(SIG_BLOCK, &setusr1, NULL);
+#endif
+
+ while (!fuse_session_exited(mt->se)) {
+ int isforget = 0;
+ struct fuse_chan *ch = mt->prevch;
+ struct fuse_buf fbuf = {
+ .mem = w->buf,
+ .size = w->bufsize,
+ };
+ int res;
+
+#if defined(__ANDROID__)
+ pthread_sigmask(SIG_UNBLOCK, &setusr1, NULL);
+#else
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+#endif
+ res = fuse_session_receive_buf(mt->se, &fbuf, &ch);
+#if defined(__ANDROID__)
+ pthread_sigmask(SIG_BLOCK, &setusr1, NULL);
+#else
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+#endif
+ if (res == -EINTR)
+ continue;
+ if (res <= 0) {
+ if (res < 0) {
+ fuse_session_exit(mt->se);
+ mt->error = -1;
+ }
+ 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 (!(fbuf.flags & FUSE_BUF_IS_FD)) {
+ struct fuse_in_header *in = fbuf.mem;
+
+ if (in->opcode == FUSE_FORGET ||
+ in->opcode == FUSE_BATCH_FORGET)
+ isforget = 1;
+ }
+
+ if (!isforget)
+ mt->numavail--;
+ if (mt->numavail == 0)
+ fuse_loop_start_thread(mt);
+ pthread_mutex_unlock(&mt->lock);
+
+ fuse_session_process_buf(mt->se, &fbuf, ch);
+
+ pthread_mutex_lock(&mt->lock);
+ if (!isforget)
+ mt->numavail++;
+ if (mt->numavail > 10) {
+ 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);
+ free(w->buf);
+ 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 */
+ pthread_attr_init(&attr);
+ stack_size = getenv(ENVNAME_THREAD_STACK);
+ if (stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
+ fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
+
+ /* Disallow signal reception in worker threads */
+ sigemptyset(&newset);
+ sigaddset(&newset, SIGTERM);
+ sigaddset(&newset, SIGINT);
+ sigaddset(&newset, SIGHUP);
+ sigaddset(&newset, SIGQUIT);
+ pthread_sigmask(SIG_BLOCK, &newset, &oldset);
+ res = pthread_create(thread_id, &attr, func, arg);
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ pthread_attr_destroy(&attr);
+ if (res != 0) {
+ fprintf(stderr, "fuse: error creating thread: %s\n",
+ strerror(res));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int fuse_loop_start_thread(struct fuse_mt *mt)
+{
+ int res;
+ struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
+ if (!w) {
+ fprintf(stderr, "fuse: failed to allocate worker structure\n");
+ return -1;
+ }
+ memset(w, 0, sizeof(struct fuse_worker));
+ w->bufsize = fuse_chan_bufsize(mt->prevch);
+ w->buf = malloc(w->bufsize);
+ w->mt = mt;
+ if (!w->buf) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ free(w);
+ return -1;
+ }
+
+ res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
+ if (res == -1) {
+ free(w->buf);
+ free(w);
+ return -1;
+ }
+ list_add_worker(w, &mt->main);
+ 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);
+ free(w->buf);
+ free(w);
+}
+
+int fuse_session_loop_mt(struct fuse_session *se)
+{
+ int err;
+ struct fuse_mt mt;
+ struct fuse_worker *w;
+
+ memset(&mt, 0, sizeof(struct fuse_mt));
+ mt.se = se;
+ mt.prevch = fuse_session_next_chan(se, NULL);
+ mt.error = 0;
+ mt.numworker = 0;
+ mt.numavail = 0;
+ mt.main.thread_id = pthread_self();
+ mt.main.prev = mt.main.next = &mt.main;
+ sem_init(&mt.finish, 0, 0);
+ fuse_mutex_init(&mt.lock);
+
+ 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)
+#if defined(__ANDROID__)
+ pthread_kill(w->thread_id, SIGUSR1);
+#else
+ pthread_cancel(w->thread_id);
+#endif
+ 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);
+ fuse_session_reset(se);
+ return err;
+}
diff --git a/fuse/fuse_lowlevel.c b/fuse/fuse_lowlevel.c
new file mode 100644
index 000000000..5f223c98f
--- /dev/null
+++ b/fuse/fuse_lowlevel.c
@@ -0,0 +1,2968 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#define _GNU_SOURCE
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_kernel.h"
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+#include "fuse_common_compat.h"
+#include "fuse_lowlevel_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/file.h>
+
+#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_chan *ch;
+ struct fuse_ll *f;
+};
+
+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;
+ ST_ATIM_NSEC_SET(stbuf, attr->atimensec);
+ ST_MTIM_NSEC_SET(stbuf, attr->mtimensec);
+}
+
+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)
+{
+ pthread_mutex_destroy(&req->lock);
+ free(req);
+}
+
+void fuse_free_req(fuse_req_t req)
+{
+ int ctr;
+ struct fuse_ll *f = req->f;
+
+ pthread_mutex_lock(&f->lock);
+ req->u.ni.func = NULL;
+ req->u.ni.data = NULL;
+ list_del_req(req);
+ ctr = --req->ctr;
+ pthread_mutex_unlock(&f->lock);
+ if (!ctr)
+ destroy_req(req);
+}
+
+static struct fuse_req *fuse_ll_alloc_req(struct fuse_ll *f)
+{
+ struct fuse_req *req;
+
+ req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
+ if (req == NULL) {
+ fprintf(stderr, "fuse: failed to allocate request\n");
+ } else {
+ req->f = f;
+ req->ctr = 1;
+ list_init_req(req);
+ fuse_mutex_init(&req->lock);
+ }
+
+ return req;
+}
+
+
+static int fuse_send_msg(struct fuse_ll *f, struct fuse_chan *ch,
+ struct iovec *iov, int count)
+{
+ struct fuse_out_header *out = iov[0].iov_base;
+
+ out->len = iov_length(iov, count);
+ if (f->debug) {
+ if (out->unique == 0) {
+ fprintf(stderr, "NOTIFY: code=%d length=%u\n",
+ out->error, out->len);
+ } else if (out->error) {
+ fprintf(stderr,
+ " unique: %llu, error: %i (%s), outsize: %i\n",
+ (unsigned long long) out->unique, out->error,
+ strerror(-out->error), out->len);
+ } else {
+ fprintf(stderr,
+ " unique: %llu, success, outsize: %i\n",
+ (unsigned long long) out->unique, out->len);
+ }
+ }
+
+ return fuse_chan_send(ch, iov, count);
+}
+
+int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
+ int count)
+{
+ struct fuse_out_header out;
+
+ if (error <= -1000 || error > 0) {
+ fprintf(stderr, "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->f, 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;
+}
+
+size_t fuse_dirent_size(size_t namelen)
+{
+ return FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + namelen);
+}
+
+char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
+ loff_t off)
+{
+ unsigned namelen = strlen(name);
+ unsigned entlen = FUSE_NAME_OFFSET + namelen;
+ unsigned entsize = fuse_dirent_size(namelen);
+ unsigned padlen = entsize - entlen;
+ struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
+
+ dirent->ino = stbuf->st_ino;
+ dirent->off = off;
+ dirent->namelen = namelen;
+ dirent->type = (stbuf->st_mode & 0170000) >> 12;
+ strncpy(dirent->name, name, namelen);
+ if (padlen)
+ memset(buf + entlen, 0, padlen);
+
+ return buf + entsize;
+}
+
+size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
+ const char *name, const struct stat *stbuf, loff_t off)
+{
+ size_t entsize;
+
+ (void) req;
+ entsize = fuse_dirent_size(strlen(name));
+ if (entsize <= bufsize && buf)
+ fuse_add_dirent(buf, name, stbuf, off);
+ return entsize;
+}
+
+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)
+{
+ if (req->ch)
+ fuse_chan_send(req->ch, NULL, 0);
+ 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);
+}
+
+static void fill_open(struct fuse_open_out *arg,
+ const struct fuse_file_info *f)
+{
+ arg->fh = f->fh;
+ if (f->direct_io)
+ arg->open_flags |= FOPEN_DIRECT_IO;
+ if (f->keep_cache)
+ arg->open_flags |= FOPEN_KEEP_CACHE;
+ if (f->nonseekable)
+ arg->open_flags |= FOPEN_NONSEEKABLE;
+}
+
+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
+{
+ struct fuse_entry_out arg;
+ size_t size = req->f->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->f->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)
+{
+ char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)];
+ size_t entrysize = req->f->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->f->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_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_ll *f, 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(f, 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(f, 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
+static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_ll *f)
+{
+ struct fuse_ll_pipe *llp = pthread_getspecific(f->pipe_key);
+ if (llp == NULL) {
+ int res;
+
+ llp = malloc(sizeof(struct fuse_ll_pipe));
+ if (llp == NULL)
+ return NULL;
+
+ res = pipe(llp->pipe);
+ if (res == -1) {
+ free(llp);
+ return NULL;
+ }
+
+ if (fcntl(llp->pipe[0], F_SETFL, O_NONBLOCK) == -1 ||
+ fcntl(llp->pipe[1], F_SETFL, O_NONBLOCK) == -1) {
+ close(llp->pipe[0]);
+ close(llp->pipe[1]);
+ free(llp);
+ return NULL;
+ }
+
+ /*
+ *the default size is 16 pages on linux
+ */
+ llp->size = pagesize * 16;
+ llp->can_grow = 1;
+
+ pthread_setspecific(f->pipe_key, llp);
+ }
+
+ return llp;
+}
+#endif
+
+static void fuse_ll_clear_pipe(struct fuse_ll *f)
+{
+ struct fuse_ll_pipe *llp = pthread_getspecific(f->pipe_key);
+ if (llp) {
+ pthread_setspecific(f->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) {
+ fprintf(stderr, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
+ return -EIO;
+ }
+ if (res != len) {
+ fprintf(stderr, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int fuse_send_data_iov(struct fuse_ll *f, 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_fd_size;
+ size_t idx;
+ size_t headerlen;
+ struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
+
+ if (f->broken_splice_nonblock)
+ goto fallback;
+
+ if (flags & FUSE_BUF_NO_SPLICE)
+ goto fallback;
+
+ total_fd_size = 0;
+ for (idx = buf->idx; idx < buf->count; idx++) {
+ if (buf->buf[idx].flags & FUSE_BUF_IS_FD) {
+ total_fd_size = buf->buf[idx].size;
+ if (idx == buf->idx)
+ total_fd_size -= buf->off;
+ }
+ }
+ if (total_fd_size < 2 * pagesize)
+ goto fallback;
+
+ if (f->conn.proto_minor < 14 ||
+ !(f->conn.want & FUSE_CAP_SPLICE_WRITE))
+ goto fallback;
+
+ llp = fuse_ll_get_pipe(f);
+ 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) {
+ 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;
+ fprintf(stderr, "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)
+ f->broken_splice_nonblock = 1;
+
+ pthread_setspecific(f->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);
+ if (res != 0) {
+ free(mbuf);
+ goto clear_pipe;
+ }
+ free(tmpbuf);
+ 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(f, ch, iov, iov_count);
+ free(mbuf);
+ return res;
+ }
+ free(mbuf);
+ res = now_len;
+ }
+ len = res;
+ out->len = headerlen + len;
+
+ if (f->debug) {
+ fprintf(stderr,
+ " unique: %llu, success, outsize: %i (splice)\n",
+ (unsigned long long) out->unique, out->len);
+ }
+
+ splice_flags = 0;
+ if ((flags & FUSE_BUF_SPLICE_MOVE) &&
+ (f->conn.want & FUSE_CAP_SPLICE_MOVE))
+ splice_flags |= SPLICE_F_MOVE;
+
+ res = splice(llp->pipe[0], NULL,
+ fuse_chan_fd(ch), NULL, out->len, splice_flags);
+ if (res == -1) {
+ res = -errno;
+ perror("fuse: splice from pipe");
+ goto clear_pipe;
+ }
+ if (res != out->len) {
+ res = -EIO;
+ fprintf(stderr, "fuse: short splice from pipe: %u/%u\n",
+ res, out->len);
+ goto clear_pipe;
+ }
+ return 0;
+
+clear_pipe:
+ fuse_ll_clear_pipe(f);
+ return res;
+
+fallback:
+ return fuse_send_data_iov_fallback(f, ch, iov, iov_count, buf, len);
+}
+#else
+static int fuse_send_data_iov(struct fuse_ll *f, 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(f, 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->f, 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->f->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->f->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));
+}
+
+static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ char *name = (char *) inarg;
+
+ if (req->f->op.lookup)
+ req->f->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->f->op.forget)
+ req->f->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->f->op.forget_multi) {
+ req->f->op.forget_multi(req, arg->count,
+ (struct fuse_forget_data *) param);
+ } else if (req->f->op.forget) {
+ for (i = 0; i < arg->count; i++) {
+ struct fuse_forget_one *forget = &param[i];
+ struct fuse_req *dummy_req;
+
+ dummy_req = fuse_ll_alloc_req(req->f);
+ if (dummy_req == NULL)
+ break;
+
+ dummy_req->unique = req->unique;
+ dummy_req->ctx = req->ctx;
+ dummy_req->ch = NULL;
+
+ req->f->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->f->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;
+ fi.fh_old = fi.fh;
+ fip = &fi;
+ }
+ }
+
+ if (req->f->op.getattr)
+ req->f->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->f->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;
+ fi->fh_old = fi->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_ATIME_NOW |
+ FUSE_SET_ATTR_MTIME_NOW;
+
+ req->f->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->f->op.access)
+ req->f->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->f->op.readlink)
+ req->f->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->f->conn.proto_minor >= 12)
+ req->ctx.umask = arg->umask;
+ else
+ name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
+
+ if (req->f->op.mknod)
+ req->f->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->f->conn.proto_minor >= 12)
+ req->ctx.umask = arg->umask;
+
+ if (req->f->op.mkdir)
+ req->f->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->f->op.unlink)
+ req->f->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->f->op.rmdir)
+ req->f->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->f->op.symlink)
+ req->f->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->f->op.rename)
+ req->f->op.rename(req, nodeid, oldname, arg->newdir, newname);
+ 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->f->op.link)
+ req->f->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
+ 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->f->op.create) {
+ struct fuse_file_info fi;
+ char *name = PARAM(arg);
+
+ memset(&fi, 0, sizeof(fi));
+ fi.flags = arg->flags;
+
+ if (req->f->conn.proto_minor >= 12)
+ req->ctx.umask = arg->umask;
+ else
+ name = (char *) inarg + sizeof(struct fuse_open_in);
+
+ req->f->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->f->op.open)
+ req->f->op.open(req, nodeid, &fi);
+ 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->f->op.read) {
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+ if (req->f->conn.proto_minor >= 9) {
+ fi.lock_owner = arg->lock_owner;
+ fi.flags = arg->flags;
+ }
+ req->f->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.fh_old = fi.fh;
+ fi.writepage = arg->write_flags & 1;
+
+ if (req->f->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->f->op.write)
+ req->f->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_ll *f = req->f;
+ 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.fh_old = fi.fh;
+ fi.writepage = arg->write_flags & 1;
+
+ if (req->f->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) {
+ fprintf(stderr, "fuse: do_write_buf: buffer size too small\n");
+ fuse_reply_err(req, EIO);
+ goto out;
+ }
+ bufv.buf[0].size = arg->size;
+
+ req->f->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(f);
+}
+
+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.fh_old = fi.fh;
+ fi.flush = 1;
+ if (req->f->conn.proto_minor >= 7)
+ fi.lock_owner = arg->lock_owner;
+
+ if (req->f->op.flush)
+ req->f->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;
+ fi.fh_old = fi.fh;
+ if (req->f->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->f->op.release)
+ req->f->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;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+
+ if (req->f->op.fsync)
+ req->f->op.fsync(req, nodeid, arg->fsync_flags & 1, &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->f->op.opendir)
+ req->f->op.opendir(req, nodeid, &fi);
+ 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;
+ fi.fh_old = fi.fh;
+
+ if (req->f->op.readdir)
+ req->f->op.readdir(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;
+ fi.fh_old = fi.fh;
+
+ if (req->f->op.releasedir)
+ req->f->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;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+
+ if (req->f->op.fsyncdir)
+ req->f->op.fsyncdir(req, nodeid, arg->fsync_flags & 1, &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->f->op.statfs)
+ req->f->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_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
+ char *name = PARAM(arg);
+ char *value = name + strlen(name) + 1;
+
+ if (req->f->op.setxattr)
+ req->f->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->f->op.getxattr)
+ req->f->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->f->op.listxattr)
+ req->f->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->f->op.removexattr)
+ req->f->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->f->op.getlk)
+ req->f->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->f->op.flock)
+ req->f->op.flock(req, nodeid, &fi, op);
+ else
+ fuse_reply_err(req, ENOSYS);
+ } else {
+ convert_fuse_file_lock(&arg->lk, &flock);
+ if (req->f->op.setlk)
+ req->f->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_ll *f, struct fuse_req *req)
+{
+ struct fuse_req *curr;
+
+ for (curr = f->list.next; curr != &f->list; curr = curr->next) {
+ if (curr->unique == req->u.i.unique) {
+ fuse_interrupt_func_t func;
+ void *data;
+
+ curr->ctr++;
+ pthread_mutex_unlock(&f->lock);
+
+ /* Ugh, ugly locking */
+ pthread_mutex_lock(&curr->lock);
+ pthread_mutex_lock(&f->lock);
+ curr->interrupted = 1;
+ func = curr->u.ni.func;
+ data = curr->u.ni.data;
+ pthread_mutex_unlock(&f->lock);
+ if (func)
+ func(curr, data);
+ pthread_mutex_unlock(&curr->lock);
+
+ pthread_mutex_lock(&f->lock);
+ curr->ctr--;
+ if (!curr->ctr)
+ destroy_req(curr);
+
+ return 1;
+ }
+ }
+ for (curr = f->interrupts.next; curr != &f->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_ll *f = req->f;
+
+ (void) nodeid;
+ if (f->debug)
+ fprintf(stderr, "INTERRUPT: %llu\n",
+ (unsigned long long) arg->unique);
+
+ req->u.i.unique = arg->unique;
+
+ pthread_mutex_lock(&f->lock);
+ if (find_interrupted(f, req))
+ destroy_req(req);
+ else
+ list_add_req(req, &f->interrupts);
+ pthread_mutex_unlock(&f->lock);
+}
+
+static struct fuse_req *check_interrupt(struct fuse_ll *f, struct fuse_req *req)
+{
+ struct fuse_req *curr;
+
+ for (curr = f->interrupts.next; curr != &f->interrupts;
+ curr = curr->next) {
+ if (curr->u.i.unique == req->unique) {
+ req->interrupted = 1;
+ list_del_req(curr);
+ free(curr);
+ return NULL;
+ }
+ }
+ curr = f->interrupts.next;
+ if (curr != &f->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->f->op.bmap)
+ req->f->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->f->conn.want & FUSE_CAP_IOCTL_DIR)) {
+ fuse_reply_err(req, ENOTTY);
+ return;
+ }
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+ fi.fh_old = fi.fh;
+
+ if (sizeof(void *) == 4 && req->f->conn.proto_minor >= 16 &&
+ !(flags & FUSE_IOCTL_32BIT)) {
+ req->ioctl_64bit = 1;
+ }
+
+ if (req->f->op.ioctl)
+ req->f->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.fh_old = fi.fh;
+
+ if (req->f->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->ch = req->ch;
+ ph->f = req->f;
+ }
+
+ req->f->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->f->op.fallocate)
+ req->f->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
+static 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_ll *f = req->f;
+ size_t bufsize = fuse_chan_bufsize(req->ch);
+
+ (void) nodeid;
+ if (f->debug) {
+ fprintf(stderr, "INIT: %u.%u\n", arg->major, arg->minor);
+ if (arg->major == 7 && arg->minor >= 6) {
+ fprintf(stderr, "flags=0x%08x\n", arg->flags);
+ fprintf(stderr, "max_readahead=0x%08x\n",
+ arg->max_readahead);
+ }
+ }
+ f->conn.proto_major = arg->major;
+ f->conn.proto_minor = arg->minor;
+ f->conn.capable = 0;
+ f->conn.want = 0;
+
+ memset(&outarg, 0, sizeof(outarg));
+ outarg.major = FUSE_KERNEL_VERSION;
+ outarg.minor = FUSE_KERNEL_MINOR_VERSION;
+
+ if (arg->major < 7) {
+ fprintf(stderr, "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 (f->conn.async_read)
+ f->conn.async_read = arg->flags & FUSE_ASYNC_READ;
+ if (arg->max_readahead < f->conn.max_readahead)
+ f->conn.max_readahead = arg->max_readahead;
+ if (arg->flags & FUSE_ASYNC_READ)
+ f->conn.capable |= FUSE_CAP_ASYNC_READ;
+ if (arg->flags & FUSE_POSIX_LOCKS)
+ f->conn.capable |= FUSE_CAP_POSIX_LOCKS;
+ if (arg->flags & FUSE_ATOMIC_O_TRUNC)
+ f->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC;
+ if (arg->flags & FUSE_EXPORT_SUPPORT)
+ f->conn.capable |= FUSE_CAP_EXPORT_SUPPORT;
+ if (arg->flags & FUSE_BIG_WRITES)
+ f->conn.capable |= FUSE_CAP_BIG_WRITES;
+ if (arg->flags & FUSE_DONT_MASK)
+ f->conn.capable |= FUSE_CAP_DONT_MASK;
+ if (arg->flags & FUSE_FLOCK_LOCKS)
+ f->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
+ } else {
+ f->conn.async_read = 0;
+ f->conn.max_readahead = 0;
+ }
+
+ if (req->f->conn.proto_minor >= 14) {
+#ifdef HAVE_SPLICE
+#ifdef HAVE_VMSPLICE
+ f->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
+ if (f->splice_write)
+ f->conn.want |= FUSE_CAP_SPLICE_WRITE;
+ if (f->splice_move)
+ f->conn.want |= FUSE_CAP_SPLICE_MOVE;
+#endif
+ f->conn.capable |= FUSE_CAP_SPLICE_READ;
+ if (f->splice_read)
+ f->conn.want |= FUSE_CAP_SPLICE_READ;
+#endif
+ }
+ if (req->f->conn.proto_minor >= 18)
+ f->conn.capable |= FUSE_CAP_IOCTL_DIR;
+
+ if (f->atomic_o_trunc)
+ f->conn.want |= FUSE_CAP_ATOMIC_O_TRUNC;
+ if (f->op.getlk && f->op.setlk && !f->no_remote_posix_lock)
+ f->conn.want |= FUSE_CAP_POSIX_LOCKS;
+ if (f->op.flock && !f->no_remote_flock)
+ f->conn.want |= FUSE_CAP_FLOCK_LOCKS;
+ if (f->big_writes)
+ f->conn.want |= FUSE_CAP_BIG_WRITES;
+
+ if (bufsize < FUSE_MIN_READ_BUFFER) {
+ fprintf(stderr, "fuse: warning: buffer size too small: %zu\n",
+ bufsize);
+ bufsize = FUSE_MIN_READ_BUFFER;
+ }
+
+ bufsize -= 4096;
+ if (bufsize < f->conn.max_write)
+ f->conn.max_write = bufsize;
+
+ f->got_init = 1;
+ if (f->op.init)
+ f->op.init(f->userdata, &f->conn);
+
+ if (f->no_splice_read)
+ f->conn.want &= ~FUSE_CAP_SPLICE_READ;
+ if (f->no_splice_write)
+ f->conn.want &= ~FUSE_CAP_SPLICE_WRITE;
+ if (f->no_splice_move)
+ f->conn.want &= ~FUSE_CAP_SPLICE_MOVE;
+
+ if (f->conn.async_read || (f->conn.want & FUSE_CAP_ASYNC_READ))
+ outarg.flags |= FUSE_ASYNC_READ;
+ if (f->conn.want & FUSE_CAP_POSIX_LOCKS)
+ outarg.flags |= FUSE_POSIX_LOCKS;
+ if (f->conn.want & FUSE_CAP_ATOMIC_O_TRUNC)
+ outarg.flags |= FUSE_ATOMIC_O_TRUNC;
+ if (f->conn.want & FUSE_CAP_EXPORT_SUPPORT)
+ outarg.flags |= FUSE_EXPORT_SUPPORT;
+ if (f->conn.want & FUSE_CAP_BIG_WRITES)
+ outarg.flags |= FUSE_BIG_WRITES;
+ if (f->conn.want & FUSE_CAP_DONT_MASK)
+ outarg.flags |= FUSE_DONT_MASK;
+ if (f->conn.want & FUSE_CAP_FLOCK_LOCKS)
+ outarg.flags |= FUSE_FLOCK_LOCKS;
+ outarg.max_readahead = f->conn.max_readahead;
+ outarg.max_write = f->conn.max_write;
+ if (f->conn.proto_minor >= 13) {
+ if (f->conn.max_background >= (1 << 16))
+ f->conn.max_background = (1 << 16) - 1;
+ if (f->conn.congestion_threshold > f->conn.max_background)
+ f->conn.congestion_threshold = f->conn.max_background;
+ if (!f->conn.congestion_threshold) {
+ f->conn.congestion_threshold =
+ f->conn.max_background * 3 / 4;
+ }
+
+ outarg.max_background = f->conn.max_background;
+ outarg.congestion_threshold = f->conn.congestion_threshold;
+ }
+
+ if (f->debug) {
+ fprintf(stderr, " INIT: %u.%u\n", outarg.major, outarg.minor);
+ fprintf(stderr, " flags=0x%08x\n", outarg.flags);
+ fprintf(stderr, " max_readahead=0x%08x\n",
+ outarg.max_readahead);
+ fprintf(stderr, " max_write=0x%08x\n", outarg.max_write);
+ fprintf(stderr, " max_background=%i\n",
+ outarg.max_background);
+ fprintf(stderr, " congestion_threshold=%i\n",
+ outarg.congestion_threshold);
+ }
+
+ send_reply_ok(req, &outarg, arg->minor < 5 ? 8 : sizeof(outarg));
+}
+
+static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_ll *f = req->f;
+
+ (void) nodeid;
+ (void) inarg;
+
+ f->got_destroy = 1;
+ if (f->op.destroy)
+ f->op.destroy(f->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_ll *f = req->f;
+ struct fuse_notify_req *nreq;
+ struct fuse_notify_req *head;
+
+ pthread_mutex_lock(&f->lock);
+ head = &f->notify_list;
+ for (nreq = head->next; nreq != head; nreq = nreq->next) {
+ if (nreq->unique == req->unique) {
+ list_del_nreq(nreq);
+ break;
+ }
+ }
+ pthread_mutex_unlock(&f->lock);
+
+ if (nreq != head)
+ nreq->reply(nreq, req, nodeid, inarg, buf);
+}
+
+static int send_notify_iov(struct fuse_ll *f, struct fuse_chan *ch,
+ int notify_code, struct iovec *iov, int count)
+{
+ struct fuse_out_header out;
+
+ if (!f->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(f, ch, 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->f, ph->ch, FUSE_NOTIFY_POLL, iov, 2);
+ } else {
+ return 0;
+ }
+}
+
+int fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch, fuse_ino_t ino,
+ loff_t off, loff_t len)
+{
+ struct fuse_notify_inval_inode_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[2];
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ outarg.ino = ino;
+ outarg.off = off;
+ outarg.len = len;
+
+ iov[1].iov_base = &outarg;
+ iov[1].iov_len = sizeof(outarg);
+
+ return send_notify_iov(f, ch, FUSE_NOTIFY_INVAL_INODE, iov, 2);
+}
+
+int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent,
+ const char *name, size_t namelen)
+{
+ struct fuse_notify_inval_entry_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[3];
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ outarg.parent = parent;
+ 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(f, ch, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
+}
+
+int fuse_lowlevel_notify_delete(struct fuse_chan *ch,
+ fuse_ino_t parent, fuse_ino_t child,
+ const char *name, size_t namelen)
+{
+ struct fuse_notify_delete_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[3];
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ if (f->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(f, ch, FUSE_NOTIFY_DELETE, iov, 3);
+}
+
+int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino,
+ loff_t offset, struct fuse_bufvec *bufv,
+ enum fuse_buf_copy_flags flags)
+{
+ struct fuse_out_header out;
+ struct fuse_notify_store_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[3];
+ size_t size = fuse_buf_size(bufv);
+ int res;
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ if (f->conn.proto_minor < 15)
+ return -ENOSYS;
+
+ out.unique = 0;
+ out.error = FUSE_NOTIFY_STORE;
+
+ outarg.nodeid = ino;
+ outarg.offset = offset;
+ outarg.size = size;
+
+ 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(f, ch, 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_ll *f = req->f;
+ 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) {
+ fprintf(stderr, "fuse: retrieve reply: buffer size too small\n");
+ fuse_reply_none(req);
+ goto out;
+ }
+ bufv.buf[0].size = arg->size;
+
+ if (req->f->op.retrieve_reply) {
+ req->f->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(f);
+}
+
+int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, fuse_ino_t ino,
+ size_t size, loff_t offset, void *cookie)
+{
+ struct fuse_notify_retrieve_out outarg;
+ struct fuse_ll *f;
+ struct iovec iov[2];
+ struct fuse_retrieve_req *rreq;
+ int err;
+
+ if (!ch)
+ return -EINVAL;
+
+ f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
+ if (!f)
+ return -ENODEV;
+
+ if (f->conn.proto_minor < 15)
+ return -ENOSYS;
+
+ rreq = malloc(sizeof(*rreq));
+ if (rreq == NULL)
+ return -ENOMEM;
+
+ pthread_mutex_lock(&f->lock);
+ rreq->cookie = cookie;
+ rreq->nreq.unique = f->notify_ctr++;
+ rreq->nreq.reply = fuse_ll_retrieve_reply;
+ list_add_nreq(&rreq->nreq, &f->notify_list);
+ pthread_mutex_unlock(&f->lock);
+
+ outarg.notify_unique = rreq->nreq.unique;
+ outarg.nodeid = ino;
+ outarg.offset = offset;
+ outarg.size = size;
+
+ iov[1].iov_base = &outarg;
+ iov[1].iov_len = sizeof(outarg);
+
+ err = send_notify_iov(f, ch, FUSE_NOTIFY_RETRIEVE, iov, 2);
+ if (err) {
+ pthread_mutex_lock(&f->lock);
+ list_del_nreq(&rreq->nreq);
+ pthread_mutex_unlock(&f->lock);
+ free(rreq);
+ }
+
+ return err;
+}
+
+void *fuse_req_userdata(fuse_req_t req)
+{
+ return req->f->userdata;
+}
+
+const struct fuse_ctx *fuse_req_ctx(fuse_req_t req)
+{
+ return &req->ctx;
+}
+
+/*
+ * The size of fuse_ctx got extended, so need to be careful about
+ * incompatibility (i.e. a new binary cannot work with an old
+ * library).
+ */
+const struct fuse_ctx *fuse_req_ctx_compat24(fuse_req_t req);
+const struct fuse_ctx *fuse_req_ctx_compat24(fuse_req_t req)
+{
+ return fuse_req_ctx(req);
+}
+#ifndef __NetBSD__
+FUSE_SYMVER(".symver fuse_req_ctx_compat24,fuse_req_ctx@FUSE_2.4");
+#endif
+
+
+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->f->lock);
+ req->u.ni.func = func;
+ req->u.ni.data = data;
+ pthread_mutex_unlock(&req->f->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->f->lock);
+ interrupted = req->interrupted;
+ pthread_mutex_unlock(&req->f->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_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" },
+ [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
+};
+
+#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 int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
+ struct fuse_bufvec *src)
+{
+ int res = fuse_buf_copy(dst, src, 0);
+ if (res < 0) {
+ fprintf(stderr, "fuse: copy from pipe: %s\n", strerror(-res));
+ return res;
+ }
+ if (res < fuse_buf_size(dst)) {
+ fprintf(stderr, "fuse: copy from pipe: short read\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void fuse_ll_process_buf(void *data, const struct fuse_buf *buf,
+ struct fuse_chan *ch)
+{
+ struct fuse_ll *f = (struct fuse_ll *) data;
+ 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) {
+ fprintf(stderr, "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 (f->debug) {
+ fprintf(stderr,
+ "unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %zu, pid: %u\n",
+ (unsigned long long) in->unique,
+ opname((enum fuse_opcode) in->opcode), in->opcode,
+ (unsigned long) in->nodeid, buf->size, in->pid);
+ }
+
+ req = fuse_ll_alloc_req(f);
+ 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(f, 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;
+
+ err = EIO;
+ if (!f->got_init) {
+ enum fuse_opcode expected;
+
+ expected = f->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;
+ if (f->allow_root && in->uid != f->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)
+ goto reply_err;
+
+ err = ENOSYS;
+ if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
+ goto reply_err;
+ if (in->opcode != FUSE_INTERRUPT) {
+ struct fuse_req *intr;
+ pthread_mutex_lock(&f->lock);
+ intr = check_interrupt(f, req);
+ list_add_req(req, &f->list);
+ pthread_mutex_unlock(&f->lock);
+ if (intr)
+ fuse_reply_err(intr, EAGAIN);
+ }
+
+ if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
+ (in->opcode != FUSE_WRITE || !f->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 = 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 && f->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(f);
+ goto out_free;
+}
+
+static void fuse_ll_process(void *data, const char *buf, size_t len,
+ struct fuse_chan *ch)
+{
+ struct fuse_buf fbuf = {
+ .mem = (void *) buf,
+ .size = len,
+ };
+
+ fuse_ll_process_buf(data, &fbuf, ch);
+}
+
+enum {
+ KEY_HELP,
+ KEY_VERSION,
+};
+
+static const struct fuse_opt fuse_ll_opts[] = {
+ { "debug", offsetof(struct fuse_ll, debug), 1 },
+ { "-d", offsetof(struct fuse_ll, debug), 1 },
+ { "allow_root", offsetof(struct fuse_ll, allow_root), 1 },
+ { "max_write=%u", offsetof(struct fuse_ll, conn.max_write), 0 },
+ { "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 },
+ { "max_background=%u", offsetof(struct fuse_ll, conn.max_background), 0 },
+ { "congestion_threshold=%u",
+ offsetof(struct fuse_ll, conn.congestion_threshold), 0 },
+ { "async_read", offsetof(struct fuse_ll, conn.async_read), 1 },
+ { "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 },
+ { "atomic_o_trunc", offsetof(struct fuse_ll, atomic_o_trunc), 1},
+ { "no_remote_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
+ { "no_remote_lock", offsetof(struct fuse_ll, no_remote_flock), 1},
+ { "no_remote_flock", offsetof(struct fuse_ll, no_remote_flock), 1},
+ { "no_remote_posix_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
+ { "big_writes", offsetof(struct fuse_ll, big_writes), 1},
+ { "splice_write", offsetof(struct fuse_ll, splice_write), 1},
+ { "no_splice_write", offsetof(struct fuse_ll, no_splice_write), 1},
+ { "splice_move", offsetof(struct fuse_ll, splice_move), 1},
+ { "no_splice_move", offsetof(struct fuse_ll, no_splice_move), 1},
+ { "splice_read", offsetof(struct fuse_ll, splice_read), 1},
+ { "no_splice_read", offsetof(struct fuse_ll, no_splice_read), 1},
+ FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
+ FUSE_OPT_KEY("-h", KEY_HELP),
+ FUSE_OPT_KEY("--help", KEY_HELP),
+ FUSE_OPT_KEY("-V", KEY_VERSION),
+ FUSE_OPT_KEY("--version", KEY_VERSION),
+ FUSE_OPT_END
+};
+
+static void fuse_ll_version(void)
+{
+ fprintf(stderr, "using FUSE kernel interface version %i.%i\n",
+ FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
+}
+
+static void fuse_ll_help(void)
+{
+ fprintf(stderr,
+" -o max_write=N set maximum size of write requests\n"
+" -o max_readahead=N set maximum readahead\n"
+" -o max_background=N set number of maximum background requests\n"
+" -o congestion_threshold=N set kernel's congestion threshold\n"
+" -o async_read perform reads asynchronously (default)\n"
+" -o sync_read perform reads synchronously\n"
+" -o atomic_o_trunc enable atomic open+truncate support\n"
+" -o big_writes enable larger than 4kB writes\n"
+" -o no_remote_lock disable remote file locking\n"
+" -o no_remote_flock disable remote file locking (BSD)\n"
+" -o no_remote_posix_lock disable remove file locking (POSIX)\n"
+" -o [no_]splice_write use splice to write to the fuse device\n"
+" -o [no_]splice_move move data while splicing to the fuse device\n"
+" -o [no_]splice_read use splice to read from the fuse device\n"
+);
+}
+
+static int fuse_ll_opt_proc(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ (void) data; (void) outargs;
+
+ switch (key) {
+ case KEY_HELP:
+ fuse_ll_help();
+ break;
+
+ case KEY_VERSION:
+ fuse_ll_version();
+ break;
+
+ default:
+ fprintf(stderr, "fuse: unknown option `%s'\n", arg);
+ }
+
+ return -1;
+}
+
+int fuse_lowlevel_is_lib_option(const char *opt)
+{
+ return fuse_opt_match(fuse_ll_opts, opt);
+}
+
+static void fuse_ll_destroy(void *data)
+{
+ struct fuse_ll *f = (struct fuse_ll *) data;
+ struct fuse_ll_pipe *llp;
+
+ if (f->got_init && !f->got_destroy) {
+ if (f->op.destroy)
+ f->op.destroy(f->userdata);
+ }
+ llp = pthread_getspecific(f->pipe_key);
+ if (llp != NULL)
+ fuse_ll_pipe_free(llp);
+ pthread_key_delete(f->pipe_key);
+ pthread_mutex_destroy(&f->lock);
+ free(f->cuse_data);
+ free(f);
+}
+
+static void fuse_ll_pipe_destructor(void *data)
+{
+ struct fuse_ll_pipe *llp = data;
+ fuse_ll_pipe_free(llp);
+}
+
+#ifdef HAVE_SPLICE
+static int fuse_ll_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+ struct fuse_chan **chp)
+{
+ struct fuse_chan *ch = *chp;
+ struct fuse_ll *f = fuse_session_data(se);
+ size_t bufsize = buf->size;
+ struct fuse_ll_pipe *llp;
+ struct fuse_buf tmpbuf;
+ int err;
+ int res;
+
+ if (f->conn.proto_minor < 14 || !(f->conn.want & FUSE_CAP_SPLICE_READ))
+ goto fallback;
+
+ llp = fuse_ll_get_pipe(f);
+ 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;
+ goto fallback;
+ }
+ llp->size = res;
+ }
+ if (llp->size < bufsize)
+ goto fallback;
+ }
+
+ res = splice(fuse_chan_fd(ch), NULL, llp->pipe[1], NULL, bufsize, 0);
+ err = errno;
+
+ if (fuse_session_exited(se))
+ return 0;
+
+ if (res == -1) {
+ if (err == ENODEV) {
+ fuse_session_exit(se);
+ return 0;
+ }
+ if (err != EINTR && err != EAGAIN)
+ perror("fuse: splice from device");
+ return -err;
+ }
+
+ if (res < sizeof(struct fuse_in_header)) {
+ fprintf(stderr, "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 = { .buf[0] = *buf, .count = 1 };
+
+ res = fuse_buf_copy(&dst, &src, 0);
+ if (res < 0) {
+ fprintf(stderr, "fuse: copy from pipe: %s\n",
+ strerror(-res));
+ fuse_ll_clear_pipe(f);
+ return res;
+ }
+ if (res < tmpbuf.size) {
+ fprintf(stderr, "fuse: copy from pipe: short read\n");
+ fuse_ll_clear_pipe(f);
+ return -EIO;
+ }
+ buf->size = tmpbuf.size;
+ return buf->size;
+ }
+
+ *buf = tmpbuf;
+
+ return res;
+
+fallback:
+ res = fuse_chan_recv(chp, buf->mem, bufsize);
+ if (res <= 0)
+ return res;
+
+ buf->size = res;
+
+ return res;
+}
+#else
+static int fuse_ll_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+ struct fuse_chan **chp)
+{
+ (void) se;
+
+ int res = fuse_chan_recv(chp, buf->mem, buf->size);
+ if (res <= 0)
+ return res;
+
+ buf->size = res;
+
+ return res;
+}
+#endif
+
+
+/*
+ * always call fuse_lowlevel_new_common() internally, to work around a
+ * misfeature in the FreeBSD runtime linker, which links the old
+ * version of a symbol to internal references.
+ */
+struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
+ const struct fuse_lowlevel_ops *op,
+ size_t op_size, void *userdata)
+{
+ int err;
+ struct fuse_ll *f;
+ struct fuse_session *se;
+ struct fuse_session_ops sop = {
+ .process = fuse_ll_process,
+ .destroy = fuse_ll_destroy,
+ };
+
+ if (sizeof(struct fuse_lowlevel_ops) < op_size) {
+ fprintf(stderr, "fuse: warning: library too old, some operations may not work\n");
+ op_size = sizeof(struct fuse_lowlevel_ops);
+ }
+
+ f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll));
+ if (f == NULL) {
+ fprintf(stderr, "fuse: failed to allocate fuse object\n");
+ goto out;
+ }
+
+ f->conn.async_read = 1;
+ f->conn.max_write = UINT_MAX;
+ f->conn.max_readahead = UINT_MAX;
+ f->atomic_o_trunc = 0;
+ list_init_req(&f->list);
+ list_init_req(&f->interrupts);
+ list_init_nreq(&f->notify_list);
+ f->notify_ctr = 1;
+ fuse_mutex_init(&f->lock);
+
+ err = pthread_key_create(&f->pipe_key, fuse_ll_pipe_destructor);
+ if (err) {
+ fprintf(stderr, "fuse: failed to create thread specific key: %s\n",
+ strerror(err));
+ goto out_free;
+ }
+
+ if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
+ goto out_key_destroy;
+
+ if (f->debug)
+ fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
+
+ memcpy(&f->op, op, op_size);
+ f->owner = getuid();
+ f->userdata = userdata;
+
+ se = fuse_session_new(&sop, f);
+ if (!se)
+ goto out_key_destroy;
+
+ se->receive_buf = fuse_ll_receive_buf;
+ se->process_buf = fuse_ll_process_buf;
+
+ return se;
+
+out_key_destroy:
+ pthread_key_delete(f->pipe_key);
+out_free:
+ pthread_mutex_destroy(&f->lock);
+ free(f);
+out:
+ return NULL;
+}
+
+
+struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
+ const struct fuse_lowlevel_ops *op,
+ size_t op_size, void *userdata)
+{
+ return fuse_lowlevel_new_common(args, op, op_size, userdata);
+}
+
+#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 == -1) {
+ ret = -EIO;
+ goto out_free;
+ }
+
+ if (ret == bufsize) {
+ free(buf);
+ bufsize *= 4;
+ goto retry;
+ }
+
+ 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[])
+{
+ return -ENOSYS;
+}
+#endif
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+static void fill_open_compat(struct fuse_open_out *arg,
+ const struct fuse_file_info_compat *f)
+{
+ arg->fh = f->fh;
+ if (f->direct_io)
+ arg->open_flags |= FOPEN_DIRECT_IO;
+ if (f->keep_cache)
+ arg->open_flags |= FOPEN_KEEP_CACHE;
+}
+
+static void convert_statfs_compat(const struct statfs *compatbuf,
+ struct statvfs *buf)
+{
+ buf->f_bsize = compatbuf->f_bsize;
+ buf->f_blocks = compatbuf->f_blocks;
+ buf->f_bfree = compatbuf->f_bfree;
+ buf->f_bavail = compatbuf->f_bavail;
+ buf->f_files = compatbuf->f_files;
+ buf->f_ffree = compatbuf->f_ffree;
+ buf->f_namemax = compatbuf->f_namelen;
+}
+
+int fuse_reply_open_compat(fuse_req_t req,
+ const struct fuse_file_info_compat *f)
+{
+ struct fuse_open_out arg;
+
+ memset(&arg, 0, sizeof(arg));
+ fill_open_compat(&arg, f);
+ return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf)
+{
+ struct statvfs newbuf;
+
+ memset(&newbuf, 0, sizeof(newbuf));
+ convert_statfs_compat(stbuf, &newbuf);
+
+ return fuse_reply_statfs(req, &newbuf);
+}
+
+struct fuse_session *fuse_lowlevel_new_compat(const char *opts,
+ const struct fuse_lowlevel_ops_compat *op,
+ size_t op_size, void *userdata)
+{
+ struct fuse_session *se;
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
+
+ if (opts &&
+ (fuse_opt_add_arg(&args, "") == -1 ||
+ fuse_opt_add_arg(&args, "-o") == -1 ||
+ fuse_opt_add_arg(&args, opts) == -1)) {
+ fuse_opt_free_args(&args);
+ return NULL;
+ }
+ se = fuse_lowlevel_new(&args, (const struct fuse_lowlevel_ops *) op,
+ op_size, userdata);
+ fuse_opt_free_args(&args);
+
+ return se;
+}
+
+struct fuse_ll_compat_conf {
+ unsigned max_read;
+ int set_max_read;
+};
+
+static const struct fuse_opt fuse_ll_opts_compat[] = {
+ { "max_read=", offsetof(struct fuse_ll_compat_conf, set_max_read), 1 },
+ { "max_read=%u", offsetof(struct fuse_ll_compat_conf, max_read), 0 },
+ FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_END
+};
+
+int fuse_sync_compat_args(struct fuse_args *args)
+{
+ struct fuse_ll_compat_conf conf;
+
+ memset(&conf, 0, sizeof(conf));
+ if (fuse_opt_parse(args, &conf, fuse_ll_opts_compat, NULL) == -1)
+ return -1;
+
+ if (fuse_opt_insert_arg(args, 1, "-osync_read"))
+ return -1;
+
+ if (conf.set_max_read) {
+ char tmpbuf[64];
+
+ sprintf(tmpbuf, "-omax_readahead=%u", conf.max_read);
+ if (fuse_opt_insert_arg(args, 1, tmpbuf) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+FUSE_SYMVER(".symver fuse_reply_statfs_compat,fuse_reply_statfs@FUSE_2.4");
+FUSE_SYMVER(".symver fuse_reply_open_compat,fuse_reply_open@FUSE_2.4");
+FUSE_SYMVER(".symver fuse_lowlevel_new_compat,fuse_lowlevel_new@FUSE_2.4");
+
+#else /* __FreeBSD__ || __NetBSD__ */
+
+int fuse_sync_compat_args(struct fuse_args *args)
+{
+ (void) args;
+ return 0;
+}
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args,
+ const struct fuse_lowlevel_ops_compat25 *op,
+ size_t op_size, void *userdata)
+{
+ if (fuse_sync_compat_args(args) == -1)
+ return NULL;
+
+ return fuse_lowlevel_new_common(args,
+ (const struct fuse_lowlevel_ops *) op,
+ op_size, userdata);
+}
+
+FUSE_SYMVER(".symver fuse_lowlevel_new_compat25,fuse_lowlevel_new@FUSE_2.5");
diff --git a/fuse/fuse_misc.h b/fuse/fuse_misc.h
new file mode 100644
index 000000000..eedf0e0f7
--- /dev/null
+++ b/fuse/fuse_misc.h
@@ -0,0 +1,57 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "config.h"
+#include <pthread.h>
+
+/*
+ Versioned symbols cannot be used in some cases because it
+ - confuse the dynamic linker in uClibc
+ - not supported on MacOSX (in MachO binary format)
+*/
+#if (!defined(__UCLIBC__) && !defined(__APPLE__))
+#define FUSE_SYMVER(x) __asm__(x)
+#else
+#define FUSE_SYMVER(x)
+#endif
+
+#ifndef USE_UCLIBC
+#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL)
+#else
+/* Is this hack still needed? */
+static inline void fuse_mutex_init(pthread_mutex_t *mut)
+{
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
+ pthread_mutex_init(mut, &attr);
+ pthread_mutexattr_destroy(&attr);
+}
+#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_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_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_MTIM_NSEC_SET(stbuf, val) do { } while (0)
+#endif
diff --git a/fuse/fuse_mt.c b/fuse/fuse_mt.c
new file mode 100644
index 000000000..fd5ac23e5
--- /dev/null
+++ b/fuse/fuse_mt.c
@@ -0,0 +1,122 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_lowlevel.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+
+struct procdata {
+ struct fuse *f;
+ struct fuse_chan *prevch;
+ struct fuse_session *prevse;
+ fuse_processor_t proc;
+ void *data;
+};
+
+static void mt_session_proc(void *data, const char *buf, size_t len,
+ struct fuse_chan *ch)
+{
+ struct procdata *pd = (struct procdata *) data;
+ struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
+
+ (void) len;
+ (void) ch;
+ pd->proc(pd->f, cmd, pd->data);
+}
+
+static void mt_session_exit(void *data, int val)
+{
+ struct procdata *pd = (struct procdata *) data;
+ if (val)
+ fuse_session_exit(pd->prevse);
+ else
+ fuse_session_reset(pd->prevse);
+}
+
+static int mt_session_exited(void *data)
+{
+ struct procdata *pd = (struct procdata *) data;
+ return fuse_session_exited(pd->prevse);
+}
+
+static int mt_chan_receive(struct fuse_chan **chp, char *buf, size_t size)
+{
+ struct fuse_cmd *cmd;
+ struct procdata *pd = (struct procdata *) fuse_chan_data(*chp);
+
+ assert(size >= sizeof(cmd));
+
+ cmd = fuse_read_cmd(pd->f);
+ if (cmd == NULL)
+ return 0;
+
+ *(struct fuse_cmd **) buf = cmd;
+
+ return sizeof(cmd);
+}
+
+int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data)
+{
+ int res;
+ struct procdata pd;
+ struct fuse_session *prevse = fuse_get_session(f);
+ struct fuse_session *se;
+ struct fuse_chan *prevch = fuse_session_next_chan(prevse, NULL);
+ struct fuse_chan *ch;
+ struct fuse_session_ops sop = {
+ .exit = mt_session_exit,
+ .exited = mt_session_exited,
+ .process = mt_session_proc,
+ };
+ struct fuse_chan_ops cop = {
+ .receive = mt_chan_receive,
+ };
+
+ pd.f = f;
+ pd.prevch = prevch;
+ pd.prevse = prevse;
+ pd.proc = proc;
+ pd.data = data;
+
+ se = fuse_session_new(&sop, &pd);
+ if (se == NULL)
+ return -1;
+
+ ch = fuse_chan_new(&cop, fuse_chan_fd(prevch),
+ sizeof(struct fuse_cmd *), &pd);
+ if (ch == NULL) {
+ fuse_session_destroy(se);
+ return -1;
+ }
+ fuse_session_add_chan(se, ch);
+ res = fuse_session_loop_mt(se);
+ fuse_session_destroy(se);
+ return res;
+}
+
+int fuse_loop_mt(struct fuse *f)
+{
+ if (f == NULL)
+ return -1;
+
+ int res = fuse_start_cleanup_thread(f);
+ if (res)
+ return -1;
+
+ res = fuse_session_loop_mt(fuse_get_session(f));
+ fuse_stop_cleanup_thread(f);
+ return res;
+}
+
+FUSE_SYMVER(".symver fuse_loop_mt_proc,__fuse_loop_mt@FUSE_UNVERSIONED");
diff --git a/fuse/fuse_opt.c b/fuse/fuse_opt.c
new file mode 100644
index 000000000..a2118cedc
--- /dev/null
+++ b/fuse/fuse_opt.c
@@ -0,0 +1,426 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse_opt.h"
+#include "fuse_misc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+struct fuse_opt_context {
+ void *data;
+ const struct fuse_opt *opt;
+ fuse_opt_proc_t proc;
+ int argctr;
+ int argc;
+ char **argv;
+ struct fuse_args outargs;
+ char *opts;
+ int nonopt;
+};
+
+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)
+{
+ fprintf(stderr, "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);
+}
+
+int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos,
+ const char *arg);
+int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos, const char *arg)
+{
+ return fuse_opt_insert_arg_common(args, pos, arg);
+}
+
+static int next_arg(struct fuse_opt_context *ctx, const char *opt)
+{
+ if (ctx->argctr + 1 >= ctx->argc) {
+ fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
+ return -1;
+ }
+ ctx->argctr++;
+ return 0;
+}
+
+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 *copy = strdup(param);
+ if (!copy)
+ return alloc_failed();
+
+ *(char **) var = copy;
+ } else {
+ if (sscanf(param, format, var) != 1) {
+ fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int process_opt(struct fuse_opt_context *ctx,
+ const struct fuse_opt *opt, unsigned sep,
+ const char *arg, int iso)
+{
+ if (opt->offset == -1U) {
+ if (call_proc(ctx, arg, opt->value, iso) == -1)
+ return -1;
+ } else {
+ void *var = ctx->data + opt->offset;
+ if (sep && opt->templ[sep + 1]) {
+ const char *param = arg + sep;
+ if (opt->templ[sep] == '=')
+ param ++;
+ if (process_opt_param(var, opt->templ + sep + 1,
+ param, arg) == -1)
+ return -1;
+ } else
+ *(int *)var = opt->value;
+ }
+ return 0;
+}
+
+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) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+ }
+ res = process_real_option_group(ctx, copy);
+ free(copy);
+ return res;
+}
+
+static int process_one(struct fuse_opt_context *ctx, const char *arg)
+{
+ if (ctx->nonopt || arg[0] != '-')
+ return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
+ else if (arg[1] == 'o') {
+ if (arg[2])
+ return process_option_group(ctx, arg + 2);
+ else {
+ if (next_arg(ctx, arg) == -1)
+ return -1;
+
+ 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;
+}
+
+/* This symbol version was mistakenly added to the version script */
+FUSE_SYMVER(".symver fuse_opt_insert_arg_compat,fuse_opt_insert_arg@FUSE_2.5");
diff --git a/fuse/fuse_session.c b/fuse/fuse_session.c
new file mode 100644
index 000000000..18c8c4249
--- /dev/null
+++ b/fuse/fuse_session.c
@@ -0,0 +1,243 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_common_compat.h"
+#include "fuse_lowlevel_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+struct fuse_chan {
+ struct fuse_chan_ops op;
+
+ struct fuse_session *se;
+
+ int fd;
+
+ size_t bufsize;
+
+ void *data;
+
+ int compat;
+};
+
+struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
+{
+ struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
+ if (se == NULL) {
+ fprintf(stderr, "fuse: failed to allocate session\n");
+ return NULL;
+ }
+
+ memset(se, 0, sizeof(*se));
+ se->op = *op;
+ se->data = data;
+
+ return se;
+}
+
+void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch)
+{
+ assert(se->ch == NULL);
+ assert(ch->se == NULL);
+ se->ch = ch;
+ ch->se = se;
+}
+
+void fuse_session_remove_chan(struct fuse_chan *ch)
+{
+ struct fuse_session *se = ch->se;
+ if (se) {
+ assert(se->ch == ch);
+ se->ch = NULL;
+ ch->se = NULL;
+ }
+}
+
+struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
+ struct fuse_chan *ch)
+{
+ assert(ch == NULL || ch == se->ch);
+ if (ch == NULL)
+ return se->ch;
+ else
+ return NULL;
+}
+
+void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
+ struct fuse_chan *ch)
+{
+ se->op.process(se->data, buf, len, ch);
+}
+
+void fuse_session_process_buf(struct fuse_session *se,
+ const struct fuse_buf *buf, struct fuse_chan *ch)
+{
+ if (se->process_buf) {
+ se->process_buf(se->data, buf, ch);
+ } else {
+ assert(!(buf->flags & FUSE_BUF_IS_FD));
+ fuse_session_process(se->data, buf->mem, buf->size, ch);
+ }
+}
+
+int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
+ struct fuse_chan **chp)
+{
+ int res;
+
+ if (se->receive_buf) {
+ res = se->receive_buf(se, buf, chp);
+ } else {
+ res = fuse_chan_recv(chp, buf->mem, buf->size);
+ if (res > 0)
+ buf->size = res;
+ }
+
+ return res;
+}
+
+
+void fuse_session_destroy(struct fuse_session *se)
+{
+ if (se->op.destroy)
+ se->op.destroy(se->data);
+ if (se->ch != NULL)
+ fuse_chan_destroy(se->ch);
+ free(se);
+}
+
+void fuse_session_exit(struct fuse_session *se)
+{
+ if (se->op.exit)
+ se->op.exit(se->data, 1);
+ se->exited = 1;
+}
+
+void fuse_session_reset(struct fuse_session *se)
+{
+ if (se->op.exit)
+ se->op.exit(se->data, 0);
+ se->exited = 0;
+}
+
+int fuse_session_exited(struct fuse_session *se)
+{
+ if (se->op.exited)
+ return se->op.exited(se->data);
+ else
+ return se->exited;
+}
+
+void *fuse_session_data(struct fuse_session *se)
+{
+ return se->data;
+}
+
+static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd,
+ size_t bufsize, void *data,
+ int compat)
+{
+ struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
+ if (ch == NULL) {
+ fprintf(stderr, "fuse: failed to allocate channel\n");
+ return NULL;
+ }
+
+ memset(ch, 0, sizeof(*ch));
+ ch->op = *op;
+ ch->fd = fd;
+ ch->bufsize = bufsize;
+ ch->data = data;
+ ch->compat = compat;
+
+ return ch;
+}
+
+struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
+ size_t bufsize, void *data)
+{
+ return fuse_chan_new_common(op, fd, bufsize, data, 0);
+}
+
+struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op,
+ int fd, size_t bufsize, void *data)
+{
+ return fuse_chan_new_common((struct fuse_chan_ops *) op, fd, bufsize,
+ data, 24);
+}
+
+int fuse_chan_fd(struct fuse_chan *ch)
+{
+ return ch->fd;
+}
+
+int fuse_chan_clearfd(struct fuse_chan *ch)
+{
+ if (ch == NULL)
+ return -1;
+
+ int fd = ch->fd;
+ ch->fd = -1;
+ return fd;
+}
+
+size_t fuse_chan_bufsize(struct fuse_chan *ch)
+{
+ return ch->bufsize;
+}
+
+void *fuse_chan_data(struct fuse_chan *ch)
+{
+ return ch->data;
+}
+
+struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
+{
+ return ch->se;
+}
+
+int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)
+{
+ struct fuse_chan *ch = *chp;
+ if (ch->compat)
+ return ((struct fuse_chan_ops_compat24 *) &ch->op)
+ ->receive(ch, buf, size);
+ else
+ return ch->op.receive(chp, buf, size);
+}
+
+int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
+{
+ int res;
+
+ res = fuse_chan_recv(&ch, buf, size);
+ return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0;
+}
+
+int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
+{
+ return ch->op.send(ch, iov, count);
+}
+
+void fuse_chan_destroy(struct fuse_chan *ch)
+{
+ fuse_session_remove_chan(ch);
+ if (ch->op.destroy)
+ ch->op.destroy(ch);
+ free(ch);
+}
+
+#ifndef __FreeBSD__
+FUSE_SYMVER(".symver fuse_chan_new_compat24,fuse_chan_new@FUSE_2.4");
+#endif
diff --git a/fuse/fuse_signals.c b/fuse/fuse_signals.c
new file mode 100644
index 000000000..353cb24bd
--- /dev/null
+++ b/fuse/fuse_signals.c
@@ -0,0 +1,72 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#include "fuse_lowlevel.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+static struct fuse_session *fuse_instance;
+
+static void exit_handler(int sig)
+{
+ (void) sig;
+ if (fuse_instance)
+ fuse_session_exit(fuse_instance);
+}
+
+static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
+{
+ struct sigaction sa;
+ struct sigaction old_sa;
+
+ 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;
+}
+
+int fuse_set_signal_handlers(struct fuse_session *se)
+{
+ if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 ||
+ set_one_signal_handler(SIGINT, exit_handler, 0) == -1 ||
+ set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 ||
+ set_one_signal_handler(SIGPIPE, SIG_IGN, 0) == -1)
+ return -1;
+
+ fuse_instance = se;
+ return 0;
+}
+
+void fuse_remove_signal_handlers(struct fuse_session *se)
+{
+ if (fuse_instance != se)
+ fprintf(stderr,
+ "fuse: fuse_remove_signal_handlers: unknown session\n");
+ else
+ fuse_instance = NULL;
+
+ set_one_signal_handler(SIGHUP, exit_handler, 1);
+ set_one_signal_handler(SIGINT, exit_handler, 1);
+ set_one_signal_handler(SIGTERM, exit_handler, 1);
+ set_one_signal_handler(SIGPIPE, SIG_IGN, 1);
+}
+
diff --git a/fuse/fuse_versionscript b/fuse/fuse_versionscript
new file mode 100644
index 000000000..de16ab2e6
--- /dev/null
+++ b/fuse/fuse_versionscript
@@ -0,0 +1,210 @@
+FUSE_UNVERSIONED {
+};
+
+FUSE_2.2 {
+ global:
+ fuse_destroy;
+ fuse_exit;
+ fuse_exited;
+ fuse_invalidate;
+ fuse_is_lib_option;
+ fuse_loop;
+ fuse_loop_mt;
+ fuse_loop_mt_proc;
+ fuse_main;
+ fuse_main_compat1;
+ fuse_main_compat2;
+ fuse_mount_compat1;
+ fuse_new_compat1;
+ fuse_new_compat2;
+ fuse_process_cmd;
+ fuse_read_cmd;
+ fuse_set_getcontext_func;
+ fuse_setup_compat2;
+};
+
+FUSE_2.4 {
+ global:
+ fuse_add_dirent;
+ fuse_chan_bufsize;
+ fuse_chan_data;
+ fuse_chan_destroy;
+ fuse_chan_fd;
+ fuse_chan_receive;
+ fuse_chan_send;
+ fuse_chan_session;
+ fuse_dirent_size;
+ fuse_kern_chan_new;
+ fuse_lowlevel_is_lib_option;
+ fuse_reply_attr;
+ fuse_reply_buf;
+ fuse_reply_entry;
+ fuse_reply_err;
+ fuse_reply_none;
+ fuse_reply_readlink;
+ fuse_reply_write;
+ fuse_reply_xattr;
+ fuse_req_userdata;
+ fuse_session_add_chan;
+ fuse_session_destroy;
+ fuse_session_exit;
+ fuse_session_exited;
+ fuse_session_loop;
+ fuse_session_loop_mt;
+ fuse_session_new;
+ fuse_session_next_chan;
+ fuse_session_process;
+ fuse_session_reset;
+} FUSE_2.2;
+
+FUSE_2.5 {
+ global:
+ fuse_lowlevel_new_compat;
+ fuse_main_real_compat22;
+ fuse_mount_compat22;
+ fuse_new_compat22;
+ fuse_opt_parse;
+ fuse_opt_add_opt;
+ fuse_opt_add_arg;
+ fuse_opt_free_args;
+ fuse_opt_match;
+ fuse_parse_cmdline;
+ fuse_remove_signal_handlers;
+ fuse_reply_create;
+ fuse_reply_open;
+ fuse_reply_open_compat;
+ fuse_reply_statfs;
+ fuse_reply_statfs_compat;
+ fuse_setup_compat22;
+ fuse_set_signal_handlers;
+} FUSE_2.4;
+
+FUSE_2.6 {
+ global:
+ fuse_add_direntry;
+ fuse_chan_new;
+ fuse_chan_new_compat24;
+ fuse_chan_recv;
+ fuse_daemonize;
+ fuse_get_session;
+ fuse_interrupted;
+ fuse_lowlevel_new;
+ fuse_lowlevel_new_compat25;
+ fuse_main_real;
+ fuse_main_real_compat25;
+ fuse_mount;
+ fuse_mount_compat25;
+ fuse_new;
+ fuse_new_compat25;
+ fuse_opt_insert_arg;
+ fuse_reply_lock;
+ fuse_req_interrupt_func;
+ fuse_req_interrupted;
+ fuse_session_remove_chan;
+ fuse_setup;
+ fuse_setup_compat25;
+ fuse_teardown;
+ fuse_teardown_compat22;
+ fuse_unmount;
+ fuse_unmount_compat22;
+} FUSE_2.5;
+
+FUSE_2.7 {
+ global:
+ fuse_fs_access;
+ fuse_fs_bmap;
+ fuse_fs_chmod;
+ fuse_fs_chown;
+ fuse_fs_create;
+ fuse_fs_destroy;
+ fuse_fs_fgetattr;
+ fuse_fs_flush;
+ fuse_fs_fsync;
+ fuse_fs_fsyncdir;
+ fuse_fs_ftruncate;
+ fuse_fs_getattr;
+ fuse_fs_getxattr;
+ fuse_fs_init;
+ fuse_fs_link;
+ fuse_fs_listxattr;
+ fuse_fs_lock;
+ fuse_fs_mkdir;
+ fuse_fs_mknod;
+ fuse_fs_new;
+ fuse_fs_open;
+ fuse_fs_opendir;
+ fuse_fs_read;
+ fuse_fs_readdir;
+ fuse_fs_readlink;
+ fuse_fs_release;
+ fuse_fs_releasedir;
+ fuse_fs_removexattr;
+ fuse_fs_rename;
+ fuse_fs_rmdir;
+ fuse_fs_setxattr;
+ fuse_fs_statfs;
+ fuse_fs_symlink;
+ fuse_fs_truncate;
+ fuse_fs_unlink;
+ fuse_fs_utimens;
+ fuse_fs_write;
+ fuse_register_module;
+ fuse_reply_iov;
+ fuse_version;
+} FUSE_2.6;
+
+FUSE_2.7.5 {
+ global:
+ fuse_reply_bmap;
+} FUSE_2.7;
+
+FUSE_2.8 {
+ global:
+ cuse_lowlevel_new;
+ cuse_lowlevel_main;
+ cuse_lowlevel_setup;
+ cuse_lowlevel_teardown;
+ fuse_fs_ioctl;
+ fuse_fs_poll;
+ fuse_get_context;
+ fuse_getgroups;
+ fuse_lowlevel_notify_inval_entry;
+ fuse_lowlevel_notify_inval_inode;
+ fuse_lowlevel_notify_poll;
+ fuse_notify_poll;
+ fuse_opt_add_opt_escaped;
+ fuse_pollhandle_destroy;
+ fuse_reply_ioctl;
+ fuse_reply_ioctl_iov;
+ fuse_reply_ioctl_retry;
+ fuse_reply_poll;
+ fuse_req_ctx;
+ fuse_req_getgroups;
+ fuse_session_data;
+} FUSE_2.7.5;
+
+FUSE_2.9 {
+ global:
+ fuse_buf_copy;
+ fuse_buf_size;
+ fuse_fs_read_buf;
+ fuse_fs_write_buf;
+ fuse_lowlevel_notify_retrieve;
+ fuse_lowlevel_notify_store;
+ fuse_reply_data;
+ fuse_session_process_buf;
+ fuse_session_receive_buf;
+ fuse_start_cleanup_thread;
+ fuse_stop_cleanup_thread;
+ fuse_clean_cache;
+ fuse_lowlevel_notify_delete;
+ fuse_fs_flock;
+} FUSE_2.8;
+
+FUSE_2.9.1 {
+ global:
+ fuse_fs_fallocate;
+
+ local:
+ *;
+} FUSE_2.9;
diff --git a/fuse/helper.c b/fuse/helper.c
new file mode 100644
index 000000000..c5349bfc0
--- /dev/null
+++ b/fuse/helper.c
@@ -0,0 +1,480 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_opt.h"
+#include "fuse_lowlevel.h"
+#include "fuse_common_compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/param.h>
+
+enum {
+ KEY_HELP,
+ KEY_HELP_NOHEADER,
+ KEY_VERSION,
+};
+
+struct helper_opts {
+ int singlethread;
+ int foreground;
+ int nodefault_subtype;
+ char *mountpoint;
+};
+
+#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 }
+
+static const struct fuse_opt fuse_helper_opts[] = {
+ FUSE_HELPER_OPT("-d", foreground),
+ FUSE_HELPER_OPT("debug", foreground),
+ FUSE_HELPER_OPT("-f", foreground),
+ FUSE_HELPER_OPT("-s", singlethread),
+ FUSE_HELPER_OPT("fsname=", nodefault_subtype),
+ FUSE_HELPER_OPT("subtype=", nodefault_subtype),
+
+ FUSE_OPT_KEY("-h", KEY_HELP),
+ FUSE_OPT_KEY("--help", KEY_HELP),
+ FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
+ FUSE_OPT_KEY("-V", KEY_VERSION),
+ FUSE_OPT_KEY("--version", KEY_VERSION),
+ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_END
+};
+
+static void usage(const char *progname)
+{
+ fprintf(stderr,
+ "usage: %s mountpoint [options]\n\n", progname);
+ fprintf(stderr,
+ "general options:\n"
+ " -o opt,[opt...] mount options\n"
+ " -h --help print help\n"
+ " -V --version print version\n"
+ "\n");
+}
+
+static void helper_help(void)
+{
+ fprintf(stderr,
+ "FUSE options:\n"
+ " -d -o debug enable debug output (implies -f)\n"
+ " -f foreground operation\n"
+ " -s disable multi-threaded operation\n"
+ "\n"
+ );
+}
+
+static void helper_version(void)
+{
+ fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
+}
+
+static int fuse_helper_opt_proc(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ struct helper_opts *hopts = data;
+
+ switch (key) {
+ case KEY_HELP:
+ usage(outargs->argv[0]);
+ /* fall through */
+
+ case KEY_HELP_NOHEADER:
+ helper_help();
+ return fuse_opt_add_arg(outargs, "-h");
+
+ case KEY_VERSION:
+ helper_version();
+ return 1;
+
+ case FUSE_OPT_KEY_NONOPT:
+ if (!hopts->mountpoint) {
+ char mountpoint[PATH_MAX];
+ if (realpath(arg, mountpoint) == NULL) {
+ fprintf(stderr,
+ "fuse: bad mount point `%s': %s\n",
+ arg, strerror(errno));
+ return -1;
+ }
+ return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
+ } else {
+ fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
+ return -1;
+ }
+
+ default:
+ return 1;
+ }
+}
+
+static int add_default_subtype(const char *progname, struct fuse_args *args)
+{
+ int res;
+ char *subtype_opt;
+ const char *basename = strrchr(progname, '/');
+ if (basename == NULL)
+ basename = progname;
+ else if (basename[1] != '\0')
+ basename++;
+
+ subtype_opt = (char *) malloc(strlen(basename) + 64);
+ if (subtype_opt == NULL) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ return -1;
+ }
+ sprintf(subtype_opt, "-osubtype=%s", basename);
+ res = fuse_opt_add_arg(args, subtype_opt);
+ free(subtype_opt);
+ return res;
+}
+
+int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint,
+ int *multithreaded, int *foreground)
+{
+ int res;
+ struct helper_opts hopts;
+
+ memset(&hopts, 0, sizeof(hopts));
+ res = fuse_opt_parse(args, &hopts, fuse_helper_opts,
+ fuse_helper_opt_proc);
+ if (res == -1)
+ return -1;
+
+ if (!hopts.nodefault_subtype) {
+ res = add_default_subtype(args->argv[0], args);
+ if (res == -1)
+ goto err;
+ }
+ if (mountpoint)
+ *mountpoint = hopts.mountpoint;
+ else
+ free(hopts.mountpoint);
+
+ if (multithreaded)
+ *multithreaded = !hopts.singlethread;
+ if (foreground)
+ *foreground = hopts.foreground;
+ return 0;
+
+err:
+ free(hopts.mountpoint);
+ return -1;
+}
+
+int fuse_daemonize(int foreground)
+{
+ if (!foreground) {
+ int nullfd;
+
+ /*
+ * 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:
+ _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);
+ }
+ }
+ return 0;
+}
+
+static struct fuse_chan *fuse_mount_common(const char *mountpoint,
+ struct fuse_args *args)
+{
+ struct fuse_chan *ch;
+ int fd;
+
+ /*
+ * 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);
+
+ fd = fuse_mount_compat25(mountpoint, args);
+ if (fd == -1)
+ return NULL;
+
+ ch = fuse_kern_chan_new(fd);
+ if (!ch)
+ fuse_kern_unmount(mountpoint, fd);
+
+ return ch;
+}
+
+struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args)
+{
+ return fuse_mount_common(mountpoint, args);
+}
+
+static void fuse_unmount_common(const char *mountpoint, struct fuse_chan *ch)
+{
+ if (mountpoint) {
+ int fd = ch ? fuse_chan_clearfd(ch) : -1;
+ fuse_kern_unmount(mountpoint, fd);
+ if (ch)
+ fuse_chan_destroy(ch);
+ }
+}
+
+void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
+{
+ fuse_unmount_common(mountpoint, ch);
+}
+
+struct fuse *fuse_setup_common(int argc, char *argv[],
+ const struct fuse_operations *op,
+ size_t op_size,
+ char **mountpoint,
+ int *multithreaded,
+ int *fd,
+ void *user_data,
+ int compat)
+{
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+ struct fuse_chan *ch;
+ struct fuse *fuse;
+ int foreground;
+ int res;
+
+ res = fuse_parse_cmdline(&args, mountpoint, multithreaded, &foreground);
+ if (res == -1)
+ return NULL;
+
+ ch = fuse_mount_common(*mountpoint, &args);
+ if (!ch) {
+ fuse_opt_free_args(&args);
+ goto err_free;
+ }
+
+ fuse = fuse_new_common(ch, &args, op, op_size, user_data, compat);
+ fuse_opt_free_args(&args);
+ if (fuse == NULL)
+ goto err_unmount;
+
+ res = fuse_daemonize(foreground);
+ if (res == -1)
+ goto err_unmount;
+
+ res = fuse_set_signal_handlers(fuse_get_session(fuse));
+ if (res == -1)
+ goto err_unmount;
+
+ if (fd)
+ *fd = fuse_chan_fd(ch);
+
+ return fuse;
+
+err_unmount:
+ fuse_unmount_common(*mountpoint, ch);
+ if (fuse)
+ fuse_destroy(fuse);
+err_free:
+ free(*mountpoint);
+ return NULL;
+}
+
+struct fuse *fuse_setup(int argc, char *argv[],
+ const struct fuse_operations *op, size_t op_size,
+ char **mountpoint, int *multithreaded, void *user_data)
+{
+ return fuse_setup_common(argc, argv, op, op_size, mountpoint,
+ multithreaded, NULL, user_data, 0);
+}
+
+static void fuse_teardown_common(struct fuse *fuse, char *mountpoint)
+{
+ struct fuse_session *se = fuse_get_session(fuse);
+ struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+ fuse_remove_signal_handlers(se);
+ fuse_unmount_common(mountpoint, ch);
+ fuse_destroy(fuse);
+ free(mountpoint);
+}
+
+void fuse_teardown(struct fuse *fuse, char *mountpoint)
+{
+ fuse_teardown_common(fuse, mountpoint);
+}
+
+static int fuse_main_common(int argc, char *argv[],
+ const struct fuse_operations *op, size_t op_size,
+ void *user_data, int compat)
+{
+ struct fuse *fuse;
+ char *mountpoint;
+ int multithreaded;
+ int res;
+
+ fuse = fuse_setup_common(argc, argv, op, op_size, &mountpoint,
+ &multithreaded, NULL, user_data, compat);
+ if (fuse == NULL)
+ return 1;
+
+ if (multithreaded)
+ res = fuse_loop_mt(fuse);
+ else
+ res = fuse_loop(fuse);
+
+ fuse_teardown_common(fuse, mountpoint);
+ if (res == -1)
+ return 1;
+
+ return 0;
+}
+
+int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
+ size_t op_size, void *user_data)
+{
+ return fuse_main_common(argc, argv, op, op_size, user_data, 0);
+}
+
+#undef fuse_main
+int fuse_main(void);
+int fuse_main(void)
+{
+ fprintf(stderr, "fuse_main(): This function does not exist\n");
+ return -1;
+}
+
+int fuse_version(void)
+{
+ return FUSE_VERSION;
+}
+
+#include "fuse_compat.h"
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+struct fuse *fuse_setup_compat22(int argc, char *argv[],
+ const struct fuse_operations_compat22 *op,
+ size_t op_size, char **mountpoint,
+ int *multithreaded, int *fd)
+{
+ return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
+ op_size, mountpoint, multithreaded, fd, NULL,
+ 22);
+}
+
+struct fuse *fuse_setup_compat2(int argc, char *argv[],
+ const struct fuse_operations_compat2 *op,
+ char **mountpoint, int *multithreaded,
+ int *fd)
+{
+ return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
+ sizeof(struct fuse_operations_compat2),
+ mountpoint, multithreaded, fd, NULL, 21);
+}
+
+int fuse_main_real_compat22(int argc, char *argv[],
+ const struct fuse_operations_compat22 *op,
+ size_t op_size)
+{
+ return fuse_main_common(argc, argv, (struct fuse_operations *) op,
+ op_size, NULL, 22);
+}
+
+void fuse_main_compat1(int argc, char *argv[],
+ const struct fuse_operations_compat1 *op)
+{
+ fuse_main_common(argc, argv, (struct fuse_operations *) op,
+ sizeof(struct fuse_operations_compat1), NULL, 11);
+}
+
+int fuse_main_compat2(int argc, char *argv[],
+ const struct fuse_operations_compat2 *op)
+{
+ return fuse_main_common(argc, argv, (struct fuse_operations *) op,
+ sizeof(struct fuse_operations_compat2), NULL,
+ 21);
+}
+
+int fuse_mount_compat1(const char *mountpoint, const char *args[])
+{
+ /* just ignore mount args for now */
+ (void) args;
+ return fuse_mount_compat22(mountpoint, NULL);
+}
+
+FUSE_SYMVER(".symver fuse_setup_compat2,__fuse_setup@FUSE_UNVERSIONED");
+FUSE_SYMVER(".symver fuse_setup_compat22,fuse_setup@FUSE_2.2");
+FUSE_SYMVER(".symver fuse_teardown,__fuse_teardown@FUSE_UNVERSIONED");
+FUSE_SYMVER(".symver fuse_main_compat2,fuse_main@FUSE_UNVERSIONED");
+FUSE_SYMVER(".symver fuse_main_real_compat22,fuse_main_real@FUSE_2.2");
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+
+struct fuse *fuse_setup_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size, char **mountpoint,
+ int *multithreaded, int *fd)
+{
+ return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
+ op_size, mountpoint, multithreaded, fd, NULL,
+ 25);
+}
+
+int fuse_main_real_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size)
+{
+ return fuse_main_common(argc, argv, (struct fuse_operations *) op,
+ op_size, NULL, 25);
+}
+
+void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint)
+{
+ (void) fd;
+ fuse_teardown_common(fuse, mountpoint);
+}
+
+int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args)
+{
+ return fuse_kern_mount(mountpoint, args);
+}
+
+FUSE_SYMVER(".symver fuse_setup_compat25,fuse_setup@FUSE_2.5");
+FUSE_SYMVER(".symver fuse_teardown_compat22,fuse_teardown@FUSE_2.2");
+FUSE_SYMVER(".symver fuse_main_real_compat25,fuse_main_real@FUSE_2.5");
+FUSE_SYMVER(".symver fuse_mount_compat25,fuse_mount@FUSE_2.5");
diff --git a/fuse/include/Makefile.am b/fuse/include/Makefile.am
new file mode 100644
index 000000000..663e164cc
--- /dev/null
+++ b/fuse/include/Makefile.am
@@ -0,0 +1,17 @@
+## Process this file with automake to produce Makefile.in
+
+fuseincludedir=$(includedir)/fuse
+
+fuseinclude_HEADERS = \
+ fuse.h \
+ fuse_compat.h \
+ fuse_common.h \
+ fuse_common_compat.h \
+ fuse_lowlevel.h \
+ fuse_lowlevel_compat.h \
+ fuse_opt.h \
+ cuse_lowlevel.h
+
+include_HEADERS = old/fuse.h ulockmgr.h
+
+noinst_HEADERS = fuse_kernel.h
diff --git a/fuse/include/cuse_lowlevel.h b/fuse/include/cuse_lowlevel.h
new file mode 100644
index 000000000..fb445a773
--- /dev/null
+++ b/fuse/include/cuse_lowlevel.h
@@ -0,0 +1,87 @@
+/*
+ CUSE: Character device in Userspace
+ Copyright (C) 2008-2009 SUSE Linux Products GmbH
+ Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+
+ Read example/cusexmp.c for usages.
+*/
+
+#ifndef _CUSE_LOWLEVEL_H_
+#define _CUSE_LOWLEVEL_H_
+
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION 29
+#endif
+
+#include "fuse_lowlevel.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */
+
+struct fuse_session;
+
+struct cuse_info {
+ unsigned dev_major;
+ unsigned dev_minor;
+ unsigned dev_info_argc;
+ const char **dev_info_argv;
+ unsigned flags;
+};
+
+/*
+ * Most ops behave almost identically to the matching fuse_lowlevel
+ * ops except that they don't take @ino.
+ *
+ * init_done : called after initialization is complete
+ * read/write : always direct IO, simultaneous operations allowed
+ * ioctl : might be in unrestricted mode depending on ci->flags
+ */
+struct cuse_lowlevel_ops {
+ void (*init) (void *userdata, struct fuse_conn_info *conn);
+ void (*init_done) (void *userdata);
+ void (*destroy) (void *userdata);
+ void (*open) (fuse_req_t req, struct fuse_file_info *fi);
+ void (*read) (fuse_req_t req, size_t size, loff_t off,
+ struct fuse_file_info *fi);
+ void (*write) (fuse_req_t req, const char *buf, size_t size, loff_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_ */
diff --git a/fuse/include/fuse.h b/fuse/include/fuse.h
new file mode 100644
index 000000000..b82325ec2
--- /dev/null
+++ b/fuse/include/fuse.h
@@ -0,0 +1,1064 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#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. To use the newest API define it to 26 (recommended for any
+ * new application), to use the old API define it to 21 (default) 22
+ * or 25, to use the even older 1.X API define it to 11.
+ */
+
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION 21
+#endif
+
+#include "fuse_common.h"
+
+#include <fcntl.h>
+#include <time.h>
+#include <utime.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if defined(__ANDROID__)
+#include <pthread.h>
+#endif
+#include <sys/statvfs.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ----------------------------------------------------------- *
+ * Basic FUSE API *
+ * ----------------------------------------------------------- */
+
+/** Handle for a FUSE filesystem */
+struct fuse;
+
+/** Structure containing a raw command */
+struct fuse_cmd;
+
+/** Function to add an entry in a readdir() operation
+ *
+ * @param buf the buffer passed to the readdir() operation
+ * @param name the file name of the directory entry
+ * @param stat file attributes, can be NULL
+ * @param off offset of the next entry or zero
+ * @return 1 if buffer is full, zero otherwise
+ */
+typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
+ const struct stat *stbuf, loff_t off);
+
+/* Used by deprecated getdir() method */
+typedef struct fuse_dirhandle *fuse_dirh_t;
+typedef int (*fuse_dirfil_t) (fuse_dirh_t h, const char *name, int type,
+ ino_t ino);
+
+/**
+ * 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, ftruncate, fgetattr, lock,
+ * init and destroy are special purpose methods, without which a full
+ * featured filesystem can still be implemented.
+ *
+ * Almost all operations take a path which can be of any length.
+ *
+ * Changed in fuse 2.8.0 (regardless of API version)
+ * Previously, paths were limited to a length of PATH_MAX.
+ *
+ * See http://fuse.sourceforge.net/wiki/ for more information. There
+ * is also a snapshot of the relevant wiki pages in the doc/ folder.
+ */
+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.
+ */
+ int (*getattr) (const char *, struct stat *);
+
+ /** 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);
+
+ /* Deprecated, use readdir() instead */
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_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 */
+ int (*rename) (const char *, const char *);
+
+ /** Create a hard link to a file */
+ int (*link) (const char *, const char *);
+
+ /** Change the permission bits of a file */
+ int (*chmod) (const char *, mode_t);
+
+ /** Change the owner and group of a file */
+ int (*chown) (const char *, uid_t, gid_t);
+
+ /** Change the size of a file */
+ int (*truncate) (const char *, loff_t);
+
+ /** Change the access and/or modification times of a file
+ *
+ * Deprecated, use utimens() instead.
+ */
+ int (*utime) (const char *, struct utimbuf *);
+
+ /** File open operation
+ *
+ * No creation (O_CREAT, O_EXCL) and by default also no
+ * truncation (O_TRUNC) flags will be passed to open(). If an
+ * application specifies O_TRUNC, fuse first calls truncate()
+ * and then open(). Only if 'atomic_o_trunc' has been
+ * specified and kernel version is 2.6.24 or later, O_TRUNC is
+ * passed on to open.
+ *
+ * Unless the 'default_permissions' mount option is given,
+ * open should check if the operation is permitted for the
+ * given flags. Optionally open may also return an arbitrary
+ * filehandle in the fuse_file_info structure, which will be
+ * passed to all file operations.
+ *
+ * Changed in version 2.2
+ */
+ 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.
+ *
+ * Changed in version 2.2
+ */
+ int (*read) (const char *, char *, size_t, loff_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).
+ *
+ * Changed in version 2.2
+ */
+ int (*write) (const char *, const char *, size_t, loff_t,
+ struct fuse_file_info *);
+
+ /** Get file system statistics
+ *
+ * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
+ *
+ * Replaced 'struct statfs' parameter with 'struct statvfs' in
+ * version 2.5
+ */
+ 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. So if a
+ * filesystem wants to return write errors in close() and the file
+ * has cached dirty data, this is a good place to write back data
+ * and return any errors. Since many applications ignore close()
+ * errors this is not always useful.
+ *
+ * NOTE: The flush() method may be called more than once for each
+ * open(). This happens if more than one file descriptor refers
+ * to an opened file 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 always be called
+ * after some writes, or that if will be called at all.
+ *
+ * Changed in version 2.2
+ */
+ 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 descriptor. 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.
+ *
+ * Changed in version 2.2
+ */
+ 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.
+ *
+ * Changed in version 2.2
+ */
+ 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, closedir and fsyncdir.
+ *
+ * Introduced in version 2.3
+ */
+ int (*opendir) (const char *, struct fuse_file_info *);
+
+ /** Read directory
+ *
+ * This supersedes the old getdir() interface. New applications
+ * should use this.
+ *
+ * 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. This
+ * works just like the old getdir() method.
+ *
+ * 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'.
+ *
+ * Introduced in version 2.3
+ */
+ int (*readdir) (const char *, void *, fuse_fill_dir_t, loff_t,
+ struct fuse_file_info *);
+
+ /** Release directory
+ *
+ * Introduced in version 2.3
+ */
+ int (*releasedir) (const char *, struct fuse_file_info *);
+
+ /** Synchronize directory contents
+ *
+ * If the datasync parameter is non-zero, then only the user data
+ * should be flushed, not the meta data
+ *
+ * Introduced in version 2.3
+ */
+ int (*fsyncdir) (const char *, int, struct fuse_file_info *);
+
+ /**
+ * Initialize filesystem
+ *
+ * The return value will passed in the private_data field of
+ * fuse_context to all file operations and as a parameter to the
+ * destroy() method.
+ *
+ * Introduced in version 2.3
+ * Changed in version 2.6
+ */
+ void *(*init) (struct fuse_conn_info *conn);
+
+ /**
+ * Clean up filesystem
+ *
+ * Called on filesystem exit.
+ *
+ * Introduced in version 2.3
+ */
+ void (*destroy) (void *);
+
+ /**
+ * 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
+ *
+ * Introduced in version 2.5
+ */
+ 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.
+ *
+ * Introduced in version 2.5
+ */
+ int (*create) (const char *, mode_t, struct fuse_file_info *);
+
+ /**
+ * Change the size of an open file
+ *
+ * This method is called instead of the truncate() method if the
+ * truncation was invoked from an ftruncate() system call.
+ *
+ * If this method is not implemented or under Linux kernel
+ * versions earlier than 2.6.15, the truncate() method will be
+ * called instead.
+ *
+ * Introduced in version 2.5
+ */
+ int (*ftruncate) (const char *, loff_t, struct fuse_file_info *);
+
+ /**
+ * Get attributes from an open file
+ *
+ * This method is called instead of the getattr() method if the
+ * file information is available.
+ *
+ * Currently this is only called after the create() method if that
+ * is implemented (see above). Later it may be called for
+ * invocations of fstat() too.
+ *
+ * Introduced in version 2.5
+ */
+ int (*fgetattr) (const char *, struct stat *, 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.
+ *
+ * Introduced in version 2.6
+ */
+ 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.
+ *
+ * See the utimensat(2) man page for details.
+ *
+ * Introduced in version 2.6
+ */
+ int (*utimens) (const char *, const struct timespec tv[2]);
+
+ /**
+ * 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
+ *
+ * Introduced in version 2.6
+ */
+ int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
+
+ /**
+ * Flag indicating that the filesystem can accept a NULL path
+ * as the first argument for the following operations:
+ *
+ * read, write, flush, release, fsync, readdir, releasedir,
+ * fsyncdir, ftruncate, fgetattr, lock, ioctl and poll
+ *
+ * If this flag is set these operations continue to work on
+ * unlinked files even if "-ohard_remove" option was specified.
+ */
+ unsigned int flag_nullpath_ok:1;
+
+ /**
+ * Flag indicating that the path need not be calculated for
+ * the following operations:
+ *
+ * read, write, flush, release, fsync, readdir, releasedir,
+ * fsyncdir, ftruncate, fgetattr, lock, ioctl and poll
+ *
+ * Closely related to flag_nullpath_ok, but if this flag is
+ * set then the path will not be calculaged even if the file
+ * wasn't unlinked. However the path can still be non-NULL if
+ * it needs to be calculated for some other reason.
+ */
+ unsigned int flag_nopath:1;
+
+ /**
+ * Flag indicating that the filesystem accepts special
+ * UTIME_NOW and UTIME_OMIT values in its utimens operation.
+ */
+ unsigned int flag_utime_omit_ok:1;
+
+ /**
+ * Reserved flags, don't set
+ */
+ unsigned int flag_reserved:29;
+
+ /**
+ * 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.
+ *
+ * Introduced in version 2.8
+ */
+ int (*ioctl) (const char *, int cmd, void *arg,
+ struct fuse_file_info *, unsigned int flags, void *data);
+
+ /**
+ * 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.
+ *
+ * Introduced in version 2.8
+ */
+ 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.
+ *
+ * Introduced in version 2.9
+ */
+ int (*write_buf) (const char *, struct fuse_bufvec *buf, loff_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.
+ *
+ * Introduced in version 2.9
+ */
+ int (*read_buf) (const char *, struct fuse_bufvec **bufp,
+ size_t size, loff_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.
+ *
+ * Introduced in version 2.9
+ */
+ 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.
+ *
+ * Introduced in version 2.9.1
+ */
+ int (*fallocate) (const char *, int, loff_t, loff_t,
+ 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;
+
+ /** Thread ID of the calling process */
+ pid_t pid;
+
+ /** Private filesystem data */
+ void *private_data;
+
+ /** Umask of the calling process (introduced in version 2.8) */
+ mode_t umask;
+};
+
+/**
+ * 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 (-d -s and -h)
+ * - passes relevant mount options to the fuse_mount()
+ * - 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
+ *
+ * Note: this is currently implemented as a macro.
+ *
+ * @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 user_data user data supplied in the context during the init() method
+ * @return 0 on success, nonzero on failure
+ */
+/*
+ int fuse_main(int argc, char *argv[], const struct fuse_operations *op,
+ void *user_data);
+*/
+#define fuse_main(argc, argv, op, user_data) \
+ fuse_main_real(argc, argv, op, sizeof(*(op)), user_data)
+
+/* ----------------------------------------------------------- *
+ * More detailed API *
+ * ----------------------------------------------------------- */
+
+/**
+ * Create a new FUSE filesystem.
+ *
+ * @param ch the communication channel
+ * @param args argument vector
+ * @param op the filesystem operations
+ * @param op_size the size of the fuse_operations structure
+ * @param user_data user data supplied in the context during the init() method
+ * @return the created FUSE handle
+ */
+struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
+ const struct fuse_operations *op, size_t op_size,
+ void *user_data);
+
+/**
+ * Destroy the FUSE handle.
+ *
+ * The communication channel attached to the handle is also destroyed.
+ *
+ * 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.
+ *
+ * @param f the FUSE handle
+ * @return 0 if no error occurred, -1 otherwise
+ */
+int fuse_loop(struct fuse *f);
+
+/**
+ * Exit from event loop
+ *
+ * @param f the FUSE handle
+ */
+void fuse_exit(struct fuse *f);
+
+/**
+ * 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.
+ *
+ * Calling this function requires the pthreads library to be linked to
+ * the application.
+ *
+ * @param f the FUSE handle
+ * @return 0 if no error occurred, -1 otherwise
+ */
+int fuse_loop_mt(struct fuse *f);
+
+/**
+ * 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);
+
+/**
+ * Obsolete, doesn't do anything
+ *
+ * @return -EINVAL
+ */
+int fuse_invalidate(struct fuse *f, const char *path);
+
+/* Deprecated, don't use */
+int fuse_is_lib_option(const char *opt);
+
+/**
+ * The real main function
+ *
+ * Do not call this directly, use fuse_main()
+ */
+int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
+ size_t op_size, void *user_data);
+
+/**
+ * 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);
+int fuse_fs_fgetattr(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);
+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,
+ loff_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, loff_t off,
+ struct fuse_file_info *fi);
+int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
+ size_t size, loff_t off, struct fuse_file_info *fi);
+int fuse_fs_write_buf(struct fuse_fs *fs, const char *path,
+ struct fuse_bufvec *buf, loff_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, loff_t off,
+ struct fuse_file_info *fi);
+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);
+int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid);
+int fuse_fs_truncate(struct fuse_fs *fs, const char *path, loff_t size);
+int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, loff_t size,
+ struct fuse_file_info *fi);
+int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
+ const struct timespec tv[2]);
+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);
+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);
+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,
+ loff_t offset, loff_t length, struct fuse_file_info *fi);
+void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn);
+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 user_data user data supplied in the context during the init() method
+ * @return a new filesystem object
+ */
+struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
+ void *user_data);
+
+/**
+ * Filesystem module
+ *
+ * Filesystem modules are registered with the FUSE_REGISTER_MODULE()
+ * macro.
+ *
+ * If the "-omodules=modname:..." option is present, filesystem
+ * objects are created and pushed onto the stack with the 'factory'
+ * function.
+ */
+struct fuse_module {
+ /**
+ * Name of filesystem
+ */
+ const char *name;
+
+ /**
+ * 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
+ */
+ struct fuse_fs *(*factory)(struct fuse_args *args,
+ struct fuse_fs *fs[]);
+
+ struct fuse_module *next;
+ struct fusemod_so *so;
+ int ctr;
+};
+
+/**
+ * Register a filesystem module
+ *
+ * This function is used by FUSE_REGISTER_MODULE and there's usually
+ * no need to call it directly
+ */
+void fuse_register_module(struct fuse_module *mod);
+
+/**
+ * Register filesystem module
+ *
+ * For the parameters, see description of the fields in 'struct
+ * fuse_module'
+ */
+#define FUSE_REGISTER_MODULE(name_, factory_) \
+ static __attribute__((constructor)) void name_ ## _register(void) \
+ { \
+ static struct fuse_module mod = \
+ { #name_, factory_, NULL, NULL, 0 }; \
+ fuse_register_module(&mod); \
+ }
+
+
+/* ----------------------------------------------------------- *
+ * Advanced API for event handling, don't worry about this... *
+ * ----------------------------------------------------------- */
+
+/* NOTE: the following functions are deprecated, and will be removed
+ from the 3.0 API. Use the lowlevel session functions instead */
+
+/** Function type used to process commands */
+typedef void (*fuse_processor_t)(struct fuse *, struct fuse_cmd *, void *);
+
+/** This is the part of fuse_main() before the event loop */
+struct fuse *fuse_setup(int argc, char *argv[],
+ const struct fuse_operations *op, size_t op_size,
+ char **mountpoint, int *multithreaded,
+ void *user_data);
+
+/** This is the part of fuse_main() after the event loop */
+void fuse_teardown(struct fuse *fuse, char *mountpoint);
+
+/** Read a single command. If none are read, return NULL */
+struct fuse_cmd *fuse_read_cmd(struct fuse *f);
+
+/** Process a single command */
+void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd);
+
+/** Multi threaded event loop, which calls the custom command
+ processor function */
+int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data);
+
+/** Return the exited flag, which indicates if fuse_exit() has been
+ called */
+int fuse_exited(struct fuse *f);
+
+/** This function is obsolete and implemented as a no-op */
+void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
+
+/** Get session from fuse object */
+struct fuse_session *fuse_get_session(struct fuse *f);
+
+/* ----------------------------------------------------------- *
+ * Compatibility stuff *
+ * ----------------------------------------------------------- */
+
+#if FUSE_USE_VERSION < 26
+# include "fuse_compat.h"
+# undef fuse_main
+# if FUSE_USE_VERSION == 25
+# define fuse_main(argc, argv, op) \
+ fuse_main_real_compat25(argc, argv, op, sizeof(*(op)))
+# define fuse_new fuse_new_compat25
+# define fuse_setup fuse_setup_compat25
+# define fuse_teardown fuse_teardown_compat22
+# define fuse_operations fuse_operations_compat25
+# elif FUSE_USE_VERSION == 22
+# define fuse_main(argc, argv, op) \
+ fuse_main_real_compat22(argc, argv, op, sizeof(*(op)))
+# define fuse_new fuse_new_compat22
+# define fuse_setup fuse_setup_compat22
+# define fuse_teardown fuse_teardown_compat22
+# define fuse_operations fuse_operations_compat22
+# define fuse_file_info fuse_file_info_compat
+# elif FUSE_USE_VERSION == 24
+# error Compatibility with high-level API version 24 not supported
+# else
+# define fuse_dirfil_t fuse_dirfil_t_compat
+# define __fuse_read_cmd fuse_read_cmd
+# define __fuse_process_cmd fuse_process_cmd
+# define __fuse_loop_mt fuse_loop_mt_proc
+# if FUSE_USE_VERSION == 21
+# define fuse_operations fuse_operations_compat2
+# define fuse_main fuse_main_compat2
+# define fuse_new fuse_new_compat2
+# define __fuse_setup fuse_setup_compat2
+# define __fuse_teardown fuse_teardown_compat22
+# define __fuse_exited fuse_exited
+# define __fuse_set_getcontext_func fuse_set_getcontext_func
+# else
+# define fuse_statfs fuse_statfs_compat1
+# define fuse_operations fuse_operations_compat1
+# define fuse_main fuse_main_compat1
+# define fuse_new fuse_new_compat1
+# define FUSE_DEBUG FUSE_DEBUG_COMPAT1
+# endif
+# endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_H_ */
diff --git a/fuse/include/fuse_common.h b/fuse/include/fuse_common.h
new file mode 100644
index 000000000..f08778bd1
--- /dev/null
+++ b/fuse/include/fuse_common.h
@@ -0,0 +1,505 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+/** @file */
+
+#if !defined(_FUSE_H_) && !defined(_FUSE_LOWLEVEL_H_)
+#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
+#endif
+
+#ifndef _FUSE_COMMON_H_
+#define _FUSE_COMMON_H_
+
+#include "fuse_opt.h"
+#include <stdint.h>
+#include <sys/types.h>
+
+/** Major version of FUSE library interface */
+#define FUSE_MAJOR_VERSION 2
+
+/** Minor version of FUSE library interface */
+#define FUSE_MINOR_VERSION 9
+
+#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min))
+#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
+
+/* This interface uses 64 bit off_t */
+#if _FILE_OFFSET_BITS != 64
+#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags!
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Information about open files
+ *
+ * Changed in version 2.5
+ */
+struct fuse_file_info {
+ /** Open flags. Available in open() and release() */
+ int flags;
+
+ /** Old file handle, don't use */
+ unsigned long fh_old;
+
+ /** In case of a write operation indicates if this was caused by a
+ writepage */
+ int writepage;
+
+ /** Can be filled in by open, to use direct I/O on this file.
+ Introduced in version 2.4 */
+ unsigned int direct_io : 1;
+
+ /** Can be filled in by open, to indicate, that cached file data
+ need not be invalidated. Introduced in version 2.4 */
+ unsigned int keep_cache : 1;
+
+ /** Indicates a flush operation. Set in flush operation, also
+ maybe set in highlevel lock operation and lowlevel release
+ operation. Introduced in version 2.6 */
+ unsigned int flush : 1;
+
+ /** Can be filled in by open, to indicate that the file is not
+ seekable. Introduced in version 2.8 */
+ unsigned int 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(). Introduced in version
+ 2.9 */
+ unsigned int flock_release : 1;
+
+ /** Padding. Do not use*/
+ unsigned int padding : 27;
+
+ /** File handle. May be filled in by filesystem in open().
+ Available in all other file operations */
+ uint64_t fh;
+
+ /** Lock owner id. Available in locking operations and flush */
+ uint64_t lock_owner;
+};
+
+/**
+ * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want'
+ *
+ * FUSE_CAP_ASYNC_READ: filesystem supports asynchronous read requests
+ * FUSE_CAP_POSIX_LOCKS: filesystem supports "remote" locking
+ * FUSE_CAP_ATOMIC_O_TRUNC: filesystem handles the O_TRUNC open flag
+ * FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
+ * FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB
+ * FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations
+ * FUSE_CAP_SPLICE_WRITE: ability to use splice() to write to the fuse device
+ * FUSE_CAP_SPLICE_MOVE: ability to move data to the fuse device with splice()
+ * FUSE_CAP_SPLICE_READ: ability to use splice() to read from the fuse device
+ * FUSE_CAP_IOCTL_DIR: ioctl support on directories
+ */
+#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_BIG_WRITES (1 << 5)
+#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)
+
+/**
+ * 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.
+ */
+struct fuse_conn_info {
+ /**
+ * Major version of the protocol (read-only)
+ */
+ unsigned proto_major;
+
+ /**
+ * Minor version of the protocol (read-only)
+ */
+ unsigned proto_minor;
+
+ /**
+ * Is asynchronous read supported (read-write)
+ */
+ unsigned async_read;
+
+ /**
+ * Maximum size of the write buffer
+ */
+ unsigned max_write;
+
+ /**
+ * Maximum readahead
+ */
+ unsigned max_readahead;
+
+ /**
+ * Capability flags, that the kernel supports
+ */
+ unsigned capable;
+
+ /**
+ * Capability flags, that the filesystem wants to enable
+ */
+ unsigned want;
+
+ /**
+ * Maximum number of backgrounded requests
+ */
+ unsigned max_background;
+
+ /**
+ * Kernel congestion threshold parameter
+ */
+ unsigned congestion_threshold;
+
+ /**
+ * For future use.
+ */
+ unsigned reserved[23];
+};
+
+struct fuse_session;
+struct fuse_chan;
+struct fuse_pollhandle;
+
+/**
+ * Create a FUSE mountpoint
+ *
+ * Returns a control file descriptor suitable for passing to
+ * fuse_new()
+ *
+ * @param mountpoint the mount point path
+ * @param args argument vector
+ * @return the communication channel on success, NULL on failure
+ */
+struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args);
+
+/**
+ * Umount a FUSE mountpoint
+ *
+ * @param mountpoint the mount point path
+ * @param ch the communication channel
+ */
+void fuse_unmount(const char *mountpoint, struct fuse_chan *ch);
+
+/**
+ * Parse common options
+ *
+ * The following options are parsed:
+ *
+ * '-f' foreground
+ * '-d' '-odebug' foreground, but keep the debug option
+ * '-s' single threaded
+ * '-h' '--help' help
+ * '-ho' help without header
+ * '-ofsname=..' file system name, if not present, then set to the program
+ * name
+ *
+ * All parameters may be NULL
+ *
+ * @param args argument vector
+ * @param mountpoint the returned mountpoint, should be freed after use
+ * @param multithreaded set to 1 unless the '-s' option is present
+ * @param foreground set to 1 if one of the relevant options is present
+ * @return 0 on success, -1 on failure
+ */
+int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint,
+ int *multithreaded, int *foreground);
+
+/**
+ * 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);
+
+/**
+ * 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.
+ */
+ loff_t pos;
+};
+
+/**
+ * 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];
+};
+
+/* 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, \
+ } } \
+ } )
+
+/**
+ * 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.
+ *
+ * @param se the session to exit
+ * @return 0 on success, -1 on failure
+ */
+int fuse_set_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()
+ */
+void fuse_remove_signal_handlers(struct fuse_session *se);
+
+/* ----------------------------------------------------------- *
+ * Compatibility stuff *
+ * ----------------------------------------------------------- */
+
+#if FUSE_USE_VERSION < 26
+# ifdef __FreeBSD__
+# if FUSE_USE_VERSION < 25
+# error On FreeBSD API version 25 or greater must be used
+# endif
+# endif
+# include "fuse_common_compat.h"
+# undef FUSE_MINOR_VERSION
+# undef fuse_main
+# define fuse_unmount fuse_unmount_compat22
+# if FUSE_USE_VERSION == 25
+# define FUSE_MINOR_VERSION 5
+# define fuse_mount fuse_mount_compat25
+# elif FUSE_USE_VERSION == 24 || FUSE_USE_VERSION == 22
+# define FUSE_MINOR_VERSION 4
+# define fuse_mount fuse_mount_compat22
+# elif FUSE_USE_VERSION == 21
+# define FUSE_MINOR_VERSION 1
+# define fuse_mount fuse_mount_compat22
+# elif FUSE_USE_VERSION == 11
+# warning Compatibility with API version 11 is deprecated
+# undef FUSE_MAJOR_VERSION
+# define FUSE_MAJOR_VERSION 1
+# define FUSE_MINOR_VERSION 1
+# define fuse_mount fuse_mount_compat1
+# else
+# error Compatibility with API version other than 21, 22, 24, 25 and 11 not supported
+# endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_COMMON_H_ */
diff --git a/fuse/include/fuse_common_compat.h b/fuse/include/fuse_common_compat.h
new file mode 100644
index 000000000..34440ff71
--- /dev/null
+++ b/fuse/include/fuse_common_compat.h
@@ -0,0 +1,26 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+/* these definitions provide source compatibility to prior versions.
+ Do not include this file directly! */
+
+struct fuse_file_info_compat {
+ int flags;
+ unsigned long fh;
+ int writepage;
+ unsigned int direct_io : 1;
+ unsigned int keep_cache : 1;
+};
+
+int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args);
+
+int fuse_mount_compat22(const char *mountpoint, const char *opts);
+
+int fuse_mount_compat1(const char *mountpoint, const char *args[]);
+
+void fuse_unmount_compat22(const char *mountpoint);
diff --git a/fuse/include/fuse_compat.h b/fuse/include/fuse_compat.h
new file mode 100644
index 000000000..b825deebf
--- /dev/null
+++ b/fuse/include/fuse_compat.h
@@ -0,0 +1,203 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+/* these definitions provide source compatibility to prior versions.
+ Do not include this file directly! */
+
+#include "fuse_lowlevel.h"
+
+struct fuse_operations_compat25 {
+ int (*getattr) (const char *, struct stat *);
+ int (*readlink) (const char *, char *, size_t);
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
+ int (*mknod) (const char *, mode_t, dev_t);
+ int (*mkdir) (const char *, mode_t);
+ int (*unlink) (const char *);
+ int (*rmdir) (const char *);
+ int (*symlink) (const char *, const char *);
+ int (*rename) (const char *, const char *);
+ int (*link) (const char *, const char *);
+ int (*chmod) (const char *, mode_t);
+ int (*chown) (const char *, uid_t, gid_t);
+ int (*truncate) (const char *, loff_t);
+ int (*utime) (const char *, struct utimbuf *);
+ int (*open) (const char *, struct fuse_file_info *);
+ int (*read) (const char *, char *, size_t, loff_t,
+ struct fuse_file_info *);
+ int (*write) (const char *, const char *, size_t, loff_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, loff_t,
+ struct fuse_file_info *);
+ int (*releasedir) (const char *, struct fuse_file_info *);
+ int (*fsyncdir) (const char *, int, struct fuse_file_info *);
+ void *(*init) (void);
+ void (*destroy) (void *);
+ int (*access) (const char *, int);
+ int (*create) (const char *, mode_t, struct fuse_file_info *);
+ int (*ftruncate) (const char *, loff_t, struct fuse_file_info *);
+ int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *);
+};
+
+struct fuse *fuse_new_compat25(int fd, struct fuse_args *args,
+ const struct fuse_operations_compat25 *op,
+ size_t op_size);
+
+int fuse_main_real_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size);
+
+struct fuse *fuse_setup_compat25(int argc, char *argv[],
+ const struct fuse_operations_compat25 *op,
+ size_t op_size, char **mountpoint,
+ int *multithreaded, int *fd);
+
+void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint);
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+#include <sys/statfs.h>
+
+struct fuse_operations_compat22 {
+ int (*getattr) (const char *, struct stat *);
+ int (*readlink) (const char *, char *, size_t);
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
+ int (*mknod) (const char *, mode_t, dev_t);
+ int (*mkdir) (const char *, mode_t);
+ int (*unlink) (const char *);
+ int (*rmdir) (const char *);
+ int (*symlink) (const char *, const char *);
+ int (*rename) (const char *, const char *);
+ int (*link) (const char *, const char *);
+ int (*chmod) (const char *, mode_t);
+ int (*chown) (const char *, uid_t, gid_t);
+ int (*truncate) (const char *, loff_t);
+ int (*utime) (const char *, struct utimbuf *);
+ int (*open) (const char *, struct fuse_file_info_compat *);
+ int (*read) (const char *, char *, size_t, loff_t,
+ struct fuse_file_info_compat *);
+ int (*write) (const char *, const char *, size_t, loff_t,
+ struct fuse_file_info_compat *);
+ int (*statfs) (const char *, struct statfs *);
+ int (*flush) (const char *, struct fuse_file_info_compat *);
+ int (*release) (const char *, struct fuse_file_info_compat *);
+ int (*fsync) (const char *, int, struct fuse_file_info_compat *);
+ int (*setxattr) (const char *, const char *, const char *, size_t, int);
+ int (*getxattr) (const char *, const char *, char *, size_t);
+ int (*listxattr) (const char *, char *, size_t);
+ int (*removexattr) (const char *, const char *);
+ int (*opendir) (const char *, struct fuse_file_info_compat *);
+ int (*readdir) (const char *, void *, fuse_fill_dir_t, loff_t,
+ struct fuse_file_info_compat *);
+ int (*releasedir) (const char *, struct fuse_file_info_compat *);
+ int (*fsyncdir) (const char *, int, struct fuse_file_info_compat *);
+ void *(*init) (void);
+ void (*destroy) (void *);
+};
+
+struct fuse *fuse_new_compat22(int fd, const char *opts,
+ const struct fuse_operations_compat22 *op,
+ size_t op_size);
+
+struct fuse *fuse_setup_compat22(int argc, char *argv[],
+ const struct fuse_operations_compat22 *op,
+ size_t op_size, char **mountpoint,
+ int *multithreaded, int *fd);
+
+int fuse_main_real_compat22(int argc, char *argv[],
+ const struct fuse_operations_compat22 *op,
+ size_t op_size);
+
+typedef int (*fuse_dirfil_t_compat) (fuse_dirh_t h, const char *name, int type);
+struct fuse_operations_compat2 {
+ int (*getattr) (const char *, struct stat *);
+ int (*readlink) (const char *, char *, size_t);
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
+ int (*mknod) (const char *, mode_t, dev_t);
+ int (*mkdir) (const char *, mode_t);
+ int (*unlink) (const char *);
+ int (*rmdir) (const char *);
+ int (*symlink) (const char *, const char *);
+ int (*rename) (const char *, const char *);
+ int (*link) (const char *, const char *);
+ int (*chmod) (const char *, mode_t);
+ int (*chown) (const char *, uid_t, gid_t);
+ int (*truncate) (const char *, loff_t);
+ int (*utime) (const char *, struct utimbuf *);
+ int (*open) (const char *, int);
+ int (*read) (const char *, char *, size_t, loff_t);
+ int (*write) (const char *, const char *, size_t, loff_t);
+ int (*statfs) (const char *, struct statfs *);
+ int (*flush) (const char *);
+ int (*release) (const char *, int);
+ int (*fsync) (const char *, int);
+ int (*setxattr) (const char *, const char *, const char *,
+ size_t, int);
+ int (*getxattr) (const char *, const char *, char *, size_t);
+ int (*listxattr) (const char *, char *, size_t);
+ int (*removexattr) (const char *, const char *);
+};
+
+int fuse_main_compat2(int argc, char *argv[],
+ const struct fuse_operations_compat2 *op);
+
+struct fuse *fuse_new_compat2(int fd, const char *opts,
+ const struct fuse_operations_compat2 *op);
+
+struct fuse *fuse_setup_compat2(int argc, char *argv[],
+ const struct fuse_operations_compat2 *op,
+ char **mountpoint, int *multithreaded, int *fd);
+
+struct fuse_statfs_compat1 {
+ long block_size;
+ long blocks;
+ long blocks_free;
+ long files;
+ long files_free;
+ long namelen;
+};
+
+struct fuse_operations_compat1 {
+ int (*getattr) (const char *, struct stat *);
+ int (*readlink) (const char *, char *, size_t);
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
+ int (*mknod) (const char *, mode_t, dev_t);
+ int (*mkdir) (const char *, mode_t);
+ int (*unlink) (const char *);
+ int (*rmdir) (const char *);
+ int (*symlink) (const char *, const char *);
+ int (*rename) (const char *, const char *);
+ int (*link) (const char *, const char *);
+ int (*chmod) (const char *, mode_t);
+ int (*chown) (const char *, uid_t, gid_t);
+ int (*truncate) (const char *, loff_t);
+ int (*utime) (const char *, struct utimbuf *);
+ int (*open) (const char *, int);
+ int (*read) (const char *, char *, size_t, loff_t);
+ int (*write) (const char *, const char *, size_t, loff_t);
+ int (*statfs) (struct fuse_statfs_compat1 *);
+ int (*release) (const char *, int);
+ int (*fsync) (const char *, int);
+};
+
+#define FUSE_DEBUG_COMPAT1 (1 << 1)
+
+struct fuse *fuse_new_compat1(int fd, int flags,
+ const struct fuse_operations_compat1 *op);
+
+void fuse_main_compat1(int argc, char *argv[],
+ const struct fuse_operations_compat1 *op);
+
+#endif /* __FreeBSD__ || __NetBSD__ */
diff --git a/fuse/include/fuse_kernel.h b/fuse/include/fuse_kernel.h
new file mode 100644
index 000000000..c632b58fb
--- /dev/null
+++ b/fuse/include/fuse_kernel.h
@@ -0,0 +1,691 @@
+/*
+ This file defines the kernel interface of FUSE
+ Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
+
+ 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.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
+ *
+ * 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 open, 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
+ */
+
+#ifndef _LINUX_FUSE_H
+#define _LINUX_FUSE_H
+
+#include <sys/types.h>
+#define __u64 uint64_t
+#define __s64 int64_t
+#define __u32 uint32_t
+#define __s32 int32_t
+#define __u16 uint16_t
+
+/*
+ * 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 19
+
+/** 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 {
+ __u64 ino;
+ __u64 size;
+ __u64 blocks;
+ __u64 atime;
+ __u64 mtime;
+ __u64 ctime;
+ __u32 atimensec;
+ __u32 mtimensec;
+ __u32 ctimensec;
+ __u32 mode;
+ __u32 nlink;
+ __u32 uid;
+ __u32 gid;
+ __u32 rdev;
+ __u32 blksize;
+ __u32 padding;
+};
+
+struct fuse_kstatfs {
+ __u64 blocks;
+ __u64 bfree;
+ __u64 bavail;
+ __u64 files;
+ __u64 ffree;
+ __u32 bsize;
+ __u32 namelen;
+ __u32 frsize;
+ __u32 padding;
+ __u32 spare[6];
+};
+
+struct fuse_file_lock {
+ __u64 start;
+ __u64 end;
+ __u32 type;
+ __u32 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)
+
+/**
+ * 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
+ */
+#define FOPEN_DIRECT_IO (1 << 0)
+#define FOPEN_KEEP_CACHE (1 << 1)
+#define FOPEN_NONSEEKABLE (1 << 2)
+
+/**
+ * INIT request/reply flags
+ *
+ * FUSE_POSIX_LOCKS: remote locking for POSIX file locks
+ * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
+ * FUSE_DONT_MASK: don't apply umask to file mode on create operations
+ * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks
+ */
+#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_FLOCK_LOCKS (1 << 10)
+
+/**
+ * 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
+ */
+#define FUSE_WRITE_CACHE (1 << 0)
+#define FUSE_WRITE_LOCKOWNER (1 << 1)
+
+/**
+ * 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_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_MAX_IOV 256
+
+/**
+ * Poll flags
+ *
+ * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify
+ */
+#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
+
+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,
+
+ /* CUSE specific operations */
+ CUSE_INIT = 4096,
+};
+
+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_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 {
+ __u64 nodeid; /* Inode ID */
+ __u64 generation; /* Inode generation: nodeid:gen must
+ be unique for the fs's lifetime */
+ __u64 entry_valid; /* Cache timeout for the name */
+ __u64 attr_valid; /* Cache timeout for the attributes */
+ __u32 entry_valid_nsec;
+ __u32 attr_valid_nsec;
+ struct fuse_attr attr;
+};
+
+struct fuse_forget_in {
+ __u64 nlookup;
+};
+
+struct fuse_forget_one {
+ __u64 nodeid;
+ __u64 nlookup;
+};
+
+struct fuse_batch_forget_in {
+ __u32 count;
+ __u32 dummy;
+};
+
+struct fuse_getattr_in {
+ __u32 getattr_flags;
+ __u32 dummy;
+ __u64 fh;
+};
+
+#define FUSE_COMPAT_ATTR_OUT_SIZE 96
+
+struct fuse_attr_out {
+ __u64 attr_valid; /* Cache timeout for the attributes */
+ __u32 attr_valid_nsec;
+ __u32 dummy;
+ struct fuse_attr attr;
+};
+
+#define FUSE_COMPAT_MKNOD_IN_SIZE 8
+
+struct fuse_mknod_in {
+ __u32 mode;
+ __u32 rdev;
+ __u32 umask;
+ __u32 padding;
+};
+
+struct fuse_mkdir_in {
+ __u32 mode;
+ __u32 umask;
+};
+
+struct fuse_rename_in {
+ __u64 newdir;
+};
+
+struct fuse_link_in {
+ __u64 oldnodeid;
+};
+
+struct fuse_setattr_in {
+ __u32 valid;
+ __u32 padding;
+ __u64 fh;
+ __u64 size;
+ __u64 lock_owner;
+ __u64 atime;
+ __u64 mtime;
+ __u64 unused2;
+ __u32 atimensec;
+ __u32 mtimensec;
+ __u32 unused3;
+ __u32 mode;
+ __u32 unused4;
+ __u32 uid;
+ __u32 gid;
+ __u32 unused5;
+};
+
+struct fuse_open_in {
+ __u32 flags;
+ __u32 unused;
+};
+
+struct fuse_create_in {
+ __u32 flags;
+ __u32 mode;
+ __u32 umask;
+ __u32 padding;
+};
+
+struct fuse_open_out {
+ __u64 fh;
+ __u32 open_flags;
+ __u32 padding;
+};
+
+struct fuse_release_in {
+ __u64 fh;
+ __u32 flags;
+ __u32 release_flags;
+ __u64 lock_owner;
+};
+
+struct fuse_flush_in {
+ __u64 fh;
+ __u32 unused;
+ __u32 padding;
+ __u64 lock_owner;
+};
+
+struct fuse_read_in {
+ __u64 fh;
+ __u64 offset;
+ __u32 size;
+ __u32 read_flags;
+ __u64 lock_owner;
+ __u32 flags;
+ __u32 padding;
+};
+
+#define FUSE_COMPAT_WRITE_IN_SIZE 24
+
+struct fuse_write_in {
+ __u64 fh;
+ __u64 offset;
+ __u32 size;
+ __u32 write_flags;
+ __u64 lock_owner;
+ __u32 flags;
+ __u32 padding;
+};
+
+struct fuse_write_out {
+ __u32 size;
+ __u32 padding;
+};
+
+#define FUSE_COMPAT_STATFS_SIZE 48
+
+struct fuse_statfs_out {
+ struct fuse_kstatfs st;
+};
+
+struct fuse_fsync_in {
+ __u64 fh;
+ __u32 fsync_flags;
+ __u32 padding;
+};
+
+struct fuse_setxattr_in {
+ __u32 size;
+ __u32 flags;
+};
+
+struct fuse_getxattr_in {
+ __u32 size;
+ __u32 padding;
+};
+
+struct fuse_getxattr_out {
+ __u32 size;
+ __u32 padding;
+};
+
+struct fuse_lk_in {
+ __u64 fh;
+ __u64 owner;
+ struct fuse_file_lock lk;
+ __u32 lk_flags;
+ __u32 padding;
+};
+
+struct fuse_lk_out {
+ struct fuse_file_lock lk;
+};
+
+struct fuse_access_in {
+ __u32 mask;
+ __u32 padding;
+};
+
+struct fuse_init_in {
+ __u32 major;
+ __u32 minor;
+ __u32 max_readahead;
+ __u32 flags;
+};
+
+struct fuse_init_out {
+ __u32 major;
+ __u32 minor;
+ __u32 max_readahead;
+ __u32 flags;
+ __u16 max_background;
+ __u16 congestion_threshold;
+ __u32 max_write;
+};
+
+#define CUSE_INIT_INFO_MAX 4096
+
+struct cuse_init_in {
+ __u32 major;
+ __u32 minor;
+ __u32 unused;
+ __u32 flags;
+};
+
+struct cuse_init_out {
+ __u32 major;
+ __u32 minor;
+ __u32 unused;
+ __u32 flags;
+ __u32 max_read;
+ __u32 max_write;
+ __u32 dev_major; /* chardev major */
+ __u32 dev_minor; /* chardev minor */
+ __u32 spare[10];
+};
+
+struct fuse_interrupt_in {
+ __u64 unique;
+};
+
+struct fuse_bmap_in {
+ __u64 block;
+ __u32 blocksize;
+ __u32 padding;
+};
+
+struct fuse_bmap_out {
+ __u64 block;
+};
+
+struct fuse_ioctl_in {
+ __u64 fh;
+ __u32 flags;
+ __u32 cmd;
+ __u64 arg;
+ __u32 in_size;
+ __u32 out_size;
+};
+
+struct fuse_ioctl_iovec {
+ __u64 base;
+ __u64 len;
+};
+
+struct fuse_ioctl_out {
+ __s32 result;
+ __u32 flags;
+ __u32 in_iovs;
+ __u32 out_iovs;
+};
+
+struct fuse_poll_in {
+ __u64 fh;
+ __u64 kh;
+ __u32 flags;
+ __u32 padding;
+};
+
+struct fuse_poll_out {
+ __u32 revents;
+ __u32 padding;
+};
+
+struct fuse_notify_poll_wakeup_out {
+ __u64 kh;
+};
+
+struct fuse_fallocate_in {
+ __u64 fh;
+ __u64 offset;
+ __u64 length;
+ __u32 mode;
+ __u32 padding;
+};
+
+struct fuse_in_header {
+ __u32 len;
+ __u32 opcode;
+ __u64 unique;
+ __u64 nodeid;
+ __u32 uid;
+ __u32 gid;
+ __u32 pid;
+ __u32 padding;
+};
+
+struct fuse_out_header {
+ __u32 len;
+ __s32 error;
+ __u64 unique;
+};
+
+struct fuse_dirent {
+ __u64 ino;
+ __u64 off;
+ __u32 namelen;
+ __u32 type;
+ char name[];
+};
+
+#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
+#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1))
+#define FUSE_DIRENT_SIZE(d) \
+ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
+
+struct fuse_notify_inval_inode_out {
+ __u64 ino;
+ __s64 off;
+ __s64 len;
+};
+
+struct fuse_notify_inval_entry_out {
+ __u64 parent;
+ __u32 namelen;
+ __u32 padding;
+};
+
+struct fuse_notify_delete_out {
+ __u64 parent;
+ __u64 child;
+ __u32 namelen;
+ __u32 padding;
+};
+
+struct fuse_notify_store_out {
+ __u64 nodeid;
+ __u64 offset;
+ __u32 size;
+ __u32 padding;
+};
+
+struct fuse_notify_retrieve_out {
+ __u64 notify_unique;
+ __u64 nodeid;
+ __u64 offset;
+ __u32 size;
+ __u32 padding;
+};
+
+/* Matches the size of fuse_write_in */
+struct fuse_notify_retrieve_in {
+ __u64 dummy1;
+ __u64 offset;
+ __u32 size;
+ __u32 dummy2;
+ __u64 dummy3;
+ __u64 dummy4;
+};
+
+#endif /* _LINUX_FUSE_H */
diff --git a/fuse/include/fuse_lowlevel.h b/fuse/include/fuse_lowlevel.h
new file mode 100644
index 000000000..6971f732e
--- /dev/null
+++ b/fuse/include/fuse_lowlevel.h
@@ -0,0 +1,1845 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#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 26 (recommended for any
+ * new application), to use the old API define it to 24 (default) or
+ * 25
+ */
+
+#ifndef FUSE_USE_VERSION
+#define FUSE_USE_VERSION 24
+#endif
+
+#include "fuse_common.h"
+
+#include <utime.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ----------------------------------------------------------- *
+ * Miscellaneous definitions *
+ * ----------------------------------------------------------- */
+
+/** The node ID of the root inode */
+#define FUSE_ROOT_ID 1
+
+/** Inode number type */
+typedef unsigned long 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;
+
+/**
+ * Channel
+ *
+ * A communication channel, providing hooks for sending and receiving
+ * messages
+ */
+struct fuse_chan;
+
+/** 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.
+ *
+ * The generation must be non-zero, otherwise FUSE will treat
+ * it as an error.
+ *
+ */
+ unsigned long 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 the attributes */
+ double attr_timeout;
+
+ /** Validity timeout (in seconds) for the name */
+ double entry_timeout;
+};
+
+/** Additional context associated with requests */
+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 (introduced in version 2.8) */
+ mode_t umask;
+};
+
+struct fuse_forget_data {
+ uint64_t ino;
+ uint64_t nlookup;
+};
+
+/* '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)
+
+/* ----------------------------------------------------------- *
+ * 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.
+ *
+ * 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.
+ */
+struct fuse_lowlevel_ops {
+ /**
+ * Initialize filesystem
+ *
+ * Called before any other filesystem method
+ *
+ * There's no reply to this function
+ *
+ * @param userdata the user data passed to fuse_lowlevel_new()
+ */
+ void (*init) (void *userdata, struct fuse_conn_info *conn);
+
+ /**
+ * Clean up filesystem
+ *
+ * Called on filesystem exit
+ *
+ * There's no reply to this function
+ *
+ * @param userdata the user data passed to fuse_lowlevel_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, remdir 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, unsigned long nlookup);
+
+ /**
+ * Get file attributes
+ *
+ * Valid replies:
+ * fuse_reply_attr
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi for future use, currently always 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.
+ *
+ * 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
+ *
+ * Changed in version 2.5:
+ * file information filled in for ftruncate
+ */
+ 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).
+ *
+ * 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);
+
+ /**
+ * 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 (with the exception of O_CREAT, O_EXCL, O_NOCTTY and
+ * O_TRUNC) are available in fi->flags.
+ *
+ * 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.
+ *
+ * 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, loff_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.
+ *
+ * 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, loff_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.
+ *
+ * If the filesystem supports file locking operations (setlk,
+ * getlk) it should remove all locks belonging to 'fi->owner'.
+ *
+ * Valid replies:
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param fi file information
+ */
+ 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.
+ *
+ * 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.
+ *
+ * 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).
+ *
+ * Filesystem may also implement stateless directory I/O and not
+ * store anything in fi->fh, though that makes it impossible to
+ * implement standard conforming directory stream operations in
+ * case the contents of the directory can change between opendir
+ * and releasedir.
+ *
+ * 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.
+ *
+ * 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, loff_t off,
+ struct fuse_file_info *fi);
+
+ /**
+ * Release an open directory
+ *
+ * For every opendir call there will be exactly one releasedir
+ * call.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * 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() 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
+ *
+ * Introduced in version 2.5
+ *
+ * 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.
+ *
+ * Open flags (with the exception of O_NOCTTY) are available in
+ * fi->flags.
+ *
+ * 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).
+ *
+ * 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 method is not implemented or under Linux kernel
+ * versions earlier than 2.6.15, the mknod() and open() methods
+ * will be called instead.
+ *
+ * Introduced in version 2.5
+ *
+ * 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
+ *
+ * Introduced in version 2.6
+ *
+ * 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.
+ *
+ * Introduced in version 2.6
+ *
+ * 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
+ *
+ * Introduced in version 2.6
+ *
+ * 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);
+
+ /**
+ * 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.
+ *
+ * Introduced in version 2.8
+ *
+ * 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
+ */
+ 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);
+
+ /**
+ * Poll for IO readiness
+ *
+ * Introduced in version 2.8
+ *
+ * Note: If ph is non-NULL, the client should notify
+ * when IO readiness events occur by calling
+ * fuse_lowelevel_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.
+ *
+ * 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.
+ *
+ * Introduced in version 2.9
+ *
+ * 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, loff_t off,
+ struct fuse_file_info *fi);
+
+ /**
+ * Callback function for the retrieve request
+ *
+ * Introduced in version 2.9
+ *
+ * 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,
+ loff_t offset, struct fuse_bufvec *bufv);
+
+ /**
+ * Forget about multiple inodes
+ *
+ * See description of the forget function for more
+ * information.
+ *
+ * Introduced in version 2.9
+ *
+ * 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.
+ *
+ * Introduced in version 2.9
+ *
+ * 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.
+ *
+ * Introduced in version 2.9
+ *
+ * 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,
+ loff_t offset, loff_t length, struct fuse_file_info *fi);
+};
+
+/**
+ * Reply with an error code or success
+ *
+ * Possible requests:
+ * all except forget
+ *
+ * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr,
+ * removexattr and setlk may send a zero code
+ *
+ * @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
+ *
+ * @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
+ *
+ * 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);
+
+/**
+ * Reply with open parameters
+ *
+ * currently the following members of 'fi' are used:
+ * fh, direct_io, keep_cache
+ *
+ * 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)
+ *
+ * Possible requests:
+ * read, readdir, getxattr, listxattr
+ *
+ * @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.
+ *
+ * Note: offsets do not necessarily represent physical offsets, and
+ * could be any marker, that enables the implementation to find a
+ * specific point in the directory stream.
+ *
+ * @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,
+ loff_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);
+
+/* ----------------------------------------------------------- *
+ * 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
+ *
+ * @param ch the channel through which to send the invalidation
+ * @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_chan *ch, fuse_ino_t ino,
+ loff_t off, loff_t len);
+
+/**
+ * Notify to invalidate parent attributes and the dentry matching
+ * parent/name
+ *
+ * To avoid a deadlock don't call this function from a filesystem operation and
+ * don't call it with a lock held that can also be held by a filesystem
+ * operation.
+ *
+ * @param ch the channel through which to send the invalidation
+ * @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_chan *ch, fuse_ino_t parent,
+ const char *name, size_t namelen);
+
+/**
+ * Notify to invalidate parent attributes and delete the dentry matching
+ * parent/name if the dentry's inode number matches child (otherwise it
+ * will invalidate the matching dentry).
+ *
+ * To avoid a deadlock don't call this function from a filesystem operation and
+ * don't call it with a lock held that can also be held by a filesystem
+ * operation.
+ *
+ * @param ch the channel through which to send the notification
+ * @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_chan *ch,
+ 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.
+ *
+ * @param ch the channel through which to send the invalidation
+ * @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_chan *ch, fuse_ino_t ino,
+ loff_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.
+ *
+ * @param ch the channel through which to send the invalidation
+ * @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_chan *ch, fuse_ino_t ino,
+ size_t size, loff_t offset, void *cookie);
+
+
+/* ----------------------------------------------------------- *
+ * Utility functions *
+ * ----------------------------------------------------------- */
+
+/**
+ * Get the userdata from the request
+ *
+ * @param req request handle
+ * @return the user data passed to fuse_lowlevel_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);
+
+/* ----------------------------------------------------------- *
+ * Filesystem setup *
+ * ----------------------------------------------------------- */
+
+/* Deprecated, don't use */
+int fuse_lowlevel_is_lib_option(const char *opt);
+
+/**
+ * Create a low level session
+ *
+ * @param args argument vector
+ * @param op the low level filesystem operations
+ * @param op_size sizeof(struct fuse_lowlevel_ops)
+ * @param userdata user data
+ * @return the created session object, or NULL on failure
+ */
+struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
+ const struct fuse_lowlevel_ops *op,
+ size_t op_size, void *userdata);
+
+/* ----------------------------------------------------------- *
+ * Session interface *
+ * ----------------------------------------------------------- */
+
+/**
+ * Session operations
+ *
+ * This is used in session creation
+ */
+struct fuse_session_ops {
+ /**
+ * Hook to process a request (mandatory)
+ *
+ * @param data user data passed to fuse_session_new()
+ * @param buf buffer containing the raw request
+ * @param len request length
+ * @param ch channel on which the request was received
+ */
+ void (*process) (void *data, const char *buf, size_t len,
+ struct fuse_chan *ch);
+
+ /**
+ * Hook for session exit and reset (optional)
+ *
+ * @param data user data passed to fuse_session_new()
+ * @param val exited status (1 - exited, 0 - not exited)
+ */
+ void (*exit) (void *data, int val);
+
+ /**
+ * Hook for querying the current exited status (optional)
+ *
+ * @param data user data passed to fuse_session_new()
+ * @return 1 if exited, 0 if not exited
+ */
+ int (*exited) (void *data);
+
+ /**
+ * Hook for cleaning up the channel on destroy (optional)
+ *
+ * @param data user data passed to fuse_session_new()
+ */
+ void (*destroy) (void *data);
+};
+
+/**
+ * Create a new session
+ *
+ * @param op session operations
+ * @param data user data
+ * @return new session object, or NULL on failure
+ */
+struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data);
+
+/**
+ * Assign a channel to a session
+ *
+ * Note: currently only a single channel may be assigned. This may
+ * change in the future
+ *
+ * If a session is destroyed, the assigned channel is also destroyed
+ *
+ * @param se the session
+ * @param ch the channel
+ */
+void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch);
+
+/**
+ * Remove a channel from a session
+ *
+ * If the channel is not assigned to a session, then this is a no-op
+ *
+ * @param ch the channel to remove
+ */
+void fuse_session_remove_chan(struct fuse_chan *ch);
+
+/**
+ * Iterate over the channels assigned to a session
+ *
+ * The iterating function needs to start with a NULL channel, and
+ * after that needs to pass the previously returned channel to the
+ * function.
+ *
+ * @param se the session
+ * @param ch the previous channel, or NULL
+ * @return the next channel, or NULL if no more channels exist
+ */
+struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
+ struct fuse_chan *ch);
+
+/**
+ * Process a raw request
+ *
+ * @param se the session
+ * @param buf buffer containing the raw request
+ * @param len request length
+ * @param ch channel on which the request was received
+ */
+void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
+ struct fuse_chan *ch);
+
+/**
+ * Process a raw request supplied in a generic buffer
+ *
+ * This is a more generic version of fuse_session_process(). 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
+ * @param ch channel on which the request was received
+ */
+void fuse_session_process_buf(struct fuse_session *se,
+ const struct fuse_buf *buf, struct fuse_chan *ch);
+
+/**
+ * Receive a raw request supplied in a generic buffer
+ *
+ * This is a more generic version of fuse_chan_recv(). The fuse_buf
+ * supplied to this function contains a suitably allocated memory
+ * buffer. This may be overwritten with a file descriptor buffer.
+ *
+ * @param se the session
+ * @param buf the fuse_buf to store the request in
+ * @param chp pointer to the channel
+ * @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,
+ struct fuse_chan **chp);
+
+/**
+ * Destroy a session
+ *
+ * @param se the session
+ */
+void fuse_session_destroy(struct fuse_session *se);
+
+/**
+ * Exit a session
+ *
+ * @param se the session
+ */
+void fuse_session_exit(struct fuse_session *se);
+
+/**
+ * Reset the exited status of a session
+ *
+ * @param se the session
+ */
+void fuse_session_reset(struct fuse_session *se);
+
+/**
+ * Query the exited status of a session
+ *
+ * @param se the session
+ * @return 1 if exited, 0 if not exited
+ */
+int fuse_session_exited(struct fuse_session *se);
+
+/**
+ * Get the user data provided to the session
+ *
+ * @param se the session
+ * @return the user data
+ */
+void *fuse_session_data(struct fuse_session *se);
+
+/**
+ * Enter a single threaded event loop
+ *
+ * @param se the session
+ * @return 0 on success, -1 on error
+ */
+int fuse_session_loop(struct fuse_session *se);
+
+/**
+ * Enter a multi-threaded event loop
+ *
+ * @param se the session
+ * @return 0 on success, -1 on error
+ */
+int fuse_session_loop_mt(struct fuse_session *se);
+
+/* ----------------------------------------------------------- *
+ * Channel interface *
+ * ----------------------------------------------------------- */
+
+/**
+ * Channel operations
+ *
+ * This is used in channel creation
+ */
+struct fuse_chan_ops {
+ /**
+ * Hook for receiving a raw request
+ *
+ * @param ch pointer to the channel
+ * @param buf the buffer to store the request in
+ * @param size the size of the buffer
+ * @return the actual size of the raw request, or -1 on error
+ */
+ int (*receive)(struct fuse_chan **chp, char *buf, size_t size);
+
+ /**
+ * Hook for sending a raw reply
+ *
+ * A return value of -ENOENT means, that the request was
+ * interrupted, and the reply was discarded
+ *
+ * @param ch the channel
+ * @param iov vector of blocks
+ * @param count the number of blocks in vector
+ * @return zero on success, -errno on failure
+ */
+ int (*send)(struct fuse_chan *ch, const struct iovec iov[],
+ size_t count);
+
+ /**
+ * Destroy the channel
+ *
+ * @param ch the channel
+ */
+ void (*destroy)(struct fuse_chan *ch);
+};
+
+/**
+ * Create a new channel
+ *
+ * @param op channel operations
+ * @param fd file descriptor of the channel
+ * @param bufsize the minimal receive buffer size
+ * @param data user data
+ * @return the new channel object, or NULL on failure
+ */
+struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
+ size_t bufsize, void *data);
+
+/**
+ * Query the file descriptor of the channel
+ *
+ * @param ch the channel
+ * @return the file descriptor passed to fuse_chan_new()
+ */
+int fuse_chan_fd(struct fuse_chan *ch);
+
+/**
+ * Query the minimal receive buffer size
+ *
+ * @param ch the channel
+ * @return the buffer size passed to fuse_chan_new()
+ */
+size_t fuse_chan_bufsize(struct fuse_chan *ch);
+
+/**
+ * Query the user data
+ *
+ * @param ch the channel
+ * @return the user data passed to fuse_chan_new()
+ */
+void *fuse_chan_data(struct fuse_chan *ch);
+
+/**
+ * Query the session to which this channel is assigned
+ *
+ * @param ch the channel
+ * @return the session, or NULL if the channel is not assigned
+ */
+struct fuse_session *fuse_chan_session(struct fuse_chan *ch);
+
+/**
+ * Receive a raw request
+ *
+ * A return value of -ENODEV means, that the filesystem was unmounted
+ *
+ * @param ch pointer to the channel
+ * @param buf the buffer to store the request in
+ * @param size the size of the buffer
+ * @return the actual size of the raw request, or -errno on error
+ */
+int fuse_chan_recv(struct fuse_chan **ch, char *buf, size_t size);
+
+/**
+ * Send a raw reply
+ *
+ * A return value of -ENOENT means, that the request was
+ * interrupted, and the reply was discarded
+ *
+ * @param ch the channel
+ * @param iov vector of blocks
+ * @param count the number of blocks in vector
+ * @return zero on success, -errno on failure
+ */
+int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[],
+ size_t count);
+
+/**
+ * Destroy a channel
+ *
+ * @param ch the channel
+ */
+void fuse_chan_destroy(struct fuse_chan *ch);
+
+/* ----------------------------------------------------------- *
+ * Compatibility stuff *
+ * ----------------------------------------------------------- */
+
+#if FUSE_USE_VERSION < 26
+# include "fuse_lowlevel_compat.h"
+# define fuse_chan_ops fuse_chan_ops_compat24
+# define fuse_chan_new fuse_chan_new_compat24
+# if FUSE_USE_VERSION == 25
+# define fuse_lowlevel_ops fuse_lowlevel_ops_compat25
+# define fuse_lowlevel_new fuse_lowlevel_new_compat25
+# elif FUSE_USE_VERSION == 24
+# define fuse_lowlevel_ops fuse_lowlevel_ops_compat
+# define fuse_lowlevel_new fuse_lowlevel_new_compat
+# define fuse_file_info fuse_file_info_compat
+# define fuse_reply_statfs fuse_reply_statfs_compat
+# define fuse_reply_open fuse_reply_open_compat
+# else
+# error Compatibility with low-level API version < 24 not supported
+# endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_LOWLEVEL_H_ */
diff --git a/fuse/include/fuse_lowlevel_compat.h b/fuse/include/fuse_lowlevel_compat.h
new file mode 100644
index 000000000..f13adbdf3
--- /dev/null
+++ b/fuse/include/fuse_lowlevel_compat.h
@@ -0,0 +1,157 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+/* these definitions provide source compatibility to prior versions.
+ Do not include this file directly! */
+
+#include "fuse_common.h"
+
+struct fuse_lowlevel_ops_compat25 {
+ void (*init) (void *userdata);
+ void (*destroy) (void *userdata);
+ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
+ void (*getattr) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+ void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+ int to_set, struct fuse_file_info *fi);
+ void (*readlink) (fuse_req_t req, fuse_ino_t ino);
+ void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, dev_t rdev);
+ void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode);
+ void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
+ const char *name);
+ void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ fuse_ino_t newparent, const char *newname);
+ void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+ const char *newname);
+ void (*open) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+ void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, loff_t off,
+ struct fuse_file_info *fi);
+ void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+ size_t size, loff_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, loff_t off,
+ struct fuse_file_info *fi);
+ void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info *fi);
+ void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info *fi);
+ void (*statfs) (fuse_req_t req);
+ void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ const char *value, size_t size, int flags);
+ void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ size_t size);
+ void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
+ void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
+ void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
+ void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, struct fuse_file_info *fi);
+};
+
+struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args,
+ const struct fuse_lowlevel_ops_compat25 *op,
+ size_t op_size, void *userdata);
+
+size_t fuse_dirent_size(size_t namelen);
+
+char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
+ loff_t off);
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+#include <sys/statfs.h>
+
+struct fuse_lowlevel_ops_compat {
+ void (*init) (void *userdata);
+ void (*destroy) (void *userdata);
+ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
+ void (*getattr) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+ int to_set, struct fuse_file_info_compat *fi);
+ void (*readlink) (fuse_req_t req, fuse_ino_t ino);
+ void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, dev_t rdev);
+ void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode);
+ void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
+ void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
+ const char *name);
+ void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ fuse_ino_t newparent, const char *newname);
+ void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+ const char *newname);
+ void (*open) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, loff_t off,
+ struct fuse_file_info_compat *fi);
+ void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+ size_t size, loff_t off, struct fuse_file_info_compat *fi);
+ void (*flush) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*release) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info_compat *fi);
+ void (*opendir) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, loff_t off,
+ struct fuse_file_info_compat *fi);
+ void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+ struct fuse_file_info_compat *fi);
+ void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
+ struct fuse_file_info_compat *fi);
+ void (*statfs) (fuse_req_t req);
+ void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ const char *value, size_t size, int flags);
+ void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
+ size_t size);
+ void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
+ void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
+ void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
+ void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
+ mode_t mode, struct fuse_file_info_compat *fi);
+};
+
+int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf);
+
+int fuse_reply_open_compat(fuse_req_t req,
+ const struct fuse_file_info_compat *fi);
+
+struct fuse_session *fuse_lowlevel_new_compat(const char *opts,
+ const struct fuse_lowlevel_ops_compat *op,
+ size_t op_size, void *userdata);
+
+#endif /* __FreeBSD__ || __NetBSD__ */
+
+struct fuse_chan_ops_compat24 {
+ int (*receive)(struct fuse_chan *ch, char *buf, size_t size);
+ int (*send)(struct fuse_chan *ch, const struct iovec iov[],
+ size_t count);
+ void (*destroy)(struct fuse_chan *ch);
+};
+
+struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op,
+ int fd, size_t bufsize, void *data);
+
+int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size);
+struct fuse_chan *fuse_kern_chan_new(int fd);
diff --git a/fuse/include/fuse_opt.h b/fuse/include/fuse_opt.h
new file mode 100644
index 000000000..add0a3089
--- /dev/null
+++ b/fuse/include/fuse_opt.h
@@ -0,0 +1,270 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#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 <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().
+ */
+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_ */
diff --git a/fuse/include/old/fuse.h b/fuse/include/old/fuse.h
new file mode 100644
index 000000000..3db0945a9
--- /dev/null
+++ b/fuse/include/old/fuse.h
@@ -0,0 +1,9 @@
+/*
+ This header is for compatibility with older software using FUSE.
+
+ Please use 'pkg-config --cflags fuse' to set include path. The
+ correct usage is still '#include <fuse.h>', not '#include
+ <fuse/fuse.h>'.
+*/
+
+#include "fuse/fuse.h"
diff --git a/fuse/include/ulockmgr.h b/fuse/include/ulockmgr.h
new file mode 100644
index 000000000..c3ceef5d0
--- /dev/null
+++ b/fuse/include/ulockmgr.h
@@ -0,0 +1,26 @@
+/*
+ libulockmgr: Userspace Lock Manager Library
+ Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "fuse_lowlevel.h"
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+/**
+ * Perform POSIX locking operation
+ *
+ * @param fd the file descriptor
+ * @param cmd the locking command (F_GETFL, F_SETLK or F_SETLKW)
+ * @param lock the lock parameters
+ * @param owner the lock owner ID cookie
+ * @param owner_len length of the lock owner ID cookie
+ * @return 0 on success -errno on error
+ */
+int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
+ size_t owner_len);
diff --git a/fuse/modules/iconv.c b/fuse/modules/iconv.c
new file mode 100644
index 000000000..9b78cfd4b
--- /dev/null
+++ b/fuse/modules/iconv.c
@@ -0,0 +1,739 @@
+/*
+ fuse iconv module: file name charset conversion
+ Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <iconv.h>
+#include <pthread.h>
+#include <locale.h>
+#include <langinfo.h>
+
+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;
+ tmp = realloc(newpath, newpathlen + 1);
+ err = -ENOMEM;
+ if (!tmp)
+ goto err;
+
+ p = tmp + (p - newpath);
+ 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 iconv *ic = iconv_get();
+ char *newpath;
+ int err = iconv_convpath(ic, path, &newpath, 0);
+ if (!err) {
+ err = fuse_fs_getattr(ic->next, newpath, stbuf);
+ free(newpath);
+ }
+ return err;
+}
+
+static int iconv_fgetattr(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_fgetattr(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, loff_t off)
+{
+ 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);
+ free(newname);
+ }
+ return res;
+}
+
+static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ loff_t offset, struct fuse_file_info *fi)
+{
+ 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);
+ 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)
+{
+ 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);
+ 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 iconv *ic = iconv_get();
+ char *newpath;
+ int err = iconv_convpath(ic, path, &newpath, 0);
+ if (!err) {
+ err = fuse_fs_chmod(ic->next, newpath, mode);
+ free(newpath);
+ }
+ return err;
+}
+
+static int iconv_chown(const char *path, uid_t uid, gid_t gid)
+{
+ 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);
+ free(newpath);
+ }
+ return err;
+}
+
+static int iconv_truncate(const char *path, loff_t size)
+{
+ 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);
+ free(newpath);
+ }
+ return err;
+}
+
+static int iconv_ftruncate(const char *path, loff_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_ftruncate(ic->next, newpath, size, fi);
+ free(newpath);
+ }
+ return err;
+}
+
+static int iconv_utimens(const char *path, const struct timespec ts[2])
+{
+ 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);
+ 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, loff_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,
+ loff_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 void *iconv_init(struct fuse_conn_info *conn)
+{
+ struct iconv *ic = iconv_get();
+ fuse_fs_init(ic->next, conn);
+ 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,
+ .fgetattr = iconv_fgetattr,
+ .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,
+ .ftruncate = iconv_ftruncate,
+ .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,
+
+ .flag_nullpath_ok = 1,
+ .flag_nopath = 1,
+};
+
+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 *old = strdup(setlocale(LC_CTYPE, ""));
+ char *charmap = strdup(nl_langinfo(CODESET));
+ setlocale(LC_CTYPE, old);
+ free(old);
+ fprintf(stderr,
+" -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;
+ char *old = NULL;
+ const char *from;
+ const char *to;
+
+ ic = calloc(1, sizeof(struct iconv));
+ if (ic == NULL) {
+ fprintf(stderr, "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]) {
+ fprintf(stderr, "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 = strdup(setlocale(LC_CTYPE, ""));
+ ic->tofs = iconv_open(from, to);
+ if (ic->tofs == (iconv_t) -1) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
+ from, to);
+ goto out_iconv_close_to;
+ }
+ if (old) {
+ setlocale(LC_CTYPE, old);
+ free(old);
+ }
+
+ 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);
+ free(old);
+ }
+ return NULL;
+}
+
+FUSE_REGISTER_MODULE(iconv, iconv_new);
diff --git a/fuse/modules/subdir.c b/fuse/modules/subdir.c
new file mode 100644
index 000000000..05b3379af
--- /dev/null
+++ b/fuse/modules/subdir.c
@@ -0,0 +1,697 @@
+/*
+ fuse subdir module: offset paths with a base directory
+ Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+
+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 subdir *d = subdir_get();
+ char *newpath;
+ int err = subdir_addpath(d, path, &newpath);
+ if (!err) {
+ err = fuse_fs_getattr(d->next, newpath, stbuf);
+ free(newpath);
+ }
+ return err;
+}
+
+static int subdir_fgetattr(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_fgetattr(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, loff_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_readdir(d->next, newpath, buf, filler, offset,
+ fi);
+ 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)
+{
+ 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);
+ 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 subdir *d = subdir_get();
+ char *newpath;
+ int err = subdir_addpath(d, path, &newpath);
+ if (!err) {
+ err = fuse_fs_chmod(d->next, newpath, mode);
+ free(newpath);
+ }
+ return err;
+}
+
+static int subdir_chown(const char *path, uid_t uid, gid_t gid)
+{
+ 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);
+ free(newpath);
+ }
+ return err;
+}
+
+static int subdir_truncate(const char *path, loff_t size)
+{
+ struct subdir *d = subdir_get();
+ char *newpath;
+ int err = subdir_addpath(d, path, &newpath);
+ if (!err) {
+ err = fuse_fs_truncate(d->next, newpath, size);
+ free(newpath);
+ }
+ return err;
+}
+
+static int subdir_ftruncate(const char *path, loff_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_ftruncate(d->next, newpath, size, fi);
+ free(newpath);
+ }
+ return err;
+}
+
+static int subdir_utimens(const char *path, const struct timespec ts[2])
+{
+ struct subdir *d = subdir_get();
+ char *newpath;
+ int err = subdir_addpath(d, path, &newpath);
+ if (!err) {
+ err = fuse_fs_utimens(d->next, newpath, ts);
+ 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, loff_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,
+ loff_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 void *subdir_init(struct fuse_conn_info *conn)
+{
+ struct subdir *d = subdir_get();
+ fuse_fs_init(d->next, conn);
+ 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,
+ .fgetattr = subdir_fgetattr,
+ .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,
+ .ftruncate = subdir_ftruncate,
+ .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,
+
+ .flag_nullpath_ok = 1,
+ .flag_nopath = 1,
+};
+
+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)
+{
+ fprintf(stderr,
+" -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) {
+ fprintf(stderr, "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]) {
+ fprintf(stderr, "fuse-subdir: exactly one next filesystem required\n");
+ goto out_free;
+ }
+
+ if (!d->base) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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);
diff --git a/fuse/mount.c b/fuse/mount.c
new file mode 100644
index 000000000..eb0bb17d3
--- /dev/null
+++ b/fuse/mount.c
@@ -0,0 +1,644 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "config.h"
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_opt.h"
+#include "fuse_common_compat.h"
+#include "mount_util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#ifdef __NetBSD__
+#include <perfuse.h>
+
+#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 umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
+#endif
+
+#define FUSERMOUNT_PROG "fusermount"
+#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
+
+#if defined(__ANDROID__) && !defined(FUSERMOUNT_DIR)
+# define FUSERMOUNT_DIR "/system/xbin"
+#endif
+
+#ifndef HAVE_FORK
+#define fork() vfork()
+#endif
+
+#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_ROOT,
+ KEY_RO,
+ KEY_HELP,
+ KEY_VERSION,
+};
+
+struct mount_opts {
+ int allow_other;
+ int allow_root;
+ int ishelp;
+ int flags;
+ int nonempty;
+ int auto_unmount;
+ int blkdev;
+ char *fsname;
+ char *subtype;
+ char *subtype_opt;
+ char *mtab_opts;
+ char *fusermount_opts;
+ char *kernel_opts;
+};
+
+#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("allow_root", allow_root),
+ FUSE_MOUNT_OPT("nonempty", nonempty),
+ FUSE_MOUNT_OPT("blkdev", blkdev),
+ FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
+ FUSE_MOUNT_OPT("fsname=%s", fsname),
+ FUSE_MOUNT_OPT("subtype=%s", subtype),
+ FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
+ FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
+ FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_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("large_read", KEY_KERN_OPT),
+ FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
+ FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
+ FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
+ FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
+ FUSE_OPT_KEY("user=", 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("atime", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
+ FUSE_OPT_KEY("-h", KEY_HELP),
+ FUSE_OPT_KEY("--help", KEY_HELP),
+ FUSE_OPT_KEY("-V", KEY_VERSION),
+ FUSE_OPT_KEY("--version", KEY_VERSION),
+ FUSE_OPT_END
+};
+
+static void mount_help(void)
+{
+ fprintf(stderr,
+" -o allow_other allow access to other users\n"
+" -o allow_root allow access to root\n"
+" -o auto_unmount auto unmount on process termination\n"
+" -o nonempty allow mounts over non-empty file/dir\n"
+" -o default_permissions enable permission checking by kernel\n"
+" -o fsname=NAME set filesystem name\n"
+" -o subtype=NAME set filesystem type\n"
+" -o large_read issue large read requests (2.4 only)\n"
+" -o max_read=N set maximum size of read requests\n"
+"\n");
+}
+
+static void exec_fusermount(const char *argv[])
+{
+ execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
+ execvp(FUSERMOUNT_PROG, (char **) argv);
+}
+
+static void mount_version(void)
+{
+ int pid = fork();
+ if (!pid) {
+ const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
+ exec_fusermount(argv);
+ _exit(1);
+ } else if (pid != -1)
+ waitpid(pid, NULL, 0);
+}
+
+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},
+ {"atime", MS_NOATIME, 0},
+ {"noatime", MS_NOATIME, 1},
+#ifndef __NetBSD__
+ {"dirsync", MS_DIRSYNC, 1},
+#endif
+ {NULL, 0, 0}
+};
+
+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;
+ }
+ }
+ fprintf(stderr, "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)
+{
+ struct mount_opts *mo = data;
+
+ switch (key) {
+ case KEY_ALLOW_ROOT:
+ if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
+ fuse_opt_add_arg(outargs, "-oallow_root") == -1)
+ return -1;
+ return 0;
+
+ 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);
+
+ case KEY_HELP:
+ mount_help();
+ mo->ishelp = 1;
+ break;
+
+ case KEY_VERSION:
+ mount_version();
+ mo->ishelp = 1;
+ break;
+ }
+ 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) {
+ perror("recvmsg");
+ return -1;
+ }
+ if(!rv) {
+ /* EOF */
+ return -1;
+ }
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg->cmsg_type == SCM_RIGHTS) {
+ fprintf(stderr, "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;
+ int pid;
+
+ if (!mountpoint)
+ return;
+
+ 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 */
+ 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;
+
+ pid = fork();
+ if(pid == -1)
+ return;
+
+ if(pid == 0) {
+ const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
+ "--", mountpoint, NULL };
+
+ exec_fusermount(argv);
+ _exit(1);
+ }
+ waitpid(pid, NULL, 0);
+}
+
+void fuse_unmount_compat22(const char *mountpoint)
+{
+ fuse_kern_unmount(mountpoint, -1);
+}
+
+static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
+ const char *opts, int quiet)
+{
+ int fds[2], pid;
+ int res;
+ int rv;
+
+ if (!mountpoint) {
+ fprintf(stderr, "fuse: missing mountpoint parameter\n");
+ return -1;
+ }
+
+ res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
+ if(res == -1) {
+ perror("fuse: socketpair() failed");
+ return -1;
+ }
+
+ pid = fork();
+ if(pid == -1) {
+ perror("fuse: fork() failed");
+ close(fds[0]);
+ close(fds[1]);
+ return -1;
+ }
+
+ if(pid == 0) {
+ char env[10];
+ const char *argv[32];
+ int a = 0;
+
+ if (quiet) {
+ int fd = open("/dev/null", O_RDONLY);
+ if (fd != -1) {
+ dup2(fd, 1);
+ dup2(fd, 2);
+ }
+ }
+
+ argv[a++] = FUSERMOUNT_PROG;
+ if (opts) {
+ argv[a++] = "-o";
+ argv[a++] = opts;
+ }
+ argv[a++] = "--";
+ argv[a++] = mountpoint;
+ argv[a++] = NULL;
+
+ close(fds[1]);
+ fcntl(fds[0], F_SETFD, 0);
+ snprintf(env, sizeof(env), "%i", fds[0]);
+ setenv(FUSE_COMMFD_ENV, env, 1);
+ exec_fusermount(argv);
+ perror("fuse: failed to exec fusermount");
+ _exit(1);
+ }
+
+ close(fds[0]);
+ rv = receive_fd(fds[1]);
+
+ if (!mo->auto_unmount) {
+ /* with auto_unmount option fusermount will not exit until
+ this socket is closed */
+ close(fds[1]);
+ waitpid(pid, NULL, 0); /* bury zombie */
+ }
+
+ return rv;
+}
+
+int fuse_mount_compat22(const char *mountpoint, const char *opts)
+{
+ struct mount_opts mo;
+ memset(&mo, 0, sizeof(mo));
+ mo.flags = MS_NOSUID | MS_NODEV;
+
+ return fuse_mount_fusermount(mountpoint, &mo, opts, 0);
+}
+
+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) {
+ fprintf(stderr, "fuse: missing mountpoint parameter\n");
+ return -1;
+ }
+
+ res = stat(mnt, &stbuf);
+ if (res == -1) {
+ fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
+ mnt, strerror(errno));
+ return -1;
+ }
+
+ if (!mo->nonempty) {
+ res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode,
+ stbuf.st_size);
+ if (res == -1)
+ return -1;
+ }
+
+ if (mo->auto_unmount) {
+ /* Tell the caller to fallback to fusermount because
+ auto-unmount does not work otherwise. */
+ return -2;
+ }
+
+ fd = open(devname, O_RDWR);
+ if (fd == -1) {
+ if (errno == ENODEV || errno == ENOENT)
+ fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
+ else
+ fprintf(stderr, "fuse: failed to open %s: %s\n",
+ devname, strerror(errno));
+ return -1;
+ }
+
+ 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) {
+ fprintf(stderr, "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 fusermount
+ */
+ if (errno == EPERM) {
+ res = -2;
+ } else {
+ int errno_save = errno;
+ if (mo->blkdev && errno == ENODEV &&
+ !fuse_mnt_check_fuseblk())
+ fprintf(stderr,
+ "fuse: 'fuseblk' support missing\n");
+ else
+ fprintf(stderr, "fuse: mount failed: %s\n",
+ strerror(errno_save));
+ }
+
+ goto out_close;
+ }
+
+#ifndef __NetBSD__
+#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 */
+#endif /* __NetBSD__ */
+ 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;
+}
+
+int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
+{
+ struct mount_opts mo;
+ int res = -1;
+ char *mnt_opts = NULL;
+
+ memset(&mo, 0, sizeof(mo));
+ mo.flags = MS_NOSUID | MS_NODEV;
+
+ if (args &&
+ fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
+ return -1;
+
+ if (mo.allow_other && mo.allow_root) {
+ fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
+ goto out;
+ }
+ res = 0;
+ if (mo.ishelp)
+ goto out;
+
+ 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 == -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);
+ free(mo.fsname);
+ free(mo.subtype);
+ free(mo.fusermount_opts);
+ free(mo.subtype_opt);
+ free(mo.kernel_opts);
+ free(mo.mtab_opts);
+ return res;
+}
+
+FUSE_SYMVER(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
+FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
diff --git a/fuse/mount_bsd.c b/fuse/mount_bsd.c
new file mode 100644
index 000000000..3aec3e3eb
--- /dev/null
+++ b/fuse/mount_bsd.c
@@ -0,0 +1,391 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "fuse_i.h"
+#include "fuse_misc.h"
+#include "fuse_opt.h"
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <paths.h>
+#include <limits.h>
+
+#define FUSERMOUNT_PROG "mount_fusefs"
+#define FUSE_DEV_TRUNK "/dev/fuse"
+
+enum {
+ KEY_ALLOW_ROOT,
+ KEY_RO,
+ KEY_HELP,
+ KEY_VERSION,
+ KEY_KERN
+};
+
+struct mount_opts {
+ int allow_other;
+ int allow_root;
+ int ishelp;
+ char *kernel_opts;
+};
+
+#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 },
+ { "allow_root", offsetof(struct mount_opts, allow_root), 1 },
+ FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
+ FUSE_OPT_KEY("-r", KEY_RO),
+ FUSE_OPT_KEY("-h", KEY_HELP),
+ FUSE_OPT_KEY("--help", KEY_HELP),
+ FUSE_OPT_KEY("-V", KEY_VERSION),
+ FUSE_OPT_KEY("--version", KEY_VERSION),
+ /* standard FreeBSD mount options */
+ FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("async", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("union", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("force", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("update", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
+ /* options supported under both Linux and FBSD */
+ FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
+ FUSE_OPT_KEY("max_read=", KEY_KERN),
+ FUSE_OPT_KEY("subtype=", KEY_KERN),
+ /* FBSD FUSE specific mount options */
+ FUSE_DUAL_OPT_KEY("private", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
+ FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
+ FUSE_OPT_KEY("nosync_unmount", KEY_KERN),
+ /* stock FBSD mountopt parsing routine lets anything be negated... */
+ /*
+ * Linux specific mount options, but let just the mount util
+ * handle them
+ */
+ FUSE_OPT_KEY("fsname=", KEY_KERN),
+ FUSE_OPT_KEY("nonempty", KEY_KERN),
+ FUSE_OPT_KEY("large_read", KEY_KERN),
+ FUSE_OPT_END
+};
+
+static void mount_help(void)
+{
+ fprintf(stderr,
+ " -o allow_root allow access to root\n"
+ );
+ system(FUSERMOUNT_PROG " --help");
+ fputc('\n', stderr);
+}
+
+static void mount_version(void)
+{
+ system(FUSERMOUNT_PROG " --version");
+}
+
+static int fuse_mount_opt_proc(void *data, const char *arg, int key,
+ struct fuse_args *outargs)
+{
+ struct mount_opts *mo = data;
+
+ switch (key) {
+ case KEY_ALLOW_ROOT:
+ if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
+ fuse_opt_add_arg(outargs, "-oallow_root") == -1)
+ return -1;
+ return 0;
+
+ case KEY_RO:
+ arg = "ro";
+ /* fall through */
+
+ case KEY_KERN:
+ return fuse_opt_add_opt(&mo->kernel_opts, arg);
+
+ case KEY_HELP:
+ mount_help();
+ mo->ishelp = 1;
+ break;
+
+ case KEY_VERSION:
+ mount_version();
+ mo->ishelp = 1;
+ break;
+ }
+ return 1;
+}
+
+void fuse_unmount_compat22(const char *mountpoint)
+{
+ char dev[128];
+ char *ssc, *umount_cmd;
+ FILE *sf;
+ int rv;
+ char seekscript[] =
+ /* error message is annoying in help output */
+ "exec 2>/dev/null; "
+ "/usr/bin/fstat " FUSE_DEV_TRUNK "* | "
+ "/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; "
+ " { if ($3 == %d) print $10; }' | "
+ "/usr/bin/sort | "
+ "/usr/bin/uniq | "
+ "/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'";
+
+ (void) mountpoint;
+
+ /*
+ * If we don't know the fd, we have to resort to the scripted
+ * solution -- iterating over the fd-s is unpractical, as we
+ * don't know how many of open files we have. (This could be
+ * looked up in procfs -- however, that's optional on FBSD; or
+ * read out from the kmem -- however, that's bound to
+ * privileges (in fact, that's what happens when we call the
+ * setgid kmem fstat(1) utility).
+ */
+ if (asprintf(&ssc, seekscript, getpid()) == -1)
+ return;
+
+ errno = 0;
+ sf = popen(ssc, "r");
+ free(ssc);
+ if (! sf)
+ return;
+
+ fgets(dev, sizeof(dev), sf);
+ rv = pclose(sf);
+ if (rv)
+ return;
+
+ if (asprintf(&umount_cmd, "/sbin/umount %s", dev) == -1)
+ return;
+ system(umount_cmd);
+ free(umount_cmd);
+}
+
+static void do_unmount(char *dev, int fd)
+{
+ char device_path[SPECNAMELEN + 12];
+ const char *argv[4];
+ const char umount_cmd[] = "/sbin/umount";
+ pid_t pid;
+
+ snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev);
+
+ argv[0] = umount_cmd;
+ argv[1] = "-f";
+ argv[2] = device_path;
+ argv[3] = NULL;
+
+ pid = fork();
+
+ if (pid == -1)
+ return;
+
+ if (pid == 0) {
+ close(fd);
+ execvp(umount_cmd, (char **)argv);
+ exit(1);
+ }
+
+ waitpid(pid, NULL, 0);
+}
+
+void fuse_kern_unmount(const char *mountpoint, int fd)
+{
+ char *ep, dev[128];
+ struct stat sbuf;
+
+ (void)mountpoint;
+
+ if (fstat(fd, &sbuf) == -1)
+ goto out;
+
+ devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
+
+ if (strncmp(dev, "fuse", 4))
+ goto out;
+
+ strtol(dev + 4, &ep, 10);
+ if (*ep != '\0')
+ goto out;
+
+ do_unmount(dev, fd);
+
+out:
+ close(fd);
+}
+
+/* Check if kernel is doing init in background */
+static int init_backgrounded(void)
+{
+ unsigned ibg, len;
+
+ len = sizeof(ibg);
+
+ if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
+ return 0;
+
+ return ibg;
+}
+
+
+static int fuse_mount_core(const char *mountpoint, const char *opts)
+{
+ const char *mountprog = FUSERMOUNT_PROG;
+ int fd;
+ char *fdnam, *dev;
+ pid_t pid, cpid;
+ int status;
+
+ fdnam = getenv("FUSE_DEV_FD");
+
+ if (fdnam) {
+ char *ep;
+
+ fd = strtol(fdnam, &ep, 10);
+
+ if (*ep != '\0') {
+ fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
+ return -1;
+ }
+
+ if (fd < 0)
+ 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) {
+ if (! init_backgrounded()) {
+ /*
+ * If init is not backgrounded, we have to
+ * call the mount util backgrounded, to avoid
+ * deadlock.
+ */
+
+ pid = fork();
+
+ if (pid == -1) {
+ perror("fuse: fork() failed");
+ close(fd);
+ exit(1);
+ }
+ }
+
+ if (pid == 0) {
+ const char *argv[32];
+ int a = 0;
+
+ if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
+ perror("fuse: failed to assemble mount arguments");
+ exit(1);
+ }
+
+ argv[a++] = mountprog;
+ if (opts) {
+ argv[a++] = "-o";
+ argv[a++] = opts;
+ }
+ argv[a++] = fdnam;
+ argv[a++] = mountpoint;
+ argv[a++] = NULL;
+ execvp(mountprog, (char **) argv);
+ perror("fuse: failed to exec mount program");
+ exit(1);
+ }
+
+ exit(0);
+ }
+
+ if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
+ perror("fuse: failed to mount file system");
+ close(fd);
+ return -1;
+ }
+
+out:
+ return fd;
+}
+
+int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
+{
+ struct mount_opts mo;
+ int res = -1;
+
+ memset(&mo, 0, sizeof(mo));
+ /* mount util should not try to spawn the daemon */
+ setenv("MOUNT_FUSEFS_SAFE", "1", 1);
+ /* to notify the mount util it's called from lib */
+ setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
+
+ if (args &&
+ fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
+ return -1;
+
+ if (mo.allow_other && mo.allow_root) {
+ fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
+ goto out;
+ }
+ if (mo.ishelp)
+ return 0;
+
+ res = fuse_mount_core(mountpoint, mo.kernel_opts);
+out:
+ free(mo.kernel_opts);
+ return res;
+}
+
+FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
diff --git a/fuse/mount_util.c b/fuse/mount_util.c
new file mode 100644
index 000000000..020b223dc
--- /dev/null
+++ b/fuse/mount_util.c
@@ -0,0 +1,368 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include "mount_util.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <paths.h>
+#ifndef __NetBSD__
+#include <mntent.h>
+#endif
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#if defined(__ANDROID__)
+#include <paths.h>
+#endif
+
+#ifdef __NetBSD__
+#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
+#define mtab_needs_update(mnt) 0
+#elif defined(__ANDROID__)
+#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 a symlink,
+ * - is on a read-only filesystem.
+ */
+ res = lstat(_PATH_MOUNTED, &stbuf);
+ if (res == -1) {
+ if (errno == ENOENT)
+ return 0;
+ } else {
+ uid_t ruid;
+ int err;
+
+ if (S_ISLNK(stbuf.st_mode))
+ return 0;
+
+ 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 /* __NetBSD__ */
+
+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);
+ setuid(geteuid());
+ execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
+ "-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
+ fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+ res = waitpid(res, &status, 0);
+ if (res == -1)
+ fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
+
+ if (status != 0)
+ res = -1;
+
+ 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);
+ setuid(geteuid());
+ if (lazy) {
+ execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
+ "-l", NULL, &env);
+ } else {
+ execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
+ NULL, &env);
+ }
+ fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+ res = waitpid(res, &status, 0);
+ if (res == -1)
+ fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
+
+ if (status != 0) {
+ res = -1;
+ }
+
+ 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);
+ setuid(geteuid());
+ execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
+ "--fake", mnt, NULL, &env);
+ fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+ res = waitpid(res, &status, 0);
+ if (res == -1)
+ fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
+
+ if (status != 0)
+ res = -1;
+
+ 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_empty(const char *progname, const char *mnt,
+ mode_t rootmode, loff_t rootsize)
+{
+ int isempty = 1;
+
+ if (S_ISDIR(rootmode)) {
+ struct dirent *ent;
+ DIR *dp = opendir(mnt);
+ if (dp == NULL) {
+ fprintf(stderr,
+ "%s: failed to open mountpoint for reading: %s\n",
+ progname, strerror(errno));
+ return -1;
+ }
+ while ((ent = readdir(dp)) != NULL) {
+ if (strcmp(ent->d_name, ".") != 0 &&
+ strcmp(ent->d_name, "..") != 0) {
+ isempty = 0;
+ break;
+ }
+ }
+ closedir(dp);
+ } else if (rootsize)
+ isempty = 0;
+
+ if (!isempty) {
+ fprintf(stderr, "%s: mountpoint is not empty\n", progname);
+ fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
+ return -1;
+ }
+ return 0;
+}
+
+int fuse_mnt_check_fuseblk(void)
+{
+ char buf[256];
+ FILE *f = fopen("/proc/filesystems", "r");
+ if (!f)
+ return 1;
+
+ while (fgets(buf, sizeof(buf), f))
+ if (strstr(buf, "fuseblk\n")) {
+ fclose(f);
+ return 1;
+ }
+
+ fclose(f);
+ return 0;
+}
diff --git a/fuse/mount_util.h b/fuse/mount_util.h
new file mode 100644
index 000000000..0e0f564d7
--- /dev/null
+++ b/fuse/mount_util.h
@@ -0,0 +1,19 @@
+/*
+ FUSE: Filesystem in Userspace
+ Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB.
+*/
+
+#include <sys/types.h>
+
+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_empty(const char *progname, const char *mnt,
+ mode_t rootmode, loff_t rootsize);
+int fuse_mnt_check_fuseblk(void);
diff --git a/fuse/ulockmgr.c b/fuse/ulockmgr.c
new file mode 100644
index 000000000..ebd68c6a7
--- /dev/null
+++ b/fuse/ulockmgr.c
@@ -0,0 +1,444 @@
+/*
+ libulockmgr: Userspace Lock Manager Library
+ Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
+
+ This program can be distributed under the terms of the GNU LGPLv2.
+ See the file COPYING.LIB
+*/
+
+/* #define DEBUG 1 */
+
+#include "ulockmgr.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+struct message {
+ unsigned intr : 1;
+ unsigned nofd : 1;
+ pthread_t thr;
+ int cmd;
+ int fd;
+ struct flock lock;
+ int error;
+};
+
+struct fd_store {
+ struct fd_store *next;
+ int fd;
+ int inuse;
+};
+
+struct owner {
+ struct owner *next;
+ struct owner *prev;
+ struct fd_store *fds;
+ void *id;
+ size_t id_len;
+ int cfd;
+};
+
+static pthread_mutex_t ulockmgr_lock;
+static int ulockmgr_cfd = -1;
+static struct owner owner_list = { .next = &owner_list, .prev = &owner_list };
+
+#define MAX_SEND_FDS 2
+
+static void list_del_owner(struct owner *owner)
+{
+ struct owner *prev = owner->prev;
+ struct owner *next = owner->next;
+ prev->next = next;
+ next->prev = prev;
+}
+
+static void list_add_owner(struct owner *owner, struct owner *next)
+{
+ struct owner *prev = next->prev;
+ owner->next = next;
+ owner->prev = prev;
+ prev->next = owner;
+ next->prev = owner;
+}
+
+/*
+ * There's a bug in the linux kernel (< 2.6.22) recv() implementation
+ * on AF_UNIX, SOCK_STREAM sockets, that could cause it to return
+ * zero, even if data was available. Retrying the recv will return
+ * the data in this case.
+ */
+static int do_recv(int sock, void *buf, size_t len, int flags)
+{
+ int res = recv(sock, buf, len, flags);
+ if (res == 0)
+ res = recv(sock, buf, len, flags);
+
+ return res;
+}
+
+static int ulockmgr_send_message(int sock, void *buf, size_t buflen,
+ int *fdp, int numfds)
+{
+ struct msghdr msg;
+ struct cmsghdr *p_cmsg;
+ struct iovec vec;
+ size_t cmsgbuf[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
+ int res;
+
+ assert(numfds <= MAX_SEND_FDS);
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+ p_cmsg = CMSG_FIRSTHDR(&msg);
+ p_cmsg->cmsg_level = SOL_SOCKET;
+ p_cmsg->cmsg_type = SCM_RIGHTS;
+ p_cmsg->cmsg_len = CMSG_LEN(sizeof(int) * numfds);
+ memcpy(CMSG_DATA(p_cmsg), fdp, sizeof(int) * numfds);
+ msg.msg_controllen = p_cmsg->cmsg_len;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ vec.iov_base = buf;
+ vec.iov_len = buflen;
+ res = sendmsg(sock, &msg, MSG_NOSIGNAL);
+ if (res == -1) {
+ perror("libulockmgr: sendmsg");
+ return -1;
+ }
+ if ((size_t) res != buflen) {
+ fprintf(stderr, "libulockmgr: sendmsg short\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int ulockmgr_start_daemon(void)
+{
+ int sv[2];
+ int res;
+ char tmp[64];
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ if (res == -1) {
+ perror("libulockmgr: socketpair");
+ return -1;
+ }
+ snprintf(tmp, sizeof(tmp), "exec ulockmgr_server %i", sv[0]);
+ res = system(tmp);
+ close(sv[0]);
+ if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) {
+ close(sv[1]);
+ return -1;
+ }
+ ulockmgr_cfd = sv[1];
+ return 0;
+}
+
+static struct owner *ulockmgr_new_owner(const void *id, size_t id_len)
+{
+ int sv[2];
+ int res;
+ char c = 'm';
+ struct owner *o;
+
+ if (ulockmgr_cfd == -1 && ulockmgr_start_daemon() == -1)
+ return NULL;
+
+ o = calloc(1, sizeof(struct owner) + id_len);
+ if (!o) {
+ fprintf(stderr, "libulockmgr: failed to allocate memory\n");
+ return NULL;
+ }
+ o->id = o + 1;
+ o->id_len = id_len;
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ if (res == -1) {
+ perror("libulockmgr: socketpair");
+ goto out_free;
+ }
+ res = ulockmgr_send_message(ulockmgr_cfd, &c, sizeof(c), &sv[0], 1);
+ close(sv[0]);
+ if (res == -1) {
+ close(ulockmgr_cfd);
+ ulockmgr_cfd = -1;
+ goto out_close;
+ }
+
+ o->cfd = sv[1];
+ memcpy(o->id, id, id_len);
+ list_add_owner(o, &owner_list);
+
+ return o;
+
+out_close:
+ close(sv[1]);
+out_free:
+ free(o);
+ return NULL;
+}
+
+static int ulockmgr_send_request(struct message *msg, const void *id,
+ size_t id_len)
+{
+ int sv[2];
+ int cfd;
+ struct owner *o;
+ struct fd_store *f = NULL;
+ struct fd_store *newf = NULL;
+ struct fd_store **fp;
+ int fd = msg->fd;
+ int cmd = msg->cmd;
+ int res;
+ int unlockall = (cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
+ msg->lock.l_start == 0 && msg->lock.l_len == 0);
+
+ for (o = owner_list.next; o != &owner_list; o = o->next)
+ if (o->id_len == id_len && memcmp(o->id, id, id_len) == 0)
+ break;
+
+ if (o == &owner_list)
+ o = NULL;
+
+ if (!o && cmd != F_GETLK && msg->lock.l_type != F_UNLCK)
+ o = ulockmgr_new_owner(id, id_len);
+
+ if (!o) {
+ if (cmd == F_GETLK) {
+ res = fcntl(msg->fd, F_GETLK, &msg->lock);
+ return (res == -1) ? -errno : 0;
+ } else if (msg->lock.l_type == F_UNLCK)
+ return 0;
+ else
+ return -ENOLCK;
+ }
+
+ if (unlockall)
+ msg->nofd = 1;
+ else {
+ for (fp = &o->fds; *fp; fp = &(*fp)->next) {
+ f = *fp;
+ if (f->fd == fd) {
+ msg->nofd = 1;
+ break;
+ }
+ }
+ }
+
+ if (!msg->nofd) {
+ newf = f = calloc(1, sizeof(struct fd_store));
+ if (!f) {
+ fprintf(stderr, "libulockmgr: failed to allocate memory\n");
+ return -ENOLCK;
+ }
+ }
+
+ res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ if (res == -1) {
+ perror("libulockmgr: socketpair");
+ free(newf);
+ return -ENOLCK;
+ }
+
+ cfd = sv[1];
+ sv[1] = msg->fd;
+ res = ulockmgr_send_message(o->cfd, msg, sizeof(struct message), sv,
+ msg->nofd ? 1 : 2);
+ close(sv[0]);
+ if (res == -1) {
+ free(newf);
+ close(cfd);
+ return -EIO;
+ }
+
+ if (newf) {
+ newf->fd = msg->fd;
+ newf->next = o->fds;
+ o->fds = newf;
+ }
+ if (f)
+ f->inuse++;
+
+ res = do_recv(cfd, msg, sizeof(struct message), MSG_WAITALL);
+ if (res == -1) {
+ perror("libulockmgr: recv");
+ msg->error = EIO;
+ } else if (res != sizeof(struct message)) {
+ fprintf(stderr, "libulockmgr: recv short\n");
+ msg->error = EIO;
+ } else if (cmd == F_SETLKW && msg->error == EAGAIN) {
+ pthread_mutex_unlock(&ulockmgr_lock);
+ while (1) {
+ sigset_t old;
+ sigset_t unblock;
+ int errno_save;
+
+ sigemptyset(&unblock);
+ sigaddset(&unblock, SIGUSR1);
+ pthread_sigmask(SIG_UNBLOCK, &unblock, &old);
+ res = do_recv(cfd, msg, sizeof(struct message),
+ MSG_WAITALL);
+ errno_save = errno;
+ pthread_sigmask(SIG_SETMASK, &old, NULL);
+ if (res == sizeof(struct message))
+ break;
+ else if (res >= 0) {
+ fprintf(stderr, "libulockmgr: recv short\n");
+ msg->error = EIO;
+ break;
+ } else if (errno_save != EINTR) {
+ errno = errno_save;
+ perror("libulockmgr: recv");
+ msg->error = EIO;
+ break;
+ }
+ msg->intr = 1;
+ res = send(o->cfd, msg, sizeof(struct message),
+ MSG_NOSIGNAL);
+ if (res == -1) {
+ perror("libulockmgr: send");
+ msg->error = EIO;
+ break;
+ }
+ if (res != sizeof(struct message)) {
+ fprintf(stderr, "libulockmgr: send short\n");
+ msg->error = EIO;
+ break;
+ }
+ }
+ pthread_mutex_lock(&ulockmgr_lock);
+
+ }
+ if (f)
+ f->inuse--;
+ close(cfd);
+ if (unlockall) {
+ for (fp = &o->fds; *fp;) {
+ f = *fp;
+ if (f->fd == fd && !f->inuse) {
+ *fp = f->next;
+ free(f);
+ } else
+ fp = &f->next;
+ }
+ if (!o->fds) {
+ list_del_owner(o);
+ close(o->cfd);
+ free(o);
+ }
+ /* Force OK on unlock-all, since it _will_ succeed once the
+ owner is deleted */
+ msg->error = 0;
+ }
+
+ return -msg->error;
+}
+
+#ifdef DEBUG
+static uint32_t owner_hash(const unsigned char *id, size_t id_len)
+{
+ uint32_t h = 0;
+ size_t i;
+ for (i = 0; i < id_len; i++)
+ h = ((h << 8) | (h >> 24)) ^ id[i];
+
+ return h;
+}
+#endif
+
+static int ulockmgr_canonicalize(int fd, struct flock *lock)
+{
+ loff_t offset;
+ if (lock->l_whence == SEEK_CUR) {
+ offset = lseek(fd, 0, SEEK_CUR);
+ if (offset == (loff_t) -1)
+ return -errno;
+ } else if (lock->l_whence == SEEK_END) {
+ struct stat stbuf;
+ int res = fstat(fd, &stbuf);
+ if (res == -1)
+ return -errno;
+
+ offset = stbuf.st_size;
+ } else
+ offset = 0;
+
+ lock->l_whence = SEEK_SET;
+ lock->l_start += offset;
+
+ if (lock->l_start < 0)
+ return -EINVAL;
+
+ if (lock->l_len < 0) {
+ lock->l_start += lock->l_len;
+ if (lock->l_start < 0)
+ return -EINVAL;
+ lock->l_len = -lock->l_len;
+ }
+ if (lock->l_len && lock->l_start + lock->l_len - 1 < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
+ size_t owner_len)
+{
+ int err;
+ struct message msg;
+ sigset_t old;
+ sigset_t block;
+
+ if (cmd != F_GETLK && cmd != F_SETLK && cmd != F_SETLKW)
+ return -EINVAL;
+
+ if (lock->l_type != F_RDLCK && lock->l_type != F_WRLCK &&
+ lock->l_type != F_UNLCK)
+ return -EINVAL;
+
+ if (lock->l_whence != SEEK_SET && lock->l_whence != SEEK_CUR &&
+ lock->l_whence != SEEK_END)
+ return -EINVAL;
+
+#ifdef DEBUG
+ fprintf(stderr, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n",
+ cmd, lock->l_type, lock->l_whence, lock->l_start, lock->l_len,
+ owner_hash(owner, owner_len));
+#endif
+
+ /* Unlock should never block anyway */
+ if (cmd == F_SETLKW && lock->l_type == F_UNLCK)
+ cmd = F_SETLK;
+
+ memset(&msg, 0, sizeof(struct message));
+ msg.cmd = cmd;
+ msg.fd = fd;
+ msg.lock = *lock;
+ err = ulockmgr_canonicalize(fd, &msg.lock);
+ if (err)
+ return err;
+
+ sigemptyset(&block);
+ sigaddset(&block, SIGUSR1);
+ pthread_sigmask(SIG_BLOCK, &block, &old);
+ pthread_mutex_lock(&ulockmgr_lock);
+ err = ulockmgr_send_request(&msg, owner, owner_len);
+ pthread_mutex_unlock(&ulockmgr_lock);
+ pthread_sigmask(SIG_SETMASK, &old, NULL);
+ if (!err && cmd == F_GETLK) {
+ if (msg.lock.l_type == F_UNLCK)
+ lock->l_type = F_UNLCK;
+ else
+ *lock = msg.lock;
+ }
+
+ return err;
+}