[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