diff --git a/manifest b/manifest
index 88c8ce4d45..478a58abac 100644
--- a/manifest
+++ b/manifest
@@ -85,7 +85,8 @@ s dev/dump=../devices/pseudo/dump@0:dump
 d dev/fd 0555 root root
 s dev/fm=../devices/pseudo/fm@0:fm
 s dev/fssnapctl=../devices/pseudo/fssnap@0:ctl
-s dev/full=../devices/pseudo/mm@0:full
+s dev/fuse=../devices/pseudo/fuse@0:fuse
+s dev/allkmem=../devices/pseudo/mm@0:allkmem
 s dev/icmp6=../devices/pseudo/icmp6@0:icmp6
 s dev/icmp=../devices/pseudo/icmp@0:icmp
 s dev/igb=../devices/pseudo/clone@0:igb
@@ -770,6 +771,7 @@ f kernel/drv/amd64/sgen 0755 root sys
 f kernel/drv/amd64/si3124 0755 root sys
 f kernel/drv/amd64/simnet 0755 root sys
 f kernel/drv/amd64/feth 0755 root sys
+f kernel/drv/amd64/fuse 0755 root sys
 f kernel/drv/amd64/skd 0755 root sys
 f kernel/drv/amd64/smbios 0755 root sys
 f kernel/drv/amd64/smntemp 0755 root sys
@@ -927,6 +929,7 @@ f kernel/drv/ses.conf 0644 root sys
 f kernel/drv/sgen.conf 0644 root sys
 f kernel/drv/simnet.conf 0644 root sys
 f kernel/drv/feth.conf 0644 root sys
+f kernel/drv/fuse.conf 0644 root sys
 f kernel/drv/skd.conf 0644 root sys
 f kernel/drv/smbios.conf 0644 root sys
 f kernel/drv/smp.conf 0644 root sys
@@ -2631,6 +2634,7 @@ f usr/bin/fmtmsg 0555 root bin
 f usr/bin/fold 0555 root bin
 f usr/bin/fsstat 0555 root bin
 f usr/bin/ftp 0555 root bin
+f usr/bin/fusermount 0555 root bin
 h usr/bin/gcore=usr/lib/isaexec
 f usr/bin/gencat 0555 root bin
 f usr/bin/geniconvtbl 0555 root bin
@@ -3234,6 +3238,18 @@ f usr/include/fnmatch.h 0644 root bin
 f usr/include/form.h 0644 root bin
 f usr/include/fts.h 0644 root bin
 f usr/include/ftw.h 0644 root bin
+d usr/include/fuse 0755 root bin
+f usr/include/fuse/config.h 0644 root bin
+f usr/include/fuse/fuse.h 0644 root bin
+f usr/include/fuse/fuse_common.h 0644 root bin
+f usr/include/fuse/fuse_common_compat.h 0644 root bin
+f usr/include/fuse/fuse_compat.h 0644 root bin
+f usr/include/fuse/fuse_i.h 0644 root bin
+f usr/include/fuse/fuse_kernel.h 0644 root bin
+f usr/include/fuse/fuse_lowlevel.h 0644 root bin
+f usr/include/fuse/fuse_lowlevel_compat.h 0644 root bin
+f usr/include/fuse/fuse_misc.h 0644 root bin
+f usr/include/fuse/fuse_opt.h 0644 root bin
 d usr/include/fwflash 0755 root bin
 f usr/include/fwflash/fwflash.h 0644 root bin
 f usr/include/gelf.h 0644 root bin
@@ -5258,6 +5274,8 @@ f usr/lib/amd64/libfrureg.so.1 0755 root bin
 s usr/lib/amd64/libfrureg.so=libfrureg.so.1
 f usr/lib/amd64/libfruutils.so.1 0755 root bin
 s usr/lib/amd64/libfruutils.so=libfruutils.so.1
+f usr/lib/amd64/libfuse.so.2.7.1 0755 root bin
+s usr/lib/amd64/libfuse.so=libfuse.so.2.7.1
 s usr/lib/amd64/libgen.so.1=../../../lib/amd64/libgen.so.1
 s usr/lib/amd64/libgen.so=../../../lib/amd64/libgen.so.1
 f usr/lib/amd64/libgss.so.1 0755 root bin
@@ -5943,6 +5961,10 @@ d usr/lib/fs/dev 0755 root sys
 s usr/lib/fs/dev/mount=../../../../etc/fs/dev/mount
 d usr/lib/fs/fd 0755 root sys
 f usr/lib/fs/fd/mount 0555 root bin
+d usr/lib/fs/fuse 0755 root sys
+f usr/lib/fs/fuse/fusermount.bin 0555 root bin
+f usr/lib/fs/fuse/mount 0555 root bin
+f usr/lib/fs/fuse/umount 0555 root bin
 d usr/lib/fs/hsfs 0755 root sys
 f usr/lib/fs/hsfs/fstyp.so.1 0555 root bin
 h usr/lib/fs/hsfs/fstyp=usr/sbin/fstyp
@@ -6757,6 +6779,8 @@ f usr/lib/libfsmgt.so.1 0755 root bin
 s usr/lib/libfsmgt.so=libfsmgt.so.1
 f usr/lib/libfstyp.so.1 0755 root bin
 s usr/lib/libfstyp.so=libfstyp.so.1
+f usr/lib/libfuse.so.2.7.1 0755 root bin
+s usr/lib/libfuse.so=libfuse.so.1
 s usr/lib/libgen.so.1=../../lib/libgen.so.1
 s usr/lib/libgen.so=../../lib/libgen.so.1
 s usr/lib/libgrubmgmt.so.1=../../lib/libgrubmgmt.so.1
@@ -12477,6 +12501,7 @@ f usr/share/man/man1m/fssnap_ufs.1m 0444 root bin
 f usr/share/man/man1m/fsstat.1m 0444 root bin
 f usr/share/man/man1m/fstyp.1m 0444 root bin
 f usr/share/man/man1m/fuser.1m 0444 root bin
+f usr/share/man/man1m/fuse_mount.1m 0444 root bin
 f usr/share/man/man1m/fwflash.1m 0444 root bin
 f usr/share/man/man1m/getdev.1m 0444 root bin
 f usr/share/man/man1m/getdevpolicy.1m 0444 root bin
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 103bc26923..94f050586b 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -179,6 +179,7 @@ COMMON_SUBDIRS=		\
 	format		\
 	fs.d		\
 	fstyp		\
+	fuse		\
 	fuser		\
 	fwflash		\
 	gcore		\
diff --git a/usr/src/cmd/fuse/COPYING.LIB b/usr/src/cmd/fuse/COPYING.LIB
new file mode 100644
index 0000000000..161a3d1d47
--- /dev/null
+++ b/usr/src/cmd/fuse/COPYING.LIB
@@ -0,0 +1,482 @@
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+			    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 Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, 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 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 a program 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.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, 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 companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  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, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+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 compile 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) 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.
+
+    c) 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.
+
+    d) 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 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.
+
+  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 to
+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 Library 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
+
+     Appendix: 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 Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+    MA 02111-1307, 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/usr/src/cmd/fuse/INSTALL b/usr/src/cmd/fuse/INSTALL
new file mode 100644
index 0000000000..5bb9349572
--- /dev/null
+++ b/usr/src/cmd/fuse/INSTALL
@@ -0,0 +1,34 @@
+Building
+---
+
+Requirements:
+    Solaris Nevada >= build 62
+    Sun Studio 11
+
+Build the FUSE library and utilities:
+$ make
+
+Note: gmake(1) will fail to build the FUSE library.
+
+
+Installing
+---
+
+Populate the proto area:
+$ make install
+
+Generate the package:
+$ make pkg
+
+Add the SUNWlibfuse package to the system (as root):
+# pkgadd -d packages SUNWlibfuse
+
+Assign the execution profile to a user (as root):
+# usermod -P "FUSE File System Management" <username>
+
+
+Uninstalling
+---
+
+Remove the SUNWlibfuse from the system (as root):
+# pkgrm SUNWlibfuse
diff --git a/usr/src/cmd/fuse/Makefile b/usr/src/cmd/fuse/Makefile
new file mode 100644
index 0000000000..6ab60bb749
--- /dev/null
+++ b/usr/src/cmd/fuse/Makefile
@@ -0,0 +1,45 @@
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/fuse/Makefile
+
+include ../Makefile.cmd
+
+SUBDIRS=	$(MACH)	
+COMM_FILES=	fusermount
+
+DIRS =  $(ROOT)/usr \
+                $(ROOT)/usr/bin \
+                $(ROOT)/usr/lib \
+                $(ROOT)/usr/lib/fs \
+                $(ROOT)/usr/lib/fs/fuse
+
+all :=		TARGET= all
+clean :=	TARGET= clean
+clobber :=	TARGET= clobber
+delete :=	TARGET= delete
+install :=	TARGET= install
+lint :=		TARGET= lint
+catalog :=	TARGET= catalog
+_msg :=		TARGET= _msg
+package :=	TARGET= package
+
+.KEEP_STATE:
+
+all clobber delete install lint catalog package: $(SUBDIRS)
+
+clean:	$(SUBDIRS)
+	$(RM) $(POFILES) $(POFILE)
+
+install: $(SUBDIRS)
+	-$(RM) $(ROOTUSRBIN)
+
+check: $(CHECKHDRS)
+
+$(MACH) :	FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/fuse/Makefile.com b/usr/src/cmd/fuse/Makefile.com
new file mode 100644
index 0000000000..1f050859e4
--- /dev/null
+++ b/usr/src/cmd/fuse/Makefile.com
@@ -0,0 +1,127 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, Joyent, Inc.
+#
+# cmd/ldap/Makefile.com
+#
+include $(SRC)/cmd/Makefile.cmd
+
+ROOTFSFUSE=	$(ROOTLIB)/fs/fuse
+
+DIRS =  $(ROOT)/usr \
+	$(ROOTBIN) \
+	$(ROOTLIB) \
+	$(ROOTLIB)/fs \
+	$(ROOTFSFUSE)
+
+FUSERMOUNTPROG= fusermount.bin
+FUSERMOUNTSCRIPT= fusermount
+MOUNTFUSEPROG=	mount
+UMOUNTFUSEPROG=	umount
+FUSERMOUNT_OBJS = fusermount.o
+FUSERMOUNT_SRCS = fusermount.c
+
+MOUNT_FUSE_OBJS = mount_fuse_solaris.o
+MOUNT_FUSE_SRCS = mount_fuse_solaris.c
+
+FUSEINCLUDE=	$(ROOT)/usr/include/fuse
+OBJS=		$(FUSERMOUNT_OBJS) $(MOUNT_FUSE_OBJS)
+SRCS=		$(FUSERMOUNT_SRCS) $(MOUNT_FUSE_SRCS)
+
+PROG=		$(FUSERMOUNTPROG) $(MOUNTFUSEPROG) $(UMOUNTFUSEPROG)
+
+CLOBBERFILES += $(OBJS) $(PROG) $(PROG)
+
+LINTFLAGS += -erroff=E_INCONS_ARG_DECL2
+LINTFLAGS += -erroff=E_INCONS_VAL_TYPE_DECL2
+
+CERRWARN +=	-_gcc=-Wno-implicit-function-declaration
+CERRWARN +=	-_gcc=-Wno-parentheses
+CERRWARN +=	-_gcc=-Wno-unused-function
+CERRWARN +=	-_gcc=-Wno-unused-variable
+CERRWARN +=	$(CNOWARN_UNINIT)
+
+# not linted
+SMATCH=off
+
+all:=           TARGET= all
+install:=       TARGET= install
+clean:=         TARGET= clean
+clobber:=       TARGET= clobber
+
+CPPFLAGS += -g -fPIC \
+	$(CSTD_GNU99) \
+         -_gcc=-std=gnu99 \
+	-I$(FUSEINCLUDE) \
+        -D__SOLARIS__ \
+        -D_XOPEN_SOURCE=600 \
+        -D__EXTENSIONS__ \
+        -D_FILE_OFFSET_BITS=64 \
+        -DFUSE_USE_VERSION=26 \
+        -DFUSERMOUNT_DIR=\"usr\"
+
+
+LDLIBS +=	$(COMPLIB) -lfuse -lxnet
+
+ROOTFSPROG =	$(PROG:%=$(ROOTFSFUSE)/%)
+
+# fusermount in the sources directory is a shell script that calls lib/fs/fusermount
+ROOTBINPROG =	$(FUSERMOUNTSCRIPT:%=$(ROOTBIN)/%)
+
+
+$(ROOTFSFUSE)/%:	%
+		$(INS.file)
+
+$(ROOTBIN)/%:	../%
+		$(INS.file)
+
+.KEEP_STATE:
+
+all:	$(PROG)
+
+$(FUSERMOUNTPROG):	$(FUSERMOUNT_OBJS)
+		$(LINK.c) -o $@ $(FUSERMOUNT_OBJS) $(LDLIBS)
+		$(POST_PROCESS)
+
+$(MOUNTFUSEPROG): $(MOUNT_FUSE_OBJS)
+	$(CC) $(LDFLAGS_PROGS) $(MOUNT_FUSE_OBJS) -o mount
+	$(POST_PROCESS)
+
+$(UMOUNTFUSEPROG):
+	cp ../umount_fuse_solaris.sh umount
+
+%.o:		../%.c
+		$(COMPILE.c) -o $@ $<
+		$(POST_PROCESS_O)
+
+install: all $(DIRS) $(ROOTBINPROG) $(ROOTFSPROG)
+
+$(DIRS):
+	$(INS.dir)
+
+FRC:
+
+clean:
+	$(RM) $(OBJS)
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/cmd/fuse/amd64/Makefile b/usr/src/cmd/fuse/amd64/Makefile
new file mode 100644
index 0000000000..678acf691b
--- /dev/null
+++ b/usr/src/cmd/fuse/amd64/Makefile
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/cmd/fuse/fuse.8.txt b/usr/src/cmd/fuse/fuse.8.txt
new file mode 100644
index 0000000000..73abdbbfc5
--- /dev/null
+++ b/usr/src/cmd/fuse/fuse.8.txt
@@ -0,0 +1,103 @@
+
+NAME
+	FUSE - file system in user space
+
+DESCRIPTION
+
+	FUSE (File system in Userspace) is a framework for user-space
+	programs to export a virtual file system to the Solaris
+	kernel. FUSE also aims to provide a secure method for non-
+	privileged users to create and mount their own file system
+	implementations.
+
+    Architecture
+
+	FUSE consists of a kernel module and a user-space library. A
+	FUSE file system is a daemon that uses the library to mount
+	the file system and register file system operations.
+
+	The library communicates with the FUSE kernel module through
+	the /dev/fuse character device. File system calls are passed
+	through the FUSE device to the library, which uses the
+	registered file system operations to process the call, and
+	pass the results back through the FUSE device to the caller.
+
+    Mounting FUSE File Systems
+
+	FUSE file systems are mounted by the file system program as
+	the first step in starting the file system daemon. FUSE file
+	system programs typically have a syntax that names mount
+	arguments much as the mount(1M) command, i.e.,
+
+	# filesys_program [options] <special> <mount_point>
+	
+	FUSE is also supported by the mount(1M) and umount(1M) commands.
+	See mount_fuse(1M) for more information on mounting FUSE file
+	systems.
+
+    Security
+
+        Non-Privileged Mounts
+
+	    Non-root users can mount FUSE file systems if they have
+	    the FUSE File System Management profile. The user must also
+	    have ownership and write access to the mount point.
+
+	Mount Restrictions
+
+	    FUSE file systems are mounted with the 'nodevices' and
+	    'nosetuid' options by default. This can only be overridden
+	    by the root user.
+
+        Access Control
+
+	    FUSE supports two modes of file system access control:
+	    "local" and "remote". Remote access control is the
+	    default; in this mode, all access control is left to the
+	    file system. Local access control, enabled with the
+	    'default_permissions' mount option, lets FUSE determine
+	    access according to the ownership and permissions
+	    reported by the file system.
+
+        Block Device Access
+
+	    By default, non-root users cannot mount FUSE file systems
+	    that are backed by a block device. To do so, the user
+	    must have read-write access to the device.
+
+        Security-Related Mount Options
+
+	    See mount(1M) and mount_fuse(1M) for information on mount
+	    options relevant to security.
+
+    Configuration
+
+	FUSE configuration options can be specified in the file
+	'/etc/fuse.conf'. Currently these options are:
+
+	mount_max=N
+
+	    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.
+
+ATTRIBUTES
+	See attributes(5) for descriptions of the following
+	attributes:
+
+	____________________________________________________________
+	|       ATTRIBUTE TYPE        |       ATTRIBUTE VALUE       |
+	|_____________________________|_____________________________|
+	| Availability                | SUNWfusefs, SUNWlibfuse     |
+	|_____________________________|_____________________________|
+	| Interface Stability         |                             |
+	|_____________________________|_____________________________|
+
+SEE ALSO
+     mount(1M), mount_fuse(1M), mnttab(4), vfstab(4), fusermount(8), sshfs(8)
+
+
diff --git a/usr/src/cmd/fuse/fusermount b/usr/src/cmd/fuse/fusermount
new file mode 100755
index 0000000000..9aec4f8477
--- /dev/null
+++ b/usr/src/cmd/fuse/fusermount
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+pfexec "/usr/lib/fs/fuse/`basename $0`.bin" "$@"
+
diff --git a/usr/src/cmd/fuse/fusermount.c b/usr/src/cmd/fuse/fusermount.c
new file mode 100644
index 0000000000..7618de657e
--- /dev/null
+++ b/usr/src/cmd/fuse/fusermount.c
@@ -0,0 +1,993 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU GPL.
+    See the file COPYING.
+*/
+/* This program does the mounting and unmounting of FUSE filesystems */
+
+#include <config.h>
+
+#include <mount_util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+
+#ifndef __SOLARIS__
+#include <mntent.h>
+#include <sys/fsuid.h>
+#endif /* __SOLARIS__ */
+
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <sys/socket.h>
+#include <sys/utsname.h>
+
+#define FUSE_COMMFD_ENV         "_FUSE_COMMFD"
+
+#define FUSE_DEV_OLD "/proc/fs/fuse/dev"
+#define FUSE_DEV_NEW "/dev/fuse"
+#define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version"
+#define FUSE_CONF "/etc/fuse.conf"
+
+#ifndef MS_DIRSYNC
+#define MS_DIRSYNC 128
+#endif
+
+static const char *progname;
+
+static int user_allow_other = 0;
+static int mount_max = 1000;
+
+static const char *get_user_name(void)
+{
+    struct passwd *pw = getpwuid(getuid());
+    if (pw != NULL && pw->pw_name != NULL)
+        return pw->pw_name;
+    else {
+        fprintf(stderr, "%s: could not determine username\n", progname);
+        return NULL;
+    }
+}
+
+#ifdef __SOLARIS__
+/* Solaris doesn't have setfsuid/setfsgid.
+ * This doesn't really matter anyway as this program shouldn't be made
+ * suid on Solaris. It should instead be used via a profile with the
+ * sys_mount privilege.
+ */
+#define drop_privs()
+#define restore_privs()
+
+#else
+
+
+static uid_t oldfsuid;
+static gid_t oldfsgid;
+
+static void drop_privs(void)
+{
+    if (getuid() != 0) {
+        oldfsuid = setfsuid(getuid());
+        oldfsgid = setfsgid(getgid());
+    }
+}
+
+static void restore_privs(void)
+{
+    if (getuid() != 0) {
+        setfsuid(oldfsuid);
+        setfsgid(oldfsgid);
+    }
+}
+#endif /* __SOLARIS__ */
+
+#ifndef IGNORE_MTAB
+static int add_mount(const char *source, const char *mnt, const char *type,
+                     const char *opts)
+{
+    return fuse_mnt_add_mount(progname, source, mnt, type, opts);
+}
+
+static int unmount_fuse(const char *mnt, int quiet, int lazy)
+{
+    if (getuid() != 0) {
+        struct mntent *entp;
+        FILE *fp;
+        const char *user = NULL;
+        char uidstr[32];
+        unsigned uidlen = 0;
+        int found;
+        const char *mtab = _PATH_MOUNTED;
+
+        user = get_user_name();
+        if (user == NULL)
+            return -1;
+
+        fp = setmntent(mtab, "r");
+        if (fp == NULL) {
+            fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
+                    strerror(errno));
+            return -1;
+        }
+
+        uidlen = sprintf(uidstr, "%u", getuid());
+
+        found = 0;
+        while ((entp = getmntent(fp)) != NULL) {
+            if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
+                (strcmp(entp->mnt_type, "fuse") == 0 ||
+                 strcmp(entp->mnt_type, "fuseblk") == 0 ||
+                 strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
+                 strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
+                char *p = strstr(entp->mnt_opts, "user=");
+                if (p && (p == entp->mnt_opts || *(p-1) == ',') &&
+                    strcmp(p + 5, user) == 0) {
+                    found = 1;
+                    break;
+                }
+                /* /etc/mtab is a link pointing to /proc/mounts: */
+                else if ((p = strstr(entp->mnt_opts, "user_id=")) &&
+                         (p == entp->mnt_opts || *(p-1) == ',') &&
+                         strncmp(p + 8, uidstr, uidlen) == 0 &&
+                         (*(p+8+uidlen) == ',' || *(p+8+uidlen) == '\0')) {
+                    found = 1;
+                    break;
+                }
+            }
+        }
+        endmntent(fp);
+
+        if (!found) {
+            if (!quiet)
+                fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
+                        mnt, mtab);
+            return -1;
+        }
+    }
+
+    return fuse_mnt_umount(progname, mnt, lazy);
+}
+
+static int count_fuse_fs(void)
+{
+    struct mntent *entp;
+    int count = 0;
+    const char *mtab = _PATH_MOUNTED;
+    FILE *fp = setmntent(mtab, "r");
+    if (fp == NULL) {
+        fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
+                strerror(errno));
+        return -1;
+    }
+    while ((entp = getmntent(fp)) != NULL) {
+        if (strcmp(entp->mnt_type, "fuse") == 0 ||
+            strncmp(entp->mnt_type, "fuse.", 5) == 0)
+            count ++;
+    }
+    endmntent(fp);
+    return count;
+}
+
+
+#else /* IGNORE_MTAB */
+static int count_fuse_fs()
+{
+    return 0;
+}
+
+static int add_mount(const char *source, const char *mnt, const char *type,
+                     const char *opts)
+{
+    (void) source;
+    (void) mnt;
+    (void) type;
+    (void) opts;
+    return 0;
+}
+
+static int unmount_fuse(const char *mnt, int quiet, int lazy)
+{
+    return fuse_mnt_umount(progname, mnt, lazy);
+}
+#endif /* IGNORE_MTAB */
+
+static void strip_line(char *line)
+{
+    char *s = strchr(line, '#');
+    if (s != NULL)
+        s[0] = '\0';
+    for (s = line + strlen(line) - 1; s >= line && isspace((unsigned char) *s); s--);
+    s[1] = '\0';
+    for (s = line; isspace((unsigned char) *s); s++);
+    if (s != line)
+        memmove(line, s, strlen(s)+1);
+}
+
+static void parse_line(char *line, int linenum)
+{
+    int tmp;
+    if (strcmp(line, "user_allow_other") == 0)
+        user_allow_other = 1;
+    else if (sscanf(line, "mount_max = %i", &tmp) == 1)
+        mount_max = tmp;
+    else if(line[0])
+        fprintf(stderr, "%s: unknown parameter in %s at line %i: '%s'\n",
+                progname, FUSE_CONF, linenum, line);
+}
+
+static void read_conf(void)
+{
+    FILE *fp = fopen(FUSE_CONF, "r");
+    if (fp != NULL) {
+        int linenum = 1;
+        char line[256];
+        int isnewline = 1;
+        while (fgets(line, sizeof(line), fp) != NULL) {
+            if (isnewline) {
+                if (line[strlen(line)-1] == '\n') {
+                    strip_line(line);
+                    parse_line(line, linenum);
+                } else {
+                    fprintf(stderr, "%s: reading %s: line %i too long\n",
+                            progname, FUSE_CONF, linenum);
+                    isnewline = 0;
+                }
+            } else if(line[strlen(line)-1] == '\n')
+                isnewline = 1;
+            if (isnewline)
+                linenum ++;
+        }
+        fclose(fp);
+    } else if (errno != ENOENT) {
+        fprintf(stderr, "%s: failed to open %s: %s\n", progname, FUSE_CONF,
+                strerror(errno));
+    }
+}
+
+static int begins_with(const char *s, const char *beg)
+{
+    if (strncmp(s, beg, strlen(beg)) == 0)
+        return 1;
+    else
+        return 0;
+}
+
+struct mount_flags {
+    const char *opt;
+    unsigned long flag;
+    int on;
+    int safe;
+};
+
+static struct mount_flags mount_flags[] = {
+    {"rw",      MS_RDONLY,      0, 1},
+    {"ro",      MS_RDONLY,      1, 1},
+    {"suid",    MS_NOSUID,      0, 0},
+    {"nosuid",  MS_NOSUID,      1, 1},
+#ifdef __SOLARIS__
+    {"-g",      MS_GLOBAL,      1, 1},
+    {"-m",      MS_NOMNTTAB,    1, 1},
+    {"-O",      MS_OVERLAY,     1, 1},
+#else
+    {"dev",     MS_NODEV,       0, 0},
+    {"nodev",   MS_NODEV,       1, 1},
+    {"exec",    MS_NOEXEC,      0, 1},
+    {"noexec",  MS_NOEXEC,      1, 1},
+    {"async",   MS_SYNCHRONOUS, 0, 1},
+    {"sync",    MS_SYNCHRONOUS, 1, 1},
+    {"atime",   MS_NOATIME,     0, 1},
+    {"noatime", MS_NOATIME,     1, 1},
+    {"dirsync", MS_DIRSYNC,     1, 1},
+#endif /* __SOLARIS__ */
+    {NULL,      0,              0, 0}
+};
+
+static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
+{
+    int i;
+
+    for (i = 0; mount_flags[i].opt != NULL; i++) {
+        const char *opt = mount_flags[i].opt;
+        if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
+            *on = mount_flags[i].on;
+            *flag = mount_flags[i].flag;
+            if (!mount_flags[i].safe && getuid() != 0) {
+                *flag = 0;
+                fprintf(stderr, "%s: unsafe option %s ignored\n",
+                        progname, opt);
+            }
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int add_option(char **optsp, const char *opt, unsigned expand)
+{
+    char *newopts;
+    if (*optsp == NULL)
+        newopts = strdup(opt);
+    else {
+        unsigned oldsize = strlen(*optsp);
+        unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
+        newopts = (char *) realloc(*optsp, newsize);
+        if (newopts)
+            sprintf(newopts + oldsize, ",%s", opt);
+    }
+    if (newopts == NULL) {
+        fprintf(stderr, "%s: failed to allocate memory\n", progname);
+        return -1;
+    }
+    *optsp = newopts;
+    return 0;
+}
+
+static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
+{
+    int i;
+    int l;
+
+    if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
+        return -1;
+
+    for (i = 0; mount_flags[i].opt != NULL; i++) {
+        if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
+            add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
+            return -1;
+    }
+
+    if (add_option(mnt_optsp, opts, 0) == -1)
+        return -1;
+    /* remove comma from end of opts*/
+    l = strlen(*mnt_optsp);
+    if ((*mnt_optsp)[l-1] == ',')
+        (*mnt_optsp)[l-1] = '\0';
+    if (getuid() != 0) {
+        const char *user = get_user_name();
+        if (user == NULL)
+            return -1;
+
+        if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
+            return -1;
+        strcat(*mnt_optsp, user);
+    }
+    return 0;
+}
+
+static int opt_eq(const char *s, unsigned len, const char *opt)
+{
+    if(strlen(opt) == len && strncmp(s, opt, len) == 0)
+        return 1;
+    else
+        return 0;
+}
+
+static int get_string_opt(const char *s, unsigned len, const char *opt,
+                          char **val)
+{
+    unsigned opt_len = strlen(opt);
+
+    if (*val)
+        free(*val);
+    *val = (char *) malloc(len - opt_len + 1);
+    if (!*val) {
+        fprintf(stderr, "%s: failed to allocate memory\n", progname);
+        return 0;
+    }
+
+    memcpy(*val, s + opt_len, len - opt_len);
+    (*val)[len - opt_len] = '\0';
+    return 1;
+}
+
+static int do_mount(const char *mnt, char **typep, mode_t rootmode,
+                    int fd, const char *opts, const char *dev, char **sourcep,
+                    char **mnt_optsp, off_t rootsize)
+{
+    int res;
+#ifdef __SOLARIS__
+    /*
+     * In Solaris, nosuid is equivalent to nosetuid + nodevices. We only
+     * have MS_NOSUID for mount flags (no MS_(NO)SETUID, etc.). But if
+     * we set that as a default, it restricts specifying just nosetuid
+     * or nodevices; there is no way for the user to specify setuid +
+     * nodevices or vice-verse.
+     * On the chance that this is being run from root, we won't try
+     * to add nosetuid or nodevices restrictions; this program must
+     * be exec'd from the library, where we've already sanitized the
+     * options.
+     */
+    int flags = 0;
+#else
+    int flags = MS_NOSUID | MS_NODEV;
+#endif /* __SOLARIS__ */
+    char *optbuf;
+    char *mnt_opts = NULL;
+    const char *s;
+    char *d;
+    char *fsname = NULL;
+    char *subtype = NULL;
+    char *source = NULL;
+    char *type = NULL;
+    int check_empty = 1;
+    int blkdev = 0;
+
+    optbuf = (char *) malloc(strlen(opts) + 128);
+    if (!optbuf) {
+        fprintf(stderr, "%s: failed to allocate memory\n", progname);
+        return -1;
+    }
+
+    for (s = opts, d = optbuf; *s;) {
+        unsigned len;
+        const char *fsname_str = "fsname=";
+        const char *subtype_str = "subtype=";
+        for (len = 0; s[len] && s[len] != ','; len++);
+        if (begins_with(s, fsname_str)) {
+            if (!get_string_opt(s, len, fsname_str, &fsname))
+                goto err;
+        } else if (begins_with(s, subtype_str)) {
+            if (!get_string_opt(s, len, subtype_str, &subtype))
+                goto err;
+        } else if (opt_eq(s, len, "blkdev")) {
+            if (getuid() != 0) {
+                fprintf(stderr, "%s: option blkdev is privileged\n", progname);
+                goto err;
+            }
+            blkdev = 1;
+        } else if (opt_eq(s, len, "nonempty")) {
+            check_empty = 0;
+        } else if (!begins_with(s, "fd=") &&
+                   !begins_with(s, "rootmode=") &&
+                   !begins_with(s, "user_id=") &&
+                   !begins_with(s, "group_id=")) {
+            int on;
+            int flag;
+            int skip_option = 0;
+            if (opt_eq(s, len, "large_read")) {
+                struct utsname utsname;
+                unsigned kmaj, kmin;
+                res = uname(&utsname);
+                if (res == 0 &&
+                    sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 &&
+                    (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
+                    fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
+                    skip_option = 1;
+                }
+            }
+            if (getuid() != 0 && !user_allow_other &&
+                (opt_eq(s, len, "allow_other") ||
+                 opt_eq(s, len, "allow_root"))) {
+                fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
+                goto err;
+            }
+            if (!skip_option) {
+                if (find_mount_flag(s, len, &on, &flag)) {
+                    if (on)
+                        flags |= flag;
+                    else
+                        flags  &= ~flag;
+                } else {
+                    memcpy(d, s, len);
+                    d += len;
+                    *d++ = ',';
+                }
+            }
+        }
+        s += len;
+        if (*s)
+            s++;
+    }
+    *d = '\0';
+    res = get_mnt_opts(flags, optbuf, &mnt_opts);
+    if (res == -1)
+        goto err;
+
+    sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
+            fd, rootmode, getuid(), getgid());
+
+    if (check_empty &&
+        fuse_mnt_check_empty(progname, mnt, rootmode, rootsize) == -1)
+        goto err;
+
+    source = malloc((fsname ? strlen(fsname) : 0) +
+                    (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
+
+    type = malloc((subtype ? strlen(subtype) : 0) + 32);
+    if (!type || !source) {
+        fprintf(stderr, "%s: failed to allocate memory\n", progname);
+        goto err;
+    }
+
+    if (subtype)
+        sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
+    else
+        strcpy(type, blkdev ? "fuseblk" : "fuse");
+
+    if (fsname)
+        strcpy(source, fsname);
+    else
+        strcpy(source, subtype ? subtype : dev);
+
+#ifdef __SOLARIS__
+    res = mount(source, mnt, MS_OPTIONSTR|flags, type, NULL, 0,
+        optbuf, MAX_MNTOPT_STR);
+#else
+    res = mount(source, mnt, type, flags, optbuf);
+#endif /* __SOLARIS__ */
+
+#ifdef __SOLARIS__
+    if (res == -1 && errno == EINVAL && subtype) {
+#else
+    if (res == -1 && errno == ENODEV && subtype) {
+#endif
+        /* Probably missing subtype support */
+        strcpy(type, blkdev ? "fuseblk" : "fuse");
+        if (fsname) {
+            if (!blkdev)
+                sprintf(source, "%s#%s", subtype, fsname);
+        } else {
+            strcpy(source, type);
+        }
+#ifdef __SOLARIS__
+        res = mount(source, mnt, MS_OPTIONSTR|flags, type, NULL, 0,
+            optbuf, MAX_MNTOPT_STR);
+#else
+        res = mount(source, mnt, type, flags, optbuf);
+#endif /* __SOLARIS__ */
+    }
+    if (res == -1 && errno == EINVAL) {
+        /* It could be an old version not supporting group_id */
+        sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid());
+#ifdef __SOLARIS__
+        res = mount(dev, mnt, MS_OPTIONSTR|flags, type, NULL, 0,
+            optbuf, MAX_MNTOPT_STR);
+#else
+        res = mount(source, mnt, type, flags, optbuf);
+#endif /* __SOLARIS__ */
+    }
+    if (res == -1) {
+        int errno_save = errno;
+        if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
+            fprintf(stderr, "%s: 'fuseblk' support missing\n", progname);
+        else
+            fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save));
+        goto err;
+    } else {
+        *sourcep = source;
+        *typep = type;
+        *mnt_optsp = mnt_opts;
+    }
+    free(optbuf);
+
+    return res;
+
+ err:
+    free(fsname);
+    free(subtype);
+    free(source);
+    free(type);
+    free(mnt_opts);
+    free(optbuf);
+    return -1;
+}
+
+static int check_version(const char *dev)
+{
+    int res;
+    int majorver;
+    int minorver;
+    const char *version_file;
+    FILE *vf;
+
+    if (strcmp(dev, FUSE_DEV_OLD) != 0)
+        return 0;
+
+    version_file = FUSE_VERSION_FILE_OLD;
+    vf = fopen(version_file, "r");
+    if (vf == NULL) {
+        fprintf(stderr, "%s: kernel interface too old\n", progname);
+        return -1;
+    }
+    res = fscanf(vf, "%i.%i", &majorver, &minorver);
+    fclose(vf);
+    if (res != 2) {
+        fprintf(stderr, "%s: error reading %s\n", progname, version_file);
+        return -1;
+    }
+     if (majorver < 3) {
+        fprintf(stderr, "%s: kernel interface too old\n", progname);
+        return -1;
+    }
+    return 0;
+}
+
+static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd,
+                      int *mountpoint_fd)
+{
+    int res;
+    const char *mnt = *mntp;
+    const char *origmnt = mnt;
+
+    res = lstat(mnt, stbuf);
+    if (res == -1) {
+        fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
+                progname, mnt, strerror(errno));
+        return -1;
+    }
+
+    /* No permission checking is done for root */
+    if (getuid() == 0)
+        return 0;
+
+    if (S_ISDIR(stbuf->st_mode)) {
+        *currdir_fd = open(".", O_RDONLY);
+        if (*currdir_fd == -1) {
+            fprintf(stderr, "%s: failed to open current directory: %s\n",
+                    progname, strerror(errno));
+            return -1;
+        }
+#ifndef __SOLARIS__
+        res = chdir(mnt);
+        if (res == -1) {
+            fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n",
+                    progname, strerror(errno));
+            return -1;
+        }
+        mnt = *mntp = ".";
+#endif /* __SOLARIS__ */
+        res = lstat(mnt, stbuf);
+        if (res == -1) {
+            fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
+                    progname, origmnt, strerror(errno));
+            return -1;
+        }
+
+        if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
+            fprintf(stderr, "%s: mountpoint %s not owned by user\n",
+                    progname, origmnt);
+            return -1;
+        }
+
+        res = access(mnt, W_OK);
+        if (res == -1) {
+            fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
+                    progname, origmnt);
+            return -1;
+        }
+    } else if (S_ISREG(stbuf->st_mode)) {
+        static char procfile[256];
+        *mountpoint_fd = open(mnt, O_WRONLY);
+        if (*mountpoint_fd == -1) {
+            fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt,
+                    strerror(errno));
+            return -1;
+        }
+        res = fstat(*mountpoint_fd, stbuf);
+        if (res == -1) {
+            fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
+                    progname, mnt, strerror(errno));
+            return -1;
+        }
+        if (!S_ISREG(stbuf->st_mode)) {
+            fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n",
+                    progname, mnt);
+            return -1;
+        }
+
+        sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
+        *mntp = procfile;
+    } else {
+        fprintf(stderr,
+                "%s: mountpoint %s is not a directory or a regular file\n",
+                progname, mnt);
+        return -1;
+    }
+
+
+    return 0;
+}
+
+static int try_open(const char *dev, char **devp, int silent)
+{
+    int fd = open(dev, O_RDWR);
+    if (fd != -1) {
+        *devp = strdup(dev);
+        if (*devp == NULL) {
+            fprintf(stderr, "%s: failed to allocate memory\n", progname);
+            close(fd);
+            fd = -1;
+        }
+    } else if (errno == ENODEV ||
+               errno == ENOENT) /* check for ENOENT too, for the udev case */
+        return -2;
+    else if (!silent) {
+        fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
+                strerror(errno));
+    }
+    return fd;
+}
+
+static int try_open_fuse_device(char **devp)
+{
+    int fd;
+    int err;
+
+    drop_privs();
+    fd = try_open(FUSE_DEV_NEW, devp, 0);
+    restore_privs();
+    if (fd >= 0)
+        return fd;
+
+    err = fd;
+    fd = try_open(FUSE_DEV_OLD, devp, 1);
+    if (fd >= 0)
+        return fd;
+
+    return err;
+}
+
+static int open_fuse_device(char **devp)
+{
+    int fd = try_open_fuse_device(devp);
+    if (fd >= -1)
+        return fd;
+
+    fprintf(stderr, "%s: fuse device not found, try 'modprobe fuse' first\n",
+            progname);
+
+    return -1;
+}
+
+
+static int mount_fuse(const char *mnt, const char *opts)
+{
+    int res;
+    int fd;
+    char *dev;
+    struct stat stbuf;
+    char *type = NULL;
+    char *source = NULL;
+    char *mnt_opts = NULL;
+    const char *real_mnt = mnt;
+    int currdir_fd = -1;
+    int mountpoint_fd = -1;
+
+    fd = open_fuse_device(&dev);
+    if (fd == -1)
+        return -1;
+
+    drop_privs();
+    read_conf();
+
+    if (getuid() != 0 && mount_max != -1) {
+        int mount_count = count_fuse_fs();
+        if (mount_count >= mount_max) {
+            fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
+            close(fd);
+            return -1;
+        }
+    }
+
+    res = check_version(dev);
+    if (res != -1) {
+        res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd);
+        restore_privs();
+        if (res != -1)
+            res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts,
+                           dev, &source, &mnt_opts, stbuf.st_size);
+    } else {
+        restore_privs();
+    }
+
+    if (currdir_fd != -1) {
+        fchdir(currdir_fd);
+        close(currdir_fd);
+    }
+    if (mountpoint_fd != -1)
+        close(mountpoint_fd);
+
+    if (res == -1) {
+        close(fd);
+        return -1;
+    }
+
+    if (geteuid() == 0) {
+        res = add_mount(source, mnt, type, mnt_opts);
+        if (res == -1) {
+            umount2(mnt, 2); /* lazy umount */
+            close(fd);
+            return -1;
+        }
+    }
+
+    free(source);
+    free(type);
+    free(mnt_opts);
+    free(dev);
+
+    return fd;
+}
+
+static int send_fd(int sock_fd, int fd)
+{
+    int retval;
+    struct msghdr msg;
+    struct cmsghdr *p_cmsg;
+    struct iovec vec;
+    size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
+    int *p_fds;
+    char sendchar = 0;
+
+    msg.msg_control = cmsgbuf;
+    msg.msg_controllen = sizeof(cmsgbuf);
+    p_cmsg = CMSG_FIRSTHDR(&msg);
+    p_cmsg->cmsg_level = SOL_SOCKET;
+    p_cmsg->cmsg_type = SCM_RIGHTS;
+    p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+    p_fds = (int *) CMSG_DATA(p_cmsg);
+    *p_fds = fd;
+    msg.msg_controllen = p_cmsg->cmsg_len;
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = &vec;
+    msg.msg_iovlen = 1;
+    msg.msg_flags = 0;
+    /* "To pass file descriptors or credentials you need to send/read at
+     * least one byte" (man 7 unix) */
+    vec.iov_base = &sendchar;
+    vec.iov_len = sizeof(sendchar);
+    while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
+    if (retval != 1) {
+        perror("sending file descriptor");
+        return -1;
+    }
+    return 0;
+}
+
+static void usage(void)
+{
+    fprintf(stderr,
+            "%s: [options] mountpoint\n"
+            "Options:\n"
+            " -h                print help\n"
+            " -V                print version\n"
+            " -o opt[,opt...]   mount options\n"
+            " -u                unmount\n"
+            " -q                quiet\n"
+            " -z                lazy unmount\n",
+            progname);
+    exit(1);
+}
+
+static void show_version(void)
+{
+    printf("fusermount version: %s\n", PACKAGE_VERSION);
+    exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+    int ch;
+    int fd;
+    int res;
+    char *origmnt;
+    char *mnt;
+    static int unmount = 0;
+    static int lazy = 0;
+    static int quiet = 0;
+    char *commfd;
+    int cfd;
+    const char *opts = "";
+
+    static const struct option long_opts[] = {
+        {"unmount", no_argument, NULL, 'u'},
+#ifndef __SOLARIS__
+        {"lazy",    no_argument, NULL, 'z'},
+#endif /* __SOLARIS__ */
+        {"quiet",   no_argument, NULL, 'q'},
+        {"help",    no_argument, NULL, 'h'},
+        {"version", no_argument, NULL, 'V'},
+        {0, 0, 0, 0}};
+
+    progname = strdup(argv[0]);
+    if (progname == NULL) {
+        fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
+        exit(1);
+    }
+
+    while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts, NULL)) != -1) {
+        switch (ch) {
+        case 'h':
+            usage();
+            break;
+
+        case 'V':
+            show_version();
+            break;
+
+        case 'o':
+            opts = optarg;
+            break;
+
+        case 'u':
+            unmount = 1;
+            break;
+#ifndef __SOLARIS__
+        case 'z':
+            lazy = 1;
+            break;
+#endif /* __SOLARIS__ */
+        case 'q':
+            quiet = 1;
+            break;
+
+        default:
+            exit(1);
+        }
+    }
+
+    if (lazy && !unmount) {
+        fprintf(stderr, "%s: -z can only be used with -u\n", progname);
+        exit(1);
+    }
+
+    if (optind >= argc) {
+        fprintf(stderr, "%s: missing mountpoint argument\n", progname);
+        exit(1);
+    }
+
+    origmnt = argv[optind];
+
+    drop_privs();
+    mnt = fuse_mnt_resolve_path(progname, origmnt);
+    restore_privs();
+    if (mnt == NULL)
+        exit(1);
+
+    umask(033);
+    if (unmount) {
+#ifndef __SOLARIS__
+        if (geteuid() == 0)
+            res = unmount_fuse(mnt, quiet, lazy);
+        else {
+#endif /* __SOLARIS__ */
+            res = umount2(mnt, lazy ? 2 : 0);
+            if (res == -1 && !quiet)
+                fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
+                        mnt, strerror(errno));
+#ifndef __SOLARIS__
+        }
+#endif /* __SOLARIS__ */
+        if (res == -1)
+            exit(1);
+        return 0;
+    }
+
+    commfd = getenv(FUSE_COMMFD_ENV);
+    if (commfd == NULL) {
+        fprintf(stderr, "%s: old style mounting not supported\n", progname);
+        exit(1);
+    }
+
+    fd = mount_fuse(mnt, opts);
+    if (fd == -1)
+        exit(1);
+
+    cfd = atoi(commfd);
+    res = send_fd(cfd, fd);
+    if (res == -1)
+        exit(1);
+
+    return 0;
+}
diff --git a/usr/src/cmd/fuse/i386/Makefile b/usr/src/cmd/fuse/i386/Makefile
new file mode 100644
index 0000000000..c0aa407d0c
--- /dev/null
+++ b/usr/src/cmd/fuse/i386/Makefile
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
+
diff --git a/usr/src/cmd/fuse/mount_fuse_solaris.c b/usr/src/cmd/fuse/mount_fuse_solaris.c
new file mode 100644
index 0000000000..b098ad500d
--- /dev/null
+++ b/usr/src/cmd/fuse/mount_fuse_solaris.c
@@ -0,0 +1,154 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <alloca.h>
+#include <libintl.h>
+#include <locale.h>
+#include <err.h>
+#include "config.h"
+
+
+#define	EXIT_OK		0
+#define	EXIT_USAGE	1
+#define	EXIT_ERROR	2
+
+#define	SUBTYPE_OPTION	"subtype"
+
+static void usage(void);
+
+
+int
+main(int argc, char **argv)
+{
+	char *optstr, *optstr_save, *sep, *soptval, *fspgm;
+	char *sopt[2];
+	int opt, i;
+
+	(void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define	TEXT_DOMAIN	"SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	if (argc == 2) {
+		if (strcmp(argv[1], "-h") == 0 ||
+		    strcmp(argv[1], "-?") == 0 ||
+		    strcmp(argv[1], "--help") == 0) {
+			usage();
+		} else if (strcmp(argv[1], "--version") == 0) {
+			errx(EXIT_OK, gettext("FUSE version %s"), VERSION);
+		} else if (argv[1][0] == '-') {
+			fprintf(stderr, gettext("invalid option %s\n"),
+			    argv[1]);
+		}
+	}
+	if (argc < 3)
+		usage();
+
+	/* Look for "-o subtype=fspgm" syntax */
+	fspgm = NULL;
+	opterr = 0;
+	while ((opt = getopt(argc, argv, ":o:")) != -1) {
+
+		/* We're not validating options, just looking for subtype */
+		if (opt != 'o')
+			continue;
+		if (optarg != NULL) {
+			sopt[0] = SUBTYPE_OPTION;
+			sopt[1] = NULL;
+			if ((optstr = optstr_save = strdup(optarg)) == NULL)
+				err(EXIT_ERROR, NULL);
+			while (*optstr != '\0') {
+				if (getsubopt(&optstr, sopt, &soptval) != -1 &&
+				    soptval != NULL && strlen(soptval) > 0) {
+					fspgm = alloca(strlen(soptval) + 1);
+					if (fspgm == NULL) {
+						free(optstr_save);
+						err(EXIT_ERROR, NULL);
+					}
+					strcpy(fspgm, soptval);
+					break;
+				}
+			}
+			free(optstr_save);
+		}
+	}
+
+	/*
+	 * Look for "fspgm#special" syntax. If found, but the fspgm
+	 * was already specified with the subtype option, exit with
+	 * an error message.
+	 * Note that if the "fspgm#special" syntax is found, the
+	 * arguments are operated on in-place to parse 'fspgm'
+	 * and strip it from 'special.'
+	 */
+	soptval = argv[argc - 2];
+	sep = strchr(soptval, '#');
+	if (sep != NULL && sep + 1 != NULL && strlen(sep + 1) > 0) {
+		if (fspgm == NULL) {
+			*sep = '\0';
+			fspgm = soptval;
+			argv[argc - 2] = sep + 1;
+		} else {
+			errx(EXIT_ERROR, gettext("file system subtype must "
+			    "be specified only once"));
+		}
+	}
+
+	if (fspgm == NULL) {
+		errx(EXIT_ERROR, gettext("a FUSE file system subtype must "
+		    "be specified"));
+	}
+
+	argv[0] = fspgm;
+	execvp(fspgm, argv);
+	err(EXIT_ERROR, gettext("failed to execute %s"), fspgm);
+}
+
+
+/*
+ * usage
+ *
+ * Output program usage and exit
+ *
+ */
+static void
+usage()
+{
+
+	fprintf(stderr, gettext("usage:\n"
+	    "mount [generic options] -o subtype=filesystem[,option] "
+	    "special mount_point\n"
+	    "mount [generic options] [-o option[,option]] "
+	    "filesystem#special mount_point\n"));
+
+	exit(EXIT_USAGE);
+}
diff --git a/usr/src/cmd/fuse/umount_fuse_solaris.sh b/usr/src/cmd/fuse/umount_fuse_solaris.sh
new file mode 100755
index 0000000000..a2245366b3
--- /dev/null
+++ b/usr/src/cmd/fuse/umount_fuse_solaris.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+/usr/bin/fusermount -u $@
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 0676c48f8f..51b0be4f73 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -136,6 +136,7 @@ SUBDIRS +=				\
 	libfruutils	\
 	libfsmgt	\
 	libfstyp	\
+	libfuse		\
 	libgen		\
 	libgrubmgmt	\
 	libgss		\
@@ -406,6 +407,7 @@ HDRSUBDIRS=				\
 	libfcoe		\
 	libfru		\
 	libfstyp	\
+	libfuse		\
 	libgen		\
 	libgrubmgmt	\
 	libidmap	\
@@ -629,6 +631,7 @@ libfakekernel:	libumem libcryptoutil
 libfcoe:	libdladm
 libficl:	libuuid libumem
 libfru:		libfruutils
+libfuse:	libxnet
 libfsmgt:	libkstat
 libgrubmgmt:	libdevinfo libzfs libfstyp libefi
 $(INTEL_BLD)libgrubmgmt: libfdisk
diff --git a/usr/src/lib/libfuse/COPYING.LIB b/usr/src/lib/libfuse/COPYING.LIB
new file mode 100644
index 0000000000..161a3d1d47
--- /dev/null
+++ b/usr/src/lib/libfuse/COPYING.LIB
@@ -0,0 +1,482 @@
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+			    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 Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, 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 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 a program 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.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, 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 companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  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, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+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 compile 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) 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.
+
+    c) 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.
+
+    d) 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 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.
+
+  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 to
+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 Library 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
+
+     Appendix: 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 Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+    MA 02111-1307, 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/usr/src/lib/libfuse/INSTALL b/usr/src/lib/libfuse/INSTALL
new file mode 100644
index 0000000000..5bb9349572
--- /dev/null
+++ b/usr/src/lib/libfuse/INSTALL
@@ -0,0 +1,34 @@
+Building
+---
+
+Requirements:
+    Solaris Nevada >= build 62
+    Sun Studio 11
+
+Build the FUSE library and utilities:
+$ make
+
+Note: gmake(1) will fail to build the FUSE library.
+
+
+Installing
+---
+
+Populate the proto area:
+$ make install
+
+Generate the package:
+$ make pkg
+
+Add the SUNWlibfuse package to the system (as root):
+# pkgadd -d packages SUNWlibfuse
+
+Assign the execution profile to a user (as root):
+# usermod -P "FUSE File System Management" <username>
+
+
+Uninstalling
+---
+
+Remove the SUNWlibfuse from the system (as root):
+# pkgrm SUNWlibfuse
diff --git a/usr/src/lib/libfuse/Makefile b/usr/src/lib/libfuse/Makefile
new file mode 100644
index 0000000000..8a46552167
--- /dev/null
+++ b/usr/src/lib/libfuse/Makefile
@@ -0,0 +1,55 @@
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+SUBDIRS	=	$(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+VERS = .2.7.1
+
+include $(SRC)/lib/Makefile.lib
+
+ENCODING  = ISO8859
+
+SRCDIR=	src
+
+HDRDIR=		include
+HDRS=           config.h fuse_common.h fuse_compat.h fuse_lowlevel.h \
+		fuse_opt.h fuse.h fuse_common_compat.h fuse_i.h \
+		fuse_kernel.h fuse_lowlevel_compat.h fuse_misc.h mount_util.h
+
+ROOTHDRDIR=     $(ROOT)/usr/include/fuse
+ROOTHDRS=       $(HDRS:%=$(ROOTHDRDIR)/%)
+CHECKHDRS=      $(HDRS:%.h=%.check)
+
+.KEEP_STATE:
+
+all	:= TARGET= all
+clean	:= TARGET= clean
+clobber	:= TARGET= clobber
+install	:= TARGET= install
+
+
+# This is the default Makefile target.  The objects listed here
+# are what get build when you type just "make" with no arguments.
+#
+all install:	$(SUBDIRS)
+clobber:	$(SUBDIRS)
+
+$(ROOTHDRDIR):
+	$(INS.dir)
+
+$(ROOTHDRDIR)/%: $(HDRDIR)/% $(ROOTHDRDIR)
+	$(INS.file)
+
+install_h:	$(ROOTHDRS)
+
+check:
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libfuse/Makefile.com b/usr/src/lib/libfuse/Makefile.com
new file mode 100644
index 0000000000..76a6fa8b3e
--- /dev/null
+++ b/usr/src/lib/libfuse/Makefile.com
@@ -0,0 +1,116 @@
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2015 Igor Kozhukhov <ikozhukhov@gmail.com>
+# Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
+# Copyright (c) 2019, Joyent, Inc.
+# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+#
+
+# Make the SO name unlikely to conflict with any other
+# libsqlite that might also be found on the system.
+LIBRARY = libfuse.a
+VERS = .2.7.1
+
+include $(SRC)/lib/Makefile.lib
+
+SRCDIR = ../
+
+MYCPPFLAGS = -g -_gcc=-std=c99 -fPIC \
+	-I$(SRCDIR)/include \
+        -D__SOLARIS__ \
+        -D_XOPEN_SOURCE=600 \
+        -D__EXTENSIONS__ \
+        -D_FILE_OFFSET_BITS=64 \
+        -DFUSE_USE_VERSION=26 \
+        -DFUSERMOUNT_DIR=\"usr\"
+
+OBJECTS =  fuse.o \
+        fuse_kern_chan.o \
+        fuse_loop.o \
+        fuse_loop_mt.o \
+        fuse_lowlevel.o \
+        fuse_mt.o \
+        fuse_opt.o \
+        fuse_session.o \
+        fuse_signals.o \
+        helper.o \
+        mount.o \
+        daemon.o \
+        mount_util.o \
+	modules/iconv.o \
+        modules/subdir.o
+
+SRCS = $(SRCDIR)/fuse.c \
+	$(SRCDIR)/fuse_kern_chan.c \
+	$(SRCDIR)/fuse_loop.c \
+	$(SRCDIR)/fuse_loop_mt.c \
+	$(SRCDIR)/fuse_lowlevel.c \
+	$(SRCDIR)/fuse_mt.c \
+	$(SRCDIR)/fuse_opt.c \
+	$(SRCDIR)/fuse_session.c \
+	$(SRCDIR)/fuse_signals.c \
+	$(SRCDIR)/helper.c \
+	$(SRCDIR)/mount.c \
+	$(SRCDIR)/daemon.c \
+	$(SRCDIR)/mount_util.c \
+	$(SRCDIR)/modules/iconv.c \
+	$(SRCDIR)/modules/subdir.c
+
+HDRS=	fuse.h \
+        fuse_common.h \
+        fuse_common_compat.h \
+        fuse_compat.h \
+        fuse_kernel.h \
+        fuse_lowlevel.h \
+        fuse_lowlevel_compat.h \
+        fuse_opt.h \
+        config.h \
+	mount_util.h
+
+ROOTHDRDIR=	$(ROOT)/usr/inclujde/fuse
+
+LDLIBS+= -lxnet
+$(DYNLIB) := LDLIBS += -lc -lxnet
+LIBS = $(DYNLIB)
+
+CPPFLAGS += $(MYCPPFLAGS)
+
+CERRWARN += -_gcc=-Wno-implicit-function-declaration
+CERRWARN += $(CNOWARN_UNINIT)
+CERRWARN += -_gcc=-Wno-unused-function
+CERRWARN += -_gcc=-Wno-unused-label
+
+# not linted
+SMATCH=off
+
+MAPFILES = ../mapfile-libfuse
+
+CFLAGS += $(CTF_FLAGS)
+CTFCONVERT_POST = $(CTFCONVERT_O)
+
+ENCODING  = ISO8859
+
+.KEEP_STATE:
+
+# This is the default Makefile target.  The objects listed here
+# are what get build when you type just "make" with no arguments.
+#
+all:		$(LIBS)
+install:	all \
+		$(ROOTLIBDIR)/$(DYNLIB) \
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+objs/%.o pics/%.o: $(SRCDIR)/%.c 
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
+
+objs/%.o pics/%.o: %.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
+
+include $(SRC)/lib/Makefile.targ
+
+FRC:
diff --git a/usr/src/lib/libfuse/amd64/Makefile b/usr/src/lib/libfuse/amd64/Makefile
new file mode 100644
index 0000000000..678acf691b
--- /dev/null
+++ b/usr/src/lib/libfuse/amd64/Makefile
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libfuse/daemon.c b/usr/src/lib/libfuse/daemon.c
new file mode 100644
index 0000000000..203ae82e42
--- /dev/null
+++ b/usr/src/lib/libfuse/daemon.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS 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.
+ */
+
+/* Modifed from usr/src/lib/gss_mechs/mech_krb5/krb5/posix/daemon.c */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#define	_PATH_DEVNULL "/dev/null"
+
+int
+daemon(nochdir, noclose)
+	int nochdir, noclose;
+{
+	int cpid;
+
+	if ((cpid = fork()) == -1)
+		return (-1);
+	if (cpid)
+		exit(0);
+
+	if (setsid() == -1)
+		return (-1);
+
+	if (!nochdir)
+		(void) chdir("/");
+	if (!noclose) {
+		int devnull = open(_PATH_DEVNULL, O_RDWR, 0);
+
+		if (devnull != -1) {
+			(void) dup2(devnull, 0);
+			(void) dup2(devnull, 1);
+			(void) dup2(devnull, 2);
+			if (devnull > 2)
+				(void) close(devnull);
+		}
+	}
+	return (0);
+}
diff --git a/usr/src/lib/libfuse/fuse.8.txt b/usr/src/lib/libfuse/fuse.8.txt
new file mode 100644
index 0000000000..73abdbbfc5
--- /dev/null
+++ b/usr/src/lib/libfuse/fuse.8.txt
@@ -0,0 +1,103 @@
+
+NAME
+	FUSE - file system in user space
+
+DESCRIPTION
+
+	FUSE (File system in Userspace) is a framework for user-space
+	programs to export a virtual file system to the Solaris
+	kernel. FUSE also aims to provide a secure method for non-
+	privileged users to create and mount their own file system
+	implementations.
+
+    Architecture
+
+	FUSE consists of a kernel module and a user-space library. A
+	FUSE file system is a daemon that uses the library to mount
+	the file system and register file system operations.
+
+	The library communicates with the FUSE kernel module through
+	the /dev/fuse character device. File system calls are passed
+	through the FUSE device to the library, which uses the
+	registered file system operations to process the call, and
+	pass the results back through the FUSE device to the caller.
+
+    Mounting FUSE File Systems
+
+	FUSE file systems are mounted by the file system program as
+	the first step in starting the file system daemon. FUSE file
+	system programs typically have a syntax that names mount
+	arguments much as the mount(1M) command, i.e.,
+
+	# filesys_program [options] <special> <mount_point>
+	
+	FUSE is also supported by the mount(1M) and umount(1M) commands.
+	See mount_fuse(1M) for more information on mounting FUSE file
+	systems.
+
+    Security
+
+        Non-Privileged Mounts
+
+	    Non-root users can mount FUSE file systems if they have
+	    the FUSE File System Management profile. The user must also
+	    have ownership and write access to the mount point.
+
+	Mount Restrictions
+
+	    FUSE file systems are mounted with the 'nodevices' and
+	    'nosetuid' options by default. This can only be overridden
+	    by the root user.
+
+        Access Control
+
+	    FUSE supports two modes of file system access control:
+	    "local" and "remote". Remote access control is the
+	    default; in this mode, all access control is left to the
+	    file system. Local access control, enabled with the
+	    'default_permissions' mount option, lets FUSE determine
+	    access according to the ownership and permissions
+	    reported by the file system.
+
+        Block Device Access
+
+	    By default, non-root users cannot mount FUSE file systems
+	    that are backed by a block device. To do so, the user
+	    must have read-write access to the device.
+
+        Security-Related Mount Options
+
+	    See mount(1M) and mount_fuse(1M) for information on mount
+	    options relevant to security.
+
+    Configuration
+
+	FUSE configuration options can be specified in the file
+	'/etc/fuse.conf'. Currently these options are:
+
+	mount_max=N
+
+	    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.
+
+ATTRIBUTES
+	See attributes(5) for descriptions of the following
+	attributes:
+
+	____________________________________________________________
+	|       ATTRIBUTE TYPE        |       ATTRIBUTE VALUE       |
+	|_____________________________|_____________________________|
+	| Availability                | SUNWfusefs, SUNWlibfuse     |
+	|_____________________________|_____________________________|
+	| Interface Stability         |                             |
+	|_____________________________|_____________________________|
+
+SEE ALSO
+     mount(1M), mount_fuse(1M), mnttab(4), vfstab(4), fusermount(8), sshfs(8)
+
+
diff --git a/usr/src/lib/libfuse/fuse.c b/usr/src/lib/libfuse/fuse.c
new file mode 100644
index 0000000000..8d2b6338ea
--- /dev/null
+++ b/usr/src/lib/libfuse/fuse.c
@@ -0,0 +1,3312 @@
+/*
+    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 "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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <signal.h>
+#include <dlfcn.h>
+#include <assert.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+#define FUSE_MAX_PATH 4096
+#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
+
+#define FUSE_UNKNOWN_INO 0xffffffff
+#define OFFSET_MAX 0x7fffffffffffffffLL
+
+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 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;
+};
+
+struct fusemod_so {
+    void *handle;
+    int ctr;
+};
+
+struct fuse {
+    struct fuse_session *se;
+    struct node **name_table;
+    size_t name_table_size;
+    struct node **id_table;
+    size_t id_table_size;
+    fuse_ino_t ctr;
+    unsigned int generation;
+    unsigned int hidectr;
+    pthread_mutex_t lock;
+    pthread_rwlock_t tree_lock;
+    struct fuse_config conf;
+    int intr_installed;
+    struct fuse_fs *fs;
+};
+
+struct lock {
+    int type;
+    off_t start;
+    off_t end;
+    pid_t pid;
+    uint64_t owner;
+    struct lock *next;
+};
+
+struct node {
+    struct node *name_next;
+    struct node *id_next;
+    fuse_ino_t nodeid;
+    unsigned int generation;
+    int refctr;
+    struct node *parent;
+    char *name;
+    uint64_t nlookup;
+    int open_count;
+    int is_hidden;
+    struct timespec stat_updated;
+    struct timespec mtime;
+    off_t size;
+    int cache_valid;
+    struct lock *locks;
+};
+
+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;
+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", 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);
+}
+
+static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
+{
+    size_t hash = nodeid % f->id_table_size;
+    struct node *node;
+
+    for (node = f->id_table[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 free_node(struct node *node)
+{
+    free(node->name);
+    free(node);
+}
+
+static void unhash_id(struct fuse *f, struct node *node)
+{
+    size_t hash = node->nodeid % f->id_table_size;
+    struct node **nodep = &f->id_table[hash];
+
+    for (; *nodep != NULL; nodep = &(*nodep)->id_next)
+        if (*nodep == node) {
+            *nodep = node->id_next;
+            return;
+        }
+}
+
+static void hash_id(struct fuse *f, struct node *node)
+{
+    size_t hash = node->nodeid % f->id_table_size;
+    node->id_next = f->id_table[hash];
+    f->id_table[hash] = node;
+}
+
+static unsigned int name_hash(struct fuse *f, fuse_ino_t parent,
+                              const char *name)
+{
+    unsigned int hash = *name;
+
+    if (hash)
+        for (name += 1; *name != '\0'; name++)
+            hash = (hash << 5) - hash + *name;
+
+    return (hash + parent) % f->name_table_size;
+}
+
+static void unref_node(struct fuse *f, struct node *node);
+
+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[hash];
+
+        for (; *nodep != NULL; nodep = &(*nodep)->name_next)
+            if (*nodep == node) {
+                *nodep = node->name_next;
+                node->name_next = NULL;
+                unref_node(f, node->parent);
+                free(node->name);
+                node->name = NULL;
+                node->parent = NULL;
+                return;
+            }
+        fprintf(stderr, "fuse internal error: unable to unhash node: %llu\n",
+                (unsigned long long) node->nodeid);
+        abort();
+    }
+}
+
+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);
+    node->name = strdup(name);
+    if (node->name == NULL)
+        return -1;
+
+    parent->refctr ++;
+    node->parent = parent;
+    node->name_next = f->name_table[hash];
+    f->name_table[hash] = node;
+    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->name);
+    unhash_id(f, node);
+    free_node(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[hash]; node != NULL; node = node->name_next)
+        if (node->parent->nodeid == parent && strcmp(node->name, name) == 0)
+            return node;
+
+    return NULL;
+}
+
+static struct node *find_node(struct fuse *f, fuse_ino_t parent,
+                              const char *name)
+{
+    struct node *node;
+
+    pthread_mutex_lock(&f->lock);
+    node = lookup_node(f, parent, name);
+    if (node == NULL) {
+        node = (struct node *) calloc(1, sizeof(struct node));
+        if (node == NULL)
+            goto out_err;
+
+        node->refctr = 1;
+        node->nodeid = next_id(f);
+        node->open_count = 0;
+        node->is_hidden = 0;
+        node->generation = f->generation;
+        if (hash_name(f, node, parent, name) == -1) {
+            free(node);
+            node = NULL;
+            goto out_err;
+        }
+        hash_id(f, node);
+    }
+    node->nlookup ++;
+ out_err:
+    pthread_mutex_unlock(&f->lock);
+    return node;
+}
+
+static char *add_name(char *buf, char *s, const char *name)
+{
+    size_t len = strlen(name);
+    s -= len;
+    if (s <= buf) {
+        fprintf(stderr, "fuse: path too long: ...%s\n", s + len);
+        return NULL;
+    }
+    strncpy(s, name, len);
+    s--;
+    *s = '/';
+
+    return s;
+}
+
+static char *get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name)
+{
+    char buf[FUSE_MAX_PATH];
+    char *s = buf + FUSE_MAX_PATH - 1;
+    struct node *node;
+
+    *s = '\0';
+
+    if (name != NULL) {
+        s = add_name(buf, s, name);
+        if (s == NULL)
+            return NULL;
+    }
+
+    pthread_mutex_lock(&f->lock);
+    for (node = get_node(f, nodeid); node && node->nodeid != FUSE_ROOT_ID;
+         node = node->parent) {
+        if (node->name == NULL) {
+            s = NULL;
+            break;
+        }
+
+        s = add_name(buf, s, node->name);
+        if (s == NULL)
+            break;
+    }
+    pthread_mutex_unlock(&f->lock);
+
+    if (node == NULL || s == NULL)
+        return NULL;
+    else if (*s == '\0')
+        return strdup("/");
+    else
+        return strdup(s);
+}
+
+static char *get_path(struct fuse *f, fuse_ino_t nodeid)
+{
+    return get_path_name(f, nodeid, NULL);
+}
+
+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);
+    assert(node->nlookup >= nlookup);
+    node->nlookup -= nlookup;
+    if (!node->nlookup) {
+        unhash_name(f, node);
+        unref_node(f, node);
+    }
+    pthread_mutex_unlock(&f->lock);
+}
+
+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)
+        unhash_name(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;
+        }
+        unhash_name(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(__SOLARIS__)
+
+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__ || __SOLARIS__ */
+
+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__ || __SOLARIS__ */
+
+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)
+        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)
+        return fs->op.fgetattr(path, buf, fi);
+    else if (fs->op.getattr)
+        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)
+        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)
+        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)
+        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)
+        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)
+        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)
+        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)
+        return fuse_compat_opendir(fs, (char *)path, fi);
+    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)
+        return fuse_compat_open(fs, (char *)path, fi);
+    else
+        return 0;
+}
+
+int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size,
+                 off_t off, struct fuse_file_info *fi)
+{
+    fuse_get_context()->private_data = fs->user_data;
+    if (fs->op.read)
+        return fs->op.read(path, buf, size, off, fi);
+    else
+        return -ENOSYS;
+}
+
+int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
+                  size_t size, off_t off, struct fuse_file_info *fi)
+{
+    fuse_get_context()->private_data = fs->user_data;
+    if (fs->op.write)
+        return fs->op.write(path, buf, size, off, fi);
+    else
+        return -ENOSYS;
+}
+
+int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
+                  struct fuse_file_info *fi)
+{
+    fuse_get_context()->private_data = fs->user_data;
+    if (fs->op.fsync)
+        return 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)
+        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)
+        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)
+        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)
+        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, off_t off,
+                    struct fuse_file_info *fi)
+{
+    fuse_get_context()->private_data = fs->user_data;
+    if (fs->op.readdir)
+        return fs->op.readdir(path, buf, filler, off, fi);
+    else if (fs->op.getdir) {
+        struct fuse_dirhandle dh;
+        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)
+        return fs->op.create(path, mode, fi);
+    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)
+        return fs->op.lock(path, fi, cmd, lock);
+    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)
+        return fs->op.chown(path, uid, gid);
+    else
+        return -ENOSYS;
+}
+
+int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size)
+{
+    fuse_get_context()->private_data = fs->user_data;
+    if (fs->op.truncate)
+        return fs->op.truncate(path, size);
+    else
+        return -ENOSYS;
+}
+
+int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size,
+                      struct fuse_file_info *fi)
+{
+    fuse_get_context()->private_data = fs->user_data;
+    if (fs->op.ftruncate)
+        return fs->op.ftruncate(path, size, fi);
+    else if (fs->op.truncate)
+        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)
+        return fs->op.utimens(path, tv);
+    else if(fs->op.utime) {
+        struct utimbuf buf;
+        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)
+        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)
+        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)
+        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)
+        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)
+        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)
+        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)
+        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)
+        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)
+        return fs->op.removexattr(path, name);
+    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);
+        pthread_mutex_unlock(&f->lock);
+
+        newpath = get_path_name(f, dir, newname);
+        if (!newpath)
+            break;
+
+        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 *) malloc(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;
+    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)
+            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.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;
+    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);
+    if (fs->m)
+        fuse_put_module(fs->m);
+    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;
+
+    err = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path_name(f, parent, name);
+    if (path != NULL) {
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    reply_entry(req, &e, err);
+}
+
+static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino,
+                            unsigned long nlookup)
+{
+    struct fuse *f = req_fuse(req);
+    if (f->conf.debug)
+        fprintf(stderr, "FORGET %llu/%lu\n", (unsigned long long)ino, nlookup);
+    forget_node(f, ino, 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;
+
+    (void) fi;
+    memset(&buf, 0, sizeof(buf));
+
+    err = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        struct fuse_intr_data d;
+        fuse_prepare_interrupt(f, req, &d);
+        err = fuse_fs_getattr(f->fs, path, &buf);
+        fuse_finish_interrupt(f, req, &d);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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);
+}
+
+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;
+
+    err = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        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);
+        }
+        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)
+            err = fuse_fs_getattr(f->fs,  path, &buf);
+        fuse_finish_interrupt(f, req, &d);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        struct fuse_intr_data d;
+        if (f->conf.debug)
+            fprintf(stderr, "ACCESS %s 0%o\n", path, mask);
+        fuse_prepare_interrupt(f, req, &d);
+        err = fuse_fs_access(f->fs, path, mask);
+        fuse_finish_interrupt(f, req, &d);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path_name(f, parent, name);
+    if (path) {
+        struct fuse_intr_data d;
+        if (f->conf.debug)
+            fprintf(stderr, "MKNOD %s\n", path);
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path_name(f, parent, name);
+    if (path != NULL) {
+        struct fuse_intr_data d;
+        if (f->conf.debug)
+            fprintf(stderr, "MKDIR %s\n", path);
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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);
+    char *path;
+    int err;
+
+    err = -ENOENT;
+    pthread_rwlock_wrlock(&f->tree_lock);
+    path = get_path_name(f, parent, name);
+    if (path != NULL) {
+        struct fuse_intr_data d;
+        if (f->conf.debug)
+            fprintf(stderr, "UNLINK %s\n", path);
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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);
+    char *path;
+    int err;
+
+    err = -ENOENT;
+    pthread_rwlock_wrlock(&f->tree_lock);
+    path = get_path_name(f, parent, name);
+    if (path != NULL) {
+        struct fuse_intr_data d;
+        if (f->conf.debug)
+            fprintf(stderr, "RMDIR %s\n", path);
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path_name(f, parent, name);
+    if (path != NULL) {
+        struct fuse_intr_data d;
+        if (f->conf.debug)
+            fprintf(stderr, "SYMLINK %s\n", path);
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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;
+    int err;
+
+    err = -ENOENT;
+    pthread_rwlock_wrlock(&f->tree_lock);
+    oldpath = get_path_name(f, olddir, oldname);
+    if (oldpath != NULL) {
+        newpath = get_path_name(f, newdir, newname);
+        if (newpath != NULL) {
+            struct fuse_intr_data d;
+            if (f->conf.debug)
+                fprintf(stderr, "RENAME %s -> %s\n", oldpath, newpath);
+            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(newpath);
+        }
+        free(oldpath);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    oldpath = get_path(f, ino);
+    if (oldpath != NULL) {
+        newpath =  get_path_name(f, newparent, newname);
+        if (newpath != NULL) {
+            struct fuse_intr_data d;
+            if (f->conf.debug)
+                fprintf(stderr, "LINK %s\n", newpath);
+            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(newpath);
+        }
+        free(oldpath);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    reply_entry(req, &e, err);
+}
+
+static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path,
+                            struct fuse_file_info *fi)
+{
+    struct node *node;
+    int unlink_hidden = 0;
+
+    fuse_fs_release(f->fs, path ? path : "-", fi);
+
+    pthread_mutex_lock(&f->lock);
+    node = get_node(f, ino);
+    assert(node->open_count > 0);
+    --node->open_count;
+    if (node->is_hidden && !node->open_count) {
+        unlink_hidden = 1;
+        node->is_hidden = 0;
+    }
+    pthread_mutex_unlock(&f->lock);
+
+    if(unlink_hidden && path)
+        fuse_fs_unlink(f->fs, path);
+}
+
+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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path_name(f, parent, name);
+    if (path) {
+        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_prepare_interrupt(f, req, &d);
+            fuse_do_release(f, e.ino, path, fi);
+            fuse_finish_interrupt(f, req, &d);
+            forget_node(f, e.ino, 1);
+        } else if (f->conf.debug) {
+            fprintf(stderr, "  CREATE[%llu] flags: 0x%x %s\n",
+                    (unsigned long long) fi->fh, fi->flags, path);
+        }
+    } else
+        reply_err(req, err);
+
+    if (path)
+        free(path);
+
+    pthread_rwlock_unlock(&f->tree_lock);
+}
+
+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 = NULL;
+    int err = 0;
+
+    err = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path) {
+        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_prepare_interrupt(f, req, &d);
+            fuse_do_release(f, ino, path, fi);
+            fuse_finish_interrupt(f, req, &d);
+        } else if (f->conf.debug) {
+            fprintf(stderr, "OPEN[%llu] flags: 0x%x %s\n",
+                    (unsigned long long) fi->fh, fi->flags, path);
+        }
+    } else
+        reply_err(req, err);
+
+    if (path)
+        free(path);
+    pthread_rwlock_unlock(&f->tree_lock);
+}
+
+static void fuse_lib_read(fuse_req_t req, fuse_ino_t ino, size_t size,
+                          off_t off, struct fuse_file_info *fi)
+{
+    struct fuse *f = req_fuse_prepare(req);
+    char *path;
+    char *buf;
+    int res;
+
+    buf = (char *) malloc(size);
+    if (buf == NULL) {
+        reply_err(req, -ENOMEM);
+        return;
+    }
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        struct fuse_intr_data d;
+        if (f->conf.debug)
+            fprintf(stderr, "READ[%llu] %lu bytes from %llu\n",
+                    (unsigned long long) fi->fh, (unsigned long) size,
+                    (unsigned long long) off);
+
+        fuse_prepare_interrupt(f, req, &d);
+        res = fuse_fs_read(f->fs, path, buf, size, off, fi);
+        fuse_finish_interrupt(f, req, &d);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+
+    if (res >= 0) {
+        if (f->conf.debug)
+            fprintf(stderr, "   READ[%llu] %u bytes\n",
+                    (unsigned long long)fi->fh, res);
+        if ((size_t) res > size)
+            fprintf(stderr, "fuse: read too many bytes");
+        fuse_reply_buf(req, buf, res);
+    } else
+        reply_err(req, res);
+
+    free(buf);
+}
+
+static void fuse_lib_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
+                       size_t size, off_t off, struct fuse_file_info *fi)
+{
+    struct fuse *f = req_fuse_prepare(req);
+    char *path;
+    int res;
+
+    res = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        struct fuse_intr_data d;
+        if (f->conf.debug)
+            fprintf(stderr, "WRITE%s[%llu] %lu bytes to %llu\n",
+                    fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh,
+                    (unsigned long) size, (unsigned long long) off);
+
+        fuse_prepare_interrupt(f, req, &d);
+        res = fuse_fs_write(f->fs, path, buf, size, off, fi);
+        fuse_finish_interrupt(f, req, &d);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+
+    if (res >= 0) {
+        if (f->conf.debug)
+            fprintf(stderr, "   WRITE%s[%llu] %u bytes\n",
+                    fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh,
+                    res);
+        if ((size_t) res > size)
+            fprintf(stderr, "fuse: wrote too many bytes");
+        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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        struct fuse_intr_data d;
+        if (f->conf.debug)
+            fprintf(stderr, "FSYNC[%llu]\n", (unsigned long long) fi->fh);
+        fuse_prepare_interrupt(f, req, &d);
+        err = fuse_fs_fsync(f->fs, path, datasync, fi);
+        fuse_finish_interrupt(f, req, &d);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        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_prepare_interrupt(f, req, &d);
+            fuse_fs_releasedir(f->fs, path, &fi);
+            fuse_finish_interrupt(f, req, &d);
+            pthread_mutex_destroy(&dh->lock);
+            free(dh);
+        }
+    } else {
+        reply_err(req, err);
+        free(dh);
+    }
+    free(path);
+    pthread_rwlock_unlock(&f->tree_lock);
+}
+
+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)
+            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,
+                    off_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, off_t off, struct fuse_dh *dh,
+                        struct fuse_file_info *fi)
+{
+    int err = -ENOENT;
+    char *path;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    return err;
+}
+
+static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
+                             off_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;
+
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    fuse_prepare_interrupt(f, req, &d);
+    fuse_fs_releasedir(f->fs, path ? path : "-", &fi);
+    fuse_finish_interrupt(f, req, &d);
+    if (path)
+        free(path);
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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;
+    int err;
+
+    memset(&buf, 0, sizeof(buf));
+    pthread_rwlock_rdlock(&f->tree_lock);
+    if (!ino) {
+        err = -ENOMEM;
+        path = strdup("/");
+    } else {
+        err = -ENOENT;
+        path = get_path(f, ino);
+    }
+    if (path) {
+        struct fuse_intr_data d;
+        fuse_prepare_interrupt(f, req, &d);
+        err = fuse_fs_statfs(f->fs, path, &buf);
+        fuse_finish_interrupt(f, req, &d);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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;
+
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (f->conf.debug)
+        fprintf(stderr, "RELEASE%s[%llu] flags: 0x%x\n",
+                fi->flush ? "+FLUSH" : "",
+                (unsigned long long) fi->fh, fi->flags);
+
+    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);
+    pthread_rwlock_unlock(&f->tree_lock);
+
+    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;
+
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path && f->conf.debug)
+        fprintf(stderr, "FLUSH[%llu]\n", (unsigned long long) fi->fh);
+    err = fuse_flush_common(f, req, ino, path, fi);
+    free(path);
+    pthread_rwlock_unlock(&f->tree_lock);
+    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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        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);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    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_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 = -ENOENT;
+    pthread_rwlock_rdlock(&f->tree_lock);
+    path = get_path(f, ino);
+    if (path != NULL) {
+        fuse_prepare_interrupt(f, req, &d);
+        err = fuse_fs_bmap(f->fs, path, blocksize, &idx);
+        fuse_finish_interrupt(f, req, &d);
+        free(path);
+    }
+    pthread_rwlock_unlock(&f->tree_lock);
+    if (!err)
+        fuse_reply_bmap(req, idx);
+    else
+        reply_err(req, err);
+}
+
+static struct fuse_lowlevel_ops fuse_path_ops = {
+    .init = fuse_lib_init,
+    .destroy = fuse_lib_destroy,
+    .lookup = fuse_lib_lookup,
+    .forget = fuse_lib_forget,
+    .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 = fuse_lib_write,
+    .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,
+    .bmap = fuse_lib_bmap,
+};
+
+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;
+}
+
+int fuse_loop(struct fuse *f)
+{
+    if (f)
+        return fuse_session_loop(f->se);
+    else
+        return -1;
+}
+
+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;
+}
+
+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("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\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 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);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+    return 0;
+}
+
+struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
+                            void *user_data)
+{
+    struct fuse_fs *fs;
+
+    if (sizeof(struct fuse_operations) < op_size) {
+        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;
+}
+
+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;
+
+    /* 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;
+
+    if (fuse_opt_parse(args, &f->conf, fuse_lib_opts, fuse_lib_opt_proc) == -1)
+        goto out_free_fs;
+
+    if (f->conf.modules) {
+        char *module;
+        char *next;
+
+        for (module = f->conf.modules; module; module = next) {
+            char *p;
+            for (p = module; *p && *p != ':'; p++);
+            next = *p ? p + 1 : NULL;
+            *p = '\0';
+            if (module[0] && fuse_push_module(f, module, args) == -1)
+                goto out_free_fs;
+        }
+    }
+
+    if (!f->conf.ac_attr_timeout_set)
+        f->conf.ac_attr_timeout = f->conf.attr_timeout;
+
+#ifdef __FreeBSD__
+    /*
+     * 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) {
+        if (f->conf.help)
+            fuse_lib_help_modules();
+        goto out_free_fs;
+    }
+
+    fuse_session_add_chan(f->se, ch);
+
+    f->ctr = 0;
+    f->generation = 0;
+    /* FIXME: Dynamic hash table */
+    f->name_table_size = 14057;
+    f->name_table = (struct node **)
+        calloc(1, sizeof(struct node *) * f->name_table_size);
+    if (f->name_table == NULL) {
+        fprintf(stderr, "fuse: memory allocation failed\n");
+        goto out_free_session;
+    }
+
+    f->id_table_size = 14057;
+    f->id_table = (struct node **)
+        calloc(1, sizeof(struct node *) * f->id_table_size);
+    if (f->id_table == NULL) {
+        fprintf(stderr, "fuse: memory allocation failed\n");
+        goto out_free_name_table;
+    }
+
+    fuse_mutex_init(&f->lock);
+    pthread_rwlock_init(&f->tree_lock, NULL);
+
+    root = (struct node *) calloc(1, sizeof(struct node));
+    if (root == NULL) {
+        fprintf(stderr, "fuse: memory allocation failed\n");
+        goto out_free_id_table;
+    }
+
+    root->name = strdup("/");
+    if (root->name == NULL) {
+        fprintf(stderr, "fuse: memory allocation failed\n");
+        goto out_free_root;
+    }
+
+    if (f->conf.intr &&
+        fuse_init_intr_signal(f->conf.intr_signal, &f->intr_installed) == -1)
+        goto out_free_root_name;
+
+    root->parent = NULL;
+    root->nodeid = FUSE_ROOT_ID;
+    root->generation = 0;
+    root->refctr = 1;
+    root->nlookup = 1;
+    hash_id(f, root);
+
+    return f;
+
+ out_free_root_name:
+    free(root->name);
+ out_free_root:
+    free(root);
+ out_free_id_table:
+    free(f->id_table);
+ out_free_name_table:
+    free(f->name_table);
+ 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[i]; node != NULL; node = node->id_next) {
+                if (node->is_hidden) {
+                    char *path = get_path(f, node->nodeid);
+                    if (path) {
+                        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[i]; node != NULL; node = next) {
+            next = node->id_next;
+            free_node(node);
+        }
+    }
+    free(f->id_table);
+    free(f->name_table);
+    pthread_mutex_destroy(&f->lock);
+    pthread_rwlock_destroy(&f->tree_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;
+}
+
+/* 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;
+}
+
+#if !defined(__FreeBSD__) && !defined(__SOLARIS__)
+
+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);
+}
+
+__asm__(".symver fuse_exited,__fuse_exited@");
+__asm__(".symver fuse_process_cmd,__fuse_process_cmd@");
+__asm__(".symver fuse_read_cmd,__fuse_read_cmd@");
+__asm__(".symver fuse_set_getcontext_func,__fuse_set_getcontext_func@");
+__asm__(".symver fuse_new_compat2,fuse_new@");
+__asm__(".symver fuse_new_compat22,fuse_new@FUSE_2.2");
+
+#endif /* __FreeBSD__ || __SOLARIS__*/
+
+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);
+}
+
+#ifndef __SOLARIS__
+__asm__(".symver fuse_new_compat25,fuse_new@FUSE_2.5");
+#endif /* __SOLARIS */
diff --git a/usr/src/lib/libfuse/fuse.pc b/usr/src/lib/libfuse/fuse.pc
new file mode 100644
index 0000000000..b0c635653d
--- /dev/null
+++ b/usr/src/lib/libfuse/fuse.pc
@@ -0,0 +1,10 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: fuse
+Description: Filesystem in Userspace
+Version: 2.7.1
+Libs: -L${libdir} -lfuse -lxnet
+Cflags: -I${includedir}/fuse -D_FILE_OFFSET_BITS=64
diff --git a/usr/src/lib/libfuse/fuse_kern_chan.c b/usr/src/lib/libfuse/fuse_kern_chan.c
new file mode 100644
index 0000000000..49e88b7d3f
--- /dev/null
+++ b/usr/src/lib/libfuse/fuse_kern_chan.c
@@ -0,0 +1,95 @@
+/*
+    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 occuring 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)
+{
+    close(fuse_chan_fd(ch));
+}
+
+#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/usr/src/lib/libfuse/fuse_loop.c b/usr/src/lib/libfuse/fuse_loop.c
new file mode 100644
index 0000000000..f7c17faf1e
--- /dev/null
+++ b/usr/src/lib/libfuse/fuse_loop.c
@@ -0,0 +1,39 @@
+/*
+    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;
+        res = fuse_chan_recv(&tmpch, buf, bufsize);
+        if (res == -EINTR)
+            continue;
+        if (res <= 0)
+            break;
+        fuse_session_process(se, buf, res, tmpch);
+    }
+
+    free(buf);
+    fuse_session_reset(se);
+    return res < 0 ? -1 : 0;
+}
diff --git a/usr/src/lib/libfuse/fuse_loop_mt.c b/usr/src/lib/libfuse/fuse_loop_mt.c
new file mode 100644
index 0000000000..7bcc1c26a5
--- /dev/null
+++ b/usr/src/lib/libfuse/fuse_loop_mt.c
@@ -0,0 +1,221 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU LGPL.
+    See the file COPYING.LIB.
+*/
+
+#include "fuse_lowlevel.h"
+#include "fuse_misc.h"
+#include "fuse_kernel.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>
+
+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_start_thread(struct fuse_mt *mt);
+
+static void *fuse_do_work(void *data)
+{
+    struct fuse_worker *w = (struct fuse_worker *) data;
+    struct fuse_mt *mt = w->mt;
+
+    while (!fuse_session_exited(mt->se)) {
+        int isforget = 0;
+        struct fuse_chan *ch = mt->prevch;
+        int res = fuse_chan_recv(&ch, w->buf, w->bufsize);
+        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 (((struct fuse_in_header *) w->buf)->opcode == FUSE_FORGET)
+            isforget = 1;
+
+        if (!isforget)
+            mt->numavail--;
+        if (mt->numavail == 0)
+            fuse_start_thread(mt);
+        pthread_mutex_unlock(&mt->lock);
+
+        fuse_session_process(mt->se, w->buf, res, 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);
+    pause();
+
+    return NULL;
+}
+
+static int fuse_start_thread(struct fuse_mt *mt)
+{
+    sigset_t oldset;
+    sigset_t newset;
+    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;
+    }
+
+    /* 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(&w->thread_id, NULL, fuse_do_work, w);
+    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+    if (res != 0) {
+        fprintf(stderr, "fuse: error creating thread: %s\n", strerror(res));
+        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_start_thread(&mt);
+    pthread_mutex_unlock(&mt.lock);
+    if (!err) {
+        /* sem_wait() is interruptible */
+        while (!fuse_session_exited(se))
+            sem_wait(&mt.finish);
+
+        for (w = mt.main.next; w != &mt.main; w = w->next)
+            pthread_cancel(w->thread_id);
+        mt.exit = 1;
+        pthread_mutex_unlock(&mt.lock);
+
+        while (mt.main.next != &mt.main)
+            fuse_join_worker(&mt, mt.main.next);
+
+        err = mt.error;
+    }
+
+    pthread_mutex_destroy(&mt.lock);
+    sem_destroy(&mt.finish);
+    fuse_session_reset(se);
+    return err;
+}
diff --git a/usr/src/lib/libfuse/fuse_lowlevel.c b/usr/src/lib/libfuse/fuse_lowlevel.c
new file mode 100644
index 0000000000..463e3b3ee3
--- /dev/null
+++ b/usr/src/lib/libfuse/fuse_lowlevel.c
@@ -0,0 +1,1440 @@
+/*
+    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_opt.h"
+#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 <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+
+#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
+#define OFFSET_MAX 0x7fffffffffffffffLL
+
+struct fuse_ll;
+
+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;
+    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_ll {
+    int debug;
+    int allow_root;
+    struct fuse_lowlevel_ops op;
+    int got_init;
+    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;
+};
+
+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->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);
+}
+
+static void free_req(fuse_req_t req)
+{
+    int ctr;
+    struct fuse_ll *f = req->f;
+
+    pthread_mutex_lock(&req->lock);
+    req->u.ni.func = NULL;
+    req->u.ni.data = NULL;
+    pthread_mutex_unlock(&req->lock);
+
+    pthread_mutex_lock(&f->lock);
+    list_del_req(req);
+    ctr = --req->ctr;
+    pthread_mutex_unlock(&f->lock);
+    if (!ctr)
+        destroy_req(req);
+}
+
+static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov,
+                          int count)
+{
+    struct fuse_out_header out;
+    int res;
+
+    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);
+    out.len = iov_length(iov, count);
+
+    if (req->f->debug)
+        fprintf(stderr, "   unique: %llu, error: %i (%s), outsize: %i\n",
+                (unsigned long long) out.unique, out.error,
+                strerror(-out.error), out.len);
+    res = fuse_chan_send(req->ch, iov, count);
+    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,
+                      off_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, off_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)
+{
+    fuse_chan_send(req->ch, NULL, 0);
+    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;
+}
+
+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
+{
+    struct fuse_entry_out 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, sizeof(arg));
+}
+
+int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
+                      const struct fuse_file_info *f)
+{
+    struct {
+        struct fuse_entry_out e;
+        struct fuse_open_out o;
+    } arg;
+
+    memset(&arg, 0, sizeof(arg));
+    fill_entry(&arg.e, e);
+    fill_open(&arg.o, f);
+    return send_reply_ok(req, &arg, sizeof(arg));
+}
+
+int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
+                    double attr_timeout)
+{
+    struct fuse_attr_out 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, sizeof(arg));
+}
+
+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);
+}
+
+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, 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 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_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+    (void) inarg;
+
+    if (req->f->op.getattr)
+        req->f->op.getattr(req, nodeid, NULL);
+    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;
+        }
+        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;
+
+    if (req->f->op.mknod)
+        req->f->op.mknod(req, nodeid, PARAM(arg), 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->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_open_in *arg = (struct fuse_open_in *) inarg;
+
+    if (req->f->op.create) {
+        struct fuse_file_info fi;
+
+        memset(&fi, 0, sizeof(fi));
+        fi.flags = arg->flags;
+
+        req->f->op.create(req, nodeid, PARAM(arg), 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;
+        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;
+
+    memset(&fi, 0, sizeof(fi));
+    fi.fh = arg->fh;
+    fi.fh_old = fi.fh;
+    fi.writepage = arg->write_flags & 1;
+
+    if (req->f->op.write)
+        req->f->op.write(req, nodeid, PARAM(arg), arg->size, arg->offset, &fi);
+    else
+        fuse_reply_err(req, ENOSYS);
+}
+
+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 (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;
+
+    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) {
+            curr->ctr++;
+            pthread_mutex_unlock(&f->lock);
+
+            /* Ugh, ugly locking */
+            pthread_mutex_lock(&curr->lock);
+            pthread_mutex_lock(&f->lock);
+            curr->interrupted = 1;
+            pthread_mutex_unlock(&f->lock);
+            if (curr->u.ni.func)
+                curr->u.ni.func(curr, curr->u.ni.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_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->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;
+
+    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 || (arg->major == 7 && 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;
+    } else {
+        f->conn.async_read = 0;
+        f->conn.max_readahead = 0;
+    }
+
+    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);
+
+    memset(&outarg, 0, sizeof(outarg));
+    outarg.major = FUSE_KERNEL_VERSION;
+    outarg.minor = FUSE_KERNEL_MINOR_VERSION;
+    if (f->conn.async_read)
+        outarg.flags |= FUSE_ASYNC_READ;
+    if (f->op.getlk && f->op.setlk)
+        outarg.flags |= FUSE_POSIX_LOCKS;
+    outarg.max_readahead = f->conn.max_readahead;
+    outarg.max_write = f->conn.max_write;
+
+    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);
+    }
+
+    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);
+}
+
+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;
+}
+
+void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
+                             void *data)
+{
+    pthread_mutex_lock(&req->lock);
+    req->u.ni.func = func;
+    req->u.ni.data = data;
+    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_DESTROY]     = { do_destroy,     "DESTROY"     },
+};
+
+#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
+
+static const char *opname(enum fuse_opcode opcode)
+{
+    if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
+        return "???";
+    else
+        return fuse_ll_ops[opcode].name;
+}
+
+static void fuse_ll_process(void *data, const char *buf, size_t len,
+                     struct fuse_chan *ch)
+{
+    struct fuse_ll *f = (struct fuse_ll *) data;
+    struct fuse_in_header *in = (struct fuse_in_header *) buf;
+    const void *inarg = buf + sizeof(struct fuse_in_header);
+    struct fuse_req *req;
+
+    if (f->debug)
+        fprintf(stderr, "unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %zu\n",
+                (unsigned long long) in->unique,
+                opname((enum fuse_opcode) in->opcode), in->opcode,
+                (unsigned long) in->nodeid, len);
+
+    req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
+    if (req == NULL) {
+        fprintf(stderr, "fuse: failed to allocate request\n");
+        return;
+    }
+
+    req->f = f;
+    req->unique = in->unique;
+    req->ctx.uid = in->uid;
+    req->ctx.gid = in->gid;
+    req->ctx.pid = in->pid;
+    req->ch = ch;
+    req->ctr = 1;
+    list_init_req(req);
+    fuse_mutex_init(&req->lock);
+
+    if (!f->got_init && in->opcode != FUSE_INIT)
+        fuse_reply_err(req, EIO);
+    else 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) {
+        fuse_reply_err(req, EACCES);
+    } else if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
+        fuse_reply_err(req, ENOSYS);
+    else {
+        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);
+        }
+        fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
+    }
+}
+
+enum {
+    KEY_HELP,
+    KEY_VERSION,
+};
+
+static 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 },
+    { "async_read", offsetof(struct fuse_ll, conn.async_read), 1 },
+    { "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 },
+    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 async_read          perform reads asynchronously (default)\n"
+"    -o sync_read           perform reads synchronously\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;
+
+    if (f->got_init && !f->got_destroy) {
+        if (f->op.destroy)
+            f->op.destroy(f->userdata);
+    }
+
+    pthread_mutex_destroy(&f->lock);
+    free(f);
+}
+
+/*
+ * 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)
+{
+    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;
+    list_init_req(&f->list);
+    list_init_req(&f->interrupts);
+    fuse_mutex_init(&f->lock);
+
+    if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
+        goto out_free;
+
+    memcpy(&f->op, op, op_size);
+    f->owner = getuid();
+    f->userdata = userdata;
+
+    se = fuse_session_new(&sop, f);
+    if (!se)
+        goto out_free;
+
+    return se;
+
+ out_free:
+    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);
+}
+
+
+#if !defined(__FreeBSD__) && !defined(__SOLARIS__)
+
+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;
+}
+
+__asm__(".symver fuse_reply_statfs_compat,fuse_reply_statfs@FUSE_2.4");
+__asm__(".symver fuse_reply_open_compat,fuse_reply_open@FUSE_2.4");
+__asm__(".symver fuse_lowlevel_new_compat,fuse_lowlevel_new@FUSE_2.4");
+
+#else /* __FreeBSD__ || __SOLARIS__ */
+
+int fuse_sync_compat_args(struct fuse_args *args)
+{
+    (void) args;
+    return 0;
+}
+
+#endif /* __FreeBSD__ || __SOLARIS__ */
+
+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);
+}
+
+#ifndef __SOLARIS__
+__asm__(".symver fuse_lowlevel_new_compat25,fuse_lowlevel_new@FUSE_2.5");
+#endif /* __SOLARIS__ */
diff --git a/usr/src/lib/libfuse/fuse_mt.c b/usr/src/lib/libfuse/fuse_mt.c
new file mode 100644
index 0000000000..91ecfa534b
--- /dev/null
+++ b/usr/src/lib/libfuse/fuse_mt.c
@@ -0,0 +1,117 @@
+/*
+    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_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;
+
+    return fuse_session_loop_mt(fuse_get_session(f));
+}
+
+#ifndef __SOLARIS__
+__asm__(".symver fuse_loop_mt_proc,__fuse_loop_mt@");
+#endif /* __SOLARIS__ */
diff --git a/usr/src/lib/libfuse/fuse_opt.c b/usr/src/lib/libfuse/fuse_opt.c
new file mode 100644
index 0000000000..9dcecf406d
--- /dev/null
+++ b/usr/src/lib/libfuse/fuse_opt.c
@@ -0,0 +1,406 @@
+/*
+    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 <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);
+
+    newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
+    newarg = newargv ? strdup(arg) : NULL;
+    if (!newargv || !newarg)
+        return alloc_failed();
+
+    args->argv = newargv;
+    args->allocated = 1;
+    args->argv[args->argc++] = newarg;
+    args->argv[args->argc] = NULL;
+    return 0;
+}
+
+int fuse_opt_insert_arg(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;
+}
+
+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);
+}
+
+int fuse_opt_add_opt(char **opts, const char *opt)
+{
+    char *newopts;
+    if (!*opts)
+        newopts = strdup(opt);
+    else {
+        unsigned oldlen = strlen(*opts);
+        newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1);
+        if (newopts) {
+            newopts[oldlen] = ',';
+            strcpy(newopts + oldlen + 1, opt);
+        }
+    }
+    if (!newopts)
+        return alloc_failed();
+
+    *opts = newopts;
+    return 0;
+}
+
+static int add_opt(struct fuse_opt_context *ctx, const char *opt)
+{
+    return fuse_opt_add_opt(&ctx->opts, opt);
+}
+
+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 char* opt_param_unescape(const char* param) {
+	char* new_param = malloc(strlen(param) + 1);
+	char* p = new_param;
+
+	if(!new_param)
+		return NULL;
+
+	while(*param) {
+		if(*param == '\\' && *(param + 1) == ',') {
+			/* Ignore \ character */
+			++param;
+		}
+
+		*p++ = *param++;
+	}
+
+	*p = '\0';
+
+	return new_param;
+}
+
+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 = opt_param_unescape(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 *sep;
+    char *tmp = opts;
+
+    do {
+        int res;
+        sep = strchr(tmp, ',');
+
+        if(sep && sep != tmp && *(sep - 1) == '\\') {
+        	tmp = sep + 1;
+        	continue;
+        }
+
+        if (sep)
+            *sep = '\0';
+        res = process_gopt(ctx, opts, 1);
+        if (res == -1)
+            return -1;
+        opts = tmp = sep + 1;
+    } while (sep);
+
+    return 0;
+}
+
+static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
+{
+    int res;
+    char *copy;
+    const char *tmp = opts;
+    const char *sep = strchr(tmp, ',');
+
+    while(sep) {
+    	if(sep == tmp || *(sep - 1) != '\\') {
+    		break;
+    	}
+
+    	tmp = sep + 1;
+    	sep = strchr(tmp, ',');
+	}
+
+    if (!sep)
+        return process_gopt(ctx, opts, 1);
+
+    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 (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) {
+        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;
+}
diff --git a/usr/src/lib/libfuse/fuse_session.c b/usr/src/lib/libfuse/fuse_session.c
new file mode 100644
index 0000000000..2961e1efaa
--- /dev/null
+++ b/usr/src/lib/libfuse/fuse_session.c
@@ -0,0 +1,208 @@
+/*
+    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_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_session {
+    struct fuse_session_ops op;
+
+    void *data;
+
+    volatile int exited;
+
+    struct fuse_chan *ch;
+};
+
+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_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;
+}
+
+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;
+}
+
+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);
+}
+
+#if !defined(__FreeBSD__) && !defined(__SOLARIS__)
+__asm__(".symver fuse_chan_new_compat24,fuse_chan_new@FUSE_2.4");
+#endif /* __FreeBSD || __SOLARIS__ */
diff --git a/usr/src/lib/libfuse/fuse_signals.c b/usr/src/lib/libfuse/fuse_signals.c
new file mode 100644
index 0000000000..25998bfc81
--- /dev/null
+++ b/usr/src/lib/libfuse/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))
+{
+    struct sigaction sa;
+    struct sigaction old_sa;
+
+    memset(&sa, 0, sizeof(struct sigaction));
+    sa.sa_handler = 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 == 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) == -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)
+        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, SIG_DFL);
+    set_one_signal_handler(SIGINT, SIG_DFL);
+    set_one_signal_handler(SIGTERM, SIG_DFL);
+    set_one_signal_handler(SIGPIPE, SIG_DFL);
+}
+
diff --git a/usr/src/lib/libfuse/helper.c b/usr/src/lib/libfuse/helper.c
new file mode 100644
index 0000000000..0c4e96ddaf
--- /dev/null
+++ b/usr/src/lib/libfuse/helper.c
@@ -0,0 +1,455 @@
+/*
+    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_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>
+
+#ifdef __SOLARIS__
+extern int daemon(int, int);
+#endif
+
+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)
+{
+    int res;
+
+    if (!foreground) {
+        res = daemon(0, 0);
+        if (res == -1) {
+            perror("fuse: failed to daemonize program\n");
+            return -1;
+        }
+    }
+    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)
+{
+    int fd = ch ? fuse_chan_fd(ch) : -1;
+    fuse_kern_unmount(mountpoint, fd);
+    fuse_chan_destroy(ch);
+}
+
+void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
+{
+    fuse_unmount_common(mountpoint, ch);
+}
+
+static 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;
+
+#ifndef __SOLARIS__ /* XXX: Need to fix locking */
+    if (multithreaded)
+        res = fuse_loop_mt(fuse);
+    else
+        res = fuse_loop(fuse);
+#else
+        res = fuse_loop(fuse);
+#endif /* __SOLARIS__ */
+
+    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(__SOLARIS__)
+
+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);
+}
+
+__asm__(".symver fuse_setup_compat2,__fuse_setup@");
+__asm__(".symver fuse_setup_compat22,fuse_setup@FUSE_2.2");
+__asm__(".symver fuse_teardown,__fuse_teardown@");
+__asm__(".symver fuse_main_compat2,fuse_main@");
+__asm__(".symver fuse_main_real_compat22,fuse_main_real@FUSE_2.2");
+
+#endif /* __FreeBSD__ || __SOLARIS__ */
+
+
+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);
+}
+
+#ifndef __SOLARIS__
+__asm__(".symver fuse_setup_compat25,fuse_setup@FUSE_2.5");
+__asm__(".symver fuse_teardown_compat22,fuse_teardown@FUSE_2.2");
+__asm__(".symver fuse_main_real_compat25,fuse_main_real@FUSE_2.5");
+__asm__(".symver fuse_mount_compat25,fuse_mount@FUSE_2.5");
+#endif /* __SOLARIS__ */
diff --git a/usr/src/lib/libfuse/i386/Makefile b/usr/src/lib/libfuse/i386/Makefile
new file mode 100644
index 0000000000..c0aa407d0c
--- /dev/null
+++ b/usr/src/lib/libfuse/i386/Makefile
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
+
diff --git a/usr/src/lib/libfuse/include/config.h b/usr/src/lib/libfuse/include/config.h
new file mode 100644
index 0000000000..f706f67c00
--- /dev/null
+++ b/usr/src/lib/libfuse/include/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. */
+#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. */
+/* #undef HAVE_SETXATTR */
+
+/* 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 1
+
+/* 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 */
+#define 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.7.1"
+
+/* 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.7.1"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "2.7.1"
diff --git a/usr/src/lib/libfuse/include/fuse.h b/usr/src/lib/libfuse/include/fuse.h
new file mode 100644
index 0000000000..772e0928b0
--- /dev/null
+++ b/usr/src/lib/libfuse/include/fuse.h
@@ -0,0 +1,833 @@
+/*
+    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>
+#include <sys/statvfs.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, off_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.
+ */
+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 */
+    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 *, off_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, or truncation flags (O_CREAT, O_EXCL, O_TRUNC)
+     * will be passed to open().  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, off_t, struct fuse_file_info *);
+
+    /** Write data to an open file
+     *
+     * Write should return exactly the number of bytes requested
+     * except on error.  An exception to this is when the 'direct_io'
+     * mount option is specified (see read operation).
+     *
+     * Changed in version 2.2
+     */
+    int (*write) (const char *, const char *, size_t, off_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
+     *
+     * This method should check if the open operation is permitted for
+     * this  directory
+     *
+     * 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, off_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 *, off_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 unlikly 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
+     *
+     * 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);
+};
+
+/** 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;
+};
+
+/**
+ * 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);
+
+/**
+ * Check if a request has already been interrupted
+ *
+ * @param req request handle
+ * @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);
+
+/*
+ * 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,
+                 off_t off, struct fuse_file_info *fi);
+int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf,
+                  size_t size, off_t off, struct fuse_file_info *fi);
+int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync,
+                  struct fuse_file_info *fi);
+int fuse_fs_flush(struct fuse_fs *fs, const char *path,
+                  struct fuse_file_info *fi);
+int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
+int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
+                    struct fuse_file_info *fi);
+int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
+                    fuse_fill_dir_t filler, off_t off,
+                    struct fuse_file_info *fi);
+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_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, off_t size);
+int fuse_fs_ftruncate(struct fuse_fs *fs, const char *path, off_t size,
+                      struct fuse_file_info *fi);
+int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
+                    const struct timespec tv[2]);
+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);
+void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn);
+void fuse_fs_destroy(struct fuse_fs *fs);
+
+/**
+ * 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'
+ */
+
+#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) 
+#define FUSE_REGISTER_MODULE(name_, factory_) \
+static void name_ ## _register(void) \
+{ \
+    static struct fuse_module mod = { #name_, factory_, NULL, NULL, 0 }; \
+    fuse_register_module(&mod); \
+}
+#else
+#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); \
+}
+#endif /* __SUNPRO_C */
+
+
+/* ----------------------------------------------------------- *
+ * 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/usr/src/lib/libfuse/include/fuse_common.h b/usr/src/lib/libfuse/include/fuse_common.h
new file mode 100644
index 0000000000..909e8552c6
--- /dev/null
+++ b/usr/src/lib/libfuse/include/fuse_common.h
@@ -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.
+*/
+
+/** @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>
+
+/** Major version of FUSE library interface */
+#define FUSE_MAJOR_VERSION 2
+
+/** Minor version of FUSE library interface */
+#define FUSE_MINOR_VERSION 7
+
+#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;
+
+    /** Padding.  Do not use*/
+    unsigned int padding : 29;
+
+    /** 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;
+};
+
+/**
+ * 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;
+
+    /**
+     * For future use.
+     */
+    unsigned reserved[27];
+};
+
+struct fuse_session;
+struct fuse_chan;
+
+/**
+ * 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);
+
+/* ----------------------------------------------------------- *
+ * 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
+#    if defined(__FreeBSD__) || defined(__SOLARIS__)
+#        if FUSE_USE_VERSION < 25
+#            error On FreeBSD and Solaris 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/usr/src/lib/libfuse/include/fuse_common_compat.h b/usr/src/lib/libfuse/include/fuse_common_compat.h
new file mode 100644
index 0000000000..0383ed7b61
--- /dev/null
+++ b/usr/src/lib/libfuse/include/fuse_common_compat.h
@@ -0,0 +1,27 @@
+/*
+    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/usr/src/lib/libfuse/include/fuse_compat.h b/usr/src/lib/libfuse/include/fuse_compat.h
new file mode 100644
index 0000000000..805713020e
--- /dev/null
+++ b/usr/src/lib/libfuse/include/fuse_compat.h
@@ -0,0 +1,193 @@
+/*
+    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_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 *, off_t);
+    int (*utime) (const char *, struct utimbuf *);
+    int (*open) (const char *, struct fuse_file_info *);
+    int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
+    int (*write) (const char *, const char *, size_t, off_t,
+                  struct fuse_file_info *);
+    int (*statfs) (const char *, struct statvfs *);
+    int (*flush) (const char *, struct fuse_file_info *);
+    int (*release) (const char *, struct fuse_file_info *);
+    int (*fsync) (const char *, int, struct fuse_file_info *);
+    int (*setxattr) (const char *, const char *, const char *, size_t, int);
+    int (*getxattr) (const char *, const char *, char *, size_t);
+    int (*listxattr) (const char *, char *, size_t);
+    int (*removexattr) (const char *, const char *);
+    int (*opendir) (const char *, struct fuse_file_info *);
+    int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
+                    struct fuse_file_info *);
+    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 *, off_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(__SOLARIS__)
+#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 *, off_t);
+    int (*utime) (const char *, struct utimbuf *);
+    int (*open) (const char *, struct fuse_file_info_compat *);
+    int (*read) (const char *, char *, size_t, off_t,
+                 struct fuse_file_info_compat *);
+    int (*write) (const char *, const char *, size_t, off_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, off_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 *, off_t);
+    int (*utime)       (const char *, struct utimbuf *);
+    int (*open)        (const char *, int);
+    int (*read)        (const char *, char *, size_t, off_t);
+    int (*write)       (const char *, const char *, size_t, off_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 *, off_t);
+    int (*utime)    (const char *, struct utimbuf *);
+    int (*open)     (const char *, int);
+    int (*read)     (const char *, char *, size_t, off_t);
+    int (*write)    (const char *, const char *, size_t, off_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__ || __SOLARIS__ */
diff --git a/usr/src/lib/libfuse/include/fuse_i.h b/usr/src/lib/libfuse/include/fuse_i.h
new file mode 100644
index 0000000000..55ce0481a9
--- /dev/null
+++ b/usr/src/lib/libfuse/include/fuse_i.h
@@ -0,0 +1,36 @@
+/*
+    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"
+
+struct fuse_session;
+struct fuse_chan;
+struct fuse_lowlevel_ops;
+struct fuse_req;
+
+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);
+void fuse_kern_unmount(const char *mountpoint, int fd);
+int fuse_kern_mount(const char *mountpoint, struct fuse_args *args);
diff --git a/usr/src/lib/libfuse/include/fuse_kernel.h b/usr/src/lib/libfuse/include/fuse_kernel.h
new file mode 100644
index 0000000000..33495f8b5c
--- /dev/null
+++ b/usr/src/lib/libfuse/include/fuse_kernel.h
@@ -0,0 +1,376 @@
+/*
+    This file defines the kernel interface of FUSE
+    Copyright (C) 2001-2007  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.
+*/
+
+#ifndef linux
+#include <sys/types.h>
+#define __u64 uint64_t
+#define __u32 uint32_t
+#define __s32 int32_t
+#else
+#include <asm/types.h>
+#include <linux/major.h>
+#endif
+
+/** Version number of this interface */
+#define FUSE_KERNEL_VERSION 7
+
+/** Minor version number of this interface */
+#define FUSE_KERNEL_MINOR_VERSION 8
+
+/** The node ID of the root inode */
+#define FUSE_ROOT_ID 1
+
+/** The major number of the fuse character device */
+#define FUSE_MAJOR MISC_MAJOR
+
+/** The minor number of the fuse character device */
+#define FUSE_MINOR 229
+
+/* 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;
+};
+
+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)
+
+/**
+ * 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
+ */
+#define FOPEN_DIRECT_IO		(1 << 0)
+#define FOPEN_KEEP_CACHE	(1 << 1)
+
+/**
+ * INIT request/reply flags
+ */
+#define FUSE_ASYNC_READ		(1 << 0)
+#define FUSE_POSIX_LOCKS	(1 << 1)
+
+/**
+ * Release flags
+ */
+#define FUSE_RELEASE_FLUSH	(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,
+};
+
+/* The read buffer is required to be at least 8k, but may be much larger */
+#define FUSE_MIN_READ_BUFFER 8192
+
+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_attr_out {
+	__u64	attr_valid;	/* Cache timeout for the attributes */
+	__u32	attr_valid_nsec;
+	__u32	dummy;
+	struct fuse_attr attr;
+};
+
+struct fuse_mknod_in {
+	__u32	mode;
+	__u32	rdev;
+};
+
+struct fuse_mkdir_in {
+	__u32	mode;
+	__u32	padding;
+};
+
+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	unused1;
+	__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	mode;
+};
+
+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	padding;
+};
+
+struct fuse_write_in {
+	__u64	fh;
+	__u64	offset;
+	__u32	size;
+	__u32	write_flags;
+};
+
+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;
+};
+
+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;
+	__u32	unused;
+	__u32	max_write;
+};
+
+struct fuse_interrupt_in {
+	__u64	unique;
+};
+
+struct fuse_bmap_in {
+	__u64	block;
+	__u32	blocksize;
+	__u32	padding;
+};
+
+struct fuse_bmap_out {
+	__u64	block;
+};
+
+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[1];
+};
+
+#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)
diff --git a/usr/src/lib/libfuse/include/fuse_lowlevel.h b/usr/src/lib/libfuse/include/fuse_lowlevel.h
new file mode 100644
index 0000000000..96c7340200
--- /dev/null
+++ b/usr/src/lib/libfuse/include/fuse_lowlevel.h
@@ -0,0 +1,1376 @@
+/*
+    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. The ino/generation pair
+        should be unique for the filesystem's lifetime. It 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;
+};
+
+/* '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)
+
+/* ----------------------------------------------------------- *
+ * 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
+     *
+     * The nlookup parameter indicates the number of lookups
+     * previously performed on this inode.
+     *
+     * If the filesystem implements inode lifetimes, it is recommended
+     * that inodes acquire a single reference on each lookup, and lose
+     * nlookup references on each forget.
+     *
+     * The filesystem may ignore forget calls, if the inodes don't
+     * need to have a limited lifetime.
+     *
+     * On unmount it is not guaranteed, that all referenced inodes
+     * will receive a forget message.
+     *
+     * 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
+     *
+     * 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
+     *
+     * 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
+     *
+     * 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_err
+     *
+     * @param req request handle
+     * @param ino the inode number
+     * @param size number of bytes to read
+     * @param off offset to read from
+     * @param fi file information
+     */
+    void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+                  struct fuse_file_info *fi);
+
+    /**
+     * Write data
+     *
+     * Write should return exactly the number of bytes requested
+     * except on error.  An exception to this is when the file has
+     * been opened in 'direct_io' mode, in which case the return value
+     * of the write system call will reflect the return value of this
+     * operation.
+     *
+     * fi->fh will contain the value set by the open method, or will
+     * be undefined if the open method didn't set any value.
+     *
+     * Valid replies:
+     *   fuse_reply_write
+     *   fuse_reply_err
+     *
+     * @param req request handle
+     * @param ino the inode number
+     * @param buf data to write
+     * @param size number of bytes to write
+     * @param off offset to write to
+     * @param fi file information
+     */
+    void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+                   size_t size, off_t off, struct fuse_file_info *fi);
+
+    /**
+     * Flush method
+     *
+     * This is called on each close() of the opened file.
+     *
+     * Since file descriptors can be duplicated (dup, dup2, fork), for
+     * one open call there may be many flush calls.
+     *
+     * Filesystems shouldn't assume that flush will always be called
+     * after some writes, or that if will be called at all.
+     *
+     * fi->fh will contain the value set by the open method, or will
+     * be undefined if the open method didn't set any value.
+     *
+     * NOTE: the name of the method is misleading, since (unlike
+     * fsync) the filesystem is not forced to flush pending writes.
+     * One reason to flush data, is if the filesystem wants to return
+     * write errors.
+     *
+     * 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_err
+     *
+     * @param req request handle
+     * @param ino the inode number
+     * @param size maximum number of bytes to send
+     * @param off offset to continue reading the directory stream
+     * @param fi file information
+     */
+    void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+                     struct fuse_file_info *fi);
+
+    /**
+     * Release an open directory
+     *
+     * For every opendir call there will be exactly one releasedir
+     * call.
+     *
+     * 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_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_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 test
+     * @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);
+};
+
+/**
+ * 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
+ *
+ * @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
+ *
+ * @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 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 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, 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.  Of 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 the name of the entry
+ * @param stbuf the file attributes
+ * @param off the offset of the next entry
+ * @return the space needed for the entry
+ */
+size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
+                         const char *name, const struct stat *stbuf,
+                         off_t off);
+
+/* ----------------------------------------------------------- *
+ * 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);
+
+/**
+ * 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
+ * @parm 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);
+
+/**
+ * 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);
+
+/**
+ * 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/usr/src/lib/libfuse/include/fuse_lowlevel_compat.h b/usr/src/lib/libfuse/include/fuse_lowlevel_compat.h
new file mode 100644
index 0000000000..6a3393f28d
--- /dev/null
+++ b/usr/src/lib/libfuse/include/fuse_lowlevel_compat.h
@@ -0,0 +1,144 @@
+/*
+    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_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, off_t off,
+                  struct fuse_file_info *fi);
+    void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+                   size_t size, off_t off, struct fuse_file_info *fi);
+    void (*flush) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+    void (*release) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+    void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
+                   struct fuse_file_info *fi);
+    void (*opendir) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+    void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+                     struct fuse_file_info *fi);
+    void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
+                        struct fuse_file_info *fi);
+    void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
+                      struct fuse_file_info *fi);
+    void (*statfs) (fuse_req_t req);
+    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,
+                      off_t off);
+
+#if !defined(__FreeBSD__) && !defined(__SOLARIS__)
+
+#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, off_t off,
+                  struct fuse_file_info_compat *fi);
+    void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
+                   size_t size, off_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, off_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__ || __SOLARIS__ */
+
+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/usr/src/lib/libfuse/include/fuse_misc.h b/usr/src/lib/libfuse/include/fuse_misc.h
new file mode 100644
index 0000000000..99e5117ae6
--- /dev/null
+++ b/usr/src/lib/libfuse/include/fuse_misc.h
@@ -0,0 +1,45 @@
+/*
+    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>
+
+#ifndef USE_UCLIBC
+#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL)
+#else
+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/usr/src/lib/libfuse/include/fuse_opt.h b/usr/src/lib/libfuse/include/fuse_opt.h
new file mode 100644
index 0000000000..6123d0b489
--- /dev/null
+++ b/usr/src/lib/libfuse/include/fuse_opt.h
@@ -0,0 +1,261 @@
+/*
+    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 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 { .templ = NULL }
+
+/**
+ * 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 charater 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 arguemnt 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 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 beggining 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/usr/src/lib/libfuse/mapfile-libfuse b/usr/src/lib/libfuse/mapfile-libfuse
new file mode 100644
index 0000000000..3ace77440c
--- /dev/null
+++ b/usr/src/lib/libfuse/mapfile-libfuse
@@ -0,0 +1,89 @@
+#
+# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+#
+# Defines the public interface to sqlite
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION ILLUMOS_0.1 {	# first release of libfuse
+    global:
+      fuse_chan_fd;
+      fuse_daemonize;
+      fuse_destroy;
+      fuse_exit;
+      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_get_context;
+      fuse_get_session;
+      fuse_invalidate;
+      fuse_is_lib_option;
+      fuse_loop;
+      fuse_loop_mt;
+      fuse_main;
+      fuse_main_real;
+      fuse_mnt_check_empty;
+      fuse_mnt_check_fuseblk;
+      fuse_mnt_resolve_path;
+      fuse_mount;
+      fuse_new;
+      fuse_parse_cmdline;
+      fuse_register_module;
+      fuse_remove_signal_handlers;
+      fuse_set_signal_handlers;
+      fuse_unmount;
+      fuse_version;
+    local:
+	*;
+};
diff --git a/usr/src/lib/libfuse/modules/iconv.c b/usr/src/lib/libfuse/modules/iconv.c
new file mode 100644
index 0000000000..42c50cb374
--- /dev/null
+++ b/usr/src/lib/libfuse/modules/iconv.c
@@ -0,0 +1,712 @@
+/*
+    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 = strlen(path);
+    size_t newpathlen = pathlen * 4;
+    char *newpath = malloc(newpathlen + 1);
+    size_t plen = newpathlen;
+    char *p = newpath;
+    size_t res;
+    int err;
+
+    if (!newpath)
+        return -ENOMEM;
+
+    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, off_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,
+                         off_t offset, struct fuse_file_info *fi)
+{
+    struct iconv *ic = iconv_get();
+    char *newpath;
+    int err = iconv_convpath(ic, path, &newpath, 0);
+    if (!err) {
+        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, off_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, off_t size,
+                           struct fuse_file_info *fi)
+{
+    struct iconv *ic = iconv_get();
+    char *newpath;
+    int err = iconv_convpath(ic, path, &newpath, 0);
+    if (!err) {
+        err = fuse_fs_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(const char *path, char *buf, size_t size, off_t offset,
+                    struct fuse_file_info *fi)
+{
+    struct iconv *ic = iconv_get();
+    char *newpath;
+    int err = iconv_convpath(ic, path, &newpath, 0);
+    if (!err) {
+        err = fuse_fs_read(ic->next, newpath, buf, size, offset, fi);
+        free(newpath);
+    }
+    return err;
+}
+
+static int iconv_write(const char *path, const char *buf, size_t size,
+                       off_t offset, struct fuse_file_info *fi)
+{
+    struct iconv *ic = iconv_get();
+    char *newpath;
+    int err = iconv_convpath(ic, path, &newpath, 0);
+    if (!err) {
+        err = fuse_fs_write(ic->next, newpath, buf, size, 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_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 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	= iconv_read,
+    .write	= iconv_write,
+    .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,
+    .bmap	= iconv_bmap,
+};
+
+static 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);
+    return NULL;
+}
+
+FUSE_REGISTER_MODULE(iconv, iconv_new)
+#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) 
+#pragma init (iconv_register)
+#endif
+
diff --git a/usr/src/lib/libfuse/modules/subdir.c b/usr/src/lib/libfuse/modules/subdir.c
new file mode 100644
index 0000000000..2a963ccec1
--- /dev/null
+++ b/usr/src/lib/libfuse/modules/subdir.c
@@ -0,0 +1,665 @@
+/*
+    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 char *subdir_addpath(struct subdir *d, const char *path)
+{
+    unsigned newlen = d->baselen + strlen(path);
+    char *newpath = malloc(newlen + 2);
+    if (newpath) {
+        if (path[0] == '/')
+            path++;
+        strcpy(newpath, d->base);
+        strcpy(newpath + d->baselen, path);
+        if (!newpath[0])
+            strcpy(newpath, ".");
+    }
+    return newpath;
+}
+
+static int subdir_getattr(const char *path, struct stat *stbuf)
+{
+    struct subdir *d = subdir_get();
+    char *newpath = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        err = fuse_fs_opendir(d->next, newpath, fi);
+        free(newpath);
+    }
+    return err;
+}
+
+static int subdir_readdir(const char *path, void *buf,
+                          fuse_fill_dir_t filler, off_t offset,
+                          struct fuse_file_info *fi)
+{
+    struct subdir *d = subdir_get();
+    char *newpath = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, from);
+    char *newto = subdir_addpath(d, to);
+    int err = -ENOMEM;
+    if (newfrom && newto)
+        err = fuse_fs_rename(d->next, newfrom, newto);
+    free(newfrom);
+    free(newto);
+    return err;
+}
+
+static int subdir_link(const char *from, const char *to)
+{
+    struct subdir *d = subdir_get();
+    char *newfrom = subdir_addpath(d, from);
+    char *newto = subdir_addpath(d, to);
+    int err = -ENOMEM;
+    if (newfrom && newto)
+        err = fuse_fs_link(d->next, newfrom, newto);
+    free(newfrom);
+    free(newto);
+    return err;
+}
+
+static int subdir_chmod(const char *path, mode_t mode)
+{
+    struct subdir *d = subdir_get();
+    char *newpath = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        err = fuse_fs_chown(d->next, newpath, uid, gid);
+        free(newpath);
+    }
+    return err;
+}
+
+static int subdir_truncate(const char *path, off_t size)
+{
+    struct subdir *d = subdir_get();
+    char *newpath = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        err = fuse_fs_truncate(d->next, newpath, size);
+        free(newpath);
+    }
+    return err;
+}
+
+static int subdir_ftruncate(const char *path, off_t size,
+                         struct fuse_file_info *fi)
+{
+    struct subdir *d = subdir_get();
+    char *newpath = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        err = fuse_fs_open(d->next, newpath, fi);
+        free(newpath);
+    }
+    return err;
+}
+
+static int subdir_read(const char *path, char *buf, size_t size, off_t offset,
+                    struct fuse_file_info *fi)
+{
+    struct subdir *d = subdir_get();
+    char *newpath = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        err = fuse_fs_read(d->next, newpath, buf, size, offset, fi);
+        free(newpath);
+    }
+    return err;
+}
+
+static int subdir_write(const char *path, const char *buf, size_t size,
+                     off_t offset, struct fuse_file_info *fi)
+{
+    struct subdir *d = subdir_get();
+    char *newpath = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        err = fuse_fs_write(d->next, newpath, buf, size, offset, fi);
+        free(newpath);
+    }
+    return err;
+}
+
+static int subdir_statfs(const char *path, struct statvfs *stbuf)
+{
+    struct subdir *d = subdir_get();
+    char *newpath = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
+        free(newpath);
+    }
+    return err;
+}
+
+static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
+{
+    struct subdir *d = subdir_get();
+    char *newpath = subdir_addpath(d, path);
+    int err = -ENOMEM;
+    if (newpath) {
+        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 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	= subdir_read,
+    .write	= subdir_write,
+    .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,
+    .bmap	= subdir_bmap,
+};
+
+static 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)
+#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) 
+#pragma init (subdir_register)
+#endif
diff --git a/usr/src/lib/libfuse/mount.c b/usr/src/lib/libfuse/mount.c
new file mode 100644
index 0000000000..630e45b504
--- /dev/null
+++ b/usr/src/lib/libfuse/mount.c
@@ -0,0 +1,698 @@
+/*
+    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_opt.h"
+#include "fuse_common_compat.h"
+#include "mount_util.h"
+
+#ifdef __SOLARIS__
+#include <string.h>
+#endif /* __SOLARIS__ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.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>
+
+#define FUSERMOUNT_PROG         "fusermount"
+#define FUSE_COMMFD_ENV         "_FUSE_COMMFD"
+
+#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 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("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("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),
+#ifdef __SOLARIS__
+    FUSE_OPT_KEY("-g",                  KEY_KERN_FLAG),
+    FUSE_OPT_KEY("-m",                  KEY_KERN_FLAG),
+    FUSE_OPT_KEY("-O",                  KEY_KERN_FLAG),
+    FUSE_OPT_KEY("setuid",              KEY_KERN_OPT),
+    FUSE_OPT_KEY("nosetuid",            KEY_KERN_OPT),
+    FUSE_OPT_KEY("devices",             KEY_KERN_OPT),
+    FUSE_OPT_KEY("nodevices",           KEY_KERN_OPT),
+    FUSE_OPT_KEY("exec",                KEY_KERN_OPT),
+    FUSE_OPT_KEY("noexec",              KEY_KERN_OPT),
+    FUSE_OPT_KEY("nbmand",              KEY_KERN_OPT),
+    FUSE_OPT_KEY("nonbmand",            KEY_KERN_OPT),
+#else
+    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),
+#endif /* __SOLARIS__ */
+    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 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 struct mount_flags mount_flags[] = {
+    {"rw",      MS_RDONLY,      0},
+    {"ro",      MS_RDONLY,      1},
+    {"suid",    MS_NOSUID,      0},
+    {"nosuid",  MS_NOSUID,      1},
+#ifdef __SOLARIS__
+    {"-g",      MS_GLOBAL,      1},
+    {"-m",      MS_NOMNTTAB,    1},
+    {"-O",      MS_OVERLAY,     1},
+#else
+    {"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},
+    {"dirsync", MS_DIRSYNC,     1},
+#endif /* __SOLARIS__ */
+    {NULL,      0,              0}
+};
+
+#ifdef __SOLARIS__
+/*
+ * See comments in fuse_kern_mount()
+ */
+struct solaris_mount_opts {
+    int nosuid;
+    int setuid;
+    int nosetuid;
+    int devices;
+    int nodevices;
+};
+
+#define SOLARIS_MOUNT_OPT(t, p, n) \
+    { t, offsetof(struct solaris_mount_opts, p), n }
+static const struct fuse_opt solaris_mnt_opts[] = {
+    SOLARIS_MOUNT_OPT("suid",       setuid,         1),
+    SOLARIS_MOUNT_OPT("suid",       devices,        1),
+    SOLARIS_MOUNT_OPT("nosuid",     nosuid,         1),
+    SOLARIS_MOUNT_OPT("setuid",     setuid,         1),
+    SOLARIS_MOUNT_OPT("nosetuid",   nosetuid,       1),
+    SOLARIS_MOUNT_OPT("devices",    devices,        1),
+    SOLARIS_MOUNT_OPT("nodevices",  nodevices,      1),
+    FUSE_OPT_END
+};
+#endif /* __SOLARIS__ */
+
+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(&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;
+
+    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);
+        /* 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, 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, const char *opts,
+                                 int quiet)
+{
+    int fds[2], pid;
+    int res;
+    int rv;
+
+    if (!mountpoint) {
+        fprintf(stderr, "fuse: missing mountpoint\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);
+            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]);
+    close(fds[1]);
+    waitpid(pid, NULL, 0); /* bury zombie */
+
+    return rv;
+}
+
+int fuse_mount_compat22(const char *mountpoint, const char *opts)
+{
+    return fuse_mount_fusermount(mountpoint, 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\n");
+        return -1;
+    }
+
+    res = lstat(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;
+    }
+
+    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=%i,group_id=%i", 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));
+
+#ifdef __SOLARIS__
+	res = mount(source, mnt, MS_OPTIONSTR|mo->flags, type, NULL, 0,
+	    mo->kernel_opts, MAX_MNTOPT_STR);
+#else
+    res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
+#endif /* __SOLARIS__ */
+
+#ifdef __SOLARIS__
+    if (res == -1 && errno == EINVAL && mo->subtype) {
+#else
+    if (res == -1 && errno == ENODEV && mo->subtype) {
+#endif
+        /* 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);
+        }
+#ifdef __SOLARIS__
+		res = mount(source, mnt, MS_OPTIONSTR|mo->flags, type, NULL, 0,
+		    mo->kernel_opts, MAX_MNTOPT_STR);
+#else
+        res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
+#endif /* __SOLARIS__ */
+    }
+    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 __SOLARIS__
+    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 /* __SOLARIS__ */
+
+    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;
+#ifdef __SOLARIS__
+    struct solaris_mount_opts smo;
+    struct fuse_args sa = FUSE_ARGS_INIT(0, NULL);
+#endif /* __SOLARIS__ */
+
+    memset(&mo, 0, sizeof(mo));
+#ifdef __SOLARIS__
+    mo.flags = 0;
+    memset(&smo, 0, sizeof(smo));
+    if (args != NULL) {
+    	while (args->argv[sa.argc] != NULL)
+		fuse_opt_add_arg(&sa, args->argv[sa.argc]);
+    }
+#else
+    mo.flags = MS_NOSUID | MS_NODEV;
+#endif /* __SOLARIS__ */
+
+    if (args &&
+        fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
+        goto out; /* if SOLARIS, clean up 'sa' */
+
+#ifdef __SOLARIS__
+    /*
+     * In Solaris, nosuid is equivalent to nosetuid + nodevices. We only
+     * have MS_NOSUID for mount flags (no MS_(NO)SETUID, etc.). But if
+     * we set that as a default, it restricts specifying just nosetuid
+     * or nodevices; there is no way for the user to specify setuid +
+     * nodevices or vice-verse. So we parse the existing options, then
+     * add restrictive defaults if needed.
+     */
+    if (fuse_opt_parse(&sa, &smo, solaris_mnt_opts, NULL) == -1)
+         goto out;
+    if (smo.nosuid || (!smo.nodevices && !smo.devices
+        && !smo.nosetuid && !smo.setuid)) {
+        mo.flags |= MS_NOSUID;
+    } else {
+        /*
+         * Defaults; if neither nodevices|devices,nosetuid|setuid has
+         * been specified, add the default negative option string. If
+         * both have been specified (i.e., -osuid,nosuid), leave them
+         * alone; the last option will have precedence.
+         */
+        if (!smo.nodevices && !smo.devices)
+             if (fuse_opt_add_opt(&mo.kernel_opts, "nodevices") == -1)
+                 goto out;
+        if (!smo.nosetuid && !smo.setuid)
+            if (fuse_opt_add_opt(&mo.kernel_opts, "nosetuid") == -1)
+                 goto out;
+    }
+#endif /* __SOLARIS__ */
+
+    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, tmp_opts, 1);
+            free(tmp_opts);
+            if (res == -1)
+                res = fuse_mount_fusermount(mountpoint, mnt_opts, 0);
+        } else {
+            res = fuse_mount_fusermount(mountpoint, mnt_opts, 0);
+        }
+    }
+ out:
+#ifdef __SOLARIS__
+    fuse_opt_free_args(&sa);
+#endif /* __SOLARIS__ */
+    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;
+}
+
+#ifndef __SOLARIS__
+__asm__(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
+__asm__(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
+#endif /* __SOLARIS__ */
diff --git a/usr/src/man/man1m/Makefile b/usr/src/man/man1m/Makefile
index 1eaa441df3..1739ee5ae2 100644
--- a/usr/src/man/man1m/Makefile
+++ b/usr/src/man/man1m/Makefile
@@ -148,6 +148,7 @@ _MANFILES=	6to4relay.1m		\
 		fsstat.1m		\
 		fstyp.1m		\
 		fuser.1m		\
+		fuse_mount.1m		\
 		fwflash.1m		\
 		fwtmp.1m		\
 		getdev.1m		\
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index c4ad93a193..1e9c75dc04 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -725,6 +725,8 @@ SIMNET_OBJS +=	simnet.o
 
 FETH_OBJS +=	feth.o
 
+FUSE_OBJS +=	fuse_ini.o fuse_dev.o fuse_queue.o fuse_vfsops.o fuse_vnops.o
+
 IB_OBJS +=	ibnex.o ibnex_ioctl.o ibnex_hca.o
 
 IBCM_OBJS +=	ibcm_impl.o ibcm_sm.o ibcm_ti.o ibcm_utils.o ibcm_path.o \
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index 37098d6665..139ac8bf6d 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -1184,6 +1184,10 @@ $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/feth/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:                $(UTSBASE)/common/io/fuse/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/softmac/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
diff --git a/usr/src/uts/common/io/fuse/INSTALL b/usr/src/uts/common/io/fuse/INSTALL
new file mode 100644
index 0000000000..40bf700dd0
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/INSTALL
@@ -0,0 +1,37 @@
+
+(All the below mentioned commands are run from the 'kernel' directory.
+
+Building
+---
+
+Requirements:
+    Solaris Nevada >= build 77
+    Sun Studio 11
+
+
+Build the FUSE kernel modules
+$ make
+
+Note: gmake(1) will fail to build the kernel module.
+
+
+Installing
+---
+
+Populate the proto area:
+$ make install
+
+Generate the package:
+$ make pkg
+
+Add the SUNWfusefs package to the system (as root):
+# pkgadd -d packages SUNWfusefs
+
+Update the boot archive (x86/x64 only)
+# bootadm update-archive
+
+Uninstalling
+---
+
+Remove the SUNWlibfuse from the system (as root):
+# pkgrm SUNWfusefs
diff --git a/usr/src/uts/common/io/fuse/THIRDPARTYLICENSE.fuse b/usr/src/uts/common/io/fuse/THIRDPARTYLICENSE.fuse
new file mode 100644
index 0000000000..a4e45d4ec4
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/THIRDPARTYLICENSE.fuse
@@ -0,0 +1,60 @@
+fuse_kernel.h:
+/*
+    This -- and only this -- header file may also be distributed under
+    the terms of the BSD Licence as follows:
+
+    Copyright (C) 2001-2006 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.
+*/
+
+fuse_vnops.c:
+/*
+ * Copyright (C) 2005 Csaba Henk. 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.
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Portions Copyright 2012-2019 Jean-Pierre Andre
+ */
diff --git a/usr/src/uts/common/io/fuse/fuse.conf b/usr/src/uts/common/io/fuse/fuse.conf
new file mode 100644
index 0000000000..d048d13ff0
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/fuse.conf
@@ -0,0 +1 @@
+name="fuse" parent="pseudo"; 
diff --git a/usr/src/uts/common/io/fuse/fuse.h b/usr/src/uts/common/io/fuse/fuse.h
new file mode 100644
index 0000000000..f6ae4f33e5
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/fuse.h
@@ -0,0 +1,232 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _FUSE_H
+#define	_FUSE_H
+
+#include <sys/kmem.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include "fuse_kernel.h"
+#include "fuse_queue.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	FUSE_DEV_DESCRIPTION "fuse driver"
+#define	FUSE_DEV_TYPE "fuse"
+#define	FUSE_FS_DESCRIPTION "filesystem for fuse"
+#define	FUSE_FS_TYPE "fuse"
+
+/* Used for fuse_create_vnode() to identify if it's invoked during create op */
+#define	OTHER_OP	0
+#define	FILE_CREATE_OP	1
+
+/*
+ * Used to indicate whether to scan the cached list of filehandles while
+ * retrieving a file handle
+ */
+#define	CACHE_LIST_NO_CHECK 0
+#define	CACHE_LIST_CHECK 1
+#define	CACHE_CHECK_ONLY 2
+
+#define	FUSE_NULL_ID	0
+
+#define	FSIZE_UPDATED		0x1	/* File size modifed by write */
+#define	FSIZE_NOT_RELIABLE	0x2	/* Cached file size might be invalid */
+#define	FSIZE_UNSENT		0x4	/* File size update not started */
+
+#define	FUSE_FORCE_FH_RELEASE	0x1	/* force release of filehandle */
+
+/* Defines way to retrieve fuse_vnode_data_t associated with the vnode */
+#define	VTOFD(vp)	((fuse_vnode_data_t *)(vp)->v_data)
+
+/* From FreeBSD Fuse, used to keep track of open files */
+typedef struct fuse_file_handle {
+	list_node_t	fh_link;
+	uint64_t	fh_id;
+	pid_t		process_id;
+	int		mode;
+	int		flags;
+	int		ref;
+	cred_t		*credp;
+	struct file 	*filep;
+	uint64_t	nodeid;
+} fuse_file_handle_t;
+
+struct fuse_fh_param {
+	struct vnode *vp;
+	cred_t *credp;
+	struct fuse_file_handle *fufh;
+	uint64_t nodeid;
+	int rw_mode;
+	int flag;
+};
+
+/* used to save arguments passed to fuse_fs_create */
+struct fuse_create_data {
+	uint64_t par_nodeid;
+	cred_t	*credp;
+	int	flag;
+	int	mode;
+	char	*name;
+	int	namelen;
+};
+
+struct fuse_filehandle {
+	uint64_t fh_id;
+	offset_t fh_offset; /* 64 bits */
+	int mode;
+};
+
+typedef struct fuse_vnode_data {
+	list_t fh_list;
+	kmutex_t fh_list_lock;
+
+	uint64_t nodeid;
+	uint64_t nlookup;
+	uint64_t par_nid;
+
+	struct fuse_file_handle fh;
+	struct fuse_create_data *fcd;
+
+#ifndef DONT_CACHE_ATTRIBUTES
+	struct vattr cached_attrs;
+	struct timespec cached_attrs_bound;
+	uid_t uid;	/* most recent user which looked up the directory */
+	gid_t gid;	/* most recent user which looked up the directory */
+#endif
+	uint64_t fsize;	/* temp place holder when file size gets modified */
+	int file_size_status; /* indicates if cached file size is valid */
+	offset_t offset; /* offset of (single) unsent page */
+
+	kmutex_t f_lock; /* serializes write/setattr requests */
+	long f_mapcnt;	/* mappings to file pages */
+	/*
+	 * This field will be eventually removed as we complete
+	 * rewriting the implementation. It is still here so that the code
+	 * compiles.
+	 */
+	struct fuse_filehandle fileh;
+} fuse_vnode_data_t;
+
+/* Get nodeid from the struct vnode */
+#define	VNODE_TO_NODEID(vp)	((struct fuse_vnode_data *)(vp)->v_data)->nodeid
+/*
+ * Max number of pages that can be used in a single read request
+ * (used in FreeBSD Fuse)
+ */
+#define	FUSE_MAX_PAGES_PER_REQ	32
+
+/*
+ * TBD: Should we do it like FreeBSD Fuse, where minimum size of 128 bytes
+ * is allocated?
+ */
+#define	FUSE_BUF_ADJUST_SIZE(len)	(len)
+
+/* TBD: Should this be made atomic? */
+#define	FUSE_GET_UNIQUE(se_p)	(++se_p->unique)
+
+#define	DENTRY64_NAME_OFFSET	(offsetof(dirent64_t, d_name[0]))
+
+#define	MAX_DENTRY64_SIZE	(((DENTRY64_NAME_OFFSET) + \
+	(MAXNAMELEN) + 7) & ~ 7)
+
+/* convert from FREAD/FWRITE to O_RDONLY/O_WRONLY */
+#define	F_TO_O_FLAGS(flags)	((flags) - 1)
+
+typedef enum memory_type {
+	MEM_NOT_ALLOCATED	= 0,
+	MEM_TYPE_PAGE,
+	MEM_TYPE_KMEM
+} mem_type;
+
+typedef enum vnode_getmode {
+	VNODE_ANY,
+	VNODE_CACHED,
+	VNODE_NEW
+} v_getmode;
+
+/* This structure is obtained from FreeBSD version of Fuse */
+struct fuse_iov {
+	void	*base;		/* Pointer to allocated memory */
+	size_t	memsize;	/* Size of allocated memory */
+	size_t	len;		/* Size of available data */
+	mem_type memflag;	/* Flag indicating the memory type */
+};
+
+#define	FUSE_MAX_IOV	3
+
+/* This defines the data passed to the fuse daemon from the kernel module */
+struct fuse_data_in {
+	struct fuse_in_header *finh;	/* header for the message passed */
+	void *indata;			/* pointer to start of arguments */
+	size_t iosize;			/* size occupied by args in the msg */
+	int iovs_used;			/* Number of iovbufs used */
+	uint64_t nodeid;		/* Node ID referred in this operation */
+	struct fuse_iov iovbuf[FUSE_MAX_IOV]; /* Buffer storing header + args */
+};
+
+/* This defines the data passed from the fuse daemon to the kernel module */
+struct fuse_data_out {
+	struct fuse_out_header *fouth;	/* header for the message passed from */
+					/* the fuse daemon */
+	void *outdata;			/* pointer to start of arguments */
+	size_t outsize;			/* size occupied by arguments */
+	struct fuse_iov iovbuf;		/* Buffer storing arguments */
+};
+
+typedef	int fuse_consfunc_t(struct uio *uiop, size_t reqsize, void *buf,
+    size_t bufsize, void *arg);
+
+/* From FreeBSD Fuse, used for an I/O operation b/w daemon and kernel module */
+struct fuse_io_data {
+	struct vnode		*vp;
+	struct uio		*uiop;
+	struct fuse_file_handle	*fh;
+	enum fuse_opcode	op;
+	fuse_consfunc_t		*consfunc;
+	cred_t			*credp;
+	void			*arg;
+};
+
+/* Extern Declarations */
+extern vnodeops_t *dv_vnodeops;
+extern vnodeops_t *temp_vnodeops;
+extern const fs_operation_def_t fuse_vnodeops_template[];
+extern const fs_operation_def_t temp_vnodeops_template[];
+
+/* Function prototype */
+void fuse_buf_alloc(struct fuse_iov *iov, size_t len);
+struct vnode *fuse_create_vnode(vfs_t *vfsp, uint64_t nodeid, uint64_t par_nid,
+    int type, int iscreate);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _FUSE_H */
diff --git a/usr/src/uts/common/io/fuse/fuse_dev.c b/usr/src/uts/common/io/fuse/fuse_dev.c
new file mode 100644
index 0000000000..e5f963ca42
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/fuse_dev.c
@@ -0,0 +1,524 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Portions Copyright 2012 Jean-Pierre Andre
+ */
+
+/*
+ * Skeleton code Derived from Solaris DDK tutorial
+ */
+
+#include <sys/devops.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/mount.h>
+#include <sys/mkdev.h>
+#include <sys/inttypes.h>
+#include <sys/atomic.h>
+#include <sys/sdt.h>
+
+#include "fuse_queue.h"
+#include "fuse.h"
+
+static int fuse_dev_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int fuse_dev_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static int fuse_dev_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
+    void **resultp);
+static int fuse_dev_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
+    int flags, char *name, caddr_t valuep, int *lengthp);
+
+
+static int fuse_dev_open(dev_t *devp, int flag, int otyp, cred_t *cred);
+static int fuse_dev_close(dev_t dev, int flag, int otyp, cred_t *cred);
+static int fuse_dev_read(dev_t dev, struct uio *uiop, cred_t *credp);
+static int fuse_dev_write(dev_t dev, struct uio *uiop, cred_t *credp);
+static int fuse_dev_poll(dev_t dev, short events, int anyyet, short *reventsp,
+    struct pollhead **phpp);
+
+/* cb_ops structure */
+static struct cb_ops fuse_dev_cb_ops = {
+	fuse_dev_open,
+	fuse_dev_close,
+	nodev,	/* no strategy - nodev returns ENXIO */
+	nodev,	/* no print */
+	nodev,	/* no dump */
+	fuse_dev_read,
+	fuse_dev_write,
+	nodev,	/* no ioctl */
+	nodev,	/* no devmap */
+	nodev,	/* no mmap */
+	nodev,	/* no segmap */
+	fuse_dev_poll,
+	fuse_dev_prop_op,
+	NULL,	/* streamtab struct; if not NULL, all above */
+		/* fields are ignored */
+	D_NEW | D_MP | D_64BIT | D_U64BIT,	/* compatibility flags: see conf.h */
+	CB_REV,	/* cb_ops revision number */
+	nodev,	/* no aread */
+	nodev	/* no awrite */
+};
+/* dev_ops structure */
+static struct dev_ops fuse_dev_ops = {
+	DEVO_REV,
+	0,	/* reference count */
+	fuse_dev_getinfo,
+	nulldev,	/* no identify - nulldev returns 0 */
+	nulldev,	/* no probe */
+	fuse_dev_attach,
+	fuse_dev_detach,
+	nodev,		/* no reset - nodev returns ENXIO */
+	&fuse_dev_cb_ops,
+	(struct bus_ops *)NULL,
+	nodev		/* no power */
+};
+/* modldrv structure */
+struct modldrv fuse_dev_drv_modldrv = {
+	&mod_driverops,	/* Type of module. This is a driver. */
+	FUSE_DEV_DESCRIPTION,	/* Name of the module. */
+	&fuse_dev_ops
+};
+
+/* dev_info structure */
+static dev_info_t *fuse_dip;   /* keep track of one instance */
+
+extern fuse_session_t *fuse_alloc_session();
+
+
+/* Device autoconfiguration entry points */
+static int
+fuse_dev_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	switch (cmd) {
+		case DDI_ATTACH:
+			fuse_dip = dip;
+			/* S_IFBLK */
+			if (ddi_create_minor_node(dip, "fuse", S_IFCHR,
+			    ddi_get_instance(dip), DDI_PSEUDO, 0)
+			    != DDI_SUCCESS) {
+				DTRACE_PROBE2(fuse_dev_attach_err_create_node,
+				    char *, "Error creating minor node",
+				    dev_info_t *, dip);
+				return (DDI_FAILURE);
+			} else {
+				return (DDI_SUCCESS);
+			}
+		default:
+			return (DDI_FAILURE);
+	}
+}
+
+static int
+fuse_dev_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	switch (cmd) {
+		case DDI_DETACH:
+/* TODO: check if all sessions are closed before detaching */
+			fuse_dip = 0;
+			ddi_remove_minor_node(dip, NULL);
+			return (DDI_SUCCESS);
+		default:
+			return (DDI_FAILURE);
+	}
+}
+
+/* ARGSUSED */
+static int
+fuse_dev_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
+void **resultp)
+{
+	switch (cmd) {
+		case DDI_INFO_DEVT2DEVINFO:
+			*resultp = fuse_dip;
+			return (DDI_SUCCESS);
+		case DDI_INFO_DEVT2INSTANCE:
+			*resultp = 0;
+			return (DDI_SUCCESS);
+		default:
+			return (DDI_FAILURE);
+	}
+}
+
+static int
+fuse_dev_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
+    int flags, char *name, caddr_t valuep, int *lengthp)
+{
+	return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp));
+}
+
+/* Use context entry points */
+/* ARGSUSED */
+static int
+fuse_dev_open(dev_t *devp, int flag, int otyp, cred_t *cred)
+{
+	fuse_session_t *fs;
+	minor_t minor;
+
+	/* see: pg 154 Writing Device Drivers - May 2002 */
+	fs = fuse_alloc_session();
+	if (fs == NULL)
+		return (DDI_FAILURE);
+	fuse_init_session(fs);
+	minor = fuse_session_get_minor(fs);
+
+	*devp = makedevice(getmajor(*devp), minor);
+	return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+static int
+fuse_dev_close(dev_t dev, int flag, int otyp, cred_t *cred)
+{
+	minor_t ndx = getminor(dev);
+	fuse_session_t *sep = fuse_minor_get_session(ndx);
+
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_dev_close_err_session,
+		    char *, "failed to find session",
+		    dev_t, dev);
+		return (ENODEV);
+	}
+
+	if (sep->mounted) {
+		fuse_session_umount(sep);
+	}
+
+	fuse_deinit_session(sep);
+	fuse_free_session(sep);
+
+	/* TODO: check if this session has no pending request before de-init */
+	return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+static int
+fuse_dev_poll(dev_t dev, short events, int anyyet, short *reventsp,
+    struct pollhead **phpp)
+{
+	short revent = 0;
+	minor_t ndx = getminor(dev);
+	fuse_session_t *sep = fuse_minor_get_session(ndx);
+
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_dev_poll_err_session,
+		    char *, "failed to find session",
+		    dev_t, dev);
+		revent = POLLERR;
+	} else {
+		if (!(sep->mounted)) {
+			revent = (POLLERR | POLLHUP);
+		}
+	}
+	*reventsp = revent;
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+fuse_dev_read(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+	minor_t ndx = getminor(dev);
+	fuse_msg_node_t *msgp = NULL;
+	fuse_session_t *sep;
+	int err = DDI_SUCCESS;
+	int i;
+
+	sep = fuse_minor_get_session(ndx);
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_dev_read_err_session,
+		    char *, "failed to find session",
+		    dev_t, dev);
+		return (ENODEV);
+	}
+
+	if (sema_p_sig(& (sep->session_sema)) == 1) {
+		/* We got unblocked because of a signal, so return */
+		DTRACE_PROBE2(fuse_dev_read_err_signal,
+		    char *, "Woke up due to pending signal",
+		    fuse_session_t *, sep);
+		return (ENODEV);
+	}
+
+	/*
+	 * See if the filesystem has been unmounted. If an unmount occurred
+	 * the session should be cleared up and ENODEV should be returned to
+	 * fuselib to indicate unmount.
+	 */
+	if (!(sep->mounted)) {
+		return (ENODEV);
+	}
+
+	FUSE_SESSION_MUTEX_LOCK(sep);
+
+	for (msgp = list_head(&sep->msg_list); msgp;
+	    msgp = list_next(&(sep->msg_list), msgp)) {
+		if (msgp->fmn_state == FUSE_MSG_STATE_QUEUE) {
+			msgp->fmn_state = FUSE_MSG_STATE_READ;
+			break;
+		}
+	}
+	FUSE_SESSION_MUTEX_UNLOCK(sep);
+	if (msgp == NULL) {
+		DTRACE_PROBE2(fuse_dev_read_err_msgp,
+		    char *, "No message found on session msg_list",
+		    fuse_session_t *, sep);
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * This is not supposed to function as a seekable device. To prevent
+	 * offset from growing and eventually exceed the maximum, reset the
+	 * offset here for every call.
+	 */
+	uiop->uio_loffset = 0;
+
+	DTRACE_PROBE2(msg_read, struct fuse_in_header *, msgp->ipdata.finh,
+	    void *, msgp->ipdata.indata);
+
+	/*
+	 * For each of the iovbufs which is used try copying data to buffer
+	 * provided by daemon
+	 */
+	for (i = 0; i < msgp->ipdata.iovs_used; i++) {
+		/*
+		 * Check if there is enough room to copy all data from this
+		 * iovbuf
+		 */
+		if ((size_t)uiop->uio_resid < msgp->ipdata.iovbuf[i].len) {
+			DTRACE_PROBE2(fuse_dev_read_err_no_space,
+			    char *, "Buffer too small for request data",
+			    fuse_session_t *, sep);
+			err = ENODEV;
+			break;
+		}
+		err = uiomove(msgp->ipdata.iovbuf[i].base,
+		    msgp->ipdata.iovbuf[i].len, UIO_READ, uiop);
+		if (err)
+			break;
+	}
+
+	return (err);
+}
+
+/*
+ * This function is from FreeBSD version of Fuse. It validates the header sent
+ * by the fuse library. It converts any error value sent by the fuse library
+ * to positive, since thats how it is understood inside the Solaris kernel
+ */
+static int
+fuse_ohead_audit(struct fuse_out_header *ohead, struct uio *uio)
+{
+	if ((size_t)uio->uio_resid + sizeof (struct fuse_out_header)
+			!= ohead->len) {
+		DTRACE_PROBE3(fuse_ohead_audit_err_header,
+		    char *, "Invalid length in header",
+		    struct uio *, uio,
+		    struct fuse_out_header *, ohead);
+		return (EINVAL);
+	}
+
+	if (uio->uio_resid && ohead->error) {
+		DTRACE_PROBE3(fuse_ohead_audit_err_format,
+		    char *, "Non zero error with message body",
+		    struct uio *, uio,
+		    struct fuse_out_header *, ohead);
+		return (EINVAL);
+	}
+
+	/* Sanitize the linuxism of negative errnos */
+	ohead->error = -(ohead->error);
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+fuse_dev_write(dev_t dev, struct uio *uiop, cred_t *cred_p)
+{
+	int err = DDI_FAILURE;
+	struct fuse_out_header *outh;
+	fuse_session_t *se_p;
+	fuse_msg_node_t *msg_p = NULL;
+	fuse_msg_node_t *msg_next;
+	minor_t ndx = getminor(dev);
+	struct fuse_iov *iovbuf;
+
+	if (uiop->uio_resid < sizeof (struct fuse_out_header)) {
+		DTRACE_PROBE2(fuse_dev_write_err_response_length,
+		    char *, "response length less than header size.",
+		    struct uio *, uiop);
+		return (EINVAL);
+	}
+
+	/* Allocate memory for the header part of the message */
+	outh = kmem_alloc(sizeof (struct fuse_out_header), KM_SLEEP);
+
+	/*
+	 * This is not supposed to function as a seekable device. To prevent
+	 * offset from growing and eventually exceed the maximum, reset the
+	 * offset here for every call.
+	 */
+	uiop->uio_loffset = 0;
+
+	/* If we are unable to copy the data, then free the allocated memory */
+	if ((err = uiomove(outh, sizeof (struct fuse_out_header), UIO_WRITE,
+	    uiop)) != 0)
+		goto cleanup;
+
+	/*
+	 * Verify if the header is valid, this code is similar to FreeBSD
+	 * version of Fuse
+	 */
+	if ((err = fuse_ohead_audit(outh, uiop)))
+		goto cleanup;
+
+
+	se_p = fuse_minor_get_session(ndx);
+	if (se_p == NULL) {
+		DTRACE_PROBE2(fuse_dev_write_err_session,
+		    char *, "failed to find session",
+		    dev_t, dev);
+		err = ENODEV;
+		goto cleanup;
+	}
+	FUSE_SESSION_MUTEX_LOCK(se_p);
+
+	/* Reset error before starting the search */
+	err = DDI_FAILURE;
+	for (msg_p = list_head(&(se_p->msg_list)); msg_p; ) {
+		if (msg_p->fmn_unique == outh->unique) {
+			if (msg_p->fmn_state == FUSE_MSG_STATE_READ) {
+				msg_p->fmn_state = FUSE_MSG_STATE_WRITE;
+			/* remove it from the queue */
+				list_remove(&(se_p->msg_list), msg_p);
+				err = DDI_SUCCESS;
+				break;
+			} else if (msg_p->fmn_state == FUSE_MSG_STATE_SIG) {
+				msg_p->fmn_state = FUSE_MSG_STATE_DONE;
+				list_remove(&(se_p->msg_list), msg_p);
+				cv_signal(&msg_p->fmn_cv);
+				FUSE_SESSION_MUTEX_UNLOCK(se_p);
+				goto cleanup;
+			}
+			msg_p = list_next(&(se_p->msg_list), msg_p);
+		} else {
+			msg_next = list_next(&(se_p->msg_list), msg_p);
+			/*
+			 * if no callback handler is registered, and no
+			 * reply is expected, it is our responsibility 
+			 * to free up obsolete message nodes.
+			 */
+			if (!msg_p->frd_on_request_complete
+			    && msg_p->fmn_noreply) {
+				if (msg_p->fmn_unique < se_p->max_unique) {
+					list_remove(&(se_p->msg_list), msg_p);
+					fuse_free_msg(msg_p);
+				}
+				if (outh->unique > se_p->max_unique)
+					se_p->max_unique = outh->unique;
+			}
+			msg_p = msg_next;
+		}
+	}
+	FUSE_SESSION_MUTEX_UNLOCK(se_p);
+
+	if (err == DDI_SUCCESS) {
+		msg_p->opdata.fouth = outh;
+
+		if (uiop->uio_resid) {
+
+			iovbuf = &msg_p->opdata.iovbuf;
+
+			if (iovbuf->memsize &&
+			    iovbuf->memsize < (size_t)uiop->uio_resid &&
+			    iovbuf->memflag == MEM_TYPE_KMEM) {
+				kmem_free(iovbuf->base, iovbuf->memsize);
+				iovbuf->memsize = 0;
+			}
+
+			if (iovbuf->memsize == 0) {
+				fuse_buf_alloc(iovbuf, uiop->uio_resid);
+			}
+
+			iovbuf->len = min(iovbuf->memsize,
+						(size_t)uiop->uio_resid);
+
+			/* Save the start and length of arguments */
+			msg_p->opdata.outdata = iovbuf->base;
+			msg_p->opdata.outsize = iovbuf->len;
+
+			/* Copy the arguments part sent by fuse library */
+			if ((err = uiomove(msg_p->opdata.outdata,
+			    iovbuf->len, UIO_WRITE, uiop)) != 0) {
+				fuse_free_msg(msg_p);
+				goto out;
+			}
+		}
+
+		DTRACE_PROBE2(msg_write, struct fuse_out_header *,
+		    msg_p->opdata.fouth, void *, msg_p->opdata.outdata);
+
+		/* If an handler is registered, then it will free the memory */
+		if (msg_p->frd_on_request_complete)
+			msg_p->frd_on_request_complete(se_p, msg_p);
+		else {
+			/*
+			 * No callback handler is registered, so its our
+			 * responsibility to free up the message node
+			 * This function also frees the header we allocated
+			 * here.
+			 */
+			fuse_free_msg(msg_p);
+		}
+		/*
+		 * Here, either the handler has been called which would have
+		 * freed the entire message structure with the header or if
+		 * there was no handler registered, we did it ourselves, so
+		 * just get out of here.
+		 */
+		goto out;
+	} else {
+		DTRACE_PROBE2(fuse_dev_write_err_msgp,
+		    char *, "No matching message on session msg_list",
+		    fuse_session_t *, se_p);
+	}
+
+cleanup:
+	kmem_free(outh, sizeof (struct fuse_out_header));
+
+out:
+	return (err);
+}
diff --git a/usr/src/uts/common/io/fuse/fuse_ini.c b/usr/src/uts/common/io/fuse/fuse_ini.c
new file mode 100644
index 0000000000..1f2d69b2e4
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/fuse_ini.c
@@ -0,0 +1,100 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Portions Copyright 2015 Jean-Pierre Andre
+ */
+
+
+#include <sys/devops.h>	/* used by dev_ops */
+#include <sys/conf.h>	/* used by dev_ops and cb_ops */
+#include <sys/modctl.h>	/* used by modlinkage, modldrv, _init, _info, */
+			/* and _fini */
+#include <sys/types.h>	/* used by open, close, read, write, prop_op, */
+			/* and ddi_prop_op */
+#include <sys/file.h>	/* used by open, close */
+#include <sys/errno.h>	/* used by open, close, read, write */
+#include <sys/open.h>	/* used by open, close, read, write */
+#include <sys/cred.h>	/* used by open, close, read */
+#include <sys/uio.h>	/* used by read */
+#include <sys/stat.h>	/* defines S_IFCHR used by ddi_create_minor_node */
+#include <sys/ddi.h>	/* used by all entry points for this driver */
+			/* also used by cb_ops, ddi_get_instance, and */
+			/* ddi_prop_op */
+#include <sys/sunddi.h> /* used by all entry points for this driver */
+			/* also used by cb_ops, ddi_create_minor_node, */
+			/* ddi_get_instance, and ddi_prop_op */
+#include <sys/cmn_err.h>
+
+#include <sys/atomic.h>	/* used for debugging, added atomic counter */
+#include "fuse_kernel.h"
+
+extern struct modldrv fuse_dev_drv_modldrv;
+extern struct modlfs fuse_vfs_modldrv;
+extern void fuse_global_init();
+extern void fuse_global_fini();
+
+/*
+ * Fuse kernel module has a char device as well as a filesystem.
+ * modlinkage structure:
+ */
+static struct modlinkage ml = {
+	MODREV_1,
+	&fuse_dev_drv_modldrv,
+	&fuse_vfs_modldrv,
+	NULL
+};
+
+/* Loadable module configuration entry points */
+int
+_init(void)
+{
+	int rv;
+
+	cmn_err(CE_CONT,"!Fuse kernel %s interface %d.%d\n", FUSE_SOLARIS_VERSION,
+		FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
+	cmn_err(CE_CONT,"!Fuse kernel init at 0x%lx, vfs_modldrv at 0x%lx\n",
+		(long)_init,(long)&fuse_vfs_modldrv);
+	fuse_global_init();
+	if ((rv = mod_install(&ml)) != 0) {
+		fuse_global_fini();
+	}
+	return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&ml, modinfop));
+}
+
+int
+_fini(void)
+{
+	int rv;
+
+	if ((rv = mod_remove(&ml)) == 0) {
+		fuse_global_fini();
+	}
+	return (rv);
+}
diff --git a/usr/src/uts/common/io/fuse/fuse_kernel.h b/usr/src/uts/common/io/fuse/fuse_kernel.h
new file mode 100644
index 0000000000..e7c5930fb6
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/fuse_kernel.h
@@ -0,0 +1,384 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU GPL.
+    See the file COPYING.
+*/
+
+/* This file defines the kernel interface of FUSE */
+
+#if defined(__FreeBSD__) || defined(__SOLARIS__)
+/*
+    This -- and only this -- header file may also be distributed under
+    the terms of the BSD Licence as follows:
+
+    Copyright (C) 2001-2006 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.
+*/
+
+#include <sys/types.h>
+#define __u64 uint64_t
+#define __u32 uint32_t
+#define __s32 int32_t
+#else
+#include <sys/types.h>
+#include <linux/major.h>
+#endif
+
+#define FUSE_SOLARIS_VERSION "1.4"
+
+/** Version number of this interface */
+#define FUSE_KERNEL_VERSION 7
+
+/** Minor version number of this interface */
+#define FUSE_KERNEL_MINOR_VERSION 8
+
+/** The node ID of the root inode */
+#define FUSE_ROOT_ID 1
+
+/** The major number of the fuse character device */
+#define FUSE_MAJOR MISC_MAJOR
+
+/** The minor number of the fuse character device */
+#define FUSE_MINOR 229
+
+/* 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;
+};
+
+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)
+
+/**
+ * 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
+ */
+#define FOPEN_DIRECT_IO		(1 << 0)
+#define FOPEN_KEEP_CACHE	(1 << 1)
+
+/**
+ * INIT request/reply flags
+ */
+#define FUSE_ASYNC_READ		(1 << 0)
+#define FUSE_POSIX_LOCKS	(1 << 1)
+
+/**
+ * Release flags
+ */
+#define FUSE_RELEASE_FLUSH	(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
+};
+
+/* The read buffer is required to be at least 8k, but may be much larger */
+#define FUSE_MIN_READ_BUFFER 8192
+
+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_attr_out {
+	__u64	attr_valid;	/* Cache timeout for the attributes */
+	__u32	attr_valid_nsec;
+	__u32	dummy;
+	struct fuse_attr attr;
+};
+
+struct fuse_mknod_in {
+	__u32	mode;
+	__u32	rdev;
+};
+
+struct fuse_mkdir_in {
+	__u32	mode;
+	__u32	padding;
+};
+
+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	unused1;
+	__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	mode;
+};
+
+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	padding;
+};
+
+struct fuse_write_in {
+	__u64	fh;
+	__u64	offset;
+	__u32	size;
+	__u32	write_flags;
+};
+
+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;
+};
+
+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;
+	__u32	unused;
+	__u32	max_write;
+};
+
+struct fuse_interrupt_in {
+	__u64	unique;
+};
+
+struct fuse_bmap_in {
+	__u64	block;
+	__u32	blocksize;
+	__u32	padding;
+};
+
+struct fuse_bmap_out {
+	__u64	block;
+};
+
+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[1];
+};
+
+#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)
diff --git a/usr/src/uts/common/io/fuse/fuse_queue.c b/usr/src/uts/common/io/fuse/fuse_queue.c
new file mode 100644
index 0000000000..b1352384cf
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/fuse_queue.c
@@ -0,0 +1,403 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Portions Copyright 2015-2019 Jean-Pierre Andre
+ */
+
+#include <sys/kmem.h>
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/mutex.h>
+#include <sys/semaphore.h>
+#include <sys/list.h>
+#include <sys/uio.h>
+#include <sys/avl.h>
+#include <sys/vnode.h>
+#include <sys/atomic.h>
+#include <sys/sysmacros.h>
+#include <sys/ksynch.h>
+#include <sys/cred.h>
+#include <sys/sdt.h>
+
+#include "fuse_queue.h"
+
+static int
+fuse_avl_compare_i(const void *x1, const void *x2);
+static int fuse_avl_compare_n(const void *x1, const void *x2);
+static void fuse_avl_destroy(avl_tree_t *tree_i, avl_tree_t *tree_n);
+
+static fuse_session_t *fuse_sessions[FUSE_MAX_SESSIONS];
+
+static kmutex_t fuse_global_mutx;
+
+void
+fuse_global_init()
+{
+	(void) mutex_init(&fuse_global_mutx, NULL, MUTEX_DEFAULT,
+	    (void *) NULL);
+}
+void
+fuse_global_fini()
+{
+	mutex_destroy(&fuse_global_mutx);
+}
+static void
+fuse_global_mutex_enter()
+{
+	(void) mutex_enter(&fuse_global_mutx);
+}
+static void
+fuse_global_mutex_exit()
+{
+	(void) mutex_exit(&fuse_global_mutx);
+}
+
+/* ARGSUSED */
+static void
+frd_on_request_complete_wakeup(fuse_session_t *ses_p, fuse_msg_node_t *msg_p)
+{
+	FUSE_SESSION_MUTEX_LOCK(ses_p);
+	msg_p->fmn_state = FUSE_MSG_STATE_DONE;
+	cv_signal(&msg_p->fmn_cv);
+	FUSE_SESSION_MUTEX_UNLOCK(ses_p);
+}
+
+fuse_session_t *
+fuse_minor_get_session(minor_t ndx)
+{
+	return (ndx < FUSE_MAX_SESSIONS ?  fuse_sessions[ndx] : NULL);
+}
+
+minor_t
+fuse_session_get_minor(fuse_session_t *se)
+{
+	return (se->minor);
+}
+
+void
+fuse_session_set_cred(fuse_session_t *se, cred_t *cr)
+{
+	crhold(cr);
+	se->usercred = cr;
+}
+
+static void
+fuse_session_clear_cred(fuse_session_t *se)
+{
+	if (se->usercred != NULL)
+		crfree(se->usercred);
+	se->usercred = NULL;
+
+}
+
+void
+fuse_session_set_vfs(fuse_session_t *se, vfs_t *vfs)
+{
+	se->vfs = vfs;
+}
+
+/* double a global lock before calling it */
+void
+fuse_init_session(fuse_session_t *se)
+{
+	(void) mutex_init(&se->session_mutx, NULL, MUTEX_DEFAULT, NULL);
+	(void) sema_init(&se->session_sema, 0, NULL, SEMA_DRIVER, NULL);
+	mutex_init(&se->avl_mutx, NULL, MUTEX_DEFAULT, NULL);
+	list_create(&se->msg_list, sizeof (fuse_msg_node_t),
+	    offsetof(fuse_msg_node_t, fmn_link));
+	avl_create(&se->avl_cache_i, fuse_avl_compare_i,
+	    sizeof (fuse_avl_cache_node_t),
+	    offsetof(struct fuse_avl_cache_node, faci_cache_node));
+	avl_create(&se->avl_cache_n, fuse_avl_compare_n,
+	    sizeof (fuse_avl_cache_node_t),
+	    offsetof(struct fuse_avl_cache_node, facn_cache_node));
+	se->mounted = 0;
+	se->readonly = 0;
+}
+
+void
+fuse_deinit_session(fuse_session_t *se)
+{
+	mutex_destroy(&se->avl_mutx);
+	sema_destroy(&se->session_sema);
+	mutex_destroy(&se->session_mutx);
+	list_destroy(&se->msg_list);
+	fuse_avl_destroy(&se->avl_cache_i, &se->avl_cache_n);
+	fuse_session_clear_cred(se);
+}
+
+void
+fuse_free_session(fuse_session_t *se)
+{
+	fuse_sessions[fuse_session_get_minor(se)] = NULL;
+	kmem_free(se, sizeof (fuse_session_t));
+}
+
+fuse_session_t *
+fuse_alloc_session()
+{
+	int i;
+	fuse_global_mutex_enter();
+
+	for (i = 0; i < FUSE_MAX_SESSIONS; i++) {
+		if (fuse_sessions[i] == NULL) {
+			break;
+		}
+	}
+
+	if (i == FUSE_MAX_SESSIONS) {
+		fuse_global_mutex_exit();
+		return (NULL);
+	} else {
+		fuse_sessions[i] = kmem_zalloc(sizeof (fuse_session_t),
+		    KM_SLEEP);
+		fuse_sessions[i]->minor = i;
+
+		fuse_global_mutex_exit();
+		return (fuse_sessions[i]);
+	}
+}
+
+static void
+fuse_init_msg(fuse_msg_node_t *msg_p)
+{
+	bzero(msg_p, sizeof (fuse_msg_node_t));
+	sema_init(&msg_p->fmn_sema, 0, NULL, SEMA_DRIVER, (void *) NULL);
+	cv_init(&msg_p->fmn_cv, NULL, CV_DEFAULT, NULL);
+}
+static void
+fuse_deinit_msg(fuse_msg_node_t *msg_p)
+{
+	sema_destroy(&msg_p->fmn_sema);
+	cv_destroy(&msg_p->fmn_cv);
+}
+
+fuse_msg_node_t *
+fuse_alloc_msg()
+{
+	fuse_msg_node_t *msg_p = kmem_alloc(sizeof (fuse_msg_node_t), KM_SLEEP);
+	fuse_init_msg(msg_p);
+	return (msg_p);
+}
+
+void
+fuse_free_msg(fuse_msg_node_t *msg_p)
+{
+	int i;
+	fuse_deinit_msg(msg_p);
+	/*
+	 * Check and free if the buffer was allocated for exchange between fuse
+	 * library and kernel module
+	 */
+	for (i = 0; i < msg_p->ipdata.iovs_used; i++) {
+		if (msg_p->ipdata.iovbuf[i].memsize &&
+		    msg_p->ipdata.iovbuf[i].memflag == MEM_TYPE_KMEM) {
+			kmem_free(msg_p->ipdata.iovbuf[i].base,
+			    msg_p->ipdata.iovbuf[i].memsize);
+		}
+	}
+
+	/* Check if memory was allocated to receive fuse library response */
+	if (msg_p->opdata.fouth)
+		kmem_free(msg_p->opdata.fouth, sizeof (*(msg_p->opdata.fouth)));
+
+	if (msg_p->opdata.iovbuf.memsize &&
+	    msg_p->opdata.iovbuf.memflag == MEM_TYPE_KMEM)
+		kmem_free(msg_p->opdata.iovbuf.base,
+		    msg_p->opdata.iovbuf.memsize);
+
+	kmem_free(msg_p, sizeof (fuse_msg_node_t));
+}
+
+void
+fuse_queue_request_nowait(fuse_session_t *se, fuse_msg_node_t *req_p)
+{
+	FUSE_SESSION_MUTEX_LOCK(se);
+	req_p->fmn_state = FUSE_MSG_STATE_QUEUE;
+	/* the device write call removes the message from the queue */
+	list_insert_tail(&(se->msg_list), req_p);
+	FUSE_SESSION_MUTEX_UNLOCK(se);
+	/* wake up the reader @ device */
+	sema_v(&(se->session_sema));
+}
+
+int
+fuse_queue_request_wait(fuse_session_t *se, fuse_msg_node_t *req_p)
+{
+	int err = 0;
+	int interrupted = 0;
+
+	req_p->frd_on_request_complete = frd_on_request_complete_wakeup;
+	fuse_queue_request_nowait(se, req_p);
+
+	FUSE_SESSION_MUTEX_LOCK(se);
+
+	while (req_p->fmn_state != FUSE_MSG_STATE_DONE) {
+		if (cv_wait_sig(&req_p->fmn_cv, &se->session_mutx) != 0) {
+			continue;
+		} else {
+			interrupted = 1;
+			break;
+		}
+	}
+	if (interrupted == 0) {
+		req_p->opdata.outdata = req_p->opdata.iovbuf.base;
+		FUSE_SESSION_MUTEX_UNLOCK(se);
+		return (err);
+	}
+
+	DTRACE_PROBE3(fuse_queue_request_wait_err_no_response,
+	    char *, "no response from daemon",
+	    fuse_session_t *, se, fuse_msg_node_t *, req_p);
+
+	if (req_p->fmn_state == FUSE_MSG_STATE_DONE) {
+		goto err;
+	}
+	if (req_p->fmn_state != FUSE_MSG_STATE_QUEUE)
+		req_p->fmn_state = FUSE_MSG_STATE_SIG;
+
+	while (req_p->fmn_state != FUSE_MSG_STATE_DONE)
+		cv_wait(&req_p->fmn_cv, &se->session_mutx);
+err:
+	req_p->opdata.outdata = NULL;
+	err = EINTR;
+	FUSE_SESSION_MUTEX_UNLOCK(se);
+
+	return (err);
+}
+/*
+ *  Avl related
+ */
+static void
+fuse_avl_destroy(avl_tree_t *tree_i, avl_tree_t *tree_n)
+{
+	void *cookie = NULL;
+	fuse_avl_cache_node_t *node;
+
+	while (avl_destroy_nodes(tree_n, &cookie) != NULL) {
+	}
+	avl_destroy(tree_n);
+	cookie = NULL;
+	while ((node = avl_destroy_nodes(tree_i, &cookie)) != NULL) {
+		fuse_avl_cache_node_destroy(node);
+	}
+	avl_destroy(tree_i);
+}
+
+fuse_avl_cache_node_t *
+fuse_avl_cache_node_create(vnode_t *np, uint64_t nodeid, uint64_t par_nodeid,
+    unsigned short namelen, char *name)
+{
+	if (!VTOFD(np))
+		cmn_err(CE_WARN,"Creating vnode with no v_data\n");
+	fuse_avl_cache_node_t *nod = kmem_zalloc(
+	    sizeof (fuse_avl_cache_node_t), KM_SLEEP);
+	nod->facn_vnode_p = np;
+	nod->facn_nodeid = nodeid;
+	nod->par_nodeid = par_nodeid;
+
+	nod->namelen = namelen;
+	if (namelen > 1) {
+		nod->name = kmem_alloc(namelen, KM_SLEEP);
+		(void) strlcpy(nod->name, name, namelen);
+	}
+	return (nod);
+}
+
+void
+fuse_avl_cache_node_destroy(fuse_avl_cache_node_t *node)
+{
+	if (node->namelen > 1)
+		kmem_free(node->name, node->namelen);
+	kmem_free(node, sizeof (fuse_avl_cache_node_t));
+}
+
+/*
+ *		Sort cached nodes by their inode number
+ */
+
+static int
+fuse_avl_compare_i(const void *x1, const void *x2)
+{
+	const fuse_avl_cache_node_t *new = (const fuse_avl_cache_node_t *)x1;
+	const fuse_avl_cache_node_t *old = (const fuse_avl_cache_node_t *)x2;
+
+	/*
+	 * We first check if valid nodeid is passed, if not we try to
+	 * validate with the remaining fields of a node
+	 */
+	
+	//if (new->facn_nodeid == FUSE_NULL_ID) {
+		/*
+		 * Cannot decently search in the inode tree without
+		 * knowing the nodeid. Must use the tree by name.
+		 */
+	//	cmn_err(CE_PANIC, "Illegal search by node\n");
+	//	return (0);
+	//} else {
+	return ((new->facn_nodeid == old->facn_nodeid) ? 0 :
+	    ((new->facn_nodeid < old->facn_nodeid) ? -1 : 1));
+	//}
+	//return (-1);
+}
+
+/*
+ *		Sort cached nodes by name and parent inode
+ */
+
+static int
+fuse_avl_compare_n(const void *x1, const void *x2)
+{
+	const fuse_avl_cache_node_t *new = (const fuse_avl_cache_node_t *)x1;
+	const fuse_avl_cache_node_t *old = (const fuse_avl_cache_node_t *)x2;
+	int r;
+
+	if (new->par_nodeid != old->par_nodeid) {
+		r = (new->par_nodeid < old->par_nodeid ? -1 : 1);
+	} else {
+		if (new->namelen != old->namelen)
+			r = (new->namelen < old->namelen ? -1 : 1);
+		else {
+			r = memcmp(new->name, old->name, new->namelen);
+			if (r)
+				r = (r < 0 ? -1 : 1);
+		}
+	}
+	return (r);
+}
+
+
+void
+fuse_session_umount(fuse_session_t *sep)
+{
+	if (sep->vfs) {
+		if (vn_vfswlock(sep->vfs->vfs_vnodecovered) == 0) {
+			VFS_RELE(sep->vfs);
+			(void) dounmount(sep->vfs, 0, sep->usercred);
+		} else {
+			VFS_RELE(sep->vfs);
+		}
+	}
+}
diff --git a/usr/src/uts/common/io/fuse/fuse_queue.h b/usr/src/uts/common/io/fuse/fuse_queue.h
new file mode 100644
index 0000000000..5698743a4f
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/fuse_queue.h
@@ -0,0 +1,145 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#ifndef	_FUSE_QUEUE_H
+#define	_FUSE_QUEUE_H
+#include <sys/int_types.h>
+#include <sys/cred.h>
+#include "fuse.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+
+#define	FUSE_SESSION_MUTEX_LOCK(se)	mutex_enter(&((se)->session_mutx))
+#define	FUSE_SESSION_MUTEX_UNLOCK(se)	mutex_exit(&((se)->session_mutx))
+
+#define	FUSE_MSG_MUTEX_LOCK(x)	mutex_enter(& x->fmn_mutx)
+#define	FUSE_MSG_MUTEX_UNLOCK(x)	mutex_exit(& x->fmn_mutx)
+
+/*
+ * TODO: create an array of session each array element representing a minor
+ * device
+ */
+
+/* session is a connection between a mount point and a minor device */
+typedef struct fuse_session
+{
+	kmutex_t	session_mutx;
+	ksema_t		session_sema; /* devops read sleeps over it */
+	list_t		msg_list;  /* message awaiting service rests here */
+	avl_tree_t	avl_cache_i; /* nodeid used to track associated vnode */
+	avl_tree_t	avl_cache_n; /* name used to track associated vnode */
+	kmutex_t	avl_mutx; /* serialize actions on avl_cache */
+	minor_t		minor; /* Minor number associated with this session */
+	uint32_t	state;
+	uint64_t	unique; /* msg id used between lib and kernel module */
+	uint64_t	max_unique;
+	cred_t		*usercred;  /* Credentials passed by fuse library */
+	uint32_t	max_write;  /* Max Write value set by fuse lib */
+	vfs_t		*vfs;
+	uint32_t	mounted:1;
+	uint32_t	readonly:1;
+} fuse_session_t;
+
+
+#define	FUSE_MAX_SESSIONS 1024
+
+fuse_session_t *fuse_minor_get_session(minor_t ndx);
+minor_t fuse_session_get_minor(fuse_session_t *se);
+
+fuse_session_t *fuse_alloc_session();
+void fuse_free_session(fuse_session_t *se);
+
+void fuse_init_session(fuse_session_t *se);
+void fuse_deinit_session(fuse_session_t *se);
+
+void fuse_session_set_cred(fuse_session_t *se, cred_t *cr);
+void fuse_session_set_vfs(fuse_session_t *se, vfs_t *vfs);
+
+void fuse_session_umount(fuse_session_t *se);
+
+typedef struct fuse_avl_cache_node {
+	uint64_t facn_nodeid;
+	uint64_t par_nodeid;
+	vnode_t	*facn_vnode_p;
+	char *name;
+	unsigned short namelen;
+	avl_node_t faci_cache_node; /* tree by inode */
+	avl_node_t facn_cache_node; /* tree by name */
+} fuse_avl_cache_node_t;
+
+#define	FUSE_MAX_MSG 8192
+
+typedef struct fuse_req_data {
+	char		frd_buf[FUSE_MAX_MSG];
+	uint32_t	frd_cur_offset;
+} fuse_req_data_t;
+
+#define	FUSE_MSG_STATE_INIT  0
+#define	FUSE_MSG_STATE_QUEUE 2
+#define	FUSE_MSG_STATE_READ  3
+#define	FUSE_MSG_STATE_WRITE  4
+#define	FUSE_MSG_STATE_SIG  5
+#define	FUSE_MSG_STATE_DONE  6
+
+typedef struct fuse_msg_node fuse_msg_node_t;
+
+struct fuse_msg_node
+{
+	list_node_t	fmn_link;
+	ksema_t		fmn_sema;
+	kcondvar_t	fmn_cv;
+	kmutex_t	fmn_mutx;
+	int		fmn_state;
+	int		fmn_noreply; /* no reply expected */
+	uint64_t	fmn_unique;  /* identifies a unique message */
+	/* Message interchange struct between FUSE Kernel and FUSE lib. */
+	fuse_req_data_t	fmn_req;
+	void (*frd_on_request_complete)(fuse_session_t *ses_p,
+	    fuse_msg_node_t *msg_p);
+	struct fuse_data_in ipdata;  /* Data passed to the fuse lib */
+	struct fuse_data_out opdata; /* Data received back from the fuse lib */
+};
+
+void fuse_queue_request_nowait(fuse_session_t *se, fuse_msg_node_t *req);
+int  fuse_queue_request_wait(fuse_session_t *se, fuse_msg_node_t *req_p);
+
+fuse_msg_node_t *fuse_alloc_msg();
+void fuse_free_msg(fuse_msg_node_t *msgp);
+
+uint64_t fuse_bake_cookie();
+
+void fuse_avl_cache_node_destroy(fuse_avl_cache_node_t *node);
+fuse_avl_cache_node_t *fuse_avl_cache_node_create(vnode_t *np, uint64_t inode,
+    uint64_t par_nodeid, unsigned short namelen, char *name);
+fuse_msg_node_t *fuse_setup_message(size_t argsize, enum fuse_opcode op,
+    uint64_t nodeid, cred_t *credp, uint64_t unique);
+void fuse_destroy_cache(fuse_session_t *sep);
+
+#ifdef	__cplusplus
+}
+#endif
+#endif /* _FUSE_QUEUE_H */
diff --git a/usr/src/uts/common/io/fuse/fuse_vfsops.c b/usr/src/uts/common/io/fuse/fuse_vfsops.c
new file mode 100644
index 0000000000..62b146a504
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/fuse_vfsops.c
@@ -0,0 +1,590 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Portions Copyright 2019 Jean-Pierre Andre
+ */
+
+/*
+ * This file has been derived from OpenSolaris devfs and others in uts/common/fs
+ */
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/modctl.h>
+#include <sys/vfs.h>
+#include "../../fs/fs_subr.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/t_lock.h>
+#include <sys/systm.h>
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/cred_impl.h>
+#include <sys/pathname.h>
+#include <sys/debug.h>
+#include <sys/policy.h>
+#include <sys/modctl.h>
+#include <sys/mntent.h>
+#include <sys/vfs.h>
+
+#include <sys/fs/namenode.h>
+#include <sys/mount.h>
+#include <sys/strsubr.h>
+#include <sys/sdt.h>
+
+#include <sys/atomic.h>
+#include <sys/sysmacros.h>
+#include "fuse_queue.h"
+#include "fuse.h"
+
+/* It could be as large as PATH_MAX, but would that have any uses? */
+#define	FUSE_NAME_MAX 1024
+
+/*
+ * devfs vfs operations.
+ */
+static int fuse_mount(struct vfs *, struct vnode *, struct mounta *,
+    struct cred *);
+static int fuse_unmount(struct vfs *, int, struct cred *);
+static int fuse_root(struct vfs *, struct vnode **);
+static int fuse_statvfs(struct vfs *, struct statvfs64 *);
+
+static int fuse_init(int fstype, char *name);
+
+typedef struct fuse_vfs_data {
+	vnode_t *vfs_root_vnode;
+} fuse_vfs_data_t;
+
+extern struct mod_ops mod_fsops;
+static int devfstype;		/* fstype */
+vnodeops_t *dv_vnodeops;
+vnodeops_t *temp_vnodeops;	/* Used during create operation */
+
+
+static mntopt_t fuse_options[] = {
+	{ "fd",		NULL,	NULL,	MO_NODISPLAY|MO_HASVALUE,	NULL},
+};
+
+static mntopts_t fuse_opttbl = {
+	sizeof (fuse_options) / sizeof (mntopt_t),
+	fuse_options
+};
+static vfsdef_t fuse_vfw = {
+	VFSDEF_VERSION,
+	FUSE_FS_TYPE,
+	fuse_init,
+	VSW_HASPROTO,
+	&fuse_opttbl
+};
+
+struct modlfs fuse_vfs_modldrv = {
+	&mod_fsops, FUSE_FS_DESCRIPTION, &fuse_vfw
+};
+
+static int fuse_init(int fstype, char *name)
+{
+	int error = DDI_SUCCESS;
+
+	static const fs_operation_def_t fuse_vfsops_template[] = {
+		VFSNAME_MOUNT,   { .vfs_mount = fuse_mount },
+		VFSNAME_UNMOUNT, { .vfs_unmount = fuse_unmount },
+		VFSNAME_ROOT,    { .vfs_root = fuse_root },
+		VFSNAME_STATVFS, { .vfs_statvfs = fuse_statvfs },
+		VFSNAME_SYNC,    { .vfs_sync = fs_sync },
+		NULL, NULL
+	};
+
+	/* TODO: associate the FUSE device here? */
+	devfstype = fstype;
+	/*
+	 * Associate VFS ops vector with this fstype
+	 */
+	error = vfs_setfsops(fstype, fuse_vfsops_template, NULL);
+	if (error != 0) {
+		DTRACE_PROBE2(fuse_init_err_setops,
+		    char *, "vfs_setfsops failed",
+		    int, error);
+		return (error);
+	}
+
+	error = vn_make_ops(name, fuse_vnodeops_template, &dv_vnodeops);
+	if (error != 0) {
+		(void) vfs_freevfsops_by_type(fstype);
+		DTRACE_PROBE2(fuse_init_err_makeops,
+		    char *, "vn_make_ops failed for dv_vnodeops",
+		    int, error);
+		return (error);
+	}
+
+	error = vn_make_ops(name, temp_vnodeops_template, &temp_vnodeops);
+	if (error != 0) {
+		(void) vfs_freevfsops_by_type(fstype);
+		DTRACE_PROBE2(fuse_init_err_makeops,
+		    char *, "vn_make_ops failed for temp_vnodeops",
+		    int, error);
+		vn_freevnodeops(dv_vnodeops);
+		dv_vnodeops = NULL;
+		return (error);
+	}
+
+	return (error);
+}
+
+struct vnode *
+fuse_create_vnode(vfs_t *vfsp, uint64_t nodeid, uint64_t parent_nid, int type,
+    int iscreate)
+{
+	struct vnode *vp;
+	struct fuse_vnode_data *vdata;
+
+	/*
+	 * Allocate vnode and internal data structure
+	 */
+	vdata = kmem_zalloc(sizeof (fuse_vnode_data_t), KM_SLEEP);
+
+	vp = vn_alloc(KM_SLEEP);
+	/*
+	 * Set up various pointers
+	 */
+
+	vp->v_data = vdata;
+	vdata->nodeid = nodeid;
+	vdata->par_nid = parent_nid;
+#ifndef DONT_CACHE_ATTRIBUTES
+	vdata->uid = (uid_t)-1;
+	vdata->gid = (gid_t)-1;
+#endif
+
+	/* Create the list for storing file handles */
+	list_create(&vdata->fh_list, sizeof (fuse_file_handle_t),
+	    offsetof(fuse_file_handle_t, fh_link));
+
+	mutex_init(&vdata->fh_list_lock, NULL, MUTEX_DEFAULT, (void *) NULL);
+	mutex_init(&vdata->f_lock, NULL, MUTEX_DEFAULT, (void *) NULL);
+
+	/*
+	 * Initialize vnode and hold parent.
+	 * If we are invoked during create operation, allocate memory for
+	 * storing create related data arguments.
+	 */
+	if (iscreate) {
+		vn_setops(vp, temp_vnodeops);
+		vdata->fcd = kmem_zalloc(
+		    sizeof (struct fuse_create_data), KM_SLEEP);
+	} else {
+		vn_setops(vp, dv_vnodeops);
+	}
+
+	VFS_HOLD(vfsp);
+	VN_SET_VFS_TYPE_DEV(vp, vfsp, type, vfsp->vfs_dev);
+	vp->v_flag |= VNOSWAP | VNOMOUNT;
+
+	return (vp);
+}
+
+/* -------------------------- VFS related ---------------------------------- */
+
+
+static struct vnode *
+fuse_get_root_node(vfs_t *vfsp)
+{
+	struct vnode *vp = fuse_create_vnode(vfsp, FUSE_ROOT_ID,
+	    FUSE_NULL_ID, VDIR, OTHER_OP);
+	vp->v_flag |= VROOT;
+	return (vp);
+}
+
+static void
+fuse_process_init_msg(fuse_session_t *sep, fuse_msg_node_t *msg_p)
+{
+	struct fuse_init_out *foutarg;
+
+	if (msg_p->opdata.fouth->error) {
+		DTRACE_PROBE2(fuse_process_init_msg_err_init_req,
+		    char *, "FUSE_INIT request failed",
+		    struct fuse_out_header *, msg_p->opdata.fouth);
+		fuse_session_umount(sep);
+	} else {
+		if (msg_p->opdata.outsize != sizeof (struct fuse_init_out)) {
+			DTRACE_PROBE2(fuse_process_init_msg_err_size,
+			    char *, "FUSE_INIT reply wrong size",
+			    struct fuse_data_out *, &msg_p->opdata);
+			fuse_session_umount(sep);
+		} else {
+			foutarg = (struct fuse_init_out *)
+			    msg_p->opdata.iovbuf.base;
+			if (foutarg->major < 7) {
+				DTRACE_PROBE2(fuse_process_init_msg_err_version,
+				    char *, "FUSE_INIT reply wrong version",
+				    struct fuse_init_out *, foutarg);
+				fuse_session_umount(sep);
+			}
+			sep->max_write = foutarg->max_write;
+
+		}
+	}
+	fuse_free_msg(msg_p);
+}
+
+/*
+ * Function which sends FUSE_INIT message to the fuse library.
+ * FUSE_INIT cannot fail. The response is not waited for so that
+ * mount(2) can return immediately.
+ */
+static int
+fuse_send_mounted_notice(fuse_session_t *se_p)
+{
+	struct fuse_init_in *finitarg;
+	fuse_msg_node_t *msg_p = NULL;
+
+	msg_p = fuse_setup_message(sizeof (*finitarg), FUSE_INIT,
+	    FUSE_ROOT_ID, se_p->usercred, FUSE_GET_UNIQUE(se_p));
+
+	/* Set up arguments to the fuse library */
+	finitarg = (struct fuse_init_in *)msg_p->ipdata.indata;
+	finitarg->major = FUSE_KERNEL_VERSION;
+	finitarg->minor = FUSE_KERNEL_MINOR_VERSION;
+
+	msg_p->frd_on_request_complete = fuse_process_init_msg;
+	fuse_queue_request_nowait(se_p, msg_p);
+
+	return (0);
+}
+
+
+static int
+isdigit(int ch)
+{
+	return (ch >= '0' && ch <= '9');
+}
+
+
+/* Taken from NFS code: usr/src/stand/lib/fs/nfs/mount.c */
+
+#define	isspace(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
+#define	bad(val)	(val == NULL || !isdigit(*val))
+
+static int
+atoi(const char *p)
+{
+	int n;
+	int c, neg = 0;
+
+	if (!isdigit(c = *p)) {
+		while (isspace(c))
+			c = *++p;
+		switch (c) {
+		case '-':
+			neg++;
+			/* FALLTHROUGH */
+		case '+':
+			c = *++p;
+		}
+		if (!isdigit(c))
+			return (0);
+	}
+	for (n = '0' - c; isdigit(c = *++p); ) {
+		n *= 10; /* two steps to avoid unnecessary overflow */
+		n += '0' - c; /* accum neg to avoid surprises at MAX */
+	}
+	return (neg ? n : -n);
+}
+
+/*
+ * Given a filedescriptor as a string return the associated
+ * device.
+ */
+static dev_t
+fuse_str_to_dev(char *fdstr)
+{
+	dev_t dev = 0;
+	struct file *fp;
+	int fd = atoi(fdstr);
+	if ((fp = getf(fd)) != 0) {
+		dev = fp->f_vnode->v_rdev;
+		releasef(fd);
+	}
+	return (dev);
+}
+
+/*
+ *		Check whether no directory is open when unmounting
+ */
+
+static int fuse_check_busy(fuse_session_t *sep)
+{
+	fuse_avl_cache_node_t *item;
+	struct vnode *vp;
+	int busy;
+
+	busy = 0;
+	mutex_enter(&sep->avl_mutx);
+	item = (fuse_avl_cache_node_t*)avl_first(&(sep->avl_cache_i));
+	while (item && !busy) {
+		vp = item->facn_vnode_p;
+		if (vp && (vp->v_type == VDIR))
+			busy = vp->v_count > 1;
+		item = (fuse_avl_cache_node_t*)AVL_NEXT(&(sep->avl_cache_i),
+						item);
+	}
+	mutex_exit(&sep->avl_mutx);
+	return (busy);
+}
+
+static int
+fuse_mount(struct vfs *vfsp, struct vnode *mvp, struct mounta *uap,
+    struct cred *cr)
+{
+	fuse_vfs_data_t	 *vfsdata;
+	fuse_session_t	 *se;
+	dev_t dev;
+	char *fdstr;
+	int err;
+
+	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+		return (EPERM);
+
+	if (mvp->v_type != VDIR)
+		return (ENOTDIR);
+
+	if ((uap->flags & MS_OVERLAY) == 0 &&
+	    (mvp->v_count != 1 || (mvp->v_flag & VROOT)))
+		return (EBUSY);
+
+	if (vfs_optionisset(vfsp, "fd", &fdstr)) {
+		dev = fuse_str_to_dev(fdstr);
+	} else {
+		return (ENXIO);
+	}
+
+	vfsdata = kmem_alloc(sizeof (fuse_vfs_data_t), KM_SLEEP);
+
+	/*
+	 * Initialize vfs fields
+	 */
+	/* TODO: what should be set here? PAGE_CACHE_SIZE; */
+	vfsp->vfs_bsize = 512;
+	vfsp->vfs_fstype = devfstype;
+
+	vfsp->vfs_data = vfsdata;
+	vfsp->vfs_dev = dev;
+
+	vfs_make_fsid(& vfsp->vfs_fsid, vfsp->vfs_dev, devfstype);
+
+	/* Create root */
+	((struct fuse_vfs_data *)vfsdata)->vfs_root_vnode =
+	    fuse_get_root_node(vfsp);
+
+	/*
+	 * The session associated with this device should have been allocated
+	 * when fuse_dev_open was called.
+	 */
+	se = fuse_minor_get_session(getminor(dev));
+	if (se == NULL) {
+		DTRACE_PROBE2(fuse_mount_err_session,
+		    char *, "failed to find session",
+		    dev_t, dev);
+		return (ENXIO);
+	}
+
+	fuse_session_set_cred(se, cr);
+	fuse_session_set_vfs(se, vfsp);
+	se->mounted = 1;
+	se->readonly = (uap->flags & MS_RDONLY ? 1 : 0);
+	err = fuse_send_mounted_notice(se);
+
+	return (err);
+}
+
+static int
+fuse_unmount(struct vfs *vfsp, int flag, struct cred *crp)
+{
+	fuse_vfs_data_t *data;
+	fuse_session_t *fsep;
+
+	if (secpolicy_fs_unmount(crp, vfsp) != 0)
+		return (EPERM);
+
+	/*
+	 * We should never have a reference count of less than 2: one for the
+	 * caller, one for the root vnode.
+	 */
+	/* ASSERT(vfsp->vfs_count >= 2); */
+
+	/*
+	 * Any active vnodes will result in a hold on the root vnode
+	 */
+	data = vfsp->vfs_data;
+	if (data->vfs_root_vnode->v_count != 1) {
+		if (flag & MS_FORCE)
+			cmn_err(CE_NOTE, "Forced unmount while root"
+				" directory held count %d\n",
+				data->vfs_root_vnode->v_count);
+		else
+			return (EBUSY);
+	}
+	/*
+	 * The file system must not hold the cwd of any process
+	 */
+	fsep = fuse_minor_get_session(getminor(vfsp->vfs_dev));
+	if (fsep && fuse_check_busy(fsep)) {
+		if (flag & MS_FORCE)
+			cmn_err(CE_NOTE, "Forced unmount while file"
+					" system was busy\n");
+		else
+			return (EBUSY);
+	}
+
+	/*
+	 * Release the last hold on the root vnode
+	 */
+	VN_RELE(data->vfs_root_vnode);
+
+	kmem_free(data, sizeof (fuse_vfs_data_t));
+
+	/* Clean-up the session */
+	fsep = fuse_minor_get_session(getminor(vfsp->vfs_dev));
+
+	if (fsep != NULL) {
+		fuse_destroy_cache(fsep);
+		/* Mark the filesystem as unmounted */
+		fsep->mounted = 0;
+		if (vfsp->vfs_count != 2)
+			cmn_err(CE_WARN,"Bad fuse cache reference count %d\n",
+				(int)vfsp->vfs_count);
+
+		/*
+		 * Wake the fuselib reader so it can exit and clean
+		 * up the session
+		 */
+		sema_v(&(fsep->session_sema));
+	} else {
+		DTRACE_PROBE2(fuse_unmount_info_session,
+		    char *, "failed to find session",
+		    dev_t, vfsp->vfs_dev);
+	}
+
+	return (DDI_SUCCESS);
+}
+
+/* This code is similar to UFS implementation */
+static int fuse_root(struct vfs *vfsp, struct vnode **vpp)
+{
+	fuse_vfs_data_t	*data;
+	int err = DDI_SUCCESS;
+
+	if (!vfsp)
+		err = EIO;
+
+	if (!err) {
+		data = (fuse_vfs_data_t *)vfsp->vfs_data;
+		if (!data || !data->vfs_root_vnode)
+			err = (EIO);
+	}
+
+	if (!err) {
+		*vpp = data->vfs_root_vnode;
+		VN_HOLD(*vpp);
+	}
+
+	return (err);
+}
+
+static int
+fuse_statvfs(struct vfs *vfsp, struct statvfs64 *sp)
+{
+	int err = 0;
+	struct fuse_statfs_out *fso = NULL;
+	fuse_msg_node_t	*msgp = NULL;
+	fuse_session_t *sep = NULL;
+
+	(void) bzero(sp, sizeof (*sp));
+
+	if (vfsp->vfs_flag & VFS_UNMOUNTED)
+		return (EIO);
+
+	sep = fuse_minor_get_session(getminor(vfsp->vfs_dev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_statvfs_err_session,
+		    char *, "failed to find session",
+		    dev_t, vfsp->vfs_dev);
+		return (ENODEV);
+	}
+
+	msgp = fuse_setup_message(0, FUSE_STATFS, 0, sep->usercred,
+	    FUSE_GET_UNIQUE(sep));
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		return (err);
+	}
+
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(fuse_statvfs_err_statfs_req,
+		    char *, "FUSE_STATFS request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		fuse_free_msg(msgp);
+		return (err);
+	}
+
+	if (msgp->opdata.outsize != sizeof (struct fuse_statfs_out)) {
+		DTRACE_PROBE2(fuse_statvfs_err_size,
+		    char *, "FUSE_STATFS reply wrong size",
+		    struct fuse_data_out *, &msgp->opdata);
+		err = EINVAL;
+	} else {
+		fso = msgp->opdata.outdata;
+
+		sp->f_bsize = fso->st.bsize;
+		sp->f_blocks = fso->st.blocks;
+		sp->f_bfree = fso->st.bfree;
+		sp->f_files = fso->st.files;
+		sp->f_ffree = fso->st.ffree;
+		sp->f_favail = fso->st.ffree;
+		sp->f_bavail = fso->st.bavail;
+		sp->f_namemax = fso->st.namelen;
+
+		sp->f_frsize = fso->st.frsize ? fso->st.frsize : fso->st.bsize;
+
+
+		(void) strlcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name,
+		    sizeof (sp->f_basetype));
+
+		DTRACE_PROBE2(fuse_statvfs_info_statvfs,
+		    char *, "FUSE_STATFS reply",
+		    struct statvfs64 *, sp);
+	}
+	fuse_free_msg(msgp);
+	return (err);
+}
diff --git a/usr/src/uts/common/io/fuse/fuse_vnops.c b/usr/src/uts/common/io/fuse/fuse_vnops.c
new file mode 100644
index 0000000000..1e7b063e27
--- /dev/null
+++ b/usr/src/uts/common/io/fuse/fuse_vnops.c
@@ -0,0 +1,4604 @@
+/*
+ * Copyright (C) 2005 Csaba Henk. 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.
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Portions Copyright 2012-2019 Jean-Pierre Andre
+ */
+/*
+ * This file has been derived from OpenSolaris devfs and others in uts/common/fs
+ */
+#include <sys/vnode.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/list.h>
+#include <sys/sunddi.h>
+#include <sys/ddi.h>
+#include <sys/fcntl.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/dirent.h>
+#include <sys/mode.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/thread.h>
+#include "../../fs/fs_subr.h"
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/buf.h>
+#include <sys/note.h>
+#include <sys/ksynch.h>
+#include <sys/inttypes.h>
+#include <sys/sdt.h>
+#include <sys/vmsystm.h>
+
+#include <vm/pvn.h>
+#include <vm/seg_map.h>
+#include <vm/seg_vn.h>
+#include <vm/seg_kpm.h>
+
+#include "fuse_queue.h"
+#include "fuse.h"
+
+#ifndef CHECK_VDATA 
+#define CHECK_VDATA 0 /* Optionally check consistency of v_data */
+#endif
+
+/* Vnode operations */
+static int fuse_open(struct vnode **vpp, int flag, struct cred *cred,
+    caller_context_t *ct);
+static int fuse_close(struct vnode *vp, int flag, int count,
+    offset_t offset, struct cred *cred, caller_context_t *ct);
+static int fuse_read(struct vnode *vp, struct uio *uiop, int ioflag,
+    struct cred *cred, struct caller_context *ct);
+static int fuse_write(struct vnode *vp, struct uio *uiop, int ioflag,
+    struct cred *cred, struct caller_context *ct);
+static int fuse_getattr(struct vnode *vp, struct vattr *vap, int flags,
+    struct cred *cr, caller_context_t *ct);
+static int fuse_setattr(struct vnode *vp, struct vattr *vap,
+    int flags, struct cred *cr, caller_context_t *ct);
+static int fuse_access(struct vnode *vp, int mode, int flags,
+    struct cred *cr, caller_context_t *ct);
+static int fuse_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
+    struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
+    caller_context_t *ct, int *direntflags, pathname_t *realpnp);
+static int fuse_create(struct vnode *dvp, char *nm, struct vattr *vap,
+    vcexcl_t excl, int mode, struct vnode **vpp, struct cred *cred,
+    int flag, caller_context_t *ct, vsecattr_t *vsecp);
+static int fuse_readdir(struct vnode *dvp, struct uio *uiop,
+    struct cred *cred, int *eofp, caller_context_t *ct, int flags);
+static int fuse_fsync(struct vnode *vp, int syncflag, struct cred *cred,
+    caller_context_t *ct);
+static void fuse_inactive(struct vnode *vp, struct cred *cred,
+    caller_context_t *ct);
+static int fuse_seek(struct vnode *vp, offset_t ooff, offset_t *noffp,
+    caller_context_t *ct);
+static int fuse_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap,
+    vnode_t **vpp, cred_t *cr, caller_context_t *ct, int flags,
+    vsecattr_t *vsecp);
+static int fuse_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr,
+    caller_context_t *ct, int flags);
+static int fuse_link(struct vnode *dvp, struct vnode *srcvp, char *tnm,
+    struct cred *cred, caller_context_t *ct, int flags);
+static int fuse_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
+    cred_t *cr, caller_context_t *ct, int flags);
+static int fuse_remove(vnode_t *dvp, char *name, cred_t *cr,
+    caller_context_t *ct, int flags);
+static int fuse_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *link,
+    cred_t *cr, caller_context_t *ct, int flags);
+static int fuse_readlink(vnode_t *vp, uio_t *uio, cred_t *cr,
+    caller_context_t *ct);
+static int fuse_mknod(struct vnode *dvp, char *nm, struct vattr *vap,
+    vcexcl_t excl, int mode, struct vnode **vpp, struct cred *cred,
+    int flag, caller_context_t *ct, vsecattr_t *vsecp);
+static int fuse_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
+    offset_t off, cred_t *cr, caller_context_t *ct);
+static int fuse_getpage(struct vnode *vp, offset_t off, size_t len,
+    uint_t *protp, page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
+    enum seg_rw rw, struct cred *credp, caller_context_t *ct);
+static int fuse_putpage(struct vnode *vp, offset_t off, size_t len, int flags,
+    struct cred *credp, caller_context_t *ct);
+static int fuse_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp,
+    size_t *lenp, int flags, cred_t *credp);
+static int fuse_map(vnode_t *, offset_t, struct as *, caddr_t *, size_t,
+    uchar_t, uchar_t, uint_t, cred_t *, caller_context_t *);
+static int fuse_addmap(vnode_t *, offset_t, struct as *, caddr_t,  size_t,
+    uchar_t, uchar_t, uint_t, cred_t *, caller_context_t *);
+static int fuse_delmap(vnode_t *, offset_t, struct as *, caddr_t,  size_t,
+    uint_t, uint_t, uint_t, cred_t *, caller_context_t *);
+
+/* Other supporting routines */
+static int rdfuse(struct vnode *vp, struct uio *uiop, struct cred *credp);
+static int fuse_send_release(fuse_session_t *sep, struct fuse_file_handle *fh,
+    enum fuse_opcode op, struct vnode *vp, int flags);
+static void fuse_vnode_cache_remove(struct vnode *vp, fuse_session_t *sep);
+static void fuse_vnode_cleanup(struct vnode *vp, struct cred *credp,
+    fuse_session_t *sep);
+static void
+fuse_vnode_destroy(struct vnode *vp, struct cred *credp, fuse_session_t *sep);
+static void fuse_set_getattr(struct vnode *vp, struct vattr *vap,
+    struct fuse_attr *attr);
+static int
+fuse_add_entry(struct vnode **vpp, struct vnode *dvp, fuse_msg_node_t *msgp,
+    fuse_session_t *sep, char *name, int namelen, cred_t *credp, vtype_t vtype);
+static void
+    fuse_send_forget(uint64_t nodeid, fuse_session_t *sep, uint64_t nlookup);
+static int
+fuse_getvnode(uint64_t nodeid, struct vnode **vpp, v_getmode vmode,
+    uint32_t mode, fuse_session_t *sep, vfs_t *vfsp, int namelen,
+    char *name, uint64_t parent_nid, struct cred *credp);
+static int
+fuse_lookup_i(struct vnode *dvp, char *nm, struct vnode **vpp, cred_t *credp);
+static int
+fuse_getfilesize(struct vnode *vp, u_offset_t *fsize, struct cred *credp);
+static inline void
+fsize_change_notify(struct vnode *vp, size_t fsize, int flag);
+static int
+fuse_access_i(void *vp, int mode, struct cred *credp);
+static int
+fuse_access_inkernelcheck(void *vvp, int mode, struct cred *credp);
+static int
+fuse_fsync_fh(struct fuse_file_handle *fhp, struct fuse_fh_param *param);
+static void
+fuse_page_mapin(struct vnode *vp, struct buf **bp, struct page *pp, size_t len,
+    int flag, struct fuse_iov *iovp);
+static inline void fuse_vnode_free(struct vnode *vp, fuse_session_t *sep);
+static void fuse_free_vdata(struct vnode *vp);
+
+const fs_operation_def_t fuse_vnodeops_template[] = {
+	VOPNAME_OPEN,		{ .vop_open = fuse_open },
+	VOPNAME_CLOSE,		{ .vop_close = fuse_close },
+	VOPNAME_READ,		{ .vop_read = fuse_read },
+	VOPNAME_WRITE,		{ .vop_write = fuse_write },
+	VOPNAME_GETATTR,	{ .vop_getattr = fuse_getattr },
+	VOPNAME_SETATTR,	{ .vop_setattr = fuse_setattr },
+	VOPNAME_ACCESS,		{ .vop_access = fuse_access },
+	VOPNAME_LOOKUP,		{ .vop_lookup = fuse_lookup },
+	VOPNAME_LINK,		{ .vop_link = fuse_link },
+	VOPNAME_CREATE,		{ .vop_create = fuse_create },
+	VOPNAME_READDIR,	{ .vop_readdir = fuse_readdir },
+	VOPNAME_FSYNC,		{ .vop_fsync = fuse_fsync },
+	VOPNAME_INACTIVE,	{ .vop_inactive = fuse_inactive },
+	VOPNAME_DISPOSE,	{ .vop_dispose = fs_dispose },
+	VOPNAME_MKDIR,		{ .vop_mkdir = fuse_mkdir },
+	VOPNAME_RMDIR,		{ .vop_rmdir = fuse_rmdir },
+	VOPNAME_RENAME,		{ .vop_rename = fuse_rename },
+	VOPNAME_REMOVE,		{ .vop_remove = fuse_remove },
+	VOPNAME_SYMLINK,	{ .vop_symlink = fuse_symlink },
+	VOPNAME_READLINK,	{ .vop_readlink = fuse_readlink },
+	VOPNAME_SPACE,		{ .vop_space = fuse_space },
+	VOPNAME_GETPAGE,	{ .vop_getpage = fuse_getpage },
+	VOPNAME_PUTPAGE,	{ .vop_putpage = fuse_putpage },
+	VOPNAME_SEEK,		{ .vop_seek = fuse_seek },
+	VOPNAME_MAP,		{ .vop_map = fuse_map },
+	VOPNAME_ADDMAP,		{ .vop_addmap = fuse_addmap },
+	VOPNAME_DELMAP,		{ .vop_delmap = fuse_delmap },
+	NULL, NULL
+};
+
+
+const fs_operation_def_t temp_vnodeops_template[] = {
+	VOPNAME_OPEN, fuse_open,
+	NULL, 		NULL
+};
+
+/*
+ *	Hard-linked files are not cached because the high-level
+ *	fuse library assigns multiple nodeid to them, leading to
+ *	multiple cache entries for the same file.
+ */
+#define	cache_attrs(sep, vp, fuse_out) do {				\
+	timestruc_t ts;							\
+	fuse_vnode_data_t *v_data = VTOFD(vp);				\
+									\
+	v_data->cached_attrs_bound.tv_sec = (fuse_out)->attr_valid;	\
+	v_data->cached_attrs_bound.tv_nsec = (fuse_out)->attr_valid_nsec; \
+	gethrestime(&ts);						\
+	if (((fuse_out)->attr.nlink < 2)				\
+	    || (vp->v_type == VDIR) || sep->readonly)			\
+		timespecadd(&v_data->cached_attrs_bound, &ts);		\
+	fuse_set_getattr(vp, &v_data->cached_attrs, &(fuse_out)->attr); \
+_NOTE(CONSTCOND) } while (0)
+
+#define	ATTR_CACHE_VALID(curtime, attrtime)				\
+	((curtime.tv_sec == attrtime.tv_sec) ?				\
+	    (curtime.tv_nsec <= attrtime.tv_nsec) :			\
+	    (curtime.tv_sec <= attrtime.tv_sec))
+
+#define	invalidate_cached_attrs(vp)					\
+	if (VTOFD(vp))							\
+	    bzero(&(VTOFD(vp)->cached_attrs_bound), sizeof (timestruc_t));
+
+/* Function which allocates the requested amount of size for message passing */
+void
+fuse_buf_alloc(struct fuse_iov *iov, size_t len)
+{
+	iov->base = kmem_zalloc(FUSE_BUF_ADJUST_SIZE(len), KM_SLEEP);
+	iov->len = len;
+	iov->memsize = FUSE_BUF_ADJUST_SIZE(len);
+	iov->memflag = MEM_TYPE_KMEM;
+}
+
+/* Function which initializes the message header structure used by fuse lib */
+static void
+fuse_setup_inheader(struct fuse_in_header *finh, size_t arglen, int op,
+    uint64_t unique, uint64_t nodeid, cred_t *cred)
+{
+	finh->len	= sizeof (struct fuse_in_header) + arglen;
+	finh->opcode	= op;
+	finh->unique	= unique;
+	finh->nodeid	= nodeid;
+	finh->uid	= crgetuid(cred);
+	finh->gid	= crgetgid(cred);
+	finh->pid	= ddi_get_pid();
+}
+
+/* Function which creates a message with ony header and no arguments */
+static void
+setup_msg_onlyheader(fuse_msg_node_t *msgp, enum fuse_opcode op,
+    uint64_t unique, uint64_t nodeid, struct cred *credp)
+{
+	msgp->fmn_unique = unique;
+	msgp->ipdata.iovs_used = 1;
+	fuse_buf_alloc(&(msgp->ipdata.iovbuf[0]),
+	    (sizeof (struct fuse_in_header)));
+	msgp->ipdata.finh = msgp->ipdata.iovbuf[0].base;
+
+	fuse_setup_inheader(msgp->ipdata.finh, 0, op, unique, nodeid, credp);
+}
+
+/* Function assigns various parameters to a message node */
+static void
+fuse_setup_msgparams(fuse_msg_node_t *msgp, size_t argsize,
+    enum fuse_opcode op, uint64_t nodeid, cred_t *credp, uint64_t unique)
+{
+	msgp->ipdata.finh	= msgp->ipdata.iovbuf[0].base;
+	msgp->ipdata.indata	= ((char *)msgp->ipdata.iovbuf[0].base +
+	    sizeof (struct fuse_in_header));
+	msgp->fmn_unique	= unique;
+
+	fuse_setup_inheader(msgp->ipdata.finh, argsize, op,
+	    msgp->fmn_unique, nodeid, credp);
+	msgp->ipdata.iosize = argsize;
+}
+
+static void
+fuse_msg_refresh(fuse_msg_node_t *msgp, size_t size, int forcefree)
+{
+	if (msgp->ipdata.iovbuf[0].memsize == size ||
+	    (msgp->ipdata.iovbuf[0].memsize > size && !forcefree))
+		return;
+	else {
+		kmem_free(msgp->ipdata.iovbuf[0].base,
+		    msgp->ipdata.iovbuf[0].memsize);
+		fuse_buf_alloc(&msgp->ipdata.iovbuf[0], size);
+	}
+}
+
+/* Frees the memory allocated for data passed from the daemon */
+static void
+fuse_free_opdata(fuse_msg_node_t *msgp)
+{
+	if (msgp->opdata.iovbuf.base &&
+	    msgp->opdata.iovbuf.memflag == MEM_TYPE_KMEM) {
+		kmem_free(msgp->opdata.iovbuf.base,
+		    msgp->opdata.iovbuf.memsize);
+		if (msgp->opdata.fouth) {
+			kmem_free(msgp->opdata.fouth,
+			    sizeof (*(msgp->opdata.fouth)));
+		}
+		bzero(&(msgp->opdata), sizeof (msgp->opdata));
+	}
+}
+
+/* Function which refreshes a previously used message with new values */
+static void
+fuse_msg_recreate(fuse_msg_node_t *msgp, size_t argsize, enum fuse_opcode op,
+    uint64_t nodeid, cred_t *credp, uint64_t unique, int forcefree)
+{
+	/* Free the buffer used to receive data from fuse lib */
+	fuse_free_opdata(msgp);
+
+	fuse_msg_refresh(msgp, argsize, forcefree);
+
+	fuse_setup_msgparams(msgp, argsize, op, nodeid, credp, unique);
+}
+
+/* Function which creates a message with header and arguments */
+fuse_msg_node_t *
+fuse_setup_message(size_t argsize, enum fuse_opcode op,
+    uint64_t nodeid, cred_t *credp, uint64_t unique)
+{
+	fuse_msg_node_t *msgp = fuse_alloc_msg();
+
+	msgp->ipdata.iovs_used = 1;
+	fuse_buf_alloc(&(msgp->ipdata.iovbuf[0]),
+	    (sizeof (struct fuse_in_header) + argsize));
+	fuse_setup_msgparams(msgp, argsize, op, nodeid, credp, unique);
+
+	return (msgp);
+}
+
+/* This function is used from FreeBSD Fuse */
+static int
+checkentry(struct fuse_entry_out *feo, enum vtype vtyp)
+{
+	if (vtyp != IFTOVT(feo->attr.mode) ||
+	    feo->nodeid == FUSE_NULL_ID ||
+	    feo->nodeid == FUSE_ROOT_ID) {
+		DTRACE_PROBE2(checkentry_err_nodeid,
+		    char *, "Invalid node entry, dropped",
+		    struct fuse_entry_out *, feo);
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
+#if CHECK_VDATA
+/*
+ *	Check availability and consistency of v_data
+ *	(meant for debugging)
+ */
+
+static fuse_vnode_data_t *check_vdata(struct vnode *vp, int line)
+{
+	fuse_avl_cache_node_t tofind, *foundp;
+	fuse_session_t *sep;
+	fuse_vnode_data_t *v_data;
+
+	v_data = vp->v_data;
+	if (v_data && (v_data->nodeid != FUSE_ROOT_ID)) {
+		sep = fuse_minor_get_session(getminor(vp->v_rdev));
+		mutex_enter(&sep->avl_mutx);
+		if (v_data->nodeid != FUSE_NULL_ID) {
+			/* the nodeid is recorded */
+			tofind.facn_nodeid = v_data->nodeid;
+			foundp = avl_find(&sep->avl_cache_i, &tofind, NULL);
+		} else {
+			/* search in full list, hopefully never needed */
+			foundp = avl_first(&sep->avl_cache_i);
+			while (foundp && (foundp->facn_vnode_p != vp))
+				foundp = AVL_NEXT(&sep->avl_cache_i, foundp);
+		}
+		mutex_exit(&sep->avl_mutx);
+		if (!foundp || (foundp->facn_vnode_p != vp)) {
+			cmn_err(CE_WARN,"Bad vnode record for nodeid %lld"
+				" at line %d\n",
+				(long long)v_data->nodeid, line);
+			v_data = (fuse_vnode_data_t*)NULL;
+		}
+	}
+	/* When v_data is null, caller is expected to log and take measures */
+	return (v_data);
+}
+
+#define GET_VDATA(vp) check_vdata((vp), __LINE__)
+#else /* CHECK_VDATA */
+#define GET_VDATA(vp) ((vp)->v_data)
+#endif /* CHECK_VDATA */
+
+static void
+release_create_data(struct fuse_create_data *fcd)
+{
+	if (fcd->name)
+		kmem_free(fcd->name, fcd->namelen);
+	kmem_free(fcd, sizeof (*fcd));
+}
+
+static int
+create_filehandle(struct vnode *vp, int flag, cred_t *credp,
+    fuse_msg_node_t **msgpp)
+{
+	struct fuse_vnode_data	*fvdata = VTOFD(vp);
+	struct fuse_open_in	*foi;
+	struct fuse_entry_out	*feo;
+	struct fuse_mknod_in	*fmni;
+	int			err = 0;
+	fuse_msg_node_t		*msgp;
+	fuse_session_t		*sep;
+	fuse_avl_cache_node_t	*cache_nodep;
+	int			sent_mknod = 0;
+	fuse_avl_cache_node_t 	tofind, *foundp;
+	avl_index_t		where;
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(create_filehandle_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	msgp = fuse_setup_message((sizeof (*foi) + fvdata->fcd->namelen + 1),
+	    FUSE_CREATE, fvdata->par_nid, credp, FUSE_GET_UNIQUE(sep));
+
+	/* Set up arguments to the fuse library */
+	foi = (struct fuse_open_in *)msgp->ipdata.indata;
+
+	foi->flags = F_TO_O_FLAGS(flag & ~O_NOCTTY);
+	foi->mode = fvdata->fcd->mode;
+	(void *) strlcpy(((char *)msgp->ipdata.indata + sizeof (*foi)),
+	    fvdata->fcd->name, fvdata->fcd->namelen);
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto cleanup;
+	}
+
+	if (msgp->opdata.fouth->error == ENOSYS) {
+		DTRACE_PROBE2(create_filehandle_info_enosys,
+		    char *, "FUSE_CREATE not supported by filesys",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto sendmknod;
+	}
+
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(create_filehandle_err_create_req,
+		    char *, "FUSE_CREATE request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+
+	/* Obtained response from fuse library so interpret it */
+	goto resp_intrprt;
+
+sendmknod:
+	sent_mknod = 1;
+	/*
+	 * Try avoiding freeing and allocating memory. Do it only if
+	 * inevitable
+	 */
+	fuse_msg_recreate(msgp, (sizeof (*fmni) + fvdata->fcd->namelen + 1),
+	    FUSE_MKNOD, fvdata->par_nid, fvdata->fcd->credp,
+	    FUSE_GET_UNIQUE(sep), 0);
+
+	/* Set up arguments to the fuse library */
+	fmni = (struct fuse_mknod_in *)msgp->ipdata.indata;
+
+	fmni->mode = fvdata->fcd->mode;
+	fmni->rdev = 0;
+	(void *) strlcpy((char *)msgp->ipdata.indata + sizeof (*foi),
+	    fvdata->fcd->name, fvdata->fcd->namelen);
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto cleanup;
+	}
+
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(create_filehandle_err_mknod_req,
+		    char *, "FUSE_MKNOD request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+
+resp_intrprt:
+	feo = (struct fuse_entry_out *)msgp->opdata.outdata;
+
+	if ((err = checkentry(feo, VREG))) {
+		goto cleanup;
+	}
+
+	/* Check if there will be a collision? */
+	tofind.facn_nodeid = feo->nodeid;
+	do {
+		mutex_enter(&sep->avl_mutx);
+		foundp = avl_find(&(sep->avl_cache_i), &tofind, &where);
+		if (foundp) {
+			mutex_exit(&sep->avl_mutx);
+			DTRACE_PROBE2(create_filehandle_err_collision,
+			    char *, "Node already in cache",
+			    struct fuse_entry_out *, feo);
+			cmn_err(CE_WARN, "Vnode collision on id %lld\n",
+				(long long)tofind.facn_nodeid);
+			/*
+			 * We have the unhappy situation of forcing a purge
+			 * on the existing vnode.
+			 * Better have avl_mutx released, so have to retry.
+			 */
+			fuse_vnode_destroy(foundp->facn_vnode_p, credp, sep);
+		}
+	} while (foundp);
+	/* Add the vnode to the avl tree */
+	cache_nodep = fuse_avl_cache_node_create(vp, feo->nodeid,
+	    fvdata->fcd->par_nodeid, fvdata->fcd->namelen, fvdata->fcd->name);
+	avl_add(&(sep->avl_cache_i), cache_nodep);
+	avl_add(&(sep->avl_cache_n), cache_nodep);
+	mutex_exit(&sep->avl_mutx);
+
+	release_create_data(fvdata->fcd);
+	fvdata->fcd = NULL;
+	bzero(&fvdata->fh, sizeof (fvdata->fh));
+
+	/* Reinitialize vnode related fields */
+	fvdata->nodeid = feo->nodeid;
+	fvdata->nlookup++;
+	vn_setops(vp, dv_vnodeops);
+#ifndef DONT_CACHE_ATTRIBUTES
+	cache_attrs(sep, vp, feo);
+#endif
+
+	/*
+	 * if we sent FUSE_CREATE request to library, then we don't need to
+	 * do FUSE_OPEN, but if we got a response for FUSE_MKNOD, then we
+	 * need to send FUSE_OPEN which is taken care by the caller of this
+	 * function
+	 */
+	if (!sent_mknod)
+		msgp->opdata.outdata = feo + 1;
+	else
+		msgp->opdata.outdata = NULL;
+
+	*msgpp = msgp;
+	return (err);
+cleanup:
+	if (fvdata->fcd)
+		release_create_data(fvdata->fcd);
+	fvdata->fcd = NULL;
+	fuse_free_msg(msgp);
+	*msgpp = NULL;
+	return (err);
+}
+
+typedef int fuse_file_check_t(struct fuse_file_handle *fhp,
+    struct fuse_fh_param *fh_param);
+
+/* Function which releases a cached file handle */
+static int
+fuse_release_fh(struct fuse_file_handle *fhp, struct fuse_fh_param *param)
+{
+	struct vnode *vp = param->vp;
+	fuse_vnode_data_t *fvdatap = VTOFD(vp);
+	enum fuse_opcode op = (vp->v_type == VDIR) ?
+	    FUSE_RELEASEDIR : FUSE_RELEASE;
+	fuse_session_t *sep;
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_release_fh_info_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+	}
+
+	if ((param->flag & FUSE_FORCE_FH_RELEASE) || fhp->ref == 0) {
+		DTRACE_PROBE2(fuse_release_fh_info,
+		    char *, "file handle released.",
+		    struct fuse_file_handle *, fhp);
+		list_remove(&fvdatap->fh_list, fhp);
+		if (sep != NULL) {
+			(void) fuse_send_release(sep, fhp, op, vp, fhp->mode);
+		}
+		kmem_free(fhp, sizeof (*fhp));
+	}
+	/*
+	 * This function is always called from iterate_filehandle, and if
+	 * we return anything other than zero, iterate_filhandle will not scan
+	 * the complete list, so we always return zero from this function.
+	 */
+	return (0);
+}
+
+/*
+ * Function does a check if the filehandle passed as parameter is the one needed
+ * by the current request
+ */
+static int
+fuse_std_filecheck(struct fuse_file_handle *fhp, struct fuse_fh_param *param)
+{
+		/*
+		 * The process id is not relevant, because a file
+		 * descriptor may be inherited (this is common for
+		 * for shell scripts or files being executed).
+		 */
+	if ((param->rw_mode & fhp->mode
+			 & (FREAD | FWRITE | FAPPEND | FCREAT | FSEARCH)) &&
+	    crgetuid(param->credp) == crgetuid(fhp->credp) &&
+	    crgetgid(param->credp) == crgetgid(fhp->credp) &&
+	    (param->nodeid == fhp->nodeid)) {
+		fhp->ref++;
+		DTRACE_PROBE2(fuse_std_filecheck_info_found,
+		    char *, "Found file handle in cache list.",
+		    struct fuse_file_handle *, fhp);
+		return (1);
+	}
+	return (0);
+}
+
+/*
+ * Iterates through the list of file handles and invokes the passed function
+ * pointer
+ */
+static int
+iterate_filehandle(struct vnode *vp, fuse_file_check_t file_check_fp,
+    void *param, struct fuse_file_handle **fufhpp)
+{
+	int ret = 0;
+	struct fuse_file_handle *fh, *next_fh;
+	struct fuse_vnode_data *fvdatap = VTOFD(vp);
+
+	mutex_enter(&fvdatap->fh_list_lock);
+	if (VNODE_TO_NODEID(vp) != FUSE_NULL_ID) {
+		for (fh = list_head(&fvdatap->fh_list); fh; fh = next_fh) {
+			next_fh = list_next(&fvdatap->fh_list, fh);
+			if ((ret = file_check_fp(fh, param))) {
+				*fufhpp = fh;
+				break;
+			}
+		}
+	}
+	mutex_exit(&fvdatap->fh_list_lock);
+	return (ret);
+}
+
+static int
+get_filehandle(struct vnode *vp, int flag, struct cred *credp,
+    struct fuse_file_handle **fufhpp, int check_cache)
+{
+	struct fuse_open_in	*foin;
+	struct fuse_open_out	*foout;
+	struct fuse_file_handle *fhp;
+	int 			err = 0;
+	fuse_msg_node_t		*msgp = NULL;
+	fuse_session_t		*sep;
+	struct fuse_fh_param	fh_param;
+	fuse_vnode_data_t 	*fvdatap;
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(get_filehandle_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	/* Check if it is due to create + open */
+	if (vn_matchops(vp, temp_vnodeops)) {
+		if ((err = create_filehandle(vp, flag, credp, &msgp))) {
+			DTRACE_PROBE3(get_filehandle_err_create,
+			    char *, "create_filehandle request failed",
+			    int, err, struct vnode *, vp);
+			/* release the vnode, if creation failed */
+			fuse_free_vdata(vp);
+			vp->v_data = NULL;
+			VFS_RELE(vp->v_vfsp);
+			vn_free(vp);
+			goto out;
+		} else if (msgp && msgp->opdata.outdata) {
+			DTRACE_PROBE2(get_filehandle_info_create_ok,
+			    char *, "create_filehandle request successful",
+			    struct vnode *, vp);
+			goto resp_intrprt;
+		}
+	}
+	if ((check_cache & CACHE_LIST_CHECK)) {
+		if (!GET_VDATA(vp)) {
+			/*
+			 * Very bad : do not panic.
+			 */
+			err = ENODEV;
+			goto out;
+		}
+		/*
+		 * Check if we already have retrieved the file handle
+		 * from user space before
+		 */
+		fh_param.credp = credp;
+		fh_param.rw_mode = flag;
+		fh_param.nodeid = VNODE_TO_NODEID(vp);
+		fh_param.fufh = NULL;
+		if (iterate_filehandle(vp, fuse_std_filecheck, &fh_param,
+		    fufhpp))
+			goto out;
+		if (check_cache & CACHE_CHECK_ONLY) {
+			err = ENOENT;
+			goto out;
+		}
+	}
+	/*
+	 * Allocate a new message node only if create_filehandle wasn't able
+	 * to do it for us, if not reuse memory
+	 */
+
+	if (!msgp)
+		msgp = fuse_setup_message(sizeof (*foin),
+		    (vp->v_type == VDIR? FUSE_OPENDIR: FUSE_OPEN),
+		    VNODE_TO_NODEID(vp), credp, FUSE_GET_UNIQUE(sep));
+	else
+		fuse_msg_recreate(msgp, sizeof (*foin),
+		    (vp->v_type == VDIR? FUSE_OPENDIR: FUSE_OPEN),
+		    VNODE_TO_NODEID(vp), credp, FUSE_GET_UNIQUE(sep), 0);
+
+
+	/* Set up arguments to the fuse library */
+	foin = (struct fuse_open_in *)msgp->ipdata.indata;
+	foin->flags = F_TO_O_FLAGS(flag & ~(O_CREAT | O_EXCL | O_NOCTTY |
+	    O_TRUNC));
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto cleanup;
+	}
+
+	/* We got woken up, so fuse library has replied to our OPEN request */
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(get_filehandle_err_open_req,
+		    char *, "FUSE_OPEN request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+
+	if (msgp->opdata.outsize != sizeof (struct fuse_open_out)) {
+		DTRACE_PROBE2(get_filehandle_err_open_inval_resp,
+		    char *, "FUSE_OPEN response invalid size",
+		    size_t, msgp->opdata.outsize);
+		err = EINVAL;
+		goto cleanup;
+	}
+resp_intrprt:
+	foout = (struct fuse_open_out *)msgp->opdata.outdata;
+	fvdatap = VTOFD(vp);
+	fhp = kmem_alloc(sizeof (struct fuse_file_handle), KM_SLEEP);
+
+	fhp->fh_id = foout->fh;
+	fhp->flags = foout->open_flags;
+
+	fhp->mode = flag & ~(O_EXCL | O_NOCTTY | O_TRUNC);
+	crhold(credp);
+	fhp->credp = credp;
+	fhp->ref = 1;
+	fhp->process_id = curproc->p_pidp->pid_id;
+	fhp->nodeid = fvdatap->nodeid;
+
+	if (fufhpp)
+		*fufhpp = fhp;
+
+	/* Add it to the list */
+	DTRACE_PROBE2(get_filehandle_info_insert,
+	    char *, "file handle added to list",
+	    struct fuse_file_handle *, fhp);
+	mutex_enter(&fvdatap->fh_list_lock);
+	list_insert_head(&fvdatap->fh_list, fhp);
+	mutex_exit(&fvdatap->fh_list_lock);
+
+cleanup:
+	fuse_free_msg(msgp);
+out:
+	return (err);
+}
+
+/* ARGSUSED */
+static int fuse_open(struct vnode **vpp, int flag, struct cred *cred_p,
+    caller_context_t *ct)
+{
+	/*
+	 * Send the fuse_OPEN req to lib and get the fh from there.
+	 * alloc filehandle and associate with vnode private operation.
+	 */
+	struct vnode *vp = *vpp;
+	struct fuse_file_handle *fhp;
+	int mode;
+	int err = 0;
+
+	/* Bounce the openings of special devices */
+	if (vp) {
+		vnode_t	*svp;
+
+		switch (vp->v_type) {
+		case VBLK :
+		case VCHR :
+		case VFIFO :
+		case VSOCK :
+			svp = specvp(vp, vp->v_rdev, vp->v_type, cred_p);
+			VN_RELE(vp);
+			if (svp == NULL)
+				err = ENOSYS;
+			else
+				*vpp = svp;
+			if (!err)
+				err = VOP_OPEN(vpp, flag, cred_p, ct);
+			return (err);
+		default :
+			break;
+		}
+	}
+
+	/*
+	 * Not checking the cache is a problem for files opened twice as
+	 * a fuse_file_handle cannot be associated to an opening.
+	 * Checking the cache hides multiple openings in the same mode.
+	 * The latter is probably better.
+	 */
+	if (vp && vp->v_data && (VNODE_TO_NODEID(vp) != FUSE_NULL_ID))
+		mode = CACHE_LIST_CHECK;
+	else
+		mode = CACHE_LIST_NO_CHECK;
+	if ((err = get_filehandle(vp, flag, cred_p, &fhp, mode))) {
+		DTRACE_PROBE2(fuse_open_err_filehandle,
+		    char *, "get_filehandle failed",
+		    struct vnode *, vp);
+		goto out;
+	}
+
+	if ((flag & (FWRITE | FTRUNC | FAPPEND)) == (FWRITE | FTRUNC)) {
+		vattr_t va;
+		int err;
+
+		va.va_mask = AT_SIZE;
+		va.va_size = 0;
+		err = VOP_SETATTR(vp, &va, 0, cred_p, ct);
+	}
+
+	/*
+	 * TBD: Check can we do any optimization here? FreeBSD Fuse invokes
+	 * vnode_create_vobject to avoid as it says needless getattr...
+	 * Anything that can be done for Solaris?
+	 */
+out:
+	return (err);
+}
+
+static int
+fuse_send_release(fuse_session_t *sep, struct fuse_file_handle *fh,
+    enum fuse_opcode op, struct vnode *vp, int flags)
+{
+	struct fuse_release_in *fri;
+	fuse_msg_node_t *msgp;
+	int err = DDI_SUCCESS;
+
+	msgp = fuse_setup_message(sizeof (*fri), op, VNODE_TO_NODEID(vp),
+	    sep->usercred, FUSE_GET_UNIQUE(sep));
+
+	/* Set up arguments to the fuse library */
+	fri = (struct fuse_release_in *)msgp->ipdata.indata;
+	fri->fh	= fh->fh_id;
+	fri->flags = F_TO_O_FLAGS(flags & ~O_EXCL);
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto cleanup;
+	}
+
+	/* We got woken up, so fuse lib has replied to our release request */
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(fuse_send_release_err_release_req,
+		    char *, "FUSE_RELEASE request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+
+cleanup:
+	fuse_free_msg(msgp);
+	return (err);
+}
+
+/*
+ *		Discard a file name
+ *
+ * If the file was open, and it does not have any names left,
+ * the file will be deleted later, when it is not open any more.
+ * Just remove its name from the cache, so that the cached entry
+ * will be deleted when the file is closed.
+ */
+
+static int fuse_discard_name(struct vnode *vp, fuse_session_t *sep)
+{
+	fuse_avl_cache_node_t find, *unnamep;
+
+	if ((vp->v_type != VREG)
+	    || !vn_is_opened(vp,V_RDORWR)) {
+		VN_RELE(vp);
+		fuse_vnode_free(vp, sep);
+	} else {
+		find.facn_nodeid = VNODE_TO_NODEID(vp);
+		mutex_enter(&sep->avl_mutx);
+		unnamep = avl_find(&(sep->avl_cache_i), &find, NULL);
+		if (unnamep) {
+			char *oldname = unnamep->name;
+			int oldnamelen = unnamep->namelen;
+
+			avl_remove(&(sep->avl_cache_i), unnamep);
+			avl_remove(&(sep->avl_cache_n), unnamep);
+			unnamep->name = "";
+			unnamep->namelen = 1;
+			/* Do not insert into tree by name */
+			avl_add(&(sep->avl_cache_i), unnamep);
+			mutex_exit(&sep->avl_mutx);
+			kmem_free(oldname, oldnamelen);
+		} else
+			mutex_exit(&sep->avl_mutx);
+		VN_RELE(vp);
+	}
+	return (0);
+}
+
+/*
+ *		Flush any unsent data not sent to putpage()
+ *
+ *	This is required at close(), truncate() and utime()
+ */
+
+static int flush_unsent_data(struct vnode *vp, struct cred *credp,
+					caller_context_t *ct)
+{
+	fuse_vnode_data_t *vdata;
+	int err;
+
+	err = 0;
+	vdata = (fuse_vnode_data_t*)vp->v_data;
+	if (vdata && (vdata->file_size_status & FSIZE_UNSENT)) {
+		vdata->file_size_status &= ~FSIZE_UNSENT;
+		err = VOP_PUTPAGE(vp, vdata->offset,
+			(size_t)0, 0, credp, ct);
+	}
+	return (err);
+}
+
+/* ARGSUSED */
+static int fuse_close(struct vnode *vp, int flags, int count,
+    offset_t offset, struct cred *credp, caller_context_t *ct)
+{
+	int err = DDI_SUCCESS;
+	fuse_vnode_data_t *fvdatap = VTOFD(vp);
+	struct fuse_file_handle *fhp = NULL;
+	fuse_session_t *sep;
+	enum fuse_opcode op = (vp->v_type == VDIR) ?
+	    FUSE_RELEASEDIR : FUSE_RELEASE;
+
+		/* First, send unsent data */
+	if (vp && GET_VDATA(vp)) {
+		err = flush_unsent_data(vp, credp, ct);
+		if (err)
+			return (err);
+	}
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_close_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	/*
+	 * JPA Only check within the cache, if not referenced in the
+	 * cache, there is nothing to do.
+	 * This happens in obscure circumstances related to "rm -rf".
+	 * After "mkdir -p", the created directory is checked with FSEARCH
+	 * (mode 0x222880), so FSEARCH is also needed to find and release
+	 * the directory.
+	 */
+	if ((err = get_filehandle(vp,
+				(vp->v_type == VDIR ? flags | FSEARCH : flags),
+				credp, &fhp,
+				CACHE_LIST_CHECK | CACHE_CHECK_ONLY))) {
+		DTRACE_PROBE2(fuse_close_err_filehandle,
+		    char *, "get_filehandle failed",
+		    struct vnode *, vp);
+		if (err == ENOENT)
+			err = 0;
+		goto cleanup;
+	}
+		/*
+		 * As we have made a new reference in get_filehandle(),
+		 * we have to decrement by 2 for a real close().
+		 * However there may be several close() for a single open(),
+		 * so when the count argument is not 1, this is not the
+		 * last close(), and we should only decrement by 1.
+		 * There are no known multiple close() for a directory,
+		 * though the count may be 2 (happens on a "rm -f")
+		 */
+	fhp->ref -= ((count > 1) && (vp->v_type != VDIR) ? 1 : 2);
+	if (fhp->ref <= 0) {
+		/* Remove it from the list */
+		DTRACE_PROBE2(fuse_close_info_release,
+		    char *, "releasing file handle",
+		    struct fuse_file_handle *, fhp);
+
+		mutex_enter(&fvdatap->fh_list_lock);
+		list_remove(&fvdatap->fh_list, fhp);
+		mutex_exit(&fvdatap->fh_list_lock);
+
+		err = fuse_send_release(sep, fhp, op, vp, flags);
+		kmem_free(fhp, sizeof (*fhp));
+	}
+	/*
+	 *	If we are ending the last write, invalide the cached data
+	 *	so that the next read will be fetched from user space,
+	 *	thus updating the access time stamp.
+	 *	Do not interfere with a cache with concurrent writes.
+	 *	The cached attributes are still valid until next read.
+	 */
+	if ((vp->v_type == VREG)
+	    && (flags & FWRITE)
+	    && !vn_has_other_opens(vp,V_WRITE)
+	    && vn_has_cached_data(vp)) {
+		pvn_vplist_dirty(vp, 0, fuse_putapage, B_INVAL, credp);
+	}
+	/*
+	 * If the file is not open any more (is this the same as getting
+	 * a ref count of 0 above ?), check whether deleting the file
+	 * has been requested while it was open.
+	 * If so, release the file so that it becomes inactive and
+	 * can be deleted.
+	 */
+	if ((vp->v_type == VREG)
+	    && !vn_has_other_opens(vp,V_RDORWR)
+	    && ((flags & (FREAD + FWRITE)) == (FREAD + FWRITE)
+			/* both rd and wr */
+		? vn_is_opened(vp,V_RDANDWR)
+			/* either rd or wr, but not both */
+		: vn_is_opened(vp,V_RDORWR)
+		    && !vn_is_opened(vp,V_RDANDWR))) {
+		fuse_avl_cache_node_t find, *closep;
+
+		find.facn_nodeid = VNODE_TO_NODEID(vp);
+		mutex_enter(&sep->avl_mutx);
+		closep = avl_find(&(sep->avl_cache_i), &find, NULL);
+		mutex_exit(&sep->avl_mutx);
+		if (closep && (closep->namelen < 2)) {
+			VN_RELE(vp);
+		}
+	}
+cleanup:
+	return (err);
+}
+
+/* ARGSUSED */
+static int
+fuse_getapage(struct vnode *vp, u_offset_t off, size_t len, uint_t *protp,
+    page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
+    enum seg_rw rw, struct cred *credp)
+{
+	struct page *pp;
+	fuse_msg_node_t *msgp = NULL;
+	int err = 0;
+	struct fuse_read_in *fri;
+	fuse_session_t *sep;
+	struct fuse_file_handle *fhp = NULL;
+	int mode = FREAD;
+	struct buf *bp = NULL;
+
+	if (protp != NULL)
+		*protp = PROT_ALL;
+
+	if (pl == NULL)
+		return (0);
+
+	pl[0] = NULL;
+
+again:
+	if ((pp = page_lookup(vp, off, rw == S_CREATE ? SE_EXCL : SE_SHARED))) {
+		DTRACE_PROBE2(fuse_getapage_info_found,
+		    char *, "page found",
+		    struct page *, pp);
+		pl[0] = pp;
+		pl[1] = NULL;
+		return (err);
+	} else {
+		pp = page_create_va(vp, off, PAGESIZE, PG_WAIT,
+		    seg, addr);
+		/*
+		 * Someone raced in and created the page after we did the
+		 * lookup but before we did the create, so go back and
+		 * try to look it up again.
+		 */
+		if (pp == NULL) {
+			DTRACE_PROBE1(fuse_getapage_err_create_failed,
+			    char *, "page_create_va failed, try again");
+			goto again;
+		}
+
+		if (!GET_VDATA(vp)) {
+			cmn_err(CE_CONT,"Abnormal condition in fuse_getapage"
+				" off 0x%lx len %ld\n", (long)off,(long)len);
+			err = EIO;
+			goto cleanup;
+		}
+		DTRACE_PROBE2(fuse_getapage_info_create,
+		    char *, "page_create_va successful",
+		    struct page *, pp);
+
+		sep = fuse_minor_get_session(getminor(vp->v_rdev));
+		if (sep == NULL) {
+			DTRACE_PROBE2(fuse_getapage_err_session,
+			    char *, "failed to find session",
+			    struct vnode *, vp);
+			err = ENODEV;
+			goto cleanup;
+		}
+
+		msgp = fuse_setup_message(sizeof (*fri),
+		    (vp->v_type == VDIR) ? FUSE_READDIR : FUSE_READ,
+		    VNODE_TO_NODEID(vp), credp, FUSE_GET_UNIQUE(sep));
+
+		fri = (struct fuse_read_in *)msgp->ipdata.indata;
+
+		/*
+		 * Obtain the file handle associated with this request
+		 * if it has already been retrieved from userspace then we
+		 * should find it in our file handle list
+		 */
+		if ((err = get_filehandle(vp, mode, credp, &fhp,
+		    CACHE_LIST_CHECK))) {
+			DTRACE_PROBE2(fuse_getapage_err_filehandle,
+			    char *, "get_filehandle failed",
+			    struct vnode *, vp);
+			goto cleanup;
+		}
+
+		fri->fh = fhp->fh_id;
+		fri->offset = off;
+		fri->size = (uint32_t)PAGESIZE;
+		/* Map the page so that uio_move can be done on the page */
+		fuse_page_mapin(vp, &bp, pp, len, B_READ,
+		    &(msgp->opdata.iovbuf));
+
+		/*
+		 * queue the message for sending to userland filesystem
+		 * framework
+		 */
+		if ((err = fuse_queue_request_wait(sep, msgp))) {
+			goto cleanup;
+		}
+		/*
+		 * If all has gone well we should have got the data in the page
+		 * by this time already
+		 */
+		if ((err = msgp->opdata.fouth->error) != 0) {
+			DTRACE_PROBE2(fuse_getapage_err_read_req,
+			    char *, "FUSE_READ request failed",
+			    struct fuse_out_header *, msgp->opdata.fouth);
+			goto cleanup;
+		}
+
+#ifndef DONT_CACHE_ATTRIBUTES
+		/*
+		 *	After a successful read, the access time may
+		 *	have been modified, so invalidate the cached
+		 *	attributes.
+		 */
+		invalidate_cached_attrs(vp);
+#endif
+
+		/*
+		 * XXX: Is this required as this might have already been
+		 * done earlier?
+		 */
+		/* If the returned data is less than what was asked for */
+		if (msgp->opdata.outsize < PAGESIZE) {
+			/* Zero out the rest of the page */
+			bzero((void *)((char *)msgp->opdata.iovbuf.base +
+			    msgp->opdata.outsize),
+			    (PAGESIZE - msgp->opdata.outsize));
+		}
+
+	cleanup:
+		if (bp != NULL) {
+			bp_mapout(bp);
+			pageio_done(bp);
+		}
+
+		if (err && pp) {
+			pvn_read_done(pp, B_ERROR);
+		}
+
+		if (err == 0) {
+			pvn_plist_init(pp, pl, plsz, off, PAGESIZE, rw);
+		}
+
+		if (fhp)
+			fhp->ref--;
+
+		if (msgp)
+			fuse_free_msg(msgp);
+
+	}
+
+	return (err);
+}
+
+
+/* ARGSUSED */
+static int
+fuse_getpage(struct vnode *vp, offset_t off, size_t len, uint_t *protp,
+    page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
+    enum seg_rw rw, struct cred *credp, caller_context_t *ct)
+{
+
+	int err;
+
+	if (len > PAGESIZE) {
+		err = pvn_getpages(fuse_getapage, vp, off, len, protp, pl, plsz,
+		    seg, addr, rw, credp);
+	} else {
+		err = fuse_getapage(vp, off, len, protp, pl, plsz, seg, addr,
+		    rw, credp);
+	}
+
+	return (err);
+}
+
+
+/*
+ * Read of devices (leaf nodes) is handled by specfs.
+ * Read of directories is not supported.
+ */
+/* ARGSUSED */
+static int
+fuse_read(struct vnode *vp, struct uio *uiop, int ioflag,
+    struct cred *credp, struct caller_context *ct)
+{
+	return (rdfuse(vp, uiop, credp));
+
+}
+
+/*
+ * This routine performs the actual read operation from the vnode. As a
+ * side effect of the call made here, getpage routine gets
+ * invoked which obtains the required data from the daemon.
+ */
+static int
+rdfuse(struct vnode *vp, struct uio *uiop, struct cred *credp)
+{
+	int err;
+	ulong_t req_len = uiop->uio_resid;
+	ulong_t pageoffset;
+	ulong_t segmap_offset;
+	offset_t off;
+	ssize_t len;
+	caddr_t base = 0;
+	offset_t diff;
+	u_offset_t fsize;
+
+	/* Do consistency checks */
+	if (uiop->uio_loffset < 0) {
+		DTRACE_PROBE2(rdfuse_err_offset,
+		    char *, "uio_loffset < 0",
+		    struct uio *, uiop);
+		return (EINVAL);
+	}
+
+	if (uiop->uio_resid == 0 ||
+	    uiop->uio_loffset > MAXOFF_T) {
+		DTRACE_PROBE2(rdfuse_info_offset,
+		    char *, "uio_resid == 0 || uio_loffset > MAXOFF_T",
+		    struct uio *, uiop);
+		return (0);
+	}
+
+	if ((err = fuse_getfilesize(vp, &fsize, credp))) {
+		DTRACE_PROBE3(rdfuse_err_filesize,
+		    char *, "fuse_getfilesize failed",
+		    int, err, struct vnode *, vp);
+		return (err);
+	}
+	do {
+		/* Calculate offset within a page that must be read */
+		off = uiop->uio_loffset;
+		pageoffset = off & PAGEOFFSET;
+		len = MIN(PAGESIZE - pageoffset, (size_t)uiop->uio_resid);
+
+		/* Check if we are crossing end of file */
+		diff = fsize - off;
+
+		if (diff <= 0) {
+			err = 0;
+			break;
+		}
+
+		/* Adjust the length to be read if necessary */
+		if (diff < len)
+			len = (ssize_t)diff;
+
+		if (vpm_enable) {
+			err = vpm_data_copy(vp, off, len, uiop, 1,
+			    NULL, 0, S_READ);
+		} else {
+			segmap_offset = (off & PAGEMASK) & MAXBOFFSET;
+			base = segmap_getmapflt(segkmap, vp, off & MAXBMASK,
+			    len, 1, S_READ);
+
+			err = uiomove(base + segmap_offset + pageoffset,
+			    (long)len, UIO_READ, uiop);
+		}
+
+		if (vpm_enable)
+			(void) vpm_sync_pages(vp, off, len, 0);
+		else
+			(void) segmap_release(segkmap, base, 0);
+	} while (err == 0 && uiop->uio_resid > 0);
+
+	if ((ulong_t)uiop->uio_resid != req_len)
+		err = 0;
+
+	return (err);
+}
+
+static inline void
+fsize_change_notify(struct vnode *vp, size_t fsize, int flag)
+{
+	VTOFD(vp)->fsize = fsize;
+	VTOFD(vp)->file_size_status = flag;
+}
+
+/* Main routine which does the actual write operation */
+static int
+wrfuse(struct vnode *vp, struct uio *uiop, int ioflag,
+    struct cred *credp, caller_context_t *ct)
+{
+	u_offset_t uoff;
+	long	pageoff;	/* offset within a page */
+	caddr_t base = 0;		/* base of segmap */
+	ssize_t bytes;
+	int err;
+	offset_t unsent_offset; /* offset of a single unsent page */
+	fuse_vnode_data_t *vdata;
+	int pagecreate, newpage;
+	ssize_t premove_resid;
+	uint_t flags;
+	ulong_t segmap_offset;
+	rlim64_t limit = uiop->uio_llimit;
+	int file_size_change = 0;
+	long start_resid = uiop->uio_resid;	/* save starting resid */
+	u_offset_t start_off = uiop->uio_offset; /* save starting offset */
+	u_offset_t fsize;
+
+	if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
+		limit = MAXOFFSET_T;
+
+	unsent_offset = start_off & PAGEMASK;
+		/*
+		 * If there is unsent data in a page not covered by
+		 * the current write, sent it first.
+		 * Maybe this is not needed (depends on the meaning
+		 * of offset in putpage()), anyway it is safer.
+		 */
+	vdata = GET_VDATA(vp);
+	if (vdata && (vdata->file_size_status & FSIZE_UNSENT)) {
+		if ((vdata->offset < (offset_t)(uiop->uio_loffset & PAGEMASK))
+		   || (vdata->offset
+			> (offset_t)((uiop->uio_loffset + uiop->uio_resid - 1)
+					& PAGEMASK)))
+			flush_unsent_data(vp, credp, ct);
+		vdata->file_size_status &= ~FSIZE_UNSENT;
+	}
+	if ((rlim64_t)uiop->uio_loffset >= limit) {
+		proc_t *p = ttoproc(curthread);
+
+		mutex_enter(&p->p_lock);
+#ifdef RCA_UNSAFE
+			/* Solaris */
+		(void) rctl_action(RCHD_PROC_LGC_FSIZE, p, RCA_UNSAFE);
+#else
+			/* OpenIndiana */
+		(void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls,
+		    p, RCA_UNSAFE_SIGINFO);
+#endif
+		mutex_exit(&p->p_lock);
+		return (EFBIG);
+	}
+	if (!GET_VDATA(vp))
+		return (EIO);
+	if ((err = fuse_getfilesize(vp, &fsize, credp))) {
+		DTRACE_PROBE3(wrfuse_err_filesize,
+		    char *, "fuse_getfilesize failed",
+		    int, err, struct vnode *, vp);
+		return (err);
+	}
+
+	if (((ioflag & FAPPEND) != 0) && (vp->v_type == VREG)) {
+		uiop->uio_loffset = fsize;
+	}
+
+	/*
+	 * XXX If we later decide to support DIRECT_IO, its logic should
+	 * go here
+	 */
+
+	do {
+		uoff = uiop->uio_offset;
+		pageoff = uoff & (u_offset_t)PAGEOFFSET;
+		bytes = MIN(PAGESIZE - pageoff, (size_t)uiop->uio_resid);
+
+		if (uoff + bytes >= limit) {
+			if (uoff >= limit) {
+				err = EFBIG;
+				goto out;
+			}
+			bytes = limit - uoff;
+		}
+		/*
+		 * When is it not necessary to read in old page?
+		 * Under following cases:
+		 * 1. When we are writing one full page with page offset zero.
+		 * 2. When the file is yet to be written to or of zero size.
+		 * 3. When we are extending the file from a new page
+		 */
+		pagecreate = (bytes == (ssize_t)PAGESIZE) | (fsize == 0) |
+		    ((pageoff == 0) && (fsize <= (u_offset_t)uiop->uio_offset));
+
+		if (uoff + bytes > fsize) {
+			file_size_change = 1;
+		}
+
+		newpage = 0;
+		premove_resid = uiop->uio_resid;
+
+		if (vpm_enable) {
+				/*
+				 * If we are about to copy a partial page
+				 * and there are ready pages, flush them now
+				 */
+			if ((unsent_offset < 0)
+			    && ((uoff + bytes) & PAGEOFFSET)) {
+				err = VOP_PUTPAGE(vp, (offset_t)(start_off & PAGEMASK),
+				    (size_t)0, 0, credp, ct);
+				/* next page need not be sent */
+				unsent_offset = (uoff + bytes) & PAGEMASK;
+			}
+				/* copy the page, unless we go an error */
+			if (!err) {
+				err = vpm_data_copy(vp, uoff, bytes, uiop,
+					    !pagecreate, &newpage,
+					    pagecreate && pageoff, S_WRITE);
+				/* flush if this is a full page */
+				if (!((uoff + bytes) & PAGEOFFSET)) {
+					unsent_offset = -1;
+				}
+			}
+		} else {
+			unsent_offset = -1;
+			segmap_offset = (uoff & PAGEMASK) & MAXBOFFSET;
+			base = segmap_getmapflt(segkmap, vp,
+			    uoff & MAXBMASK, PAGESIZE, !pagecreate, S_WRITE);
+			/*
+			 * segmap_pagecreate() returns 1 if it calls
+			 * page_create_va() to allocate any pages.
+			 */
+			if (pagecreate) {
+				newpage = segmap_pagecreate(segkmap,
+				    base + segmap_offset, (size_t)PAGESIZE, 0);
+				/*
+				 * clear from the beginning of the page to the
+				 * start offset of the data.
+				 */
+				if (pageoff != 0)
+					(void) kzero(base + segmap_offset,
+					    (size_t)pageoff);
+			}
+
+			err = uiomove(base + segmap_offset + pageoff,
+			    (long)bytes, UIO_WRITE, uiop);
+			/*
+			 * If a new page was created, then we need to make sure
+			 * that the page is properly initialized. Take care to
+			 * zero out that part of the page where uio_move din't
+			 * write valid data.
+			 */
+			if (pagecreate &&
+			    (size_t)uiop->uio_offset < P2ROUNDUP(uoff + bytes,
+			    PAGESIZE)) {
+				long zoffset;
+				long nmoved;
+
+				nmoved = uiop->uio_offset - uoff;
+				ASSERT((nmoved + pageoff) <= PAGESIZE);
+
+				if ((size_t)(zoffset = pageoff + nmoved)
+							< PAGESIZE)
+					(void) kzero(
+					    base + segmap_offset + zoffset,
+					    (size_t)PAGESIZE - zoffset);
+			}
+			/*
+			 * Unlock the page which have been allocated by
+			 * page_create_va() in segmap_pagecreate()
+			 */
+			if (newpage) {
+				segmap_pageunlock(segkmap, base + segmap_offset,
+				    (size_t)PAGESIZE, S_WRITE);
+			}
+		}
+		/*
+		 * If the file size indeed changed after the
+		 * uiomove, record that fact which will be
+		 * used in fuse_putpage
+		 */
+		if (file_size_change) {
+			/*
+			 * if there was an error than first determine whether
+			 * we did extend the file
+			 */
+			if (err) {
+				if ((uoff + (premove_resid - uiop->uio_resid))
+				    > fsize) {
+					fsize = fsize + (premove_resid -
+					    uiop->uio_resid);
+					(void) fsize_change_notify(vp, fsize,
+					    FSIZE_UPDATED);
+				}
+			} else {
+				fsize = uoff + bytes;
+				(void) fsize_change_notify(vp, fsize,
+				    FSIZE_UPDATED);
+			}
+		}
+
+		if (err) {
+			DTRACE_PROBE2(wrfuse_err_write_fail,
+			    char *, "write error, invalidate pages",
+			    struct uio *, uiop);
+
+			/*
+			 * If we did not allocate a new page and if the file
+			 * size remained unchanged, then we need to destroy
+			 * the page where write failed so that we don't end up
+			 * flushing invalid data
+			 */
+			if (file_size_change == 0 && newpage == 0) {
+				/*
+				 * Similar to UFS, we unwind what uiomove
+				 * last did
+				 */
+				uiop->uio_resid = premove_resid;
+				flags = SM_DESTROY;
+			} else {
+				/*
+				 * Remove the page from the cache, if the page
+				 * is dirty it would be written back
+				 */
+				flags = SM_INVAL;
+			}
+			/*
+			 * If we failed on a write, we must
+			 * be sure to invalidate any pages that may have
+			 * been allocated.
+			 */
+			if (vpm_enable) {
+				(void) vpm_sync_pages(vp, uoff, PAGESIZE,
+				    flags);
+			} else {
+				(void) segmap_release(segkmap, base, flags);
+			}
+		} else {
+			if (vpm_enable) {
+				err = vpm_sync_pages(vp, uoff, PAGESIZE, 0);
+			} else {
+				err = segmap_release(segkmap, base, 0);
+			}
+		}
+	} while (uiop->uio_resid > 0 && err == 0 && bytes != 0);
+out:
+	/* if we did write any data, then flush it out to daemon if needed */
+	if (start_resid != uiop->uio_resid) {
+		if (unsent_offset < 0)
+			err = VOP_PUTPAGE(vp, (offset_t)(start_off & PAGEMASK),
+			    (size_t)0, B_INVAL, credp, ct);
+		else {
+			VTOFD(vp)->offset = unsent_offset;
+			fsize_change_notify(vp, fsize,
+					FSIZE_UNSENT | FSIZE_UPDATED);
+		}
+	}
+
+	return (err);
+}
+
+/*
+ * Write of devices (leaf nodes) is handled by specfs.
+ * Write of directories is not supported.
+ */
+/* ARGSUSED */
+static int
+fuse_write(struct vnode *vp, struct uio *uiop, int ioflag,
+    struct cred *credp, struct caller_context *ct)
+{
+	/* Check for valid file types */
+	if (vp->v_type != VREG && vp->v_type != VLNK)
+		return (EIO);
+
+	if (uiop->uio_loffset < (offset_t)0)
+		return (EINVAL);
+
+	if (uiop->uio_resid == 0)
+		return (0);
+
+	return (wrfuse(vp, uiop, ioflag, credp, ct));
+}
+
+static void fuse_set_getattr(struct vnode *vp, struct vattr *vap,
+    struct fuse_attr *attr)
+{
+	fuse_vnode_data_t *v_data = VTOFD(vp);
+
+	vap->va_mode = attr->mode & MODEMASK;
+		/* return cached size if some data were not flushed */
+	if (v_data->file_size_status & FSIZE_UPDATED)
+		vap->va_size = v_data->fsize;
+	else {
+		vap->va_size = attr->size;
+		v_data->file_size_status &= ~FSIZE_NOT_RELIABLE;
+	}
+	vap->va_atime.tv_sec = attr->atime;
+	vap->va_atime.tv_nsec = attr->atimensec;
+	vap->va_mtime.tv_sec = attr->mtime;
+	vap->va_mtime.tv_nsec = attr->mtimensec;
+
+	vap->va_uid = attr->uid;
+	vap->va_gid = attr->gid;
+	vap->va_nlink = attr->nlink;
+	vap->va_ctime.tv_sec = attr->ctime;
+	vap->va_ctime.tv_nsec = attr->ctimensec;
+
+	vap->va_mask = AT_ALL;
+	vap->va_type = vp->v_type;
+	/* return the file system ino, not the fuse internal nodeid */
+	vap->va_nodeid = attr->ino;
+#ifdef _LP64
+	/*
+	 * Unpack attr->rdev always received in a 32 bits field. Anyway,
+	 * we do not know whether the library is a 32-bit or a 64-bit one.
+	 */
+	vap->va_rdev = makedevice((attr->rdev >> 18) & 0x3fff,
+			attr->rdev & 0x3ffff);
+#else
+	vap->va_rdev = attr->rdev;
+#endif
+	vap->va_blksize = vp->v_vfsp->vfs_bsize;
+
+	vap->va_nblocks = attr->blocks;
+	/* TBD: What value should we set here ? */
+	vap->va_seq = 0;
+	vap->va_fsid = vp->v_vfsp->vfs_dev;
+}
+
+static int
+fuse_getattr_from_daemon(struct vnode *vp, struct vattr *vap,
+    cred_t *credp, fuse_msg_node_t **msgpp)
+{
+	int err;
+	fuse_msg_node_t *msgp;
+	fuse_session_t *sep;
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_getattr_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	msgp = fuse_alloc_msg();
+
+	/*
+	 * setup the message with only header and no arguments to be sent to
+	 * fuse library
+	 */
+	setup_msg_onlyheader(msgp, FUSE_GETATTR, FUSE_GET_UNIQUE(sep),
+	    VNODE_TO_NODEID(vp), credp);
+
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto out;
+	}
+
+	/* We got woken up, so fuse library has replied to our request */
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(fuse_getattr_err_getattr_req,
+		    char *, "FUSE_GETATTR request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto out;
+	}
+
+	if (msgp->opdata.outsize != sizeof (struct fuse_attr_out)) {
+		DTRACE_PROBE2(fuse_getattr_err_inval_resp,
+		    char *, "FUSE_GETATTR response invalid size",
+		    size_t, msgp->opdata.outsize);
+		err = EINVAL;
+		goto out;
+	}
+#ifndef DONT_CACHE_ATTRIBUTES
+	cache_attrs(sep, vp, (struct fuse_attr_out *)(msgp->opdata.outdata));
+	memcpy(vap, &(VTOFD(vp)->cached_attrs), sizeof(*vap));
+#else
+	fuse_set_getattr(vp, vap,
+	    &((struct fuse_attr_out *)msgp->opdata.outdata)->attr);
+#endif
+
+	if (msgpp) {
+		/* It's caller's responsibility to free the msg node */
+		*msgpp = msgp;
+		return (err);
+	}
+
+out:
+	fuse_free_msg(msgp);
+	return (err);
+}
+
+/*
+ * The flags parameter is generally passed as zero from most syscalls to
+ * VOP_GETATTR
+ */
+/* ARGSUSED */
+static int
+fuse_getattr(struct vnode *vp, struct vattr *vap, int flags, cred_t *credp,
+    caller_context_t *ct)
+{
+	int err = DDI_SUCCESS;
+	fuse_msg_node_t *msgp = NULL;
+#ifndef DONT_CACHE_ATTRIBUTES
+	timestruc_t ts;
+	fuse_vnode_data_t *v_data = GET_VDATA(vp);
+#endif
+
+#ifndef DONT_CACHE_ATTRIBUTES
+	gethrestime(&ts);
+
+	if (!(v_data->file_size_status & FSIZE_NOT_RELIABLE)
+	    && ATTR_CACHE_VALID(ts, v_data->cached_attrs_bound)) {
+		(void) memcpy(vap, &v_data->cached_attrs, sizeof (*vap));
+		/* File may have been written to since attrs were cached */
+		if (v_data->file_size_status & FSIZE_UPDATED)
+			vap->va_size = v_data->fsize;
+		DTRACE_PROBE2(fuse_getattr_info_cached,
+		    char *, "Using cached attributes", struct vnode *, vp);
+		return (0);
+	}
+#endif
+	err = fuse_getattr_from_daemon(vp, vap, credp, &msgp);
+
+	if (err)
+		return (err);
+
+out:
+	fuse_free_msg(msgp);
+	return (err);
+}
+
+
+/*
+ * The flags parameter is generally passed as zero from most syscalls to
+ * VOP_SETATTR.
+ */
+/* ARGSUSED */
+static int
+fuse_setattr(
+    struct vnode *vp,
+    struct vattr *vap,
+    int flags,
+    struct cred *credp,
+    caller_context_t *ct)
+{
+	int err = 0;
+	fuse_msg_node_t *msgp;
+	struct fuse_setattr_in *fsai;
+	struct fuse_file_handle *fhp;
+	struct vattr va;
+	enum vtype vtyp;
+	long int mask = vap->va_mask;
+	fuse_session_t *sep;
+
+	fhp = (struct fuse_file_handle*)NULL;
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_setattr_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	/* Check for unsettable attributes */
+	if (mask & AT_NOSET)
+		return (EINVAL);
+
+	if ((mask & (AT_MTIME | AT_ATIME))
+	    || ((mask & AT_SIZE) && vap->va_size)) {
+			/*
+			 * Write unsent data before truncating, only
+			 * needed for data visible after the truncation.
+			 * Do the same before setting the time stamps to
+			 * preserve the ordering of time stamp changes.
+			 */
+		err = flush_unsent_data(vp, credp, ct);
+		if (err)
+			return (err);
+	}
+	if (mask & AT_SIZE) {
+		if (GET_VDATA(vp))
+			fsize_change_notify(vp, 0, FSIZE_NOT_RELIABLE);
+			/*
+			 * Mark all pages beyond the truncation as invalid
+			 * otherwise old data may show in holes which
+			 * are left in rewritten parts.
+			 * Actually we cannot find pages beyond the
+			 * truncation, so we invalidate them all.
+			 */
+		if (vn_has_cached_data(vp))
+			pvn_vplist_dirty(vp, 0, fuse_putapage, B_INVAL, credp);
+	}
+
+	mutex_enter(&VTOFD(vp)->f_lock);
+
+	if ((err = VOP_GETATTR(vp, &va, flags, credp, ct)))
+		goto cleanup2;
+
+	/* Let kernel do the appropriate access checks */
+	err = secpolicy_vnode_setattr(credp, vp, vap, &va, flags,
+	    fuse_access_i, vp);
+
+	if (err)
+		goto cleanup2;
+
+	mask = vap->va_mask;
+
+	msgp = fuse_setup_message(sizeof (*fsai), FUSE_SETATTR,
+	    VNODE_TO_NODEID(vp), credp, FUSE_GET_UNIQUE(sep));
+
+	fsai = msgp->ipdata.indata;
+
+/*
+ * In case we plan to support lower versions of fuse library this macro
+ * can be changed
+ */
+#define	FUSEATTR(x) x
+
+	if (mask & AT_UID) {
+		fsai->FUSEATTR(uid) = vap->va_uid;
+		fsai->valid |= FATTR_UID;
+	}
+
+	if (mask & AT_GID) {
+		fsai->FUSEATTR(gid) = vap->va_gid;
+		fsai->valid |= FATTR_GID;
+	}
+
+	if (mask & AT_MODE) {
+		va.va_mode &= S_IFMT;
+		va.va_mode |= vap->va_mode & ~S_IFMT;
+		fsai->FUSEATTR(mode) = va.va_mode;
+		fsai->valid |= FATTR_MODE;
+	}
+
+	if (mask & AT_ATIME) {
+		fsai->FUSEATTR(atime) = vap->va_atime.tv_sec;
+		fsai->FUSEATTR(atimensec) = vap->va_atime.tv_nsec;
+		fsai->valid |= FATTR_ATIME;
+		if (!(flags & ATTR_UTIME))
+			fsai->valid |= FATTR_ATIME_NOW;
+	}
+
+	if (mask & AT_MTIME) {
+		fsai->FUSEATTR(mtime) = vap->va_mtime.tv_sec;
+		fsai->FUSEATTR(mtimensec) = vap->va_mtime.tv_nsec;
+		fsai->valid |= FATTR_MTIME;
+		if (!(flags & ATTR_UTIME))
+			fsai->valid |= FATTR_MTIME_NOW;
+	}
+
+	if (mask & AT_SIZE) {
+		struct fuse_fh_param  fh_param;
+
+		fsai->FUSEATTR(size) = vap->va_size;
+		fsai->valid |= FATTR_SIZE;
+		/*
+		 * For ftruncate() we must provide the file system with
+		 * its file handle.
+		 * We cannot tell whether truncate() or ftruncate()
+		 * was used, we can only check whether the calling
+		 * process has a write or create handle on the file to
+		 * allow truncation irrespective of current permissions.
+		 */
+		fh_param.credp = credp;
+		fh_param.rw_mode = FWRITE | FAPPEND | FCREAT;
+		fh_param.nodeid = VNODE_TO_NODEID(vp);
+		fh_param.fufh = NULL;
+		if (vn_is_opened(vp,V_WRITE)
+		   && (iterate_filehandle(vp, fuse_std_filecheck, &fh_param,
+								&fhp))) {
+			fsai->fh = fhp->fh_id;
+			fsai->valid |= FATTR_FH;
+		} else
+			fhp = (struct fuse_file_handle*)NULL;
+	}
+	/* if we received a signal or daemon replied with an error */
+	if ((err = fuse_queue_request_wait(sep, msgp)) != 0) {
+		if (fhp)
+			fhp->ref--;
+		goto cleanup;
+	}
+	if (fhp)
+		fhp->ref--;
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(fuse_setattr_err_setattr_req,
+		    char *, "FUSE_SETATTR request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+#ifndef DONT_CACHE_ATTRIBUTES
+		invalidate_cached_attrs(vp);
+#endif
+		goto cleanup;
+	}
+	vtyp =
+	    IFTOVT(((struct fuse_attr_out *)msgp->opdata.outdata)->attr.mode);
+
+	if (vp->v_type != vtyp) {
+		if (vp->v_type == VNON) {
+			vp->v_type = vtyp;
+		} else {
+			VOP_INACTIVE(vp, credp, ct);
+			err = ENOTCONN;
+			goto cleanup;
+		}
+	}
+#ifndef DONT_CACHE_ATTRIBUTES
+	cache_attrs(sep, vp, (struct fuse_attr_out *)msgp->opdata.outdata);
+	/* The cache is valid, but permissions to lookup have to be rechecked */
+	if ((vp->v_type == VDIR)
+	    && (mask & (AT_MODE | AT_UID | AT_GID))) {
+		VTOFD(vp)->uid = (uid_t)-1;
+		VTOFD(vp)->gid = (gid_t)-1;
+	}
+#endif
+
+cleanup:
+	fuse_free_msg(msgp);
+cleanup2:
+	mutex_exit(&VTOFD(vp)->f_lock);
+	return (err);
+}
+/*
+ * vnode operation to support POSIX link()
+ * The first param is the vnode of the dir containing the file, second param is
+ * the vnode of the file to hard link, the third param is the new name to be
+ * given.
+ */
+/* ARGSUSED */
+static int
+fuse_link(struct vnode *dvp, struct vnode *srcvp, char *tnm,
+    struct cred *credp, caller_context_t *ct, int flags)
+{
+	struct fuse_link_in *fli;
+	struct fuse_entry_out *feo;
+	fuse_msg_node_t *msgp;
+	char *name;
+	struct vnode *vp;
+	fuse_session_t *sep;
+	int err = DDI_SUCCESS;
+	int nmlen = strlen(tnm) + 1;
+
+	sep = fuse_minor_get_session(getminor(dvp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_link_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, dvp);
+		return (ENODEV);
+	}
+	/* Directories cannot be linked */
+	if (srcvp->v_type == VDIR)
+		return (EPERM);
+
+	/* Make sure the target does not exist */
+	err = fuse_lookup_i(dvp, tnm, &vp, credp);
+	if (!err && vp) {
+		VN_RELE(vp);
+		return (EEXIST);
+	}
+
+	msgp = fuse_setup_message(sizeof (*fli) + nmlen, FUSE_LINK,
+	    VNODE_TO_NODEID(dvp), credp, FUSE_GET_UNIQUE(sep));
+
+	/* Set up arguments to the fuse library */
+	fli = (struct fuse_link_in *)msgp->ipdata.indata;
+	fli->oldnodeid = VNODE_TO_NODEID(srcvp);
+	name = (char *)msgp->ipdata.indata + sizeof (*fli);
+	(void *) strlcpy(name, tnm, nmlen);
+	name[nmlen - 1] = '\0';
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto cleanup;
+	}
+
+	/* We got woken up, so fuse lib has replied to our release request */
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(fuse_link_err_link_req,
+		    char *, "FUSE_LINK request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+
+	feo = (struct fuse_entry_out *)msgp->opdata.outdata;
+
+	if ((err = checkentry(feo, srcvp->v_type))) {
+		goto cleanup;
+	}
+#ifndef DONT_CACHE_ATTRIBUTES
+	invalidate_cached_attrs(dvp);
+	/*
+	 * To be on the safer side we disregard the cached attribute for the
+	 * original so that fs gets a chance to pass on updated attributes next
+	 * time we need
+	 */
+	/* See the comments at the same place in FreeBSD Fuse kernel module */
+	invalidate_cached_attrs(srcvp);
+#endif
+	VTOFD(srcvp)->nlookup++;
+
+cleanup:
+	fuse_free_msg(msgp);
+	return (err);
+}
+
+/* unistd.h */
+/* Symbolic constants for the "access" routine: */
+#define	R_OK	4	/* Test for Read permission */
+#define	W_OK	2	/* Test for Write permission */
+#define	X_OK	1	/* Test for eXecute permission */
+#define	F_OK	0	/* Test for existence of File */
+
+/* ARGSUSED */
+static int
+fuse_access(struct vnode *vp, int mode, int flags, struct cred *credp,
+    caller_context_t *ct)
+{
+	return (fuse_access_i((void *)vp, mode, credp));
+}
+
+static int
+fuse_access_i(void *vvp, int mode, struct cred *credp)
+{
+	struct vnode *vp = vvp;
+	struct fuse_access_in *fai;
+	fuse_msg_node_t *msgp;
+	fuse_session_t *sep;
+	int err = 0;
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_access_i_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	/*
+	 * TBD: Check if it is mounted readonly and return error on
+	 * Write access
+	 */
+
+	/*
+	 * According to FreeBSD Fuse, if default permission option is
+	 * set while mounting or if a regular file is checked for executing
+	 * we have to do a in-kernel check. Since we don't have mount options
+	 * support yet, we just check for execute mode
+	 */
+	if (vp->v_type == VREG && mode == VEXEC) {
+		return (fuse_access_inkernelcheck(vp, mode, credp));
+	} else {
+		if (!GET_VDATA(vp))
+			return (EIO);
+		msgp = fuse_setup_message(sizeof (*fai), FUSE_ACCESS,
+		    VNODE_TO_NODEID(vp), credp, FUSE_GET_UNIQUE(sep));
+
+		fai = (struct fuse_access_in *)msgp->ipdata.indata;
+
+		fai->mask = F_OK;
+		if (mode & VREAD)
+			fai->mask |= R_OK;
+		if (mode & VWRITE)
+			fai->mask |= W_OK;
+		if (mode & VEXEC)
+			fai->mask |= X_OK;
+		/*
+		 * Do not request a check from the file system if the
+		 * file is being created or written by the current process,
+		 * This is a workaround needed to allow ftruncate()
+		 * for the process having a write descriptor to the file
+		 * when it is creating as read-only or permissions
+		 * changed since the file was opened.
+		 */
+		if ((mode & VWRITE) && vn_is_opened(vp,V_WRITE)) {
+			struct fuse_fh_param fh_param;
+			struct fuse_file_handle *fhp;
+
+			fh_param.credp = credp;
+			fh_param.rw_mode = FWRITE | FAPPEND | FCREAT;
+			fh_param.nodeid = VNODE_TO_NODEID(vp);
+			fh_param.fufh = NULL;
+			if (vn_is_opened(vp,V_WRITE)
+			   && (iterate_filehandle(vp, fuse_std_filecheck,
+						&fh_param, &fhp))) {
+				fhp->ref--;
+				fai->mask &= ~W_OK;
+			}
+		}
+
+		/*
+		 * queue the message for sending to userland filesystem
+		 * lib framework, unless there is nothing to check
+		 */
+
+		if (fai->mask)
+			err = fuse_queue_request_wait(sep, msgp);
+		if (fai->mask && !err) {
+			/*
+			 * We got woken up, so fuse library has replied to
+			 * our request
+			 */
+			err = msgp->opdata.fouth->error;
+			if (err == ENOSYS) {
+				DTRACE_PROBE2(fuse_access_i_info_enosys,
+				    char *,
+				    "FUSE_ACCESS not supported by filesys",
+				    struct fuse_out_header *,
+				    msgp->opdata.fouth);
+				/*
+				 * If the userspace filesystem has not provided
+				 * an implementation we just return SUCCESS to
+				 * the kernel
+				 */
+				err = 0;
+			} else {
+				DTRACE_PROBE2(fuse_access_i_err_access_req,
+				    char *, "FUSE_ACCESS request failed",
+				    struct fuse_out_header *,
+				    msgp->opdata.fouth);
+			}
+		}
+		fuse_free_msg(msgp);
+		return (err);
+	}
+}
+
+/* Performs the basic access check w.r.t. owner, group and public permissions */
+#define	MODESHIFT	3
+static int
+fuse_access_inkernelcheck(void *vvp, int mode, struct cred *credp)
+{
+	int shift = 0;
+	struct vnode *vp = (struct vnode *)vvp;
+	int err = 0;
+	struct vattr va;
+
+	if ((err = VOP_GETATTR(vp, &va, 0, credp, NULL)))
+		return (err);
+
+	/* Check access based on owner, group and public permissions */
+	if (crgetuid(credp) != va.va_uid) {
+		shift += MODESHIFT;
+		if (!groupmember(va.va_gid, credp))
+			shift += MODESHIFT;
+	}
+	/* Compute missing mode bits */
+	mode &= ~(va.va_mode << shift);
+
+	if (mode == 0)
+		return (0);
+
+	return (secpolicy_vnode_access(credp, vp, va.va_uid, mode));
+}
+
+/*
+ * Logic is from UFS.
+ * Decide whether it is okay to remove within a sticky directory.
+ * Two conditions need to be met:  write access to the directory
+ * is needed.  In sticky directories, write access is not sufficient;
+ * you can remove entries from a directory only if you own the directory,
+ * if you are privileged, if you own the entry or if the entry is
+ * a plain file and you have write access to that file.
+ * Function returns 0 if remove access is granted.
+ */
+static int
+fuse_sticky_remove_access(struct vnode *dvp, struct vnode *vp, struct cred *cr,
+    caller_context_t *ct)
+{
+	struct vattr dva;
+	struct vattr va;
+	uid_t uid;
+	int err;
+
+	/*
+	 * Ah! We will have to request daemon for both source and its parent
+	 * directory vnode attributes. We try getting attributes for source
+	 * before its par directory since there is a better chance that we
+	 * might be able to sneak in and get the attributes from the cache.
+	 */
+	if ((err = VOP_GETATTR(vp, &va, 0, cr, ct)))
+		return (err);
+
+	if ((err = VOP_GETATTR(dvp, &dva, 0, cr, ct)))
+		return (err);
+
+	/* Perform the check */
+	if ((dva.va_mode & VSVTX) &&
+	    (uid = crgetuid(cr)) != dva.va_uid &&
+	    uid != va.va_uid &&
+	    (va.va_mode != VREG ||
+	    fuse_access_i((void *)vp, VWRITE, cr) != 0))
+		return (secpolicy_vnode_remove(cr));
+
+	return (0);
+}
+
+/*
+ * Lookup
+ *
+ * Given the directory vnode and the name of the component, return
+ * the corresponding held vnode for that component.
+ */
+/* ARGSUSED */
+
+static int fuse_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
+    struct pathname *pnp, int flags, struct vnode *rdir, struct cred *credp,
+    caller_context_t *ct, int *direntflags, pathname_t *realpnp)
+{
+	int err = 0;
+	fuse_session_t *sep;
+
+	sep = fuse_minor_get_session(getminor(dvp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_lookup_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, dvp);
+		return (ENODEV);
+	}
+
+	if (dvp->v_type != VDIR) {
+		err = ENOTDIR;
+		goto out;
+	}
+
+	/*
+	 * if this is the root of our filesystem, we will check
+	 * for valid access permissions
+	 */
+	if (!GET_VDATA(dvp)) {
+		err = EIO; /* does not look like a fuse-type directory */
+		goto out;
+	}
+
+	if ((VTOFD(dvp))->nodeid == FUSE_ROOT_ID) {
+		if ((err = fuse_access_inkernelcheck(dvp, VEXEC, credp))) {
+			DTRACE_PROBE2(fuse_lookup_err_access,
+			    char *, "fuse_access failed",
+			    struct vnode *, dvp);
+			goto out;
+		}
+	}
+
+	/* Check for NULL name and "." */
+	if (nm[0] == '\0' || ((nm[0] == '.') && (nm[1] == '\0'))) {
+		VN_HOLD(dvp);
+		*vpp = dvp;
+		goto out;
+	}
+
+	/* Check if lookup needs to traverse beyond our filesystem */
+	if (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0') {
+		if (VTOFD(dvp)->nodeid == FUSE_ROOT_ID) {
+			if (dvp->v_vfsp->vfs_vnodecovered) {
+				err = VOP_LOOKUP(dvp->v_vfsp->vfs_vnodecovered,
+				    nm, vpp, pnp, flags, rdir, credp, ct,
+				    direntflags, realpnp);
+			} else {
+				DTRACE_PROBE2(fuse_lookup_err_vnodecovered,
+				    char *, "vnode covered is NULL",
+				    struct vnode *, dvp);
+				err = EIO;
+			}
+		} else if (VTOFD(dvp)->par_nid == FUSE_ROOT_ID) {
+			err = VFS_ROOT(dvp->v_vfsp, vpp);
+		} else if (VTOFD(dvp)->par_nid != FUSE_NULL_ID) {
+			err = fuse_getvnode(VTOFD(dvp)->par_nid, vpp,
+			    VNODE_CACHED, VDIR, sep, dvp->v_vfsp, 0, NULL,
+			    FUSE_NULL_ID, credp);
+			/*
+			 * We don't seem to have the parent vnode cached, so
+			 * we need to request daemon then
+			 */
+			if (err == ENOENT) {
+				goto request_daemon;
+			}
+		} else {
+			/*
+			 * If we don't have a valid parent nodeid then there
+			 * isn't much we can do, so we return error to the user
+			 */
+			DTRACE_PROBE2(fuse_lookup_err_parent,
+			    char *, "No valid parent nodeid",
+			    struct vnode *, dvp);
+			err = EIO;
+		}
+		goto out;
+	}
+
+request_daemon:
+	err = fuse_lookup_i(dvp, nm, vpp, credp);
+out:
+	return (err);
+}
+
+
+/*
+ * This will free the private data allocated for a vnode. The caller should
+ * ensure all the file handles associated with the passed vnode has been
+ * released appropriately before invoking this function
+ */
+static void
+fuse_free_vdata(struct vnode *vp)
+{
+	fuse_vnode_data_t *fvdatap = VTOFD(vp);
+	list_destroy(&fvdatap->fh_list);
+	mutex_destroy(&fvdatap->fh_list_lock);
+	mutex_destroy(&fvdatap->f_lock);
+	kmem_free(fvdatap, sizeof (*fvdatap));
+}
+
+/*
+ * This routine frees a vnode and ensures that appropriate cleanups are taken
+ * care of before returning it to the kernel.
+ * JPA This is called when the file system reuses a nodeid already known
+ * in cache, and the old entry has to be discarded.
+ * This probably means an inconsistency, and we should probably do the
+ * cleanup locally without reporting to user space, which may have a
+ * newer interpretation of the node id.
+ */
+static void
+fuse_vnode_destroy(struct vnode *vp, struct cred *credp, fuse_session_t *sep)
+{
+	fuse_vnode_cleanup(vp, credp, sep);
+
+	if (VTOFD(vp))
+		fuse_free_vdata(vp);
+	vp->v_data = NULL;
+	VFS_RELE(vp->v_vfsp);
+
+	vn_free(vp);
+}
+
+static int
+fuse_getvnode(uint64_t nodeid, struct vnode **vpp, v_getmode vmode,
+    uint32_t mode, fuse_session_t *sep, vfs_t *vfsp, int namelen,
+    char *name, uint64_t parent_nid, struct cred *credp)
+{
+	fuse_avl_cache_node_t tofind, *foundp;
+	fuse_avl_cache_node_t *avl_nodep;
+	int create_new;
+
+	tofind.facn_nodeid = nodeid;
+	tofind.namelen = namelen;
+	tofind.name = name;
+	tofind.par_nodeid = parent_nid;
+	do {
+		create_new = 0;
+		mutex_enter(&sep->avl_mutx);
+		if (nodeid == FUSE_NULL_ID)
+			foundp = avl_find(&(sep->avl_cache_n), &tofind, NULL);
+		else
+			foundp = avl_find(&(sep->avl_cache_i), &tofind, NULL);
+		if (foundp) {
+			*vpp = foundp->facn_vnode_p;
+			ASSERT(*vpp);
+			/*
+			 * If we need a new vnode, then try to destroy the
+			 * previously cached vnode.
+			 * Release the mutex for destroying and retry.
+			 */
+			mutex_exit(&sep->avl_mutx);
+			if (vmode == VNODE_NEW) {
+				cmn_err(CE_WARN, "Vnode collision on id %lld\n",
+					(long long)foundp->facn_nodeid);
+				fuse_vnode_destroy(*vpp, credp, sep);
+				create_new = 1;
+			} else {
+				VN_HOLD(*vpp);
+			}
+		}
+	} while (create_new);
+	if (!foundp) {
+		/*
+		 * If the caller was looking for the previously cached vnode
+		 * then return an error since we din't find it in the AVL tree
+		 */
+		if (vmode == VNODE_CACHED) {
+			mutex_exit(&sep->avl_mutx);
+			return (ENOENT);
+		}
+		/*
+		 * Create and add the vnode in AVL cache
+		 */
+		create_new = 1;
+	}
+	if (create_new) {
+		/* The mutex is still held, avl_find/avl_add must be atomic */
+		*vpp = fuse_create_vnode(vfsp, nodeid, parent_nid,
+		    mode, OTHER_OP);
+
+		avl_nodep = fuse_avl_cache_node_create(
+		    *vpp, nodeid, parent_nid, namelen, name);
+		avl_add(&(sep->avl_cache_i), avl_nodep);
+		avl_add(&(sep->avl_cache_n), avl_nodep);
+		mutex_exit(&sep->avl_mutx);
+	}
+	VTOFD(*vpp)->nlookup++;
+	return (0);
+}
+
+static int
+fuse_lookup_i(struct vnode *dvp, char *nm, struct vnode **vpp, cred_t *credp)
+{
+	int err = 0;
+	int nmlen = 0;
+	uint64_t nodeid;
+	fuse_msg_node_t *msgp;
+	fuse_session_t *sep;
+	struct fuse_entry_out *feo;
+
+	sep = fuse_minor_get_session(getminor(dvp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_lookup_i_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, dvp);
+		return (ENODEV);
+	}
+
+	nmlen = strlen(nm) + 1;
+
+	/* Be cautions against a possible attack */
+	if (nmlen > MAXNAMELEN) {
+		DTRACE_PROBE2(fuse_lookup_i_err_nametoolong,
+		    char *, "name too long", char *, nm);
+		return (ENAMETOOLONG);
+	}
+#ifndef DONT_CACHE_ATTRIBUTES
+	/*
+	 * Check whether the node is available in the cache.
+	 * If ownership or permissions of parent directory has been
+	 * changed, or caller is not the one for which lookup has been
+	 * checked last, we must call the daemon in order to check
+	 * whether lookup is allowed, even when the parent cache is valid.
+	 */
+	if ((VTOFD(dvp)->uid == crgetuid(credp))
+	    && (VTOFD(dvp)->gid == crgetgid(credp))) {
+		err = fuse_getvnode(FUSE_NULL_ID, vpp, VNODE_CACHED,
+			    0, sep, dvp->v_vfsp, nmlen, nm,
+			    VNODE_TO_NODEID(dvp), credp);
+		if (!err && *vpp && VTOFD(*vpp)) {
+			timestruc_t ts;
+
+			/*
+			 * Make sure the cached inode is valid, which
+			 * implies it is still recorded in the same parent
+			 * directories.
+			 */
+			gethrestime(&ts);
+			if (ATTR_CACHE_VALID(ts, VTOFD(*vpp)->cached_attrs_bound)) {
+				switch ((*vpp)->v_type) {
+					vnode_t	*svp;
+
+				case VBLK :
+				case VCHR :
+					svp = specvp(*vpp, (*vpp)->v_rdev,
+					    (*vpp)->v_type, credp);
+					VN_RELE(*vpp);
+					if (svp == NULL)
+						err = ENOSYS;
+					else
+						*vpp = svp;
+				}
+				return (err);
+			} else {
+				/* Cache is obsolete, give back our hold */
+				VN_RELE(*vpp);
+			}
+		}
+	}
+#endif
+	/* Need to send a FUSE_LOOKUP message to the fuse library */
+	msgp = fuse_setup_message(nmlen, FUSE_LOOKUP, VNODE_TO_NODEID(dvp),
+	    credp, FUSE_GET_UNIQUE(sep));
+
+	(void *) strlcpy(msgp->ipdata.indata, nm, nmlen);
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		fuse_free_msg(msgp);
+		return (err);
+	}
+
+	/* We got woken up, so fuse library has replied to our Lookup request */
+
+	/* Check for any error from fuse library */
+	if ((err = msgp->opdata.fouth->error) == 0) {
+		/*
+		 * If there was no error from the fuse library, then verify
+		 * obtained nodeid
+		 */
+		feo = (struct fuse_entry_out *)msgp->opdata.outdata;
+		nodeid = feo->nodeid;
+		if (!nodeid) {
+			err = ENOENT;
+		} else if (nodeid == FUSE_ROOT_ID) {
+			err = EIO;
+		} else { /* Valid nodeid perhaps */
+			err = fuse_getvnode(nodeid, vpp, VNODE_ANY,
+			    IFTOVT(feo->attr.mode), sep, dvp->v_vfsp, nmlen, nm,
+			    VNODE_TO_NODEID(dvp), credp);
+
+			if (err) {
+				fuse_send_forget(nodeid, sep, 1);
+			} else {
+#ifndef DONT_CACHE_ATTRIBUTES
+				cache_attrs(sep, (*vpp), feo);
+				VTOFD(dvp)->uid = crgetuid(credp);
+				VTOFD(dvp)->gid = crgetgid(credp);
+#endif
+
+				/*
+				 * Convert device special files
+				 * JPA do not do that for fifo's :
+				 * this changes the v_data and the nodeid
+				 * cannot be determined, leading to crashes
+				 * when the inode is needed (e.g. when
+				 * hardlinking another name.
+				 * Moreover the avl_tree should be kept
+				 * in sync with the vnodes in use.
+		 		 */
+				switch ((*vpp)->v_type) {
+					vnode_t	*svp;
+
+				case VBLK :
+				case VCHR :
+					svp = specvp(*vpp, (*vpp)->v_rdev,
+					    (*vpp)->v_type, credp);
+					VN_RELE(*vpp);
+					if (svp == NULL)
+						err = ENOSYS;
+					else
+						*vpp = svp;
+				}
+			}
+		}
+	} else {
+		DTRACE_PROBE2(fuse_lookup_i_err_lookup_req,
+		    char *, "FUSE_LOOKUP request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+	}
+
+	fuse_free_msg(msgp);
+	return (err);
+}
+
+/*
+ * The following comments are from (OpenSolaris) ZFS :
+ * Remove a directory subdir entry. If the current working
+ * directory is the same as the subdir to be removed, the
+ * remove will fail. [BUT fuse has no way to know if cwd is
+ *	 subdir of dvp as we do not maintain the dir tree in kernel]
+ *
+ *	IN:	dvp	- vnode of directory to remove from.
+ *		name	- name of directory to be removed.
+ *		cwd	- vnode of current working directory.
+ *		cr	- credentials of caller.
+ *
+ *	RETURN:	0 if success
+ *		error code if failure
+ *
+ */
+/* ARGSUSED */
+static int
+fuse_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *credp,
+    caller_context_t *ct, int flags)
+{
+	int err = 0;
+	fuse_session_t *sep;
+	fuse_msg_node_t *msgp;
+	struct vnode *vp = NULL;
+	int namelen = strlen(name) + 1;
+
+	sep = fuse_minor_get_session(getminor(dvp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_rmdir_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	if (dvp->v_type != VDIR)
+		return (ENOTDIR);
+
+	/* . and .. cannot be removed */
+	if (name[0] == '.') {
+		if (name[1] == '\0') {
+			return (EINVAL);
+		} else if (name[1] == '.' && name[2] == '\0') {
+			return (EEXIST);
+		}
+	}
+
+	err = fuse_getvnode(FUSE_NULL_ID, &vp, VNODE_CACHED,
+	    0, sep, dvp->v_vfsp, namelen, name, VNODE_TO_NODEID(dvp), credp);
+
+	if (err) {
+		/*
+		 * This means, we have never seen this directory before, so
+		 * we don't have its vnode, just send the message to daemon
+		 */
+		if (err == ENOENT) {
+			goto sendmsg;
+		} else {
+			/* This shouldn't occur */
+			DTRACE_PROBE3(fuse_rmdir_err_getvnode,
+			    char *, "getvnode returned error",
+			    int, err, uint64_t, FUSE_NULL_ID);
+			return (err);
+		}
+	}
+	ASSERT(vp != NULL);
+
+	/*
+	 * XXX: We have no way to know if cwd is subdir of dvp as we do not
+	 * maintain the dir tree in kernel
+	 */
+
+	if (vp == cwd || vp == dvp) {
+		err = EINVAL;
+		goto out;
+	}
+
+	if (vp->v_type != VDIR) {
+		err = ENOTDIR;
+		goto out;
+	}
+
+/*
+ *	JPA This causes directories from other file systems to be locked
+ *	after the current partition is unmounted (reuse of vnode ?)
+ *	On http://wesunsolve.net/bugid/id/6651136 the use of vn_vfsrlock()
+ *	is recommended in a similar situation.
+ *
+ *	if (vn_vfswlock(vp)) {
+ *		err = EBUSY;
+ *		goto out;
+ *	}
+ */
+
+	if (vn_mountedvfs(vp)) {
+		vn_vfsunlock(vp);
+		err = EBUSY;
+		goto out;
+	}
+
+sendmsg:
+	msgp = fuse_setup_message(namelen, FUSE_RMDIR, VNODE_TO_NODEID(dvp),
+	    credp, FUSE_GET_UNIQUE(sep));
+
+	(void *) strlcpy(msgp->ipdata.indata, name, namelen);
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto cleanup;
+	}
+
+	/* We got woken up, so fuse library has replied to our Lookup request */
+	/* Check for any error from fuse library */
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(fuse_rmdir_err_rmdir_req,
+		    char *, "FUSE_RMDIR request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+	if (vp) {
+		/*
+		 * If all went well, we don't need this vnode any more so
+		 * we will get rid of it
+		 */
+		/*
+		 * XXX: This call releases vnode with vn_free, can this
+		 * be an issue? Or does VN_RELE do the required job for us?
+		 */
+		VN_RELE(vp);
+		fuse_vnode_free(vp, sep);
+		vp = NULL;
+	}
+
+#ifndef DONT_CACHE_ATTRIBUTES
+	invalidate_cached_attrs(dvp);
+#endif
+
+cleanup:
+	fuse_free_msg(msgp);
+out:	if (vp)
+		VN_RELE(vp);
+	return (err);
+}
+
+/*
+ * the (OpenSolaris)devfs prototype was used in this func.
+ *
+ * - any create fails if the node doesn't exist - EROFS.
+ * - creating an existing directory read-only succeeds, otherwise EISDIR.
+ * - exclusive creates fail if the node already exists - EEXIST.
+ * - failure to create the snode for an existing device - ENOSYS.
+ */
+/*
+ * Attempt to create a new entry in a directory. If the entry
+ * already exists, truncate the file if permissible, else return
+ * an error. Return the vp of the created or trunc'd file.
+ *
+ *	IN:	dvp	- vnode of directory to put new file entry in.
+ *		name	- name of new file entry.
+ *		vap	- attributes of new file.
+ *		excl	- flag indicating exclusive or non-exclusive mode.
+ *		mode	- mode to open file with.
+ *		cr	- credentials of caller.
+ *		flag	- creat() param is passed here [filemode &
+ *                        ~(FTRUNC|FEXCL)]
+ *
+ *	OUT:	vpp	- vnode of created or trunc'd entry.
+ *
+ *	RETURN:	0 if success
+ *		error code if failure
+ *
+ * Timestamps:
+ *	dvp - ctime|mtime updated if new entry created
+ *	 vp - ctime|mtime always, atime if new
+ */
+/* ARGSUSED */
+static int
+fuse_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
+    int mode, struct vnode **vpp, struct cred *cred_p, int flag,
+    caller_context_t *ct, vsecattr_t *va)
+{
+	struct fuse_vnode_data *fvdata;
+	uint32_t stringlen;
+	int err;
+
+	if (!nm || *nm == '\0') {
+		DTRACE_PROBE2(fuse_create_err_invalid_name,
+		    char *, "Invalid filename", char *, nm);
+		return (EEXIST);
+	}
+
+		/* Check whether the target exists */
+	err = fuse_lookup_i(dvp, nm, vpp, cred_p);
+	if (!err && *vpp) {
+		VN_RELE(*vpp);
+		/* Recreating an existing file without O_EXCL is allowed */
+		if ((vap->va_type == VREG) && !excl)
+			return (0);
+		else
+			return (EEXIST);
+	}
+
+	switch (vap->va_type) {
+	case VDIR :
+		return (VOP_MKDIR(dvp, nm, vap, vpp, cred_p, ct, 0, va));
+
+	case VFIFO :
+	case VCHR :
+	case VBLK :
+	case VSOCK :
+		return (fuse_mknod(dvp, nm, vap, excl, mode, vpp, cred_p,
+				flag, ct, va));
+	case VREG :
+		break;
+
+	default :
+		DTRACE_PROBE2(fuse_create_err_invalid_type,
+		    char *, "Invalid file type",
+		    struct vattr *, vap);
+		return (EINVAL);
+	}
+
+	/*
+	 * We follow the approach as in FreeBSD Fuse and provide atomic
+	 * create + open. So this create method for a regular file doesn't send
+	 * a request to user space fuse lib, rather its done in the open method
+	 */
+	*vpp = fuse_create_vnode(dvp->v_vfsp, FUSE_NULL_ID,
+	    VNODE_TO_NODEID(dvp), vap->va_type, FILE_CREATE_OP);
+	fvdata = (struct fuse_vnode_data *)(*vpp)->v_data;
+
+	/* Copy the needed parameters, to be used in open() */
+	crhold(cred_p);
+	fvdata->fcd->credp	= cred_p;
+	fvdata->fcd->flag	= flag;
+	fvdata->fcd->mode	= MAKEIMODE(vap->va_type, vap->va_mode);
+	fvdata->fcd->par_nodeid	= VNODE_TO_NODEID(dvp);
+	stringlen		= strlen(nm) + 1;
+
+	/* Be cautious against any possible attacks */
+	if (stringlen > MAXNAMELEN) {
+		stringlen = MAXNAMELEN;
+	}
+
+	fvdata->fcd->namelen	= stringlen;
+	fvdata->fcd->name	= kmem_alloc(stringlen, KM_SLEEP);
+	(void *) strlcpy(fvdata->fcd->name, nm, stringlen);
+#ifndef DONT_CACHE_ATTRIBUTES
+	invalidate_cached_attrs(dvp);
+#endif
+
+	return (0);
+}
+
+/*
+ * This function consumes directory entries sent by Fuse library in the form
+ * of fuse_direct structures. It creates equivalent dirent64 entries which is
+ * the form expected by Solaris Kernel. The logic of this function is similar
+ * to FreeBSD Fuse function fuse_dir_buffeater()
+ */
+/* ARGSUSED */
+static int
+fuse_cons_dir(struct uio *uiop, size_t reqsize, void *buf, size_t bufsize,
+    void *arg)
+{
+	struct dirent64 *de;
+	struct fuse_dirent *fudge;
+	int err = 0;
+	size_t freclen;
+	int cou = 0;
+	unsigned short bytesavail;
+	struct fuse_iov iov, *iovp = &iov;
+
+	ASSERT(bufsize <= reqsize);
+
+	/*
+	 * Sanity check: if this fails, we would overrun the allocated space
+	 * upon entering the loop below, so we'd better leave right now.
+	 * If so, we return -1 to terminate reading.
+	 */
+	if (bufsize < FUSE_NAME_OFFSET)
+		return (-1);
+
+	/*
+	 * We preallocate a buffer of maximum dirent size so that we don't have
+	 * to frequently free and alloc again.
+	 */
+	fuse_buf_alloc(iovp, MAX_DENTRY64_SIZE);
+
+	de = (struct dirent64 *)iovp->base;
+	/*
+	 * The following piece of code is from FreeBSD Fuse with slight
+	 * modification
+	 */
+	/*
+	 * Can we avoid infite loops? An infinite loop could occur only if we
+	 * leave this function with 0 return value, because otherwise we wont't
+	 * be called again. But both 0 exit points imply that some data has
+	 * been consumed... because
+	 *   1) if a turn is not aborted, it consumes positive amount of data
+	 *   2) the 0 jump-out from within the loop can't occur in the first
+	 *	  turn
+	 *   3) if we exit 0 after the loop is over, then at least one turn
+	 *	  was completed, otherwise we hed exited above with -1.
+	 */
+
+	for (;;) {
+		if (bufsize < FUSE_NAME_OFFSET) {
+			err = -1;
+			break;
+		}
+
+		cou++;
+		fudge = (struct fuse_dirent *)buf;
+		freclen = FUSE_DIRENT_SIZE(fudge);
+
+		/*
+		 * Here is an exit condition: we terminate the whole reading
+		 * process if a fresh chunk of buffer is already too short to
+		 * cut out an entry.
+		 * (It it was not the first turn in the loop, nevermind,
+		 * return with asking for more)
+		 */
+		if (bufsize < freclen) {
+			err = ((cou == 1) ? -1 : 0);
+			break;
+		}
+
+		/* Sanity checks */
+		if (!fudge->namelen || fudge->namelen > MAXNAMELEN) {
+			DTRACE_PROBE2(fuse_cons_dir_err_invalid_name,
+			    char *, "Invalid name length",
+			    struct fuse_dirent *, fudge);
+			err = EIO;
+			break;
+		}
+
+		bytesavail = DIRENT64_RECLEN(fudge->namelen);
+
+		/*
+		 * Exit condition 2: if the pretended amount of input is more
+		 * than that the userspace wants, then it's time to stop
+		 * reading.
+		 */
+		if (bytesavail > uiop->uio_resid) {
+			err = -1;
+			break;
+		}
+
+		/*
+		 * Verify if size allocated is sufficient for this record, if
+		 * not allocate as much as we need
+		 */
+		if (bytesavail > iovp->memsize) {
+			kmem_free(iovp->base, iovp->memsize);
+			fuse_buf_alloc(iovp, bytesavail);
+		}
+
+		de = (struct dirent64 *)iovp->base;
+
+		de->d_ino = fudge->ino;
+		de->d_off = fudge->off;
+		de->d_reclen = bytesavail;
+		(void *) memcpy((char *)iovp->base + DENTRY64_NAME_OFFSET,
+		    (char *)buf + FUSE_NAME_OFFSET, fudge->namelen);
+
+		((char *)iovp->base)[DENTRY64_NAME_OFFSET + fudge->namelen] =
+		    '\0';
+
+		err = uiomove(iovp->base, bytesavail, UIO_READ, uiop);
+
+		if (err)
+			break;
+
+		buf = (char *)buf + freclen;
+		bufsize -= freclen;
+
+		uiop->uio_loffset = fudge->off;
+	}
+
+	return (err);
+}
+
+/*
+ * This function performs a generic read by sending repeated requests to the
+ * fuse library and waiting for a response until the desired amount of data
+ * is not obtained. It invokes the call back function initialized by the caller
+ * after every response from the fuse library, which in turn consumes the data
+ * The logic of this function is similar to FreeBSD Fuse function:
+ * fuse_direct_backend()
+ */
+static int
+fuse_perform_read(struct fuse_io_data *fiodata)
+{
+	struct vnode		*vp = fiodata->vp;
+	struct fuse_file_handle *fh = fiodata->fh;
+	struct uio 		*uiop = fiodata->uiop;
+	cred_t 			*credp = fiodata->credp;
+	enum fuse_opcode 	op = fiodata->op;
+	fuse_consfunc_t		*consfunc = fiodata->consfunc;
+	void			*arg = fiodata->arg;
+	fuse_msg_node_t		*msgp;
+	fuse_session_t		*sep;
+	struct fuse_read_in	*fri;
+	int			err = 0;
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_perform_read_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	if (uiop->uio_resid == 0)
+		return (0);
+
+	msgp = fuse_setup_message(sizeof (*fri), op,
+	    VNODE_TO_NODEID(vp), credp, FUSE_GET_UNIQUE(sep));
+
+	while (uiop->uio_resid > 0) {
+
+		/* Set up arguments to the fuse library */
+		fri = (struct fuse_read_in *)msgp->ipdata.indata;
+		fri->fh = fh->fh_id;
+		fri->offset = uiop->uio_offset;
+		fri->size = MIN((size_t)uiop->uio_resid,
+		    PAGESIZE * FUSE_MAX_PAGES_PER_REQ);
+
+		if ((err = fuse_queue_request_wait(sep, msgp))) {
+			goto cleanup;
+		}
+
+		/* We got woken up, so fuse lib has replied to our request */
+		if ((err = msgp->opdata.fouth->error) != 0) {
+			DTRACE_PROBE2(fuse_perform_read_err_read_req,
+			    char *, "FUSE_READ request failed",
+			    struct fuse_out_header *, msgp->opdata.fouth);
+			goto cleanup;
+		}
+
+		if ((err = consfunc(uiop, fri->size, msgp->opdata.iovbuf.base,
+		    msgp->opdata.outsize, arg)))
+			break;
+
+		/* Refresh the message node if necessary */
+		if (uiop->uio_resid)
+			fuse_msg_recreate(msgp, sizeof (*fri), op,
+			    VNODE_TO_NODEID(vp), credp,
+			    FUSE_GET_UNIQUE(sep), 0);
+	}
+
+cleanup:
+	fuse_free_msg(msgp);
+	return ((err == -1) ? 0 : err);
+}
+
+/* ARGSUSED */
+static int
+fuse_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred_p,
+    int *eof_p, caller_context_t *ct, int flags)
+{
+	int err = 0;
+	struct fuse_file_handle *fh = NULL;
+	struct fuse_io_data fiodata;
+
+	if (uiop->uio_loffset < 0 || uiop->uio_resid <= 0)
+		return (ENOENT);
+
+	if (uiop->uio_iovcnt != 1)
+		return (EINVAL);
+
+	if (dvp->v_type != VDIR)
+		return (ENOTDIR);
+	/*
+	 * the readdir() libc is called in a loop, the syscall readdir calls
+	 * getdent64 [usr/src/uts/common/syscall/getdents.c#190] getdents64()
+	 * takes first param as fd, from which it gets the File pointer and
+	 * getsvnode from it. This vnode's call back is mapped to this
+	 * filesystem, thus this function gets called. The function getdents64()
+	 * passes the last offset in uio_loffset, which we must check to figure
+	 * out if EOF has been reached. It ignores the last eof pointer and
+	 * instead figures out that EOF has reached if uio_resid has not been
+	 * changed.
+	 */
+#if 0 /* TBD: Verify if this is needed */
+	if ((uiop->uio_loffset != 0) && (uiop->uio_loffset >=
+	    ((fuse_vnode_data_t *)dvp->v_data)->fileh.fh_offset)) {
+		if (eof_p)
+			*eof_p = 1;
+		return (0);
+	}
+#endif
+
+	/* Get File handle by checking through the cached file handle list */
+	if ((err = get_filehandle(dvp, FREAD, cred_p, &fh, CACHE_LIST_CHECK))) {
+		DTRACE_PROBE2(fuse_readdir_err_filehandle,
+		    char *, "get_filehandle failed",
+		    struct vnode *, dvp);
+		goto out;
+	}
+
+	if (!fh) {
+		DTRACE_PROBE2(fuse_readdir_err_filehandle,
+		    char *, "get_filehandle returned NULL handle",
+		    struct vnode *, dvp);
+		return (EIO);
+	}
+
+	/* setup fuse i/o data structure */
+	fiodata.uiop = uiop;
+	fiodata.credp = cred_p;
+	fiodata.vp = dvp;
+	fiodata.fh = fh;
+	fiodata.op = FUSE_READDIR;
+	fiodata.consfunc = fuse_cons_dir;
+
+	err = fuse_perform_read(&fiodata);
+	fh->ref--;
+#ifndef DONT_CACHE_ATTRIBUTES
+	invalidate_cached_attrs(dvp);
+#endif
+out:
+	return (err);
+}
+
+/*
+ * Check if the source vnode is in the path to the target directory
+ */
+static int
+fuse_path_check(struct vnode *svp, struct vnode *tdvp, struct cred *credp)
+{
+	struct vnode *vp = tdvp;
+	struct vnode *par_vp = NULL;
+	fuse_session_t *sep;
+	int err = 0;
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_path_check_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	for (;;) {
+		/*
+		 * if the current vnode under investigation is same as the
+		 * source vnode then return error since source is above the
+		 * target in the path
+		 */
+		if (VNODE_TO_NODEID(vp) == VNODE_TO_NODEID(svp)) {
+			err = EINVAL;
+			break;
+		}
+		if (VNODE_TO_NODEID(vp) == FUSE_ROOT_ID) {
+			break;
+		}
+		if ((VTOFD(vp)->par_nid != FUSE_NULL_ID)) {
+			/*
+			 * Try finding the parent vnode, this call
+			 * also sets the hold on the vnode
+			 */
+			err = fuse_getvnode(VTOFD(vp)->par_nid, &par_vp,
+			    VNODE_CACHED, VDIR, sep, vp->v_vfsp, 0, NULL,
+			    FUSE_NULL_ID, credp);
+			/*
+			 * We don't seem to have the parent vnode cached, since
+			 * we can't know who the parent is, even the daemon
+			 * can't help us, so return error.
+			 */
+			if (err == ENOENT) {
+				DTRACE_PROBE2(fuse_path_check_err_parent,
+				    char *, "Parent vnode not found",
+				    struct vnode *, vp);
+				/*
+				 * XXX: Is this the right error no to return?
+				 * The root directory is not in cache,
+				 * if we reached it and the source is not
+				 * the root directory, there is no error.
+				 */
+				if ((VTOFD(vp)->par_nid == FUSE_ROOT_ID)
+				    && (VNODE_TO_NODEID(svp) != FUSE_ROOT_ID))
+					err = 0;
+				else
+					err = EINVAL;
+				break;
+			} else if (err) {
+				DTRACE_PROBE3(fuse_path_check_err_getvnode,
+				    char *, "fuse_getvnode returned error",
+				    int, err, uint64_t, VTOFD(vp)->par_nid);
+				break;
+			}
+			/* Release our hold on the currently looked up dir */
+			if (vp != tdvp) {
+				VN_RELE(vp);
+			}
+			/* Start checking from this directory onwards */
+			vp = par_vp;
+		}
+	}
+	/* Release hold if any */
+	if (vp && vp != tdvp)
+		VN_RELE(vp);
+
+	return (err);
+}
+
+/* This function interacts with the daemon for rename operation */
+static int
+fuse_rename_i(struct vnode *sdvp, char *oldname, struct vnode *tdvp,
+    char *newname, struct cred *credp)
+{
+	struct fuse_rename_in *fri;
+	fuse_session_t *sep;
+	fuse_msg_node_t *msgp;
+	char *strptr;
+	int err = 0;
+	int old_namelen = strlen(oldname) + 1;
+	int new_namelen = strlen(newname) + 1;
+#ifndef DONT_CACHE_ATTRIBUTES
+	fuse_avl_cache_node_t tofind, *foundp;
+#endif
+
+	if (old_namelen > MAXNAMELEN || new_namelen > MAXNAMELEN)
+		return (ENAMETOOLONG);
+
+	sep = fuse_minor_get_session(getminor(sdvp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_rename_i_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, sdvp);
+		return (ENODEV);
+	}
+
+#ifndef DONT_CACHE_ATTRIBUTES
+	/* Invalidate cached attributes if any found */
+	tofind.facn_nodeid = FUSE_NULL_ID;
+	tofind.namelen = old_namelen;
+	tofind.name = oldname;
+	tofind.par_nodeid = VNODE_TO_NODEID(sdvp);
+	mutex_enter(&sep->avl_mutx);
+	foundp = avl_find(&(sep->avl_cache_n), &tofind, NULL);
+	mutex_exit(&sep->avl_mutx);
+	if (foundp) {
+		invalidate_cached_attrs(foundp->facn_vnode_p);
+	}
+#endif
+	msgp = fuse_setup_message((sizeof (*fri) + old_namelen + new_namelen),
+	    FUSE_RENAME, VNODE_TO_NODEID(sdvp), credp, FUSE_GET_UNIQUE(sep));
+
+	/* Set up the arguments required by the daemon */
+	fri = (struct fuse_rename_in *)msgp->ipdata.indata;
+	fri->newdir = VNODE_TO_NODEID(tdvp);
+	/* Copy the old and new name */
+	strptr = (char *)fri + sizeof (*fri);
+	(void) memcpy(strptr, oldname, old_namelen);
+	strptr += old_namelen;
+	(void) memcpy(strptr, newname, new_namelen);
+
+	err = fuse_queue_request_wait(sep, msgp);
+	if (!err) {
+		/* Check for any error from fuse library */
+		if ((err = msgp->opdata.fouth->error) != 0) {
+			DTRACE_PROBE2(fuse_rename_err,
+				char *, "FUSE_RENAME request failed",
+				struct fuse_out_header *, msgp->opdata.fouth);
+#ifndef DONT_CACHE_ATTRIBUTES
+		} else {
+			/* On success, the parent directories have changed */
+			invalidate_cached_attrs(sdvp);
+			invalidate_cached_attrs(tdvp);
+#endif
+		}
+	}
+	fuse_free_msg(msgp);
+	return (err);
+}
+
+/*
+ * Move an entry from the provided source directory to the target
+ * directory. (comment & func. prtotype copies from zfs)
+ *
+ *	IN:	sdvp	- Source directory containing the "old entry".
+ *		oldname	- Old entry name.
+ *		tdvp	- Target directory to contain the "new entry".
+ *		newname	- New entry name.
+ *		credp	- credentials of caller.
+ *
+ *	RETURN:	0 if success
+ *		error code if failure
+ *
+ * Timestamps:
+ *	sdvp, tdvp - ctime|mtime updated
+ */
+/* ARGSUSED */
+static int
+fuse_rename(vnode_t *sdvp, char *oldname, vnode_t *tdvp, char *newname,
+    cred_t *credp,  caller_context_t *ct, int flags)
+{
+	int err = EIO;
+	int samedir;
+	fuse_session_t *sep;
+	struct vnode *svp = NULL;
+	struct vnode *tvp = NULL;
+
+	/*
+	 * Check for renaming to or from '.' or '..'
+	 */
+	if ((oldname[0] == '.' &&
+	    (oldname[1] == '\0' || (oldname[1] == '.' &&
+	    oldname[2] == '\0'))) ||
+	    (newname[0] == '.' &&
+	    (newname[1] == '\0' || (newname[1] == '.' &&
+	    newname[2] == '\0')))) {
+		err = EINVAL;
+		goto errout;
+	}
+
+	/* Try obtaining the source vnode by doing a lookup */
+	err = fuse_lookup_i(sdvp, oldname, &svp, credp);
+
+	if (err)
+		return (err);
+
+	if (!svp || !GET_VDATA(svp)) {
+		cmn_err(CE_CONT,"Abnormal condition in fuse_rename"
+			" oldname %s newname %s\n",oldname,newname);
+		return (EIO);
+	}
+
+	/*
+	 * Make sure we can delete the old (source) entry.  This
+	 * requires write permission on the containing directory.  If
+	 * that directory is "sticky" it requires further checks.
+	 */
+	if (((err = fuse_access_i((void *)sdvp, VWRITE, credp)) != 0) ||
+	    (err = fuse_sticky_remove_access(sdvp, svp, credp, ct)) != 0) {
+		goto errout;
+	}
+
+	if (sdvp == svp) {
+		err = EINVAL;
+		goto errout;
+	}
+
+	samedir = (sdvp == tdvp);
+
+	/*
+	 * Make sure we can search and rename into the new
+	 * (destination) directory.
+	 */
+	if (!samedir) {
+		err = fuse_access_i((void *)tdvp, VEXEC|VWRITE, credp);
+		if (err)
+			goto errout;
+	}
+	/*
+	 * If we are trying to move a directory, then make sure there is
+	 * no pathname conflicts, i.e. make sure the source is not above the
+	 * target in the path
+	 */
+	if (svp->v_type == VDIR && !samedir) {
+		if ((err = fuse_path_check(svp, tdvp, credp))) {
+			goto errout;
+		}
+	}
+
+	/* Try doing a lookup for the target name */
+	if ((err = fuse_lookup_i(tdvp, newname, &tvp, credp))) {
+		if (err != ENOENT) {
+			goto errout;
+		} else {
+			tvp = (struct vnode*)NULL;
+			err = 0;
+		}
+	}
+	/*
+	 * if the target exists, then validate if src and target are
+	 * of compatible type so that target directory can be rewritten to
+	 * point to src
+	 */
+	if (tvp) {
+		if ((tvp->v_type == VDIR && svp->v_type != VDIR) ||
+		    (tvp->v_type != VDIR && svp->v_type == VDIR)) {
+			err = (tvp->v_type == VDIR ? EISDIR : ENOTDIR);
+			goto errout;
+		}
+	}
+
+	/* Make sure to have everything ready for renaming */
+	sep = fuse_minor_get_session(getminor(sdvp->v_rdev));
+	if (sep) {
+		fuse_avl_cache_node_t find, *renamep;
+		char *wanted_name = (char*)NULL;
+
+		find.facn_nodeid = VNODE_TO_NODEID(svp);
+		mutex_enter(&sep->avl_mutx);
+		renamep = avl_find(&(sep->avl_cache_i), &find, NULL);
+		mutex_exit(&sep->avl_mutx);
+		wanted_name = kmem_alloc(strlen(newname) + 1, KM_SLEEP);
+		if (renamep && wanted_name) {
+			/* Inform daemon to handle the rename operation */
+			if ((err = fuse_rename_i(sdvp, oldname, tdvp,
+							newname, credp))) {
+				kmem_free(wanted_name, strlen(newname) + 1);
+				goto errout;
+			} else {
+			/*
+			 * If renaming was successful, the cached entries
+			 * do not match what is stored in the file system
+			 * any more.
+			 * The old target, if any, has been deleted,
+			 * the source has kept its nodeid, but its name
+			 * has changed, so do the same in the cache.
+			 */
+				char *discarded_name;
+				int discarded_namelen;
+
+				if (tvp && (tvp != svp)) {
+					err = fuse_discard_name(tvp, sep);
+				}
+				tvp = (struct vnode*)NULL;
+				mutex_enter(&sep->avl_mutx);
+				avl_remove(&(sep->avl_cache_i), renamep);
+				avl_remove(&(sep->avl_cache_n), renamep);
+				discarded_name = renamep->name;
+				discarded_namelen = renamep->namelen;
+				renamep->par_nodeid = VNODE_TO_NODEID(tdvp);
+				VTOFD(svp)->par_nid = renamep->par_nodeid;
+				renamep->namelen = strlen(newname) + 1;
+				renamep->name = wanted_name;
+				strlcpy(renamep->name, newname,
+							renamep->namelen);
+				avl_add(&(sep->avl_cache_i), renamep);
+				avl_add(&(sep->avl_cache_n), renamep);
+				mutex_exit(&sep->avl_mutx);
+				kmem_free(discarded_name, discarded_namelen);
+			}
+		} else {
+			if (wanted_name)
+				kmem_free(wanted_name, strlen(newname) + 1);
+			err = ENOENT;
+		}
+	} else {
+		err = ENODEV;
+	}
+
+errout:
+	if (svp)
+		VN_RELE(svp);
+	if (tvp)
+		VN_RELE(tvp);
+	return (err);
+}
+
+/* ARGSUSED */
+static int
+fuse_fsync(struct vnode *vp, int syncflag, struct cred *credp,
+    caller_context_t *ct)
+{
+	int err;
+	struct fuse_fh_param fh_param;
+
+	/* XXX: Do locking related logic here */
+
+	/* Flush out any dirty pages associated with this vnode */
+	if (vn_has_cached_data(vp) && !(syncflag & FNODSYNC) &&
+	    (vp->v_type != VCHR)) {
+		err = VOP_PUTPAGE(vp, (offset_t)0, (size_t)0, 0, credp, ct);
+		if (err)
+			return (err);
+	}
+	/*
+	 * We will iterate through all the file handles associated with this
+	 * vnode and send FUSE_FSYNC/FUSE_FSYNCDIR. See comment in FreeBSD
+	 * Fuse fsync implementation for more information
+	 */
+	fh_param.vp = vp;
+	fh_param.credp = credp;
+	fh_param.rw_mode = FWRITE;
+	fh_param.fufh = NULL;
+	(void) iterate_filehandle(vp, fuse_fsync_fh, &fh_param, NULL);
+
+	return (0);
+}
+
+/*
+ * This function sends FUSE_FSYNC/FUSE_FSYNCDIR on the passed
+ * file handle associated with the concerned vnode
+ */
+static int
+fuse_fsync_fh(struct fuse_file_handle *fhp, struct fuse_fh_param *param)
+{
+	struct fuse_fsync_in *ffsi;
+	fuse_msg_node_t *msgp;
+	fuse_session_t *sep;
+	enum fuse_opcode op = (param->vp->v_type == VDIR) ? FUSE_FSYNCDIR :
+	    FUSE_FSYNC;
+
+	sep = fuse_minor_get_session(getminor(param->vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_fsync_fh_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, param->vp);
+		return (ENODEV);
+	}
+
+	msgp = fuse_setup_message(sizeof (*ffsi), op,
+	    VNODE_TO_NODEID(param->vp), param->credp, FUSE_GET_UNIQUE(sep));
+	ffsi = (struct fuse_fsync_in *)msgp->ipdata.indata;
+	ffsi->fh = fhp->fh_id;
+	/* We want to sync file data and not just metadata */
+	ffsi->fsync_flags = 1;
+
+	(void) fuse_queue_request_wait(sep, msgp);
+
+	fuse_free_msg(msgp);
+	/*
+	 * Since this is called from iterate_filehandle() we will not return
+	 * obtained err if any. This is to ensure that all the file handles
+	 * in the list are serviced.
+	 */
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+fuse_seek(struct vnode *vp, offset_t ooff, offset_t *noffp,
+    caller_context_t *ct)
+{
+	return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
+}
+
+/*
+ * Remove an entry from a directory.
+ *
+ *	IN:	dvp	- vnode of directory to remove entry from.
+ *		name	- name of entry to remove.
+ *		credp	- credentials of caller.
+ *
+ *	RETURN:	0 if success
+ *		error code if failure
+ *
+ * Timestamps:
+ *	dvp - ctime|mtime
+ *	 vp - ctime (if nlink > 0)
+ */
+/* ARGSUSED */
+static int
+fuse_remove(vnode_t *dvp, char *name, cred_t *credp, caller_context_t *ct,
+    int flags)
+{
+	int err;
+	int seen_err;
+	fuse_msg_node_t *msgp;
+	fuse_session_t *sep;
+	int namelen = strlen(name) + 1;
+	struct vnode *vp = NULL;
+
+	sep = fuse_minor_get_session(getminor(dvp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_remove_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	/* Check if we have seen and cached the associated vnode */
+	seen_err = fuse_getvnode(FUSE_NULL_ID, &vp, VNODE_CACHED,
+	    0, sep, dvp->v_vfsp, namelen, name, VNODE_TO_NODEID(dvp), credp);
+
+	/* Do not proceed if this is a directory */
+	if (!seen_err && vp && (vp->v_type == VDIR)) {
+		return (EPERM);
+	}
+
+	msgp = fuse_setup_message(namelen, FUSE_UNLINK,
+	    VNODE_TO_NODEID(dvp), credp, FUSE_GET_UNIQUE(sep));
+
+	/* Set up arguments to the fuse library */
+	(void) memcpy(msgp->ipdata.indata, name, namelen - 1);
+	((char *)msgp->ipdata.indata)[namelen - 1] = '\0';
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto cleanup;
+	}
+
+	/* Check for any error from fuse library */
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(fuse_unlink_err_unlink_req,
+			char *, "FUSE_UNLINK request failed",
+			struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+
+#ifndef DONT_CACHE_ATTRIBUTES
+	/* On success, the parent directory has changed */
+	invalidate_cached_attrs(dvp);
+#endif
+
+	if (seen_err) {
+		err = seen_err;
+		if (err == ENOENT) {
+			/*
+			 * This means, we don't have this vnode, so
+			 * there is nothing else to do, just reset the error
+			 * and return.
+			 */
+			err = 0;
+		}
+		goto cleanup;
+	}
+	ASSERT(vp != NULL);
+	/*
+	 * Destroy the vnode, but we can't go through the sane way of
+	 * destroying the vnode which involves sending FUSE_FORGET message
+	 * to the daemon, flushing dirty data and so on. All this is a bit
+	 * too heavy weight when we don't have to care about this vnode any
+	 * more. So we just free all associated memory with this vnode and
+	 * return it to kernel.
+	 */
+	/*
+	 * XXX: This call releases vnode with vn_free, can this
+	 * be an issue? Or does VN_RELE do the required job for us?
+	 */
+	err = fuse_discard_name(vp, sep);
+cleanup:
+	fuse_free_msg(msgp);
+	return (err);
+}
+
+/*
+ * This function does the release of all the associated
+ * memory with the passed vnode
+ */
+static inline void
+fuse_vnode_free(struct vnode *vp, fuse_session_t *sep)
+{
+	struct fuse_file_handle *fhp;
+	struct fuse_vnode_data *fvdatap = VTOFD(vp);
+
+	if (VTOFD(vp)) {
+			/* Invalidate cached pages */
+		if (vn_has_cached_data(vp)) {
+			pvn_vplist_dirty(vp, 0, fuse_putapage,
+					 B_INVAL, sep->usercred);
+		}
+		mutex_enter(&fvdatap->fh_list_lock);
+		for (fhp = list_head(&fvdatap->fh_list); fhp;
+		    fhp = list_next(&fvdatap->fh_list, fhp)) {
+			list_remove(&fvdatap->fh_list, fhp);
+			kmem_free(fhp, sizeof (*fhp));
+		}
+		mutex_exit(&fvdatap->fh_list_lock);
+		fuse_vnode_cache_remove(vp, sep);
+		fuse_free_vdata(vp);
+	}
+	vp->v_data = NULL;
+
+	VFS_RELE(vp->v_vfsp);
+	if (!vn_has_cached_data(vp)) /* be safe : better leak than corruption */
+		vn_free(vp);
+}
+
+/*
+ *		Destroy all cached data when unmounting
+ */
+
+void fuse_destroy_cache(fuse_session_t *sep)
+{
+	fuse_avl_cache_node_t *item;
+	int failed;
+
+	mutex_enter(&sep->avl_mutx);
+	item = (fuse_avl_cache_node_t*)avl_first(&(sep->avl_cache_i));
+	mutex_exit(&sep->avl_mutx);
+	failed = 0;
+	if (item)
+		do {
+			struct vnode *vp = item->facn_vnode_p;
+			struct fuse_fh_param fh_param;
+
+			if (vp && VTOFD(vp)) {
+				/*
+				 * Invalidate pages associated to vnode :
+				 * http://wesunsolve.net/bugid/id/6732672
+				 * http://wesunsolve.net/bugid/id/6730916
+				 */
+				if (vn_has_cached_data(vp)) {
+				/*
+				 * This calls VOP_DISPOSE() for each page
+				 * with flag == 0x10000 and dn == 0
+				 */
+					pvn_vplist_dirty(vp, 0, fuse_putapage,
+						B_INVAL, sep->usercred);
+				}
+				/* Release any file handles */
+				fh_param.vp = vp;
+				fh_param.flag = FUSE_FORCE_FH_RELEASE;
+				iterate_filehandle(vp, fuse_release_fh,
+						&fh_param, NULL);
+				/* Finally remove from our cache */
+				fuse_vnode_cache_remove(vp, sep);
+				fuse_free_vdata(vp);
+				vp->v_data = NULL;
+				VFS_RELE(vp->v_vfsp);
+				if (!vn_has_cached_data(vp))
+	 				vn_free(vp);
+				mutex_enter(&sep->avl_mutx);
+				item = (fuse_avl_cache_node_t*)
+						avl_first(&(sep->avl_cache_i));
+				mutex_exit(&sep->avl_mutx);
+			} else {
+				failed = 1;
+				cmn_err(CE_WARN,"Could not locate fuse cached data\n");
+			}
+		} while (!failed && item);
+}
+
+/*
+ * Insert the indicated symbolic reference entry into the directory.
+ *
+ *	IN:	dvp	- Directory to contain new symbolic link.
+ *		name	- Name for new symlink entry.
+ *		vap	- Attributes of new entry.
+ *		target	- Target path of new symlink.
+ *		cr	- credentials of caller.
+ *
+ *	RETURN:	0 if success
+ *		error code if failure
+ *
+ * Timestamps:
+ *	dvp - ctime|mtime updated
+ */
+/* ARGSUSED */
+static int
+fuse_symlink(vnode_t *dvp, char *name, vattr_t *vap, char *target,
+    cred_t *credp,  caller_context_t *ct, int flags)
+{
+	fuse_msg_node_t *msgp;
+	fuse_session_t *sep;
+	int err = DDI_SUCCESS;
+	int namelen = strlen(name) + 1;
+	int targlen = strlen(target) + 1;
+	struct vnode *vp;
+
+	sep = fuse_minor_get_session(getminor(dvp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_symlink_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, dvp);
+		return (ENODEV);
+	}
+
+	/* Make sure the target does not exist */
+	err = fuse_lookup_i(dvp, name, &vp, credp);
+	if (!err && vp) {
+		VN_RELE(vp);
+		return (EEXIST);
+	}
+
+	msgp = fuse_setup_message(namelen + targlen, FUSE_SYMLINK,
+	    VNODE_TO_NODEID(dvp), credp, FUSE_GET_UNIQUE(sep));
+
+	/* Set up arguments to the fuse library */
+	(void) memcpy(msgp->ipdata.indata, name, namelen - 1);
+	((char *)msgp->ipdata.indata)[namelen - 1] = '\0';
+	(void) memcpy((char *)msgp->ipdata.indata + namelen, target,
+	    targlen - 1);
+	((char *)msgp->ipdata.indata)[namelen + targlen - 1] = '\0';
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto cleanup;
+	}
+
+	/* We got woken up, so fuse lib has replied to our release request */
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(fuse_symlink_err_symlink_req,
+		    char *, "FUSE_SYMLINK request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+
+
+	err = fuse_add_entry(&vp, dvp, msgp, sep, name, namelen, credp, VLNK);
+
+cleanup:
+	fuse_free_msg(msgp);
+	return (err);
+}
+/*
+ * Return, in the buffer contained in the provided uio structure,
+ * the symbolic path referred to by vp.
+ *
+ *	IN:	vp	- vnode of symbolic link.
+ *		uoip	- structure to contain the link path.
+ *		cr	- credentials of caller.
+ *
+ *	OUT:	uio	- structure to contain the link path.
+ *
+ *	RETURN:	0 if success
+ *		error code if failure
+ *
+ * Timestamps:
+ *	vp - atime updated
+ */
+/* ARGSUSED */
+static int
+fuse_readlink(vnode_t *vp, uio_t *uiop, cred_t *cred_p, caller_context_t *ct)
+{
+	int err = DDI_SUCCESS;
+	fuse_msg_node_t *msgp;
+	fuse_session_t *sep;
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_readlink_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	msgp = fuse_alloc_msg();
+
+	/*
+	 * setup the message with only header and no arguments to be sent to
+	 * fuse library
+	 */
+	setup_msg_onlyheader(msgp, FUSE_READLINK, FUSE_GET_UNIQUE(sep),
+	    VNODE_TO_NODEID(vp), cred_p);
+
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto out;
+	}
+
+	err = msgp->opdata.fouth->error;
+
+	if (!err) {
+		err = uiomove(msgp->opdata.outdata, msgp->opdata.outsize,
+		    UIO_READ, uiop);
+	} else {
+		DTRACE_PROBE2(fuse_readlink_err_readlink_req,
+		    char *, "FUSE_READLINK request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+	}
+
+#ifndef DONT_CACHE_ATTRIBUTES
+	invalidate_cached_attrs(vp);
+#endif
+
+out:
+	fuse_free_msg(msgp);
+	return (err);
+}
+
+/*
+ * Create a special file (BLK, CHR, FIFO, SOCK)
+ *
+ */
+/* ARGSUSED */
+static int
+fuse_mknod(struct vnode *dvp, char *name, struct vattr *vap, vcexcl_t excl,
+    int mode, struct vnode **vpp, struct cred *credp, int flag,
+    caller_context_t *ct, vsecattr_t *va)
+{
+	fuse_msg_node_t *msgp;
+	fuse_session_t *sep;
+	struct fuse_mknod_in *fmni;
+	int err = DDI_SUCCESS;
+	int namelen = strlen(name) + 1;
+
+	sep = fuse_minor_get_session(getminor(dvp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_mkfifo_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, dvp);
+		return (ENODEV);
+	}
+
+	msgp = fuse_setup_message(namelen + sizeof(*fmni), FUSE_MKNOD,
+	    VNODE_TO_NODEID(dvp), credp, FUSE_GET_UNIQUE(sep));
+
+	fmni = (struct fuse_mknod_in*)msgp->ipdata.indata;
+	switch (vap->va_type) {
+	case VBLK :
+		fmni->mode = (vap->va_mode & ~S_IFMT) | S_IFBLK;
+#ifdef _LP64
+		/* Must pack the device the old way to fit into fmni->rdev */
+		if (((unsigned int)getmajor(vap->va_rdev) > 0x3fff)
+		    || ((unsigned int)getminor(vap->va_rdev) > 0x3ffff))
+			return (EINVAL);
+		fmni->rdev = ((getmajor(vap->va_rdev) & 0x3fff) << 18)
+				| (getminor(vap->va_rdev) & 0x3ffff);
+#else
+		fmni->rdev = vap->va_rdev;
+#endif
+		break;
+	case VCHR :
+		fmni->mode = (vap->va_mode & ~S_IFMT) | S_IFCHR;
+#ifdef _LP64
+		/* Must pack the device the old way to fit into fmni->rdev */
+		if (((unsigned int)getmajor(vap->va_rdev) > 0x3fff)
+		    || ((unsigned int)getminor(vap->va_rdev) > 0x3ffff))
+			return (EINVAL);
+		fmni->rdev = ((getmajor(vap->va_rdev) & 0x3fff) << 18)
+				| (getminor(vap->va_rdev) & 0x3ffff);
+#else
+		fmni->rdev = vap->va_rdev;
+#endif
+		break;
+	case VFIFO :
+		fmni->mode = (vap->va_mode & ~S_IFMT) | S_IFIFO;
+		fmni->rdev = 0;
+		break;
+	case VSOCK :
+		fmni->mode = (vap->va_mode & ~S_IFMT) | S_IFSOCK;
+		fmni->rdev = 0;
+		break;
+	default :
+		return (ENODEV);
+		break;
+	}
+	/* Set up arguments to the fuse library */
+	memcpy((char*)msgp->ipdata.indata + sizeof(*fmni), name, namelen - 1);
+	((char *)msgp->ipdata.indata)[sizeof(*fmni) + namelen - 1] = '\0';
+
+	if ((err = fuse_queue_request_wait(sep, msgp))) {
+		goto cleanup;
+	}
+
+	/* We got woken up, so fuse lib has replied to our release request */
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(fuse_mkfifo_err_mkfifo_req,
+		    char *, "FUSE_MKFIFO request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+
+	err = fuse_add_entry(vpp, dvp, msgp, sep, name, namelen,
+				credp, vap->va_type);
+
+cleanup:
+	fuse_free_msg(msgp);
+	return (err);
+}
+
+/*
+ * Sends the FUSE_FORGET message to daemon which performs the necessary
+ * cleanup
+ */
+static void
+fuse_send_forget(uint64_t nodeid, fuse_session_t *sep, uint64_t nlookup)
+{
+	fuse_msg_node_t *msgp;
+	struct fuse_forget_in *ffi;
+
+	msgp = fuse_setup_message(sizeof (*ffi), FUSE_FORGET,
+	    nodeid, sep->usercred, FUSE_GET_UNIQUE(sep));
+
+	/* Set up arguments to the fuse library */
+	ffi = (struct fuse_forget_in *)msgp->ipdata.indata;
+	ffi->nlookup = nlookup;
+
+	/* Send the request to the fuse daemon and return */
+	msgp->fmn_noreply = 1;
+	fuse_queue_request_nowait(sep, msgp);
+}
+
+/*
+ * Returns the file size associated with the passed vnode, if needed contacts
+ * the daemon
+ */
+static int
+fuse_getfilesize(struct vnode *vp, u_offset_t *fsize, struct cred *credp)
+{
+	int err;
+	struct vattr va;
+	fuse_msg_node_t *msgp;
+
+	if (!GET_VDATA(vp)) {
+		cmn_err(CE_CONT,"Abnormal condition in fuse_getfilesize\n");
+		return (EIO);
+	}
+	/* Provide, if the updated file size is available with us */
+	if (VTOFD(vp)->file_size_status & FSIZE_UPDATED) {
+		*fsize = VTOFD(vp)->fsize;
+		DTRACE_PROBE2(fuse_getfilesize_info_cache,
+		    char *, "returning file size from cache",
+		    u_offset_t, *fsize);
+		return (0);
+	}
+#ifndef DONT_CACHE_ATTRIBUTES
+	/*
+	 * Next step is to check if the attribute cache is valid, if so
+	 * no need to ask the daemon for the file size
+	 */
+	timestruc_t ts;
+	gethrestime(&ts);
+	if (!(VTOFD(vp)->file_size_status & FSIZE_NOT_RELIABLE)
+	    && ATTR_CACHE_VALID(ts, VTOFD(vp)->cached_attrs_bound)) {
+		*fsize = VTOFD(vp)->cached_attrs.va_size;
+		DTRACE_PROBE2(fuse_getfilesize_info_attr_cache,
+		    char *, "returning file size from attr cache",
+		    u_offset_t, *fsize);
+		return (0);
+	}
+#endif
+	/* We are not lucky, have to get file size from daemon */
+	err = fuse_getattr_from_daemon(vp, &va, credp, &msgp);
+
+	if (err)
+		return (err);
+
+	*fsize = va.va_size;
+	DTRACE_PROBE2(fuse_getfilesize_info_daemon,
+	    char *, "returning file size from daemon",
+	    u_offset_t, *fsize);
+
+	fuse_free_msg(msgp);
+	return (err);
+}
+
+/* Performs the mapping of a page to kernel virtual address space */
+static void
+fuse_page_mapin(struct vnode *vp, struct buf **bp, struct page *pp, size_t len,
+    int flag, struct fuse_iov *iovp)
+{
+
+	/* Map the page into kernel virtual memory */
+	*bp = pageio_setup(pp, len, vp, flag);
+	ASSERT((*bp) != NULL);
+
+	(*bp)->b_edev = 0;
+	(*bp)->b_dev = 0;
+	(*bp)->b_lblkno = 0;
+	(*bp)->b_file = vp;
+	(*bp)->b_offset = 0;
+
+	bp_mapin((*bp));
+
+	iovp->memsize = len;
+	iovp->len = 0;
+	iovp->memflag = MEM_TYPE_PAGE;
+	iovp->base = (*bp)->b_un.b_addr;
+
+}
+
+/*
+ * Free storage space associated with the specified vnode. The portion
+ * to be freed is specified by bfp->l_start and bfp->l_len (already
+ * normalized to a "whence" of 0).
+ *
+ * This is an experimental facility whose continued existence is not
+ * guaranteed.  Currently, we only support the special case
+ * of l_len == 0, meaning free to end of file.
+ */
+/* ARGSUSED */
+static int
+fuse_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
+    offset_t offset, cred_t *cr, caller_context_t *ct)
+{
+	int error;
+
+	if (cmd != F_FREESP)
+		return (EINVAL);
+
+	ASSERT(vp->v_type == VREG);
+
+	error = convoff(vp, bfp, 0, offset);
+	if (error == 0) {
+		ASSERT(bfp->l_start >= 0);
+
+		if (bfp->l_len == 0) {
+			vattr_t va;
+
+			va.va_mask = AT_SIZE;
+			error = VOP_GETATTR(vp, &va, 0, cr, ct);
+			if (error || va.va_size == (u_offset_t)bfp->l_start) {
+				/*
+				 * do not update ctime/mtime if truncate
+				 * to previous size, just exit
+				 */
+				return (error);
+			}
+			va.va_mask = AT_SIZE;
+			va.va_size = bfp->l_start;
+			error = VOP_SETATTR(vp, &va, 0, cr, ct);
+		}
+		else
+			error = EINVAL;
+	}
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+fuse_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp,
+    size_t *lenp, int flags, cred_t *credp)
+{
+	fuse_session_t *sep;
+	u_offset_t fsize;
+	int err, diff;
+	struct fuse_write_in *fwi;
+	struct fuse_write_out *fwo;
+	struct fuse_file_handle *fhp = NULL;
+	fuse_msg_node_t *msgp = NULL;
+	struct buf *bp = NULL;
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_putapage_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return (ENODEV);
+	}
+
+	if ((err = get_filehandle(vp, FWRITE, credp, &fhp, CACHE_LIST_CHECK))) {
+		DTRACE_PROBE2(fuse_putapage_err_filehandle,
+		    char *, "get_filehandle failed",
+		    struct vnode *, vp);
+		goto cleanup;
+	}
+	if ((err = fuse_getfilesize(vp, &fsize, credp))) {
+		DTRACE_PROBE3(fuse_putapage_err_file_size,
+		    char *, "fuse_getfilesize failed",
+		    int, err, struct vnode *, vp);
+		goto cleanup;
+	}
+	msgp = fuse_setup_message(sizeof (*fwi),
+	    FUSE_WRITE, VTOFD(vp)->nodeid, credp, FUSE_GET_UNIQUE(sep));
+
+	/* Set up arguments to the fuse library */
+	fwi = (struct fuse_write_in *)msgp->ipdata.indata;
+
+	fwi->fh = fhp->fh_id;
+	fwi->offset = pp->p_offset;
+	fwi->size = (uint32_t)MIN(PAGESIZE, fsize - pp->p_offset);
+	/*
+	 * XXX:There is a lot of room for optimization here, we can do
+	 * page klustering and then create a scatter gather list
+	 * of pages and their addresses to be used by device read
+	 */
+
+	/* Map this page in to kernel virtual address space */
+	fuse_page_mapin(vp, &bp, pp, PAGESIZE, B_WRITE,
+	    &(msgp->ipdata.iovbuf[1]));
+	msgp->ipdata.iovbuf[1].len = fwi->size;
+	msgp->ipdata.iovs_used = 2;
+
+	if ((err = fuse_queue_request_wait(sep, msgp)) != 0) {
+		goto cleanup;
+	}
+
+	if ((err = msgp->opdata.fouth->error) !=  0) {
+		DTRACE_PROBE2(fuse_putapage_err_write_req,
+		    char *, "FUSE_WRITE request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+	fwo = (struct fuse_write_out *)msgp->opdata.outdata;
+	diff = fwi->size - fwo->size;
+	if (diff < 0) {
+		err = EINVAL;
+		goto cleanup;
+	}
+	if (diff > 0) {
+		/*
+		 * This means daemon could do only a partial write, we
+		 * currently don't manage the hassles of handling partial
+		 * write with possibly partial dirty pages, so we treat
+		 * as an error
+		 */
+		DTRACE_PROBE2(fuse_putapage_err_short_write,
+		    char *, "daemon executed partial write ",
+		    struct fuse_write_out *, fwo);
+		err = EIO;
+		goto cleanup;
+	}
+cleanup:
+	if (fhp)
+		fhp->ref--;
+	if (err) {
+		flags |= B_ERROR;
+	} else {
+		if (lenp)
+			*lenp = fwi->size;
+	}
+
+	if (bp != NULL) {
+		bp_mapout(bp);
+		pageio_done(bp);
+	}
+	if (msgp != NULL)
+		fuse_free_msg(msgp);
+
+	(void) pvn_write_done(pp, B_WRITE | flags);
+
+	return (err);
+}
+
+/*
+ * If len == 0, do from off to EOF.
+ *
+ * The normal cases should be len == 0 & off == 0 (entire vp list),
+ * len == MAXBSIZE (from segmap_release actions), and len == PAGESIZE
+ * (from pageout).
+ */
+/* ARGSUSED */
+static int
+fuse_putpage(struct vnode *vp, offset_t off, size_t len, int flags,
+	struct cred *credp, caller_context_t *ct)
+{
+	register page_t *pp;
+	int err = 0;
+	size_t io_len = 0;
+	u_offset_t fsize = 0;
+	u_offset_t io_off;
+	u_offset_t eoff;
+
+	if (vp->v_count == 0) {
+		DTRACE_PROBE2(fuse_putpage_err_vcount,
+		    char *, "vnode v_count == 0",
+		    struct vnode *, vp);
+		return (EAGAIN);
+	}
+
+	if (vp->v_flag & VNOMAP)
+		return (ENOSYS);
+
+	if (!vn_has_cached_data(vp))
+		return (0);
+
+	if (len == 0) {
+		err = pvn_vplist_dirty(vp, (u_offset_t)off, fuse_putapage,
+		    flags, credp);
+	} else {
+		if (!GET_VDATA(vp)) {
+			cmn_err(CE_CONT,"Abnormal condition in fuse_putpage"
+				" off 0x%lx len %ld flags 0x%x\n",
+				(long)off,(long)len,(int)flags);
+			return (EIO);
+		}
+		if ((err = fuse_getfilesize(vp, &fsize, credp))) {
+			DTRACE_PROBE3(fuse_putpage_err_filesize,
+			    char *, "fuse_getfilesize failed",
+			    int, err, struct vnode *, vp);
+			return (err);
+		}
+
+		eoff = MIN((u_offset_t)(off + len), fsize);
+
+		for (io_off = off; io_off < eoff; io_off += io_len) {
+			/*
+			 * If we are not invalidating, synchronously
+			 * freeing or writing pages use the routine
+			 * page_lookup_nowait() to prevent reclaiming
+			 * them from the free list.
+			 */
+			if ((flags & B_INVAL) || ((flags & B_ASYNC) == 0)) {
+				pp = page_lookup(vp, io_off,
+				    (flags & (B_INVAL | B_FREE)) ?
+				    SE_EXCL : SE_SHARED);
+			} else {
+				pp = page_lookup_nowait(vp, io_off,
+				    (flags & B_FREE) ? SE_EXCL : SE_SHARED);
+			}
+
+			if (pp == NULL || pvn_getdirty(pp, flags) == 0) {
+				io_len = PAGESIZE;
+			} else {
+				err = fuse_putapage(vp, pp, &io_off, &io_len,
+				    flags, credp);
+				if (err != 0)
+					break;
+			}
+		}
+	}
+	/* If invalidating, verify all pages on vnode list are gone. */
+	if (err == 0 && off == 0 && len == 0 &&
+	    (flags & B_INVAL) && vn_has_cached_data(vp)) {
+		panic("fuse_putpage: B_INVAL, pages not gone");
+	}
+	/*
+	 * If all the dirty pages was written back, remove the file size changed
+	 * status flag
+	 */
+	if (len == 0 || eoff == fsize) {
+		(void) fsize_change_notify(vp, 0, FSIZE_NOT_RELIABLE);
+	}
+	return (err);
+}
+
+/* ARGSUSED */
+static int
+fuse_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
+    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags,
+    cred_t *cr, caller_context_t *ct)
+{
+	struct segvn_crargs vn_a;
+	int error;
+
+	if (vp->v_flag & VNOMAP)
+		return (ENOSYS);
+
+	if (off < (offset_t)0 || (offset_t)(off + len) < (offset_t)0)
+		return (ENXIO);
+
+	if (vp->v_type != VREG)
+		return (ENODEV);
+
+	/*
+	 * XXX: If file is being locked, disallow mapping.
+	 */
+
+	as_rangelock(as);
+#ifdef ADDR_VACALIGN
+		/* OpenIndiana */
+	error = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags);
+#else
+		/* Solaris */
+	error = choose_addr(as, addrp, len, off, flags);
+#endif
+	if (error != 0) {
+		as_rangeunlock(as);
+		goto out;
+	}
+
+	vn_a.vp = vp;
+	vn_a.offset = off;
+	vn_a.type = (flags & MAP_TYPE);
+	vn_a.prot = prot;
+	vn_a.maxprot = maxprot;
+	vn_a.cred = cr;
+	vn_a.amp = NULL;
+	vn_a.flags = (flags & ~MAP_TYPE);
+	vn_a.szc = 0;
+	vn_a.lgrp_mem_policy_flags = 0;
+
+	error = as_map(as, *addrp, len, segvn_create, &vn_a);
+	as_rangeunlock(as);
+
+out:
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+fuse_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+    size_t len, uchar_t prot, uchar_t maxprot, uint_t flags,
+    cred_t *cr, caller_context_t *ct)
+{
+	long oldcnt;
+	int err = 0;
+	struct fuse_file_handle *fhp;
+
+	if (vp->v_flag & VNOMAP)
+		return (ENOSYS);
+
+	mutex_enter(&VTOFD(vp)->f_lock);
+	oldcnt = VTOFD(vp)->f_mapcnt;
+	VTOFD(vp)->f_mapcnt += btopr(len);
+	mutex_exit(&VTOFD(vp)->f_lock);
+	if (!oldcnt) {
+		/*
+		 * Increment the reference count on the vnode, so that
+		 * the data associated will not be freed on close()
+		 * and kept available for subsequent getapage().
+		 */
+		err = get_filehandle(vp, FREAD | FWRITE, cr, &fhp,
+					CACHE_LIST_CHECK);
+	}
+
+	return (err);
+}
+
+/* ARGSUSED */
+static int
+fuse_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+    size_t len, uint_t prot, uint_t maxprot, uint_t flags,
+    cred_t *cr, caller_context_t *ct)
+{
+	int err = 0;
+
+	if (vp->v_flag & VNOMAP)
+		return (ENOSYS);
+
+	mutex_enter(&VTOFD(vp)->f_lock);
+	VTOFD(vp)->f_mapcnt -= btopr(len);
+	mutex_exit(&VTOFD(vp)->f_lock);
+
+	if ((flags & MAP_SHARED) && (prot & PROT_WRITE) &&
+	    vn_has_cached_data(vp))
+		(void) VOP_PUTPAGE(vp, off, len, B_ASYNC, cr, ct);
+
+		/*
+		 * If there is no more reference to the vnode, we can
+		 * now release the data which may have been delayed
+		 * by incrementing the reference count in addmap().
+		 */
+	if (!VTOFD(vp)->f_mapcnt)
+		err = fuse_close(vp, flags | FREAD | FWRITE, 1, 0, cr, ct);
+
+	return (err);
+}
+
+static void
+fuse_vnode_cache_remove(struct vnode *vp, fuse_session_t *sep)
+{
+	avl_index_t where;
+	fuse_avl_cache_node_t discard, *removep;
+
+	discard.facn_nodeid = VNODE_TO_NODEID(vp);
+	mutex_enter(&sep->avl_mutx);
+	if ((removep = avl_find(& (sep->avl_cache_i), &discard, &where))
+	    != NULL) {
+		avl_remove(&(sep->avl_cache_i), removep);
+		/* Files deleted while open are not recorded in tree by name */
+		if (removep->namelen > 1)
+			avl_remove(&(sep->avl_cache_n), removep);
+		mutex_exit(&sep->avl_mutx);
+		fuse_avl_cache_node_destroy(removep);
+	} else
+		mutex_exit(&sep->avl_mutx);
+}
+
+/* Does all the necessary cleanup w.r.t a vnode */
+static void
+fuse_vnode_cleanup(struct vnode *vp, struct cred *credp, fuse_session_t *sep)
+{
+	struct fuse_fh_param fh_param;
+	/* Invalidate pages while flushing out any dirty data */
+	if (vn_has_cached_data(vp)) {
+		(void) pvn_vplist_dirty(vp, 0, fuse_putapage, B_FREE, credp);
+	}
+	/* Release if the vnode has any file handles with it */
+	if (VTOFD(vp)) {
+		fh_param.vp = vp;
+		fh_param.flag = FUSE_FORCE_FH_RELEASE;
+		(void) iterate_filehandle(vp, fuse_release_fh, &fh_param, NULL);
+	}
+	/* Let daemon know that we are getting rid of this vnode */
+	fuse_send_forget(VNODE_TO_NODEID(vp), sep, (VTOFD(vp))->nlookup);
+
+	/* Finally its time to remove it from our cache */
+	fuse_vnode_cache_remove(vp, sep);
+}
+
+/* ARGSUSED */
+static void
+fuse_inactive(struct vnode *vp, struct cred *credp, caller_context_t *ct)
+{
+	struct fuse_fh_param fh_param;
+	fuse_session_t *sep;
+	int deleted;
+
+	fh_param.vp = vp;
+	fh_param.flag = 0;
+
+	sep = fuse_minor_get_session(getminor(vp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_inactive_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, vp);
+		return;
+	}
+
+	/*
+	 * The file may have been deleted while if was open,
+	 * if so, remove from cache.
+	 */
+	deleted = 0;
+
+	if (GET_VDATA(vp) && (vp->v_type == VREG)
+	    && !vn_is_opened(vp,V_RDORWR)) {
+		fuse_avl_cache_node_t find, *closep;
+
+		find.facn_nodeid = VNODE_TO_NODEID(vp);
+		mutex_enter(&sep->avl_mutx);
+		closep = avl_find(&(sep->avl_cache_i), &find, NULL);
+		mutex_exit(&sep->avl_mutx);
+		if (closep && (closep->namelen < 2)) {
+			fuse_vnode_free(vp, sep);
+			deleted = 1;
+		}
+	}
+	if (!deleted && GET_VDATA(vp)) {
+		(void) iterate_filehandle(vp, fuse_release_fh, &fh_param, NULL);
+		mutex_enter(&vp->v_lock);
+		if (vp->v_count > 1) {
+			vp->v_count--;
+			mutex_exit(&vp->v_lock);
+			return;
+		}
+		mutex_exit(&vp->v_lock);
+#ifndef FLUSH_DATA_ON_VNODE_RELEASE
+
+	/*
+	 * We don't free any pages associated with this vnode, nor do we
+	 * remove it from the AVL tree
+	 */
+		return;
+#else
+		if (!(vp->v_flag & VROOT)) {
+			fuse_vnode_destroy(vp, credp, sep);
+		}
+#endif
+	}
+}
+
+static int
+fuse_add_entry(struct vnode **vpp, struct vnode *dvp, fuse_msg_node_t *msgp,
+    fuse_session_t *sep, char *name, int namelen, cred_t *credp, vtype_t vtype)
+{
+	int err = DDI_SUCCESS;
+	struct fuse_entry_out *feo =
+	    (struct fuse_entry_out *)msgp->opdata.outdata;
+
+	if ((err = checkentry(feo, vtype))) {
+		return (err);
+	}
+
+	err = fuse_getvnode(feo->nodeid, vpp, VNODE_NEW,
+	    IFTOVT(feo->attr.mode), sep, dvp->v_vfsp, namelen, name,
+	    VNODE_TO_NODEID(dvp), credp);
+
+	if (err) {
+		DTRACE_PROBE3(fuse_add_entry_err_getvnode,
+		    char *, "fuse_getvnode returned error",
+		    int, err, uint64_t, feo->nodeid);
+		fuse_send_forget(feo->nodeid, sep, 1);
+		return (err);
+	}
+#ifndef DONT_CACHE_ATTRIBUTES
+	invalidate_cached_attrs(dvp);
+	cache_attrs(sep, (*vpp), feo);
+#endif
+	return (err);
+}
+
+/*
+ * Create a new directory and insert it into dvp using the name
+ * provided. Return a pointer to the inserted directory.
+ *
+ *	IN:	dvp	- vnode of directory to add subdir to.
+ *		dirname	- name of new directory.
+ *		vap	- attributes of new directory.
+ *		cr	- credentials of caller.
+ *
+ *	OUT:	vpp	- vnode of created directory.
+ *
+ *	RETURN:	0 if success
+ *		error code if failure
+ *
+ */
+/* ARGSUSED */
+static int
+fuse_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp,
+    cred_t *credp, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+	struct fuse_mkdir_in *fmkdi;
+	fuse_msg_node_t *msgp;
+	int err = 0;
+	vnode_t *vp;
+	fuse_session_t *sep;
+	int namelen;
+
+	sep = fuse_minor_get_session(getminor(dvp->v_rdev));
+	if (sep == NULL) {
+		DTRACE_PROBE2(fuse_mkdir_err_session,
+		    char *, "failed to find session",
+		    struct vnode *, dvp);
+		return (ENODEV);
+	}
+
+	namelen = strlen(dirname) + 1;
+	if (namelen > MAXNAMELEN) {
+		return (ENAMETOOLONG);
+	}
+
+	/* Make sure the target does not exist */
+	err = fuse_lookup_i(dvp, dirname, &vp, credp);
+	if (!err && vp) {
+		VN_RELE(vp);
+		return (EEXIST);
+	}
+
+	msgp = fuse_setup_message((sizeof (*fmkdi) + namelen),
+	    FUSE_MKDIR, VTOFD(dvp)->nodeid, credp, FUSE_GET_UNIQUE(sep));
+
+	/* Set up arguments to the fuse library */
+	fmkdi = (struct fuse_mkdir_in *)msgp->ipdata.indata;
+
+	fmkdi->mode = MAKEIMODE(vap->va_type, vap->va_mode);
+	(void *) strlcpy(((char *)msgp->ipdata.indata + sizeof (*fmkdi)),
+	    dirname, namelen);
+
+	if ((err = fuse_queue_request_wait(sep, msgp)) != 0) {
+		goto cleanup;
+	}
+	if ((err = msgp->opdata.fouth->error) != 0) {
+		DTRACE_PROBE2(fuse_mkdir_err_mkdir_req,
+		    char *, "FUSE_MKDIR request failed",
+		    struct fuse_out_header *, msgp->opdata.fouth);
+		goto cleanup;
+	}
+	err = fuse_add_entry(vpp, dvp, msgp, sep, dirname, namelen, credp,
+	    VDIR);
+cleanup:
+	fuse_free_msg(msgp);
+	return (err);
+}
diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel
index cac96c5728..15ec6b1896 100644
--- a/usr/src/uts/intel/Makefile.intel
+++ b/usr/src/uts/intel/Makefile.intel
@@ -542,7 +542,7 @@ SCHED_KMODS	+= IA RT TS RT_DPTBL TS_DPTBL FSS FX FX_DPTBL SDC
 FS_KMODS	+= autofs ctfs dcfs dev devfs fdfs fifofs hsfs hyprlofs
 FS_KMODS	+= lofs lxautofs lx_proc lxprocfs mntfs namefs nfs objfs zfs zut
 FS_KMODS	+= pcfs procfs sockfs specfs tmpfs udfs ufs sharefs lx_sysfs
-FS_KMODS	+= smbfs bootfs lx_cgroup lx_devfs
+FS_KMODS	+= smbfs bootfs lx_cgroup lx_devfs fuse
 
 #
 #	Streams Modules (/kernel/strmod):
diff --git a/usr/src/uts/intel/fuse/Makefile b/usr/src/uts/intel/fuse/Makefile
new file mode 100644
index 0000000000..4e6d17aa69
--- /dev/null
+++ b/usr/src/uts/intel/fuse/Makefile
@@ -0,0 +1,88 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE		= fuse
+OBJECTS		= $(FUSE_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Overrides
+#
+CFLAGS		+= $(CCVERBOSE) -D__SOLARIS__
+LDFLAGS		+= -dy -Ndrv/dld -Nmisc/mac -Nmisc/dls -Ndrv/random
+
+CERRWARN	+= -_gcc=-Wno-switch
+
+#linting disabled
+SMATCH=	off
+
+# needs work
+$(OBJS_DIR)/fuse_ini.o := SMOFF += index_overflow
+$(OBJS_DIR)/fuse_dev.o := SMOFF += index_overflow
+$(OBJS_DIR)/fuse_queue.o := SMOFF += index_overflow
+$(OBJS_DIR)/fuse_vfsops.c := SMOFF += index_overflow
+$(OBJS_DIR)/fuse_vnops.c := SMOFF += index_overflow
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/os/minor_perm b/usr/src/uts/intel/os/minor_perm
index 52aa933f0a..4953a164f2 100644
--- a/usr/src/uts/intel/os/minor_perm
+++ b/usr/src/uts/intel/os/minor_perm
@@ -271,3 +271,4 @@ timerfd:* 0666 root sys
 signalfd:* 0666 root sys
 iwn:* 0666 root sys
 clone:iwn 0666 root sys
+fuse:fuse 0666 root sys
diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major
index dfbcb851d0..39ce0c97fb 100644
--- a/usr/src/uts/intel/os/name_to_major
+++ b/usr/src/uts/intel/os/name_to_major
@@ -194,6 +194,7 @@ bpf 193
 lsimega 194
 vioif 195
 overlay 196
+fuse 197
 mpt_sas 198
 mr_sas 199
 mega_sas 200
