[DRE-commits] [vagrant-sshfs] 01/09: Imported Upstream version 1.2.0

Hans-Christoph Steiner eighthave at moszumanska.debian.org
Wed Sep 21 20:14:16 UTC 2016


This is an automated email from the git hooks/post-receive script.

eighthave pushed a commit to branch master
in repository vagrant-sshfs.

commit acadd6b28103e39f703e90e90816a612f3f3b8c4
Author: Hans-Christoph Steiner <hans at eds.org>
Date:   Tue Sep 6 15:46:53 2016 +0200

    Imported Upstream version 1.2.0
---
 .gitignore                                         |  17 ++
 Gemfile                                            |  17 ++
 LICENSE                                            | 340 +++++++++++++++++++++
 README.md                                          | 271 ++++++++++++++++
 Rakefile                                           |  38 +++
 features/README.md                                 |  21 ++
 features/sshfs_cwd_mount.feature                   |  46 +++
 features/step_definitions/sshfs_cwd_mount_steps.rb |  12 +
 features/support/env.rb                            |  27 ++
 lib/vagrant-sshfs.rb                               |  29 ++
 lib/vagrant-sshfs/cap/guest/arch/sshfs_client.rb   |  29 ++
 lib/vagrant-sshfs/cap/guest/debian/sshfs_client.rb |  15 +
 lib/vagrant-sshfs/cap/guest/fedora/sshfs_client.rb |  15 +
 lib/vagrant-sshfs/cap/guest/linux/sshfs_client.rb  |  11 +
 .../cap/guest/linux/sshfs_forward_mount.rb         | 314 +++++++++++++++++++
 lib/vagrant-sshfs/cap/guest/redhat/sshfs_client.rb |  36 +++
 lib/vagrant-sshfs/cap/guest/suse/sshfs_client.rb   |  15 +
 .../cap/host/linux/sshfs_reverse_mount.rb          | 176 +++++++++++
 lib/vagrant-sshfs/command.rb                       |  59 ++++
 lib/vagrant-sshfs/errors.rb                        |  38 +++
 lib/vagrant-sshfs/plugin.rb                        | 105 +++++++
 lib/vagrant-sshfs/synced_folder.rb                 | 129 ++++++++
 .../synced_folder/sshfs_forward_mount.rb           | 116 +++++++
 .../synced_folder/sshfs_reverse_mount.rb           |  51 ++++
 lib/vagrant-sshfs/version.rb                       |   5 +
 locales/synced_folder_sshfs.yml                    |  93 ++++++
 test/libvirt/README.txt                            |  15 +
 test/libvirt/Vagrantfile                           |  35 +++
 test/misc/README.txt                               |  29 ++
 test/misc/Vagrantfile                              |  27 ++
 test/misc/dotests.sh                               |  13 +
 test/virtualbox/README.txt                         |  17 ++
 test/virtualbox/Vagrantfile                        |  42 +++
 vagrant-sshfs.gemspec                              |  31 ++
 34 files changed, 2234 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..090217c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+# Build artifacts
+pkg
+build
+
+# Ruby / Bundler
+Gemfile.lock
+.ruby-gemset
+.ruby-version
+
+# .vagrant dirs
+.vagrant
+
+# IDE config files
+.idea
+*.iml
+
+
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..2b412a4
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,17 @@
+source "https://rubygems.org"
+
+gemspec
+
+group :development do
+  # We depend on Vagrant for development, but we don't add it as a
+  # gem dependency because we expect to be installed within the
+  # Vagrant environment itself using `vagrant plugin`.
+  gem "vagrant", :git => "https://github.com/mitchellh/vagrant.git", :ref => 'v1.8.4'
+end
+
+group :plugins do
+  gem "vagrant-sshfs" , path: "."
+  # Add vagrant-libvirt plugin here, otherwise you won't be able to
+  # use libvirt as a provider when you execute `bundle exec vagrant up`
+  gem "vagrant-libvirt" , '0.0.33'
+end
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8cdb845
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,340 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    {description}
+    Copyright (C) {year}  {fullname}
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  {signature of Ty Coon}, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1aca233
--- /dev/null
+++ b/README.md
@@ -0,0 +1,271 @@
+# vagrant-sshfs
+
+This is a vagrant plugin that adds synced folder support for mounting
+folders from the Vagrant host into the Vagrant guest via
+[SSHFS](https://github.com/libfuse/sshfs). In the default mode it does 
+this by executing the `SSHFS` client software within the guest, which 
+creates an SSH connection from the Vagrant guest back to the Vagrant host. 
+
+The benefits of this approach:
+- Works on any host platform and hypervisor type
+    - Windows, Linux, Mac OS X
+    - Virtualbox, Libvirt, Hyper-V, VMWare
+- Seamlessly works on remote Vagrant solutions
+    - Works with vagrant aws/openstack/etc.. plugins
+
+The drawbacks with this approach:
+- Performance is worse than an implementation like NFS
+- There must be `sftp-server` software on the Vagrant host 
+
+`sftp-server` is usually provided by SSH server software so it already
+exists on Linux/Mac. On windows you only need to install
+[openssh](https://cygwin.com/cgi-bin2/package-cat.cgi?file=x86_64%2Fopenssh%2Fopenssh-7.2p1-1&grep=openssh)
+via [cygwin](https://cygwin.com/) and you will get `sftp-server`.
+
+## History
+
+The inspiration for this plugin came from [Fabio Kreusch](https://github.com/fabiokr)
+and his [code](https://github.com/fabiokr/vagrant-sshfs) for the original 
+vagrant-sshfs Vagrant plugin. The goal of this plugin (as opposed to
+the old implementation) is to implement SSHFS as a synced folder
+plugin just like the other synced folder plugins (NFS/RSYNC/SMB/VirtualBox).
+
+This plugin was developed mainly by copying the code from the NFS synced 
+folder plugin from the Vagrant core code and molding it to fit SSHFS.
+
+## Modes of Operation
+
+### Sharing Vagrant Host Directory to Vagrant Guest - 98% of users
+
+This plugin uses SSHFS slave mounts 
+(see [link](https://github.com/dustymabe/vagrant-sshfs/issues/11))
+to mount a directory from the Vagrant Host into the Vagrant Guest. It
+uses the `sftp-server` software that exists on the host and `sshfs`
+running in *slave mode* within the guest to create a connection using
+the existing authentication over SSH that vagrant sets up for you.
+
+### Sharing Arbitrary Host Directory to Vagrant Guest - 1% of users
+
+This plugin allows you to share a folder from an arbitrary host to the
+Vagrant Guest. This would allow you to do a folder mount to some other
+host that may have files that you need. To do this the plugin will run
+an SSHFS command from the Guest and connect to the arbitrary host that
+must have an SSH daemon running. You must provide the `ssh_host`
+option in the Vagrantfile to get this to work. You can use ssh key
+forwarding or username/password for authentication for this.
+
+See [Options](#options-specific-to-arbitrary-host-mounting) and 
+[Appendix A](#appendix-a-using-keys-and-forwarding-ssh-agent) for
+more information.
+
+### Sharing Vagrant Guest Directory to Vagrant Host - 1% of users
+
+*NOTE:* This option is dangerous as data will be destroyed upon `vagrant destroy`
+
+This plugin allows you to share a folder from a Vagrant guest into the
+host. If you have workloads where there are a lot of disk intensive
+operations (such as compilation) it may be ideal to have the files
+live in the guest where the disk intensive operations would occur.
+For discussion see [Issue #7](https://github.com/dustymabe/vagrant-sshfs/issues/7).
+
+See [Options](#options-specific-to-reverse-mounting-guest-host-mount)
+for more information on how to enable this type of mount.
+
+## Getting Started
+
+In order to use this synced folder implementation perform the
+following steps:
+
+### Install Plugin
+
+In order to install the plugin simply run the following command:
+
+```
+# vagrant plugin install vagrant-sshfs
+```
+
+### Add SSHFS Synced Folder in Vagrantfile
+
+Edit your Vagrantfile to specify a folder to mount from the host into
+the guest:
+
+```
+config.vm.synced_folder "/path/on/host", "/path/on/guest", type: "sshfs"
+```
+
+Now you can simply `vagrant up` and your folder should be mounted in
+the guest. For more options that you can add see the [Options](#options) 
+section.
+
+## Executing the `vagrant sshfs` Command
+
+The Vagrant SSHFS plugin also supports execution of the `vagrant sshfs`
+command from the command line. Executing this command with the `--mount`
+option will iterate through the Vagrant file and attempt to mount (via 
+SSHFS) any folders that aren't already mounted in the Vagrant guest.
+Executing with the `--unmount` option will unmount any mounted folders.
+
+```
+vagrant sshfs [--mount|--unmount] [vm-name]
+```
+
+## Options
+
+The SSHFS synced folder plugin supports a few options that can be
+provided in the `Vagrantfile`. The following sections describe the
+options in more detail.
+
+### Generic Options
+
+The SSHFS synced folder plugin supports a few options that can be
+provided in the `Vagrantfile`. They are described below:
+
+- `disabled`
+    - If set to 'true', ignore this folder and don't mount it.
+- `ssh_opts_append`
+    - Add some options for the ssh connection that will be established.
+    - See the ssh man page for more details on possible options.
+- `sshfs_opts_append`
+    - Add some options for the sshfs fuse mount that will made
+    - See the sshfs man page for more details on possible options.
+
+An example snippet from a `Vagrantfile`:
+
+```
+config.vm.synced_folder "/path/on/host", "/path/on/guest",
+    ssh_opts_append: "-o Compression=yes -o CompressionLevel=5",
+    sshfs_opts_append: "-o auto_cache -o cache_timeout=115200",
+    disabled: false, type: "sshfs"
+```
+
+### Options Specific to Arbitrary Host Mounting
+
+The following options are only to be used when
+[sharing an arbitrary host directory](#sharing-arbitrary-host-directory-to-vagrant-guest---1-of-users)
+with the guest. They will be ignored otherwise:
+
+- `ssh_host`
+    - The host to connect to via SSH. If not provided this will be 
+      detected as the Vagrant host that is running the Vagrant guest.
+- `ssh_port`
+    - The port to use when connecting. Defaults to port 22.
+- `ssh_username`
+    - The username to use when connecting. If not provided it is
+    detected as the current user who is interacting with Vagrant.
+- `ssh_password`
+    - The password to use when connecting. If not provided and the
+      user is not using SSH keys, then the user will be prompted for
+      the password. Please use SSH keys and don't use this option!
+- `prompt_for_password`
+    - The user can force Vagrant to interactively prompt the user for
+      a password by setting this to 'true'. Alternatively the user can
+      deny Vagrant from ever prompting for the password by setting
+      this to 'false'.
+
+An example snippet from a `Vagrantfile`:
+
+```
+config.vm.synced_folder "/path/on/host", "/path/on/guest",
+    ssh_host: "somehost.com", ssh_username: "fedora",
+    ssh_opts_append: "-o Compression=yes -o CompressionLevel=5",
+    sshfs_opts_append: "-o auto_cache -o cache_timeout=115200",
+    disabled: false, type: "sshfs"
+```
+
+### Options Specific to Reverse Mounting (Guest->Host Mount)
+
+If your host has the `sshfs` software installed then the following 
+options enable mounting a folder from a Vagrant Guest into the 
+Vagrant Host:
+
+- `reverse`
+    - This can be set to 'true' to enable reverse mounting a guest
+      folder into the Vagrant host.
+
+An example snippet from a `Vagrantfile` where we want to mount `/data`
+on the guest into `/guest/data` on the host:
+
+```
+config.vm.synced_folder "/guest/data", "/data", type: 'sshfs', reverse: true
+```
+
+## FAQ
+
+Here are some answers to some frequently asked questions:
+
+### Why do new files take time to appear inside the guest?
+
+Sometimes it can take time for files to appear on the other end of the
+sshfs mount. An example would be I create a file on my host system and
+then it doesn't show up inside the guest mount for 10 to 20 seconds.
+This is because of caching that SSHFS does to improve performance.
+Performance vs accuracy is always going to be a trade-off. If you'd
+like to disable caching completely you can disable caching completely
+by appending the `cache=no` SSHFS option to the synced folder
+definition in the Vagrantfile like so:
+
+```
+config.vm.synced_folder "/path/on/host", "/path/on/guest",
+    type: "sshfs", sshfs_opts_append: "-o cache=no"
+```
+
+All caching options that are available to sshfs can be added/modified
+in this same manner.
+
+## Appendix A: Using Keys and Forwarding SSH Agent
+
+When [sharing an arbitrary host directory](#sharing-arbitrary-host-directory-to-vagrant-guest---1-of-users)
+you may want a completely non-interactive experience. You can either
+hard code your password in the Vagrantfile or you can use SSH keys.
+A few guides for setting up ssh keys and key forwarding are on Github:
+- [Key Generation](https://help.github.com/articles/generating-ssh-keys)
+- [Key Forwarding](https://developer.github.com/guides/using-ssh-agent-forwarding/)
+
+The idea is that if `key1` is a key that is authorized to log in to the 
+Vagrant host ,meaning there is an entry for `key1` in the `~/.ssh/authorized_keys` 
+file, then you should be able to do the following to have a
+non-interactive experience with SSH keys and agent forwarding:
+
+Modify the Vagrantfile to forward your SSH agent:
+
+```
+config.ssh.forward_agent = 'true'
+```
+
+Now set up your agent and add your key to the agent:
+
+```
+# eval $(ssh-agent)
+# ssh-add /path/to/key1
+```
+
+And finally bring up your Vagrant guest:
+
+```
+# vagrant up
+```
+
+
+## Appendix B: Development
+
+For local development of this plugin here is an example of how to build, test and install this plugin on your local machine:
+
+```
+# Install development dependencies
+$ gem install bundler && bundle install
+
+# List available Rake tasks
+$ bundle exec rake -T
+
+# Run Cucumber tests
+$ bundle exec rake featuretests
+
+# Build the gem (gets generated in the 'pkg' directory
+$ bundle exec rake build
+
+# Run Vagrant in the context of the plugin
+$ bundle exec vagrant <command>
+
+# Install built gem into global Vagrant installation (run outside of git checkout!)
+$ vagrant plugin install <path to gem in pkg directory>
+```
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..b447171
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,38 @@
+# A Rakefile is like a Makefile for ruby
+
+# bundler/gem_tasks provides functionality like:
+#   bundle exec rake build
+#   bundle exec rake install
+#   bundle exec rake release
+#
+require 'bundler/gem_tasks'
+
+# cucumber/rake/task provides us with an easy way to call cucumber
+require 'cucumber/rake/task'
+
+# rake/clean provides CLEAN/CLOBBER
+# http://www.virtuouscode.com/2014/04/28/rake-part-6-clean-and-clobber/
+# CLEAN - list to let rake know what files can be cleaned up after build
+# CLOBBER - list to let rake know what files are final products of the build
+#
+require 'rake/clean'
+
+
+# Add the build dir to the list of items to clean up
+CLEAN.include('build')
+
+# We want to keep the build artifacts in the pkg dir
+CLOBBER.include('pkg')
+
+# Define a Rake::Task that will do initialization for us
+# See http://www.ultrasaurus.com/2009/12/creating-a-custom-rake-task/
+task :init do
+  FileUtils.mkdir_p 'build'
+end
+
+# Create new Cucumber::Rake::Task that will run Cucumber tests
+Cucumber::Rake::Task.new(:featuretests)
+
+# Define Rake::Task dependency - run :init before :featuretests
+task :featuretests => :init
+
diff --git a/features/README.md b/features/README.md
new file mode 100644
index 0000000..ac8a46d
--- /dev/null
+++ b/features/README.md
@@ -0,0 +1,21 @@
+
+We are using Cucumber for automated testing. Read more at the
+following two links:
+
+- [link1](https://en.wikipedia.org/wiki/Cucumber_(software))
+- [link2](http://www.methodsandtools.com/tools/cucumber.php)
+
+features/
+    This is the features directory. The features directory Contains
+    feature files, which all have a .feature extension. May contain
+    subdirectories to organize feature files.
+
+features/step_definitions
+    This directory contains step definition files, which are Ruby code 
+    and have a .rb extension.
+
+features/support
+    This directory contains supporting Ruby code. Files in support
+    load before those in step_definitions, which makes it useful for
+    such things as environment configuration (commonly done in a file
+    called env.rb).
diff --git a/features/sshfs_cwd_mount.feature b/features/sshfs_cwd_mount.feature
new file mode 100644
index 0000000..d614a53
--- /dev/null
+++ b/features/sshfs_cwd_mount.feature
@@ -0,0 +1,46 @@
+# The language in this file is Gherkin. It is the language Cucumber
+# uses to define test cases and is designed to be non-technical and
+# human readable. All Gherkin files have a .feature extension
+#
+# See more here: https://en.wikipedia.org/wiki/Cucumber_(software)
+# 
+# Additoinally in the setup/env.rb file we set up Aruba. Aruba is used 
+# to define most of the basic step definitions that we use as part of 
+# the Gherkin syntax in this file.
+#
+# For more information on the step definitions provided see:
+# https://github.com/cucumber/aruba/tree/bb5d7ff71809b5461e29153ded793d2b9a3a0624/features/testing_frameworks/cucumber/steps
+#
+Feature: SSHFS mount of vagrant current working directory
+
+  Scenario Outline: SSHFS mounting of vagrant cwd
+    Given a file named "Vagrantfile" with:
+    """
+    Vagrant.configure('2') do |config|
+      config.vm.box = '<box>'
+      # Disable the default rsync
+      config.vm.synced_folder '.', '/vagrant', disabled: true
+
+      # If using libvirt and nested virt (vagrant in vagrant) then 
+      # we need to use a different network than 192.168.121.0
+      config.vm.provider :libvirt do |libvirt|
+        libvirt.management_network_name = 'vagrant-libvirt-test'
+        libvirt.management_network_address = '192.168.129.0/24'
+      end
+
+      # Mount up the current dir. It will have the Vagrantfile in there.
+      config.vm.synced_folder './', '/testdir', type: 'sshfs'
+    end
+    """
+    When I successfully run `bundle exec vagrant up`
+    Then stdout from "bundle exec vagrant up" should contain "Installing SSHFS client..."
+    And  stdout from "bundle exec vagrant up" should contain "Mounting SSHFS shared folder..."
+    And  stdout from "bundle exec vagrant up" should contain "Folder Successfully Mounted!"
+    # The code for the following test is in ./step_definitions/sshfs_cwd_mount_steps.rb
+    And vagrant current working directory should be mounted
+
+    Examples:
+      | box      |
+      | centos/7 |
+    
+    
diff --git a/features/step_definitions/sshfs_cwd_mount_steps.rb b/features/step_definitions/sshfs_cwd_mount_steps.rb
new file mode 100644
index 0000000..1d161b5
--- /dev/null
+++ b/features/step_definitions/sshfs_cwd_mount_steps.rb
@@ -0,0 +1,12 @@
+# This is a cucumber step definition. Cucumber scenarios become automated 
+# tests with the addition of what are called step definitions. A step 
+# definition is a block of code associated with one or more steps by a 
+# regular expression (or, in simple cases, a string).
+#
+# This is the step definition for the `And vagrant current working
+# directory should be mounted` step from sshfs_cwd_mount.feature
+#
+And(/^vagrant current working directory should be mounted$/) do
+  run("vagrant ssh -c 'ls /testdir/Vagrantfile'")
+  expect(last_command_started).to have_exit_status(0)
+end
diff --git a/features/support/env.rb b/features/support/env.rb
new file mode 100644
index 0000000..5f93cc1
--- /dev/null
+++ b/features/support/env.rb
@@ -0,0 +1,27 @@
+# This is a support file for the cucumber tests. This file sets up the
+# environment for the tests to run. At this point mainly that means
+# configuring Aruba. Aruba is used to define most of the basic step
+# definitions that we use as part of the Gherkin syntax in our .feature files.
+#
+# For more information on the step definitions provided see:
+# https://github.com/cucumber/aruba/tree/bb5d7ff71809b5461e29153ded793d2b9a3a0624/features/testing_frameworks/cucumber/steps
+require 'aruba/cucumber'
+require 'komenda' # use komenda for easily executing a command
+
+# Configure aruba. The options can be inferred from here:
+# https://github.com/cucumber/aruba/tree/bb5d7ff71809b5461e29153ded793d2b9a3a0624/features/configuration
+Aruba.configure do |config|
+  # Wait up to 300 seconds for the test to run
+  config.exit_timeout = 300
+  # Output stdout and stderr on test failure
+  config.activate_announcer_on_command_failure = [:stdout, :stderr]
+  # The directory where the tests are to be run
+  config.working_directory = 'build/aruba'
+end
+
+# After running tests, clean up
+After do |_scenario|
+  if File.exist?(File.join(aruba.config.working_directory, 'Vagrantfile'))
+    Komenda.run('bundle exec vagrant destroy -f', cwd: aruba.config.working_directory, fail_on_fail: true)
+  end
+end
diff --git a/lib/vagrant-sshfs.rb b/lib/vagrant-sshfs.rb
new file mode 100644
index 0000000..d52bf71
--- /dev/null
+++ b/lib/vagrant-sshfs.rb
@@ -0,0 +1,29 @@
+begin
+  require "vagrant"
+rescue LoadError
+  raise "The Vagrant sshfs plugin must be run within Vagrant"
+end
+
+# Only load the gem on Windows since it replaces some methods in Ruby's 
+# Process class. Also load it here before Process.uid is called the first 
+# time by Vagrant. The Process.create() function actually gets used in
+# lib/vagrant-sshfs/cap/guest/linux/sshfs_forward_mount.rb
+if Vagrant::Util::Platform.windows?
+  require 'win32/process'
+end
+
+require "vagrant-sshfs/errors"
+require "vagrant-sshfs/version"
+require "vagrant-sshfs/plugin"
+
+module VagrantPlugins
+  module SyncedFolderSSHFS
+    # Returns the path to the source of this plugin
+    def self.source_root
+      @source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
+    end
+
+    I18n.load_path << File.expand_path('locales/synced_folder_sshfs.yml', source_root)
+    I18n.reload!
+  end
+end
diff --git a/lib/vagrant-sshfs/cap/guest/arch/sshfs_client.rb b/lib/vagrant-sshfs/cap/guest/arch/sshfs_client.rb
new file mode 100644
index 0000000..738ad72
--- /dev/null
+++ b/lib/vagrant-sshfs/cap/guest/arch/sshfs_client.rb
@@ -0,0 +1,29 @@
+module VagrantPlugins
+  module GuestArch
+    module Cap
+      class SSHFSClient
+        def self.sshfs_install(machine)
+          # Attempt to install sshfs but note that it may likely fail
+          # because the package file list is out of date (see [1]). A
+          # logical answer to this problem would be to update the
+          # package list and then install the package, but since arch
+          # doesn't support partial upgrades [2] that would require
+          # updating all packages in the system first. Not ideal
+          #
+          # [1] https://wiki.archlinux.org/index.php/pacman#Packages_cannot_be_retrieved_on_installation
+          # [2] https://wiki.archlinux.org/index.php/System_maintenance#Partial_upgrades_are_unsupported
+
+          error_class = VagrantPlugins::SyncedFolderSSHFS::Errors::SSHFSInstallFailed
+          error_key = :install_failed_arch
+          cmd = "pacman --noconfirm -S sshfs"
+          machine.communicate.sudo(
+            cmd, error_class: error_class, error_key: error_key)
+        end
+
+        def self.sshfs_installed(machine)
+          machine.communicate.test("pacman -Q sshfs")
+        end
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/cap/guest/debian/sshfs_client.rb b/lib/vagrant-sshfs/cap/guest/debian/sshfs_client.rb
new file mode 100644
index 0000000..58d653e
--- /dev/null
+++ b/lib/vagrant-sshfs/cap/guest/debian/sshfs_client.rb
@@ -0,0 +1,15 @@
+module VagrantPlugins
+  module GuestDebian
+    module Cap
+      class SSHFSClient
+        def self.sshfs_install(machine)
+          machine.communicate.sudo("apt-get install -y sshfs")
+        end
+
+        def self.sshfs_installed(machine)
+          machine.communicate.test("dpkg -l sshfs")
+        end
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/cap/guest/fedora/sshfs_client.rb b/lib/vagrant-sshfs/cap/guest/fedora/sshfs_client.rb
new file mode 100644
index 0000000..826f556
--- /dev/null
+++ b/lib/vagrant-sshfs/cap/guest/fedora/sshfs_client.rb
@@ -0,0 +1,15 @@
+module VagrantPlugins
+  module GuestFedora
+    module Cap
+      class SSHFSClient
+        def self.sshfs_install(machine)
+          machine.communicate.sudo("dnf -y install fuse-sshfs")
+        end
+
+        def self.sshfs_installed(machine)
+          machine.communicate.test("rpm -q fuse-sshfs")
+        end
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/cap/guest/linux/sshfs_client.rb b/lib/vagrant-sshfs/cap/guest/linux/sshfs_client.rb
new file mode 100644
index 0000000..22af479
--- /dev/null
+++ b/lib/vagrant-sshfs/cap/guest/linux/sshfs_client.rb
@@ -0,0 +1,11 @@
+module VagrantPlugins
+  module GuestLinux
+    module Cap
+      class SSHFSClient
+        def self.sshfs_installed(machine)
+          machine.communicate.test("test -x /usr/bin/sshfs")
+        end
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/cap/guest/linux/sshfs_forward_mount.rb b/lib/vagrant-sshfs/cap/guest/linux/sshfs_forward_mount.rb
new file mode 100644
index 0000000..6a3fced
--- /dev/null
+++ b/lib/vagrant-sshfs/cap/guest/linux/sshfs_forward_mount.rb
@@ -0,0 +1,314 @@
+require "log4r"
+require "vagrant/util/retryable"
+require "tempfile"
+
+# This is already done for us in lib/vagrant-sshfs.rb. We needed to
+# do it there before Process.uid is called the first time by Vagrant
+# This provides a new Process.create() that works on Windows.
+if Vagrant::Util::Platform.windows?
+  require 'win32/process'
+end
+
+module VagrantPlugins
+  module GuestLinux
+    module Cap
+      class MountSSHFS
+        extend Vagrant::Util::Retryable
+        @@logger = Log4r::Logger.new("vagrant::synced_folders::sshfs_mount")
+
+        def self.sshfs_forward_is_folder_mounted(machine, opts)
+          mounted = false
+          # expand the guest path so we can handle things like "~/vagrant"
+          expanded_guest_path = machine.guest.capability(
+            :shell_expand_guest_path, opts[:guestpath])
+          machine.communicate.execute("cat /proc/mounts") do |type, data|
+            if type == :stdout
+              data.each_line do |line|
+                if line.split()[1] == expanded_guest_path
+                  mounted = true
+                  break
+                end
+              end
+            end
+          end
+          return mounted
+        end
+
+        def self.sshfs_forward_mount_folder(machine, opts)
+          # opts contains something like:
+          #   { :type=>:sshfs,
+          #     :guestpath=>"/sharedfolder",
+          #     :hostpath=>"/guests/sharedfolder", 
+          #     :disabled=>false
+          #     :ssh_host=>"192.168.1.1"
+          #     :ssh_port=>"22"
+          #     :ssh_username=>"username"
+          #     :ssh_password=>"password"
+          #   }
+
+          # expand the guest path so we can handle things like "~/vagrant"
+          expanded_guest_path = machine.guest.capability(
+            :shell_expand_guest_path, opts[:guestpath])
+
+          # Create the mountpoint inside the guest
+          machine.communicate.tap do |comm|
+            comm.sudo("mkdir -p #{expanded_guest_path}")
+            comm.sudo("chmod 777 #{expanded_guest_path}")
+          end
+
+          # Mount path information
+          hostpath = opts[:hostpath].dup
+          hostpath.gsub!("'", "'\\\\''")
+
+          # Add in some sshfs/fuse options that are common to both mount methods
+          opts[:sshfs_opts] = ' -o allow_other ' # allow non-root users to access
+          opts[:sshfs_opts]+= ' -o noauto_cache '# disable caching based on mtime
+
+          # Add in some ssh options that are common to both mount methods
+          opts[:ssh_opts] = ' -o StrictHostKeyChecking=no '# prevent yes/no question 
+          opts[:ssh_opts]+= ' -o ServerAliveInterval=30 '  # send keepalives
+
+          # Do a normal mount only if the user provided host information
+          if opts.has_key?(:ssh_host) and opts[:ssh_host]
+            self.sshfs_normal_mount(machine, opts, hostpath, expanded_guest_path)
+          else
+            self.sshfs_slave_mount(machine, opts, hostpath, expanded_guest_path)
+          end
+        end
+
+        def self.sshfs_forward_unmount_folder(machine, opts)
+          # opts contains something like:
+          #   { :type=>:sshfs,
+          #     :guestpath=>"/sharedfolder",
+          #     :hostpath=>"/guests/sharedfolder",
+          #     :disabled=>false
+          #     :ssh_host=>"192.168.1.1"
+          #     :ssh_port=>"22"
+          #     :ssh_username=>"username"
+          #     :ssh_password=>"password"
+          #   }
+
+          # expand the guest path so we can handle things like "~/vagrant"
+          expanded_guest_path = machine.guest.capability(
+            :shell_expand_guest_path, opts[:guestpath])
+
+          # Log some information
+          machine.ui.info(I18n.t("vagrant.sshfs.actions.unmounting_folder",
+                                 guestpath: expanded_guest_path))
+
+          # Build up the command and connect
+          error_class = VagrantPlugins::SyncedFolderSSHFS::Errors::SSHFSUnmountFailed
+          cmd = "umount #{expanded_guest_path}"
+          machine.communicate.sudo(
+            cmd, error_class: error_class, error_key: :unmount_failed)
+        end
+
+        protected
+
+        def self.windows_uninherit_handles
+          # For win32-process Process.create, if we pass any file handles to the 
+          # underlying process for stdin/stdout/stderr then all file handles are 
+          # inherited by default. We'll explicitly go through and set all Handles
+          # to not be inheritable by default. See following links for more info
+          # 
+          # https://github.com/djberg96/win32-process/blob/6b380f450aebb69d44bb7accd958ecb6b9e1d246/lib/win32/process.rb#L445-L447
+          # bInheritHandles from https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
+          # 
+          # For each open IO object 
+          ObjectSpace.each_object(IO) do |io|
+            if !io.closed?
+              fileno = io.fileno 
+              @@logger.debug("Setting file handle #{fileno} to not be inherited")
+              self.windows_uninherit_handle(fileno)
+            end
+          end
+        end
+
+        def self.windows_uninherit_handle(fileno)
+          # Right now we'll be doing this using private methods from the win32-process
+          # module by calling  For each open IO object. Much of this code was copied from 
+          # that module. We access the private methods by using the object.send(:method, args)
+          # technique. In the future we want to get a patch upstream so we don't need to
+          # access privat methods.
+
+          # Get the windows IO handle and make sure we were successful getting it
+          handle = Process.send(:get_osfhandle, fileno)
+          if handle == Process::Constants::INVALID_HANDLE_VALUE
+            ptr = FFI::MemoryPointer.new(:int)
+            if Process.send(:windows_version) >= 6 && Process.get_errno(ptr) == 0
+              errno = ptr.read_int
+            else
+              errno = FFI.errno
+            end
+            raise SystemCallError.new("get_osfhandle", errno)
+          end
+
+          # Now clear the HANDLE_FLAG_INHERIT from the HANDLE so that the handle
+          # won't get shared by default. See: 
+          # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724935(v=vs.85).aspx
+          # 
+          bool = Process.send(:SetHandleInformation,
+          handle, Process::Constants::HANDLE_FLAG_INHERIT, 0)
+          raise SystemCallError.new("SetHandleInformation", FFI.errno) unless bool
+        end
+
+        # Perform a mount by running an sftp-server on the vagrant host 
+        # and piping stdin/stdout to sshfs running inside the guest
+        def self.sshfs_slave_mount(machine, opts, hostpath, expanded_guest_path)
+
+          sftp_server_path = opts[:sftp_server_exe_path]
+          ssh_path = opts[:ssh_exe_path]
+
+          # SSH connection options
+          ssh_opts = opts[:ssh_opts]
+          ssh_opts_append = opts[:ssh_opts_append].to_s # provided by user
+
+          # SSHFS executable options
+          sshfs_opts = opts[:sshfs_opts]
+          sshfs_opts_append = opts[:sshfs_opts_append].to_s # provided by user
+
+          # The sftp-server command
+          sftp_server_cmd = sftp_server_path
+
+          # The remote sshfs command that will run (in slave mode)
+          sshfs_opts+= ' -o slave '
+          sshfs_cmd = "sudo -E sshfs :#{hostpath} #{expanded_guest_path}" 
+          sshfs_cmd+= sshfs_opts + ' ' + sshfs_opts_append + ' '
+
+          # The ssh command to connect to guest and then launch sshfs
+          ssh_opts = opts[:ssh_opts]
+          ssh_opts+= ' -o User=' + machine.ssh_info[:username]
+          ssh_opts+= ' -o Port=' + machine.ssh_info[:port].to_s
+          ssh_opts+= ' -o IdentityFile=' + machine.ssh_info[:private_key_path][0]
+          ssh_opts+= ' -o UserKnownHostsFile=/dev/null '
+          ssh_opts+= ' -F /dev/null ' # Don't pick up options from user's config
+          ssh_cmd = ssh_path + ssh_opts + ' ' + ssh_opts_append + ' ' + machine.ssh_info[:host]
+          ssh_cmd+= ' "' + sshfs_cmd + '"'
+
+          # Log some information
+          @@logger.debug("sftp-server cmd: #{sftp_server_cmd}")
+          @@logger.debug("ssh cmd: #{ssh_cmd}")
+          machine.ui.info(I18n.t("vagrant.sshfs.actions.slave_mounting_folder", 
+                          hostpath: hostpath, guestpath: expanded_guest_path))
+
+          # Create two named pipes for communication between sftp-server and
+          # sshfs running in slave mode
+          r1, w1 = IO.pipe # reader/writer from pipe1
+          r2, w2 = IO.pipe # reader/writer from pipe2
+
+          # Log STDERR to predictable files so that we can inspect them
+          # later in case things go wrong. We'll use the machines data
+          # directory (i.e. .vagrant/machines/default/virtualbox/) for this
+          f1path = machine.data_dir.join('vagrant_sshfs_sftp_server_stderr.txt')
+          f2path = machine.data_dir.join('vagrant_sshfs_ssh_stderr.txt')
+          f1 = File.new(f1path, 'w+')
+          f2 = File.new(f2path, 'w+')
+
+          # The way this works is by hooking up the stdin+stdout of the
+          # sftp-server process to the stdin+stdout of the sshfs process
+          # running inside the guest in slave mode. An illustration is below:
+          # 
+          #          stdout => w1      pipe1         r1 => stdin 
+          #         />------------->==============>----------->\
+          #        /                                            \
+          #        |                                            |
+          #    sftp-server (on vm host)                      sshfs (inside guest)
+          #        |                                            |
+          #        \                                            /
+          #         \<-------------<==============<-----------</
+          #          stdin <= r2        pipe2         w2 <= stdout 
+          #
+          # Wire up things appropriately and start up the processes
+          if Vagrant::Util::Platform.windows?
+            # For windows we need to set it so not all file handles are inherited
+            # by default. See https://github.com/dustymabe/vagrant-sshfs/issues/41
+            # The r1,r2,w1,w2,f1,f2 we pass below will get set back to be shared
+            self.windows_uninherit_handles
+            # For windows, we are using win32-process' Process.create because ruby
+            # doesn't properly detach processes. See https://github.com/dustymabe/vagrant-sshfs/issues/31
+            Process.create(:command_line => sftp_server_cmd,
+                           :creation_flags => Process::DETACHED_PROCESS,
+                           :process_inherit => false,
+                           :thread_inherit => true,
+                           :startup_info => {:stdin => w2, :stdout => r1, :stderr => f1})
+
+            Process.create(:command_line => ssh_cmd,
+                           :creation_flags => Process::DETACHED_PROCESS,
+                           :process_inherit => false,
+                           :thread_inherit => true,
+                           :startup_info => {:stdin => w1, :stdout => r2, :stderr => f2})
+          else
+            p1 = spawn(sftp_server_cmd, :out => w2, :in => r1, :err => f1, :pgroup => true)
+            p2 = spawn(ssh_cmd,         :out => w1, :in => r2, :err => f2, :pgroup => true)
+
+            # Detach from the processes so they will keep running
+            Process.detach(p1)
+            Process.detach(p2)
+          end
+
+          # Check that the mount made it
+          mounted = false
+          for i in 0..6
+            machine.ui.info("Checking Mount..")
+            if self.sshfs_forward_is_folder_mounted(machine, opts)
+              mounted = true
+              break
+            end
+            sleep(2)
+          end
+          if !mounted
+            f1.rewind # Seek to beginning of the file
+            f2.rewind # Seek to beginning of the file
+            error_class = VagrantPlugins::SyncedFolderSSHFS::Errors::SSHFSSlaveMountFailed
+            raise error_class, sftp_stderr: f1.read, ssh_stderr: f2.read
+          end
+          machine.ui.info("Folder Successfully Mounted!")
+        end
+
+        # Do a normal sshfs mount in which we will ssh into the guest
+        # and then execute the sshfs command to connect the the opts[:ssh_host]
+        # and mount a folder from opts[:ssh_host] into the guest.
+        def self.sshfs_normal_mount(machine, opts, hostpath, expanded_guest_path)
+
+          # SSH connection options
+          ssh_opts = opts[:ssh_opts]
+          ssh_opts_append = opts[:ssh_opts_append].to_s # provided by user
+
+          # SSHFS executable options
+          sshfs_opts = opts[:sshfs_opts]
+          sshfs_opts_append = opts[:sshfs_opts_append].to_s # provided by user
+
+          # Host/Port and Auth Information
+          username = opts[:ssh_username]
+          password = opts[:ssh_password]
+          host     = opts[:ssh_host]
+          port     = opts[:ssh_port]
+
+          # Add echo of password if password is being used
+          echopipe = ""
+          if password
+            echopipe = "echo '#{password}' | "
+            sshfs_opts+= '-o password_stdin '
+          end
+
+          # Log some information
+          machine.ui.info(I18n.t("vagrant.sshfs.actions.normal_mounting_folder", 
+                          user: username, host: host, 
+                          hostpath: hostpath, guestpath: expanded_guest_path))
+          
+          # Build up the command and connect
+          error_class = VagrantPlugins::SyncedFolderSSHFS::Errors::SSHFSNormalMountFailed
+          cmd = echopipe 
+          cmd+= "sshfs -p #{port} "
+          cmd+= ssh_opts + ' ' + ssh_opts_append + ' '
+          cmd+= sshfs_opts + ' ' + sshfs_opts_append + ' '
+          cmd+= "#{username}@#{host}:'#{hostpath}' #{expanded_guest_path}"
+          retryable(on: error_class, tries: 3, sleep: 3) do
+            machine.communicate.sudo(
+              cmd, error_class: error_class, error_key: :normal_mount_failed)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/cap/guest/redhat/sshfs_client.rb b/lib/vagrant-sshfs/cap/guest/redhat/sshfs_client.rb
new file mode 100644
index 0000000..6179cb0
--- /dev/null
+++ b/lib/vagrant-sshfs/cap/guest/redhat/sshfs_client.rb
@@ -0,0 +1,36 @@
+module VagrantPlugins
+  module GuestRedHat
+    module Cap
+      class SSHFSClient
+        def self.sshfs_install(machine)
+          # Install epel rpm if not installed 
+          if !epel_installed(machine)
+            epel_install(machine)
+          end
+
+          # Install sshfs (comes from epel repos)
+          machine.communicate.sudo("yum -y install fuse-sshfs")
+        end
+
+        def self.sshfs_installed(machine)
+          machine.communicate.test("rpm -q fuse-sshfs")
+        end
+        
+        protected
+
+        def self.epel_installed(machine)
+          machine.communicate.test("rpm -q epel-release")
+        end
+
+        def self.epel_install(machine)
+          case machine.guest.capability("flavor")
+            when :rhel_7
+              machine.communicate.sudo("rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm")
+            when :rhel # rhel6 
+              machine.communicate.sudo("rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm")
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/cap/guest/suse/sshfs_client.rb b/lib/vagrant-sshfs/cap/guest/suse/sshfs_client.rb
new file mode 100644
index 0000000..05e1bba
--- /dev/null
+++ b/lib/vagrant-sshfs/cap/guest/suse/sshfs_client.rb
@@ -0,0 +1,15 @@
+module VagrantPlugins
+  module GuestSUSE
+    module Cap
+      class SSHFSClient
+        def self.sshfs_install(machine)
+          machine.communicate.sudo("zypper -n install sshfs")
+        end
+
+        def self.sshfs_installed(machine)
+          machine.communicate.test("rpm -q sshfs")
+        end
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/cap/host/linux/sshfs_reverse_mount.rb b/lib/vagrant-sshfs/cap/host/linux/sshfs_reverse_mount.rb
new file mode 100644
index 0000000..2aa5b23
--- /dev/null
+++ b/lib/vagrant-sshfs/cap/host/linux/sshfs_reverse_mount.rb
@@ -0,0 +1,176 @@
+require "log4r"
+require "vagrant/util/retryable"
+require "tempfile"
+
+# This is already done for us in lib/vagrant-sshfs.rb. We needed to
+# do it there before Process.uid is called the first time by Vagrant
+# This provides a new Process.create() that works on Windows.
+if Vagrant::Util::Platform.windows?
+  require 'win32/process'
+end
+
+module VagrantPlugins
+  module HostLinux
+    module Cap
+      class MountSSHFS
+        extend Vagrant::Util::Retryable
+        @@logger = Log4r::Logger.new("vagrant::synced_folders::sshfs_reverse_mount")
+
+        def self.sshfs_reverse_is_folder_mounted(env, opts)
+          mounted = false
+          hostpath = opts[:hostpath].dup
+          hostpath.gsub!("'", "'\\\\''")
+          hostpath = hostpath.chomp('/') # remove trailing / if exists
+          cat_cmd = Vagrant::Util::Which.which('cat')
+          result = Vagrant::Util::Subprocess.execute(cat_cmd, '/proc/mounts')
+          mounts = File.open('/proc/mounts', 'r')
+          mounts.each_line do |line|
+            if line.split()[1] == hostpath
+              mounted = true
+              break
+            end
+          end
+          return mounted
+        end
+
+        def self.sshfs_reverse_mount_folder(env, machine, opts)
+          # opts contains something like:
+          #   { :type=>:sshfs,
+          #     :guestpath=>"/sharedfolder",
+          #     :hostpath=>"/guests/sharedfolder", 
+          #     :disabled=>false
+          #     :ssh_host=>"192.168.1.1"
+          #     :ssh_port=>"22"
+          #     :ssh_username=>"username"
+          #     :ssh_password=>"password"
+          #   }
+          self.sshfs_mount(machine, opts)
+        end
+
+        def self.sshfs_reverse_unmount_folder(env, machine, opts)
+          self.sshfs_unmount(machine, opts)
+        end
+
+        protected
+
+        # Perform a mount by running an sftp-server on the vagrant host 
+        # and piping stdin/stdout to sshfs running inside the guest
+        def self.sshfs_mount(machine, opts)
+
+          sshfs_path = Vagrant::Util::Which.which('sshfs')
+
+          # expand the guest path so we can handle things like "~/vagrant"
+          expanded_guest_path = machine.guest.capability(
+            :shell_expand_guest_path, opts[:guestpath])
+
+          # Mount path information
+          hostpath = opts[:hostpath].dup
+          hostpath.gsub!("'", "'\\\\''")
+
+          # Add in some sshfs/fuse options that are common to both mount methods
+          opts[:sshfs_opts] = ' -o noauto_cache '# disable caching based on mtime
+
+          # Add in some ssh options that are common to both mount methods
+          opts[:ssh_opts] = ' -o StrictHostKeyChecking=no '# prevent yes/no question 
+          opts[:ssh_opts]+= ' -o ServerAliveInterval=30 '  # send keepalives
+
+          # SSH connection options
+          ssh_opts = opts[:ssh_opts]
+          ssh_opts+= ' -o Port=' + machine.ssh_info[:port].to_s
+          ssh_opts+= ' -o IdentityFile=' + machine.ssh_info[:private_key_path][0]
+          ssh_opts+= ' -o UserKnownHostsFile=/dev/null '
+          ssh_opts+= ' -F /dev/null ' # Don't pick up options from user's config
+
+          ssh_opts_append = opts[:ssh_opts_append].to_s # provided by user
+
+          # SSHFS executable options
+          sshfs_opts = opts[:sshfs_opts]
+          sshfs_opts_append = opts[:sshfs_opts_append].to_s # provided by user
+
+          username = machine.ssh_info[:username]
+          host = machine.ssh_info[:host]
+
+
+          # The sshfs command to mount the guest directory on the host
+          sshfs_cmd = "#{sshfs_path} #{ssh_opts} #{ssh_opts_append} "
+          sshfs_cmd+= "#{sshfs_opts} #{sshfs_opts_append} "
+          sshfs_cmd+= "#{username}@#{host}:#{expanded_guest_path} #{hostpath}" 
+
+          # Log some information
+          @@logger.debug("sshfs cmd: #{sshfs_cmd}")
+
+          machine.ui.info(I18n.t("vagrant.sshfs.actions.reverse_mounting_folder", 
+                          hostpath: hostpath, guestpath: expanded_guest_path))
+
+          # Log STDERR to predictable files so that we can inspect them
+          # later in case things go wrong. We'll use the machines data
+          # directory (i.e. .vagrant/machines/default/virtualbox/) for this
+          f1path = machine.data_dir.join('vagrant_sshfs_sshfs_stderr.txt')
+          f1 = File.new(f1path, 'w+')
+
+          # Launch sshfs command to mount guest dir into the host
+          if Vagrant::Util::Platform.windows?
+            # Need to handle Windows differently. Kernel.spawn fails to work, 
+            # if the shell creating the process is closed.
+            # See https://github.com/dustymabe/vagrant-sshfs/issues/31
+            Process.create(:command_line => ssh_cmd,
+                           :creation_flags => Process::DETACHED_PROCESS,
+                           :process_inherit => false,
+                           :thread_inherit => true,
+                           :startup_info => {:stdin => w2, :stdout => r1, :stderr => f1})
+          else
+            p1 = spawn(sshfs_cmd,   :out => f1, :err => f1, :pgroup => true)
+            Process.detach(p1) # Detach so process will keep running
+          end
+
+          # Check that the mount made it
+          mounted = false
+          for i in 0..6
+            machine.ui.info("Checking Mount..")
+            if self.sshfs_reverse_is_folder_mounted(machine, opts)
+              mounted = true
+              break
+            end
+            sleep(2)
+          end
+          if !mounted
+            f1.rewind # Seek to beginning of the file
+            error_class = VagrantPlugins::SyncedFolderSSHFS::Errors::SSHFSReverseMountFailed
+            raise error_class, sshfs_output: f1.read
+          end
+          machine.ui.info("Folder Successfully Mounted!")
+        end
+
+        def self.sshfs_unmount(machine, opts)
+          # opts contains something like:
+          #   { :type=>:sshfs,
+          #     :guestpath=>"/sharedfolder",
+          #     :hostpath=>"/guests/sharedfolder",
+          #     :disabled=>false
+          #     :ssh_host=>"192.168.1.1"
+          #     :ssh_port=>"22"
+          #     :ssh_username=>"username"
+          #     :ssh_password=>"password"
+          #   }
+
+          # Mount path information
+          hostpath = opts[:hostpath].dup
+          hostpath.gsub!("'", "'\\\\''")
+
+          # Log some information
+          machine.ui.info(I18n.t("vagrant.sshfs.actions.reverse_unmounting_folder",
+                                 hostpath: hostpath))
+
+          # Build up the command and connect
+          error_class = VagrantPlugins::SyncedFolderSSHFS::Errors::SSHFSUnmountFailed
+          fusermount_cmd = Vagrant::Util::Which.which('fusermount')
+          cmd = "#{fusermount_cmd} -u #{hostpath}"
+          result = Vagrant::Util::Subprocess.execute(*cmd.split())
+          if result.exit_code != 0
+            raise error_class, command: cmd, stdout: result.stdout, stderr: result.stderr
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/command.rb b/lib/vagrant-sshfs/command.rb
new file mode 100644
index 0000000..3e9cc03
--- /dev/null
+++ b/lib/vagrant-sshfs/command.rb
@@ -0,0 +1,59 @@
+module VagrantPlugins
+  module SyncedFolderSSHFS
+    module Command
+      class SSHFS < Vagrant.plugin("2", :command)
+
+        include Vagrant::Action::Builtin::MixinSyncedFolders
+
+        def self.synopsis
+          "mounts SSHFS shared folder mounts into the remote machine"
+        end
+
+        def execute
+          options = {:unmount => false} # Default to mounting shares
+          opts = OptionParser.new do |o|
+            o.banner = "Usage: vagrant sshfs [--mount|--unmount] [vm-name]"
+            o.separator ""
+            o.separator "Mount or unmount sshfs synced folders into the vagrant box"
+            o.separator ""
+
+            o.on("--mount", "Mount folders - the default") do
+              options[:unmount] = false
+            end
+            o.on("--unmount", "Unmount folders") do
+              options[:unmount] = true
+            end
+          end
+
+          # Parse the options and return if we don't have any target.
+          argv = parse_options(opts)
+          return if !argv
+
+          # Go through each machine and perform the rsync
+          error = false
+          with_target_vms(argv) do |machine|
+
+            # Is the machine up yet?
+            if !machine.communicate.ready?
+              machine.ui.error(I18n.t("vagrant.sshfs.errors.communicator_not_ready"))
+              error = true
+              next
+            end
+
+            # Determine the sshfs synced folders for this machine
+            folders = synced_folders(machine, cached: false)[:sshfs]
+            next if !folders || folders.empty?
+
+            # Mount or Unmount depending on the user's request
+            if options[:unmount]
+              SyncedFolder.new.disable(machine, folders, {})
+            else
+              SyncedFolder.new.enable(machine, folders, {})
+            end
+          end
+          return error ? 1 : 0
+        end
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/errors.rb b/lib/vagrant-sshfs/errors.rb
new file mode 100644
index 0000000..e9c7964
--- /dev/null
+++ b/lib/vagrant-sshfs/errors.rb
@@ -0,0 +1,38 @@
+module VagrantPlugins
+  module SyncedFolderSSHFS
+    module Errors
+      # A convenient superclass for all our errors.
+      class SSHFSError < Vagrant::Errors::VagrantError
+        error_namespace("vagrant.sshfs.errors")
+      end
+
+      class SSHFSNormalMountFailed < SSHFSError
+        error_key(:normal_mount_failed)
+      end
+
+      class SSHFSSlaveMountFailed < SSHFSError
+        error_key(:slave_mount_failed)
+      end
+
+      class SSHFSReverseMountFailed < SSHFSError
+        error_key(:reverse_mount_failed)
+      end
+
+      class SSHFSUnmountFailed < SSHFSError
+        error_key(:unmount_failed)
+      end
+
+      class SSHFSInstallFailed < SSHFSError
+        error_key(:install_failed)
+      end
+
+      class SSHFSNotInstalledInGuest < SSHFSError
+        error_key(:sshfs_not_in_guest)
+      end
+
+      class SSHFSExeNotAvailable < SSHFSError
+        error_key(:exe_not_in_host)
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/plugin.rb b/lib/vagrant-sshfs/plugin.rb
new file mode 100644
index 0000000..0e98b5b
--- /dev/null
+++ b/lib/vagrant-sshfs/plugin.rb
@@ -0,0 +1,105 @@
+require "vagrant"
+
+module VagrantPlugins
+  module SyncedFolderSSHFS
+    # This plugin implements SSHFS synced folders.
+    class Plugin < Vagrant.plugin("2")
+      name "SSHFS synced folders"
+      description <<-EOF
+      The SSHFS synced folders plugin enables you to use SSHFS as a synced folder
+      implementation.
+      EOF
+
+      synced_folder("sshfs", 5) do
+        require_relative "synced_folder"
+        SyncedFolder
+      end
+
+      command("sshfs", primary: true) do
+        require_relative "command"
+        Command::SSHFS
+      end
+
+      host_capability("linux", "sshfs_reverse_mount_folder") do
+        require_relative "cap/host/linux/sshfs_reverse_mount"
+        VagrantPlugins::HostLinux::Cap::MountSSHFS
+      end
+
+      host_capability("linux", "sshfs_reverse_unmount_folder") do
+        require_relative "cap/host/linux/sshfs_reverse_mount"
+        VagrantPlugins::HostLinux::Cap::MountSSHFS
+      end
+
+      host_capability("linux", "sshfs_reverse_is_folder_mounted") do
+        require_relative "cap/host/linux/sshfs_reverse_mount"
+        VagrantPlugins::HostLinux::Cap::MountSSHFS
+      end
+
+      guest_capability("linux", "sshfs_forward_mount_folder") do
+        require_relative "cap/guest/linux/sshfs_forward_mount"
+        VagrantPlugins::GuestLinux::Cap::MountSSHFS
+      end
+
+      guest_capability("linux", "sshfs_forward_unmount_folder") do
+        require_relative "cap/guest/linux/sshfs_forward_mount"
+        VagrantPlugins::GuestLinux::Cap::MountSSHFS
+      end
+
+      guest_capability("linux", "sshfs_forward_is_folder_mounted") do
+        require_relative "cap/guest/linux/sshfs_forward_mount"
+        VagrantPlugins::GuestLinux::Cap::MountSSHFS
+      end
+
+      guest_capability("redhat", "sshfs_installed") do
+        require_relative "cap/guest/redhat/sshfs_client"
+        VagrantPlugins::GuestRedHat::Cap::SSHFSClient
+      end
+
+      guest_capability("redhat", "sshfs_install") do
+        require_relative "cap/guest/redhat/sshfs_client"
+        VagrantPlugins::GuestRedHat::Cap::SSHFSClient
+      end
+
+      guest_capability("fedora", "sshfs_installed") do
+        require_relative "cap/guest/fedora/sshfs_client"
+        VagrantPlugins::GuestFedora::Cap::SSHFSClient
+      end
+
+      guest_capability("fedora", "sshfs_install") do
+        require_relative "cap/guest/fedora/sshfs_client"
+        VagrantPlugins::GuestFedora::Cap::SSHFSClient
+      end
+
+      guest_capability("debian", "sshfs_installed") do
+        require_relative "cap/guest/debian/sshfs_client"
+        VagrantPlugins::GuestDebian::Cap::SSHFSClient
+      end
+
+      guest_capability("debian", "sshfs_install") do
+        require_relative "cap/guest/debian/sshfs_client"
+        VagrantPlugins::GuestDebian::Cap::SSHFSClient
+      end
+
+      guest_capability("arch", "sshfs_installed") do
+        require_relative "cap/guest/arch/sshfs_client"
+        VagrantPlugins::GuestArch::Cap::SSHFSClient
+      end
+
+      guest_capability("arch", "sshfs_install") do
+        require_relative "cap/guest/arch/sshfs_client"
+        VagrantPlugins::GuestArch::Cap::SSHFSClient
+      end
+
+      guest_capability("suse", "sshfs_installed") do
+        require_relative "cap/guest/suse/sshfs_client"
+        VagrantPlugins::GuestSUSE::Cap::SSHFSClient
+      end
+
+      guest_capability("suse", "sshfs_install") do
+        require_relative "cap/guest/suse/sshfs_client"
+        VagrantPlugins::GuestSUSE::Cap::SSHFSClient
+      end
+
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/synced_folder.rb b/lib/vagrant-sshfs/synced_folder.rb
new file mode 100644
index 0000000..b12f5cd
--- /dev/null
+++ b/lib/vagrant-sshfs/synced_folder.rb
@@ -0,0 +1,129 @@
+require "log4r"
+
+require "vagrant/util/platform"
+require "vagrant/util/which"
+
+require_relative "synced_folder/sshfs_forward_mount"
+require_relative "synced_folder/sshfs_reverse_mount"
+
+module VagrantPlugins
+  module SyncedFolderSSHFS
+    class SyncedFolder < Vagrant.plugin("2", :synced_folder)
+
+      def initialize(*args)
+        super
+
+        @logger = Log4r::Logger.new("vagrant::synced_folders::sshfs")
+      end
+
+      # This is called early when the synced folder is set to determine
+      # if this implementation can be used for this machine. This should
+      # return true or false.
+      #
+      # @param [Machine] machine
+      # @param [Boolean] raise_error If true, should raise an exception
+      #   if it isn't usable.
+      # @return [Boolean]
+      def usable?(machine, raise_error=false)
+        return true #for now
+      end
+
+      # This is called after the machine is booted and after networks
+      # are setup.
+      #
+      # This might be called with new folders while the machine is running.
+      # If so, then this should add only those folders without removing
+      # any existing ones.
+      #
+      # No return value.
+      def enable(machine, folders, pluginopts)
+
+        # Iterate through the folders and mount if needed
+        folders.each do |id, opts|
+
+          if opts.has_key?(:reverse) and opts[:reverse]
+            do_reverse_mount(machine, opts)
+          else
+            do_forward_mount(machine, opts)
+          end
+        end
+      end
+
+      # This is called to remove the synced folders from a running
+      # machine.
+      #
+      # This is not guaranteed to be called, but this should be implemented
+      # by every synced folder implementation.
+      #
+      # @param [Machine] machine The machine to modify.
+      # @param [Hash] folders The folders to remove. This will not contain
+      #   any folders that should remain.
+      # @param [Hash] opts Any options for the synced folders.
+      def disable(machine, folders, opts)
+
+        # Iterate through the folders and mount if needed
+        folders.each do |id, opts|
+          if opts.has_key?(:reverse) and opts[:reverse]
+            do_reverse_unmount(machine, opts)
+          else
+            do_forward_unmount(machine, opts)
+          end
+        end
+      end
+
+      # This is called after destroying the machine during a
+      # `vagrant destroy` and also prior to syncing folders during
+      # a `vagrant up`.
+      #
+      # No return value.
+      #
+      # @param [Machine] machine
+      # @param [Hash] opts
+      def cleanup(machine, opts)
+      end
+
+      protected
+
+      # Function to find the path to an executable with name "name"
+      def find_executable(name)
+        error_class = VagrantPlugins::SyncedFolderSSHFS::Errors::SSHFSExeNotAvailable
+
+        # Save off PATH env var before we modify it
+        oldpath = ENV['PATH']
+
+        # Try to include paths where sftp-server may live so
+        # That we have a good chance of finding it
+        if Vagrant::Util::Platform.windows?
+          if Vagrant::Util::Platform.cygwin?
+            # If in a cygwin terminal then we can programmatically
+            # determine where sftp-server would be. ssh should already
+            # be in path.
+            cygwin_root = Vagrant::Util::Platform.cygwin_windows_path('/')
+            ENV['PATH'] += ';' + cygwin_root + '\usr\sbin'
+          else
+            # If not in a cygwin terminal then we'll have to guess
+            # where cygwin is installed and add the /bin/ (for ssh) and
+            # /usr/sbin (for sftp-server) to the PATH.
+            ENV['PATH'] += ';C:\cygwin\bin'
+            ENV['PATH'] += ';C:\cygwin\usr\sbin'
+            ENV['PATH'] += ';C:\cygwin64\bin'
+            ENV['PATH'] += ';C:\cygwin64\usr\sbin'
+          end
+        else
+          ENV['PATH'] += ':/usr/libexec/openssh' # Linux (Red Hat Family)
+          ENV['PATH'] += ':/usr/lib/openssh'     # Linux (Debian Family)
+          ENV['PATH'] += ':/usr/lib/ssh'         # Linux (Arch Linux Family)
+          ENV['PATH'] += ':/usr/libexec/'        # Mac OS X
+        end
+
+        # Try to find the executable
+        exepath = Vagrant::Util::Which.which(name)
+        raise error_class, executable: name if !exepath
+
+        # Restore the PATH variable and return
+        ENV['PATH'] = oldpath
+        return exepath
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/synced_folder/sshfs_forward_mount.rb b/lib/vagrant-sshfs/synced_folder/sshfs_forward_mount.rb
new file mode 100644
index 0000000..a495878
--- /dev/null
+++ b/lib/vagrant-sshfs/synced_folder/sshfs_forward_mount.rb
@@ -0,0 +1,116 @@
+require "log4r"
+
+require "vagrant/util/platform"
+require "vagrant/util/which"
+
+module VagrantPlugins
+  module SyncedFolderSSHFS
+    class SyncedFolder < Vagrant.plugin("2", :synced_folder)
+
+      protected
+
+      # Do a forward mount: mounting host folder into the guest
+      def do_forward_mount(machine, opts)
+
+        # Check to see if sshfs software is in the guest
+        if machine.guest.capability?(:sshfs_installed)
+          if !machine.guest.capability(:sshfs_installed)
+            can_install = machine.guest.capability?(:sshfs_install)
+            if !can_install
+              raise VagrantPlugins::SyncedFolderSSHFS::Errors::SSHFSNotInstalledInGuest
+            end
+            machine.ui.info(I18n.t("vagrant.sshfs.actions.installing"))
+            machine.guest.capability(:sshfs_install)
+          end
+        end
+
+        # If already mounted then there is nothing to do
+        if machine.guest.capability(:sshfs_forward_is_folder_mounted, opts)
+          machine.ui.info(
+            I18n.t("vagrant.sshfs.info.already_mounted",
+                   location: 'guest', folder: opts[:guestpath]))
+          return
+        end
+
+        # If the synced folder entry has host information in it then
+        # assume we are doing a normal sshfs mount to a host that isn't
+        # the machine running vagrant. Rely on password/ssh keys.
+        #
+        # If not, then we are doing a slave mount and we need to
+        # make sure we can find the sftp-server and ssh execuatable
+        # files on the host.
+        if opts.has_key?(:ssh_host) and opts[:ssh_host]
+            # Check port information and find out auth info
+            check_host_port(machine, opts)
+            get_auth_info(machine, opts)
+        else
+            opts[:ssh_exe_path] = find_executable('ssh')
+            opts[:sftp_server_exe_path] = find_executable('sftp-server')
+        end
+        # Do the mount
+        machine.ui.info(I18n.t("vagrant.sshfs.actions.mounting"))
+        machine.guest.capability(:sshfs_forward_mount_folder, opts)
+      end
+
+      def do_forward_unmount(machine, opts)
+
+        # If not mounted then there is nothing to do
+        if ! machine.guest.capability(:sshfs_forward_is_folder_mounted, opts)
+          machine.ui.info(
+            I18n.t("vagrant.sshfs.info.not_mounted",
+                   location: 'guest', folder: opts[:guestpath]))
+          return
+        end
+
+        # Do the Unmount
+        machine.ui.info(I18n.t("vagrant.sshfs.actions.unmounting"))
+        machine.guest.capability(:sshfs_forward_unmount_folder, opts)
+      end
+
+      # Check if port information was provided in the options. If not,
+      # then default to port 22 for ssh
+      def check_host_port(machine, opts)
+        if not opts.has_key?(:ssh_port) or not opts[:ssh_port]
+            opts[:ssh_port] = '22'
+        end
+      end
+
+      # Function to gather authentication information (username/password)
+      # for doing a normal sshfs mount
+      def get_auth_info(machine, opts)
+        prompt_for_password = false
+        ssh_info = machine.ssh_info
+
+        # Detect the username of the current user
+        username = `whoami`.strip
+
+        # If no username provided then default to the current
+        # user that is executing vagrant
+          if not opts.has_key?(:ssh_username) or not opts[:ssh_username]
+            opts[:ssh_username] = username
+          end
+
+        # Check to see if we need to prompt the user for a password.
+        # We will prompt if:
+        #  - User asked us to via prompt_for_password option
+        #  - User did not provide a password in options and is not fwding ssh agent
+        #
+        if opts.has_key?(:prompt_for_password) and opts[:prompt_for_password]
+            prompt_for_password = opts[:prompt_for_password]
+        end
+          if not opts.has_key?(:ssh_password) or not opts[:ssh_password]
+            if not ssh_info.has_key?(:forward_agent) or not ssh_info[:forward_agent]
+            prompt_for_password = true
+            end 
+          end
+
+        # Now do the prompt
+        if prompt_for_password
+          opts[:ssh_password] = machine.ui.ask(
+              I18n.t("vagrant.sshfs.ask.prompt_for_password", username: opts[:ssh_username]),
+              echo: false)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/synced_folder/sshfs_reverse_mount.rb b/lib/vagrant-sshfs/synced_folder/sshfs_reverse_mount.rb
new file mode 100644
index 0000000..0f19f9c
--- /dev/null
+++ b/lib/vagrant-sshfs/synced_folder/sshfs_reverse_mount.rb
@@ -0,0 +1,51 @@
+require "log4r"
+
+require "vagrant/util/platform"
+require "vagrant/util/which"
+
+module VagrantPlugins
+  module SyncedFolderSSHFS
+    class SyncedFolder < Vagrant.plugin("2", :synced_folder)
+
+      protected
+
+      # Do a reverse mount: mounting guest folder onto the host
+      def do_reverse_mount(machine, opts)
+
+        # Check to see if sshfs software is in the host
+        if machine.env.host.capability?(:sshfs_installed)
+          if !machine.env.host.capability(:sshfs_installed)
+            raise VagrantPlugins::SyncedFolderSSHFS::Errors::SSHFSNotInstalledInHost
+          end
+        end
+
+        # If already mounted then there is nothing to do
+        if machine.env.host.capability(:sshfs_reverse_is_folder_mounted, opts)
+          machine.ui.info(
+            I18n.t("vagrant.sshfs.info.already_mounted",
+                   location: 'host', folder: opts[:hostpath]))
+          return
+        end
+
+        # Do the mount
+        machine.ui.info(I18n.t("vagrant.sshfs.actions.mounting"))
+        machine.env.host.capability(:sshfs_reverse_mount_folder, machine, opts)
+      end
+
+      def do_reverse_unmount(machine, opts)
+
+        # If not mounted then there is nothing to do
+        if ! machine.env.host.capability(:sshfs_reverse_is_folder_mounted, opts)
+          machine.ui.info(
+            I18n.t("vagrant.sshfs.info.not_mounted",
+                   location: 'host', folder: opts[:hostpath]))
+          return
+        end
+
+        # Do the Unmount
+        machine.ui.info(I18n.t("vagrant.sshfs.actions.unmounting"))
+        machine.env.host.capability(:sshfs_reverse_unmount_folder, machine, opts)
+      end
+    end
+  end
+end
diff --git a/lib/vagrant-sshfs/version.rb b/lib/vagrant-sshfs/version.rb
new file mode 100644
index 0000000..9ca9257
--- /dev/null
+++ b/lib/vagrant-sshfs/version.rb
@@ -0,0 +1,5 @@
+module VagrantPlugins
+  module SyncedFolderSSHFS
+    VERSION = "1.2.0"
+  end
+end
diff --git a/locales/synced_folder_sshfs.yml b/locales/synced_folder_sshfs.yml
new file mode 100644
index 0000000..7d6324b
--- /dev/null
+++ b/locales/synced_folder_sshfs.yml
@@ -0,0 +1,93 @@
+en:
+  vagrant:
+    sshfs:
+      actions:
+        installing: Installing SSHFS client...
+        mounting: Mounting SSHFS shared folder...
+        unmounting: Unmounting SSHFS shared folder...
+        unmounting_folder: |-
+          Unmounting SSHFS shared folder mounted at %{guestpath}
+        reverse_unmounting_folder: |-
+          Unmounting SSHFS shared folder mounted at host's %{hostpath}
+        reverse_mounting_folder: |-
+          mounting folder via SSHFS: guestpath:%{guestpath} => hostpath:%{hostpath}
+        slave_mounting_folder: |-
+          Mounting folder via SSHFS: %{hostpath} => %{guestpath}
+        normal_mounting_folder: |-
+          Mounting folder via SSHFS: %{user}@%{host}:%{hostpath} => %{guestpath}
+      ask:
+        prompt_for_password: |-
+          SSHFS password for '%{username}':
+      info:
+        detected_host_ip: |-
+          Detected host IP address is '%{ip}'
+        already_mounted: |-
+          The folder %{folder} in the %{location} is already mounted.
+        not_mounted: |-
+          The folder %{folder} in the %{location} is not mounted.
+      errors:
+        communicator_not_ready: |-
+          The machine is reporting that it is not ready to communicate via ssh. Verify
+          this machine is properly running.
+        exe_not_in_host: |-
+          The '%{executable}' executable file can't be found on the host machine but
+          is required for sshfs mounting to work. Please install the software and 
+          try again.
+        sshfs_not_in_guest: |-
+          The necessary SSHFS software is not installed in the guest. 
+        install_failed_arch: |-
+          The install of the sshfs client software failed. On Arch this is most likely
+          because the package lists are not up to date [1] and partial upgrades are not
+          supported [2]. Please update your Arch system or install SSHFS manually.
+
+          [1] https://wiki.archlinux.org/index.php/pacman#Packages_cannot_be_retrieved_on_installation
+          [2] https://wiki.archlinux.org/index.php/System_maintenance#Partial_upgrades_are_unsupported
+        normal_mount_failed: |-
+          Mounting SSHFS shared folders failed. This is most often caused by either an
+          SSH Daemon not running on the target host or invalid credentials being provided.
+          Please make sure an SSH daemon is running on the host and proper credentials
+          were provided to be able to authenticate via SSH.
+
+          The command and output are:
+
+          %{command}
+
+          Stdout from the command:
+
+          %{stdout}
+
+          Stderr from the command:
+
+          %{stderr}
+        reverse_mount_failed: |-
+          Mounting SSHFS shared folder via reverse SSHFS mount failed. Please
+          look at the below output from from the processes that were run.
+
+          SSHFS command output:
+
+          %{sshfs_output}
+        slave_mount_failed: |-
+          Mounting SSHFS shared folder via slave SSHFS mount failed. Please
+          look at the below STDERR output from the processes that were run.
+
+          SSH command:
+
+          %{ssh_stderr}
+
+          SFTP command:
+
+          %{sftp_stderr}
+        unmount_failed: |-
+          Unmount the SSHFS mount failed.
+
+          The command and output are:
+
+          %{command}
+
+          Stdout from the command:
+
+          %{stdout}
+
+          Stderr from the command:
+
+          %{stderr}
diff --git a/test/libvirt/README.txt b/test/libvirt/README.txt
new file mode 100644
index 0000000..ad03582
--- /dev/null
+++ b/test/libvirt/README.txt
@@ -0,0 +1,15 @@
+
+To bring up vagrant host:
+
+vagrant up
+
+To run tests:
+
+vagrant ssh
+
+and then:
+
+cd /sharedfolder/code/github.com/dustymabe/vagrant-sshfs/
+gem install bundler
+bundle install
+bundle exec rake featuretests
diff --git a/test/libvirt/Vagrantfile b/test/libvirt/Vagrantfile
new file mode 100644
index 0000000..754a219
--- /dev/null
+++ b/test/libvirt/Vagrantfile
@@ -0,0 +1,35 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+Vagrant.configure(2) do |config|
+
+    config.ssh.insert_key = 'true'
+    config.vm.synced_folder "/guests/sharedfolder", "/sharedfolder", type: "sshfs"
+
+    config.vm.provider :libvirt do |domain|
+      domain.memory = 4096
+      domain.cpus = 4
+      domain.nested = true
+    end
+
+    host = 'viv-libvirt' # vagrant in vagrant - to test libvirt
+    box  = 'fedora/24-cloud-base'
+
+    config.vm.define host do | tmp |
+        tmp.vm.hostname = host
+        tmp.vm.box = box
+    end
+  config.vm.provision "shell", inline: <<-SHELL
+    rpms=(
+      libvirt-daemon-kvm                         # for vagrant libvirt support
+      make gcc ruby ruby-devel redhat-rpm-config # for building gems
+      gcc-c++                                    # for building unf_ext
+      libvirt-devel                              # for building ruby-libvirt gem
+      zlib-devel                                 # for building nokogiri gem
+      git                                        # for the git ls-files in gemspec file
+      bsdtar                                     # used by vagrant to unpack box files
+    )
+    dnf install -y ${rpms[@]}
+    usermod -a -G libvirt vagrant
+    systemctl start libvirtd virtlogd
+  SHELL
+end
diff --git a/test/misc/README.txt b/test/misc/README.txt
new file mode 100644
index 0000000..b716a22
--- /dev/null
+++ b/test/misc/README.txt
@@ -0,0 +1,29 @@
+
+# This directory is for testing the three different mount modes
+# that are supported by vagrant-sshfs
+
+# To test we will first create the directory on the machien where
+# we will mount the guest /etc/ into the host (the reverse mount).
+
+mkdir /tmp/reverse_mount_etc
+
+# Next we will define where our 3rd party host is (the normal mount).
+# This can be another vagrant box or whatever machine you want.
+export THIRD_PARTY_HOST='192.168.121.73'                                                                                                                                               
+export THIRD_PARTY_HOST_USER='vagrant'                                                                                                                                                 
+export THIRD_PARTY_HOST_PASS='vagrant'
+
+# Next vagrant up - will do 3 mounts (normal, slave, reverse).
+vagrant up
+
+# Next run the script to test the mounts:
+$ bash dotests.sh 
+Testing normal mount!
+a57e39fa692f294e860349a9451be67c
+Testing slave mount!
+e2c4ceac71dc414cb3ed864cff04a917
+Testing reverse mount!
+508619e7e68e446c84d1fcdf7e0dc577
+
+# We are printing out the machine-id under each mount to prove each
+# mount is from a different machine.
diff --git a/test/misc/Vagrantfile b/test/misc/Vagrantfile
new file mode 100644
index 0000000..f0ef39a
--- /dev/null
+++ b/test/misc/Vagrantfile
@@ -0,0 +1,27 @@
+Vagrant.configure(2) do |config|
+
+    config.ssh.insert_key = 'true'
+
+    # Test a forward normal mount: 
+    #     mounting a folder from a 3rd party host into guest
+    config.vm.synced_folder "/etc/", "/tmp/forward_normal_mount_etc/", type: "sshfs",
+        ssh_host: ENV['THIRD_PARTY_HOST'],
+        ssh_username: ENV['THIRD_PARTY_HOST_USER'],
+        ssh_password: ENV['THIRD_PARTY_HOST_PASS']
+
+    # Test a forward slave mount: 
+    #   mounting /etc/ from the vagrant host into the guest
+    config.vm.synced_folder "/etc/", "/tmp/forward_slave_mount_etc/", type: "sshfs"
+
+    # Test a reverse mount:
+    # mounting /etc/ from vagrant guest into vagrant host
+    config.vm.synced_folder "/tmp/reverse_mount_etc/", "/etc", type: "sshfs", reverse: true
+
+    host = 'vagrant-sshfs-tests'
+    box  = 'fedora/24-cloud-base'
+
+    config.vm.define host do | tmp |
+        tmp.vm.hostname = host
+        tmp.vm.box = box
+    end
+end
diff --git a/test/misc/dotests.sh b/test/misc/dotests.sh
new file mode 100644
index 0000000..ea6691d
--- /dev/null
+++ b/test/misc/dotests.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+set -eu
+
+# Test the three mounts we have done
+
+echo "Testing normal mount!"
+vagrant ssh -- cat /tmp/forward_normal_mount_etc/machine-id
+
+echo "Testing slave mount!"
+vagrant ssh -- cat /tmp/forward_slave_mount_etc/machine-id
+
+echo "Testing reverse mount!"
+cat /tmp/reverse_mount_etc/machine-id
diff --git a/test/virtualbox/README.txt b/test/virtualbox/README.txt
new file mode 100644
index 0000000..c68026e
--- /dev/null
+++ b/test/virtualbox/README.txt
@@ -0,0 +1,17 @@
+# XXX Note this is not working right now as nested virt. I keep
+# getting kernel tracebacks on Fedora 24.
+
+To bring up vagrant host:
+
+vagrant up
+
+To run tests:
+
+vagrant ssh
+
+and then:
+
+cd /sharedfolder/code/github.com/dustymabe/vagrant-sshfs/
+gem install bundler
+bundle install
+bundle exec rake featuretests
diff --git a/test/virtualbox/Vagrantfile b/test/virtualbox/Vagrantfile
new file mode 100644
index 0000000..2d05f9d
--- /dev/null
+++ b/test/virtualbox/Vagrantfile
@@ -0,0 +1,42 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+#
+# XXX Note this is not working right now as nested virt. I keep
+# getting kernel tracebacks on Fedora 24.
+#
+Vagrant.configure(2) do |config|
+
+  config.ssh.insert_key = 'true'
+  config.vm.synced_folder "/guests/sharedfolder", "/sharedfolder", type: "sshfs"
+
+  config.vm.provider :libvirt do |domain|
+    domain.memory = 4096
+    domain.cpus = 4
+    domain.nested = true
+  end
+
+  host = 'viv-virtbox' # vagrant in vagrant - to test virtbox
+  box  = 'fedora/24-cloud-base'
+
+  config.vm.define host do | tmp |
+      tmp.vm.hostname = host
+      tmp.vm.box = box
+  end
+  # Must use VirtualBox-5.0 - 5.1 not supported by Vagrant yet
+  config.vm.provision "shell", inline: <<-SHELL
+    dnf config-manager --add-repo http://download.virtualbox.org/virtualbox/rpm/fedora/virtualbox.repo
+    rpms=(
+      kernel-devel-$(uname -r) kernel-headers-$(uname -r)
+      gcc make VirtualBox-5.0                    # all for virtualbox support
+      make gcc ruby ruby-devel redhat-rpm-config # for building gems
+      gcc-c++                                    # for building unf_ext
+      libvirt-devel                              # for building ruby-libvirt gem
+      zlib-devel                                 # for building nokogiri gem
+      git                                        # for the git ls-files in gemspec file
+      bsdtar                                     # used by vagrant to unpack box files
+    )
+    dnf install -y ${rpms[@]}
+    /usr/lib/virtualbox/vboxdrv.sh setup
+    usermod -a -G vboxusers vagrant
+  SHELL
+end
diff --git a/vagrant-sshfs.gemspec b/vagrant-sshfs.gemspec
new file mode 100644
index 0000000..0f72cd3
--- /dev/null
+++ b/vagrant-sshfs.gemspec
@@ -0,0 +1,31 @@
+# coding: utf-8
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require 'vagrant-sshfs/version'
+
+Gem::Specification.new do |spec|
+  spec.name          = "vagrant-sshfs"
+  spec.version       = VagrantPlugins::SyncedFolderSSHFS::VERSION
+  spec.authors       = ["Dusty Mabe"]
+  spec.email         = ["dusty at dustymabe.com"]
+  spec.description   = """
+    A Vagrant synced folder plugin that mounts folders via SSHFS. 
+    This is the successor to Fabio Kreusch's implementation:
+    https://github.com/fabiokr/vagrant-sshfs"""
+  spec.summary       = spec.description
+  spec.homepage      = "https://github.com/dustymabe/vagrant-sshfs"
+  spec.license       = "GPL-2.0"
+
+  spec.files         = `git ls-files -z`.split("\x0")
+  spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
+  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
+  spec.require_paths = ["lib"]
+
+  spec.add_dependency 'win32-process'
+
+  spec.add_development_dependency 'bundler', '~> 1.7'
+  spec.add_development_dependency 'rake', '~> 10.0'
+  spec.add_development_dependency 'cucumber', '~> 2.1'
+  spec.add_development_dependency 'aruba', '~> 0.13'
+  spec.add_development_dependency 'komenda', '~> 0.1.6'
+end

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/vagrant-sshfs.git



More information about the Pkg-ruby-extras-commits mailing list