Skip to content

Commit

Permalink
Merge pull request #38 from dustymabe/dusty-reverse-mount2
Browse files Browse the repository at this point in the history
Add reverse mounting feature
  • Loading branch information
dustymabe authored Jul 28, 2016
2 parents d8644b8 + 61ba845 commit f326e90
Show file tree
Hide file tree
Showing 19 changed files with 506 additions and 116 deletions.
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ folder plugin from the Vagrant core code and molding it to fit SSHFS.

## Modes of Operation

### Sharing Vagrant Host Directory to Vagrant Guest - 99% of users
### 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))
Expand All @@ -58,6 +58,19 @@ 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 destroy 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
Expand Down Expand Up @@ -159,6 +172,23 @@ config.vm.synced_folder "/path/on/host", "/path/on/guest",
disabled: false
```

### 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
```

## Appendix A: Using Keys and Forwarding SSH Agent

When [sharing an arbitrary host directory](#sharing-arbitrary-host-directory-to-vagrant-guest---1-of-users)
Expand Down
2 changes: 1 addition & 1 deletion lib/vagrant-sshfs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# 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/linux/sshfs_mount.rb
# lib/vagrant-sshfs/cap/guest/linux/sshfs_forward_mount.rb
if Vagrant::Util::Platform.windows?
require 'win32/process'
end
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class MountSSHFS
extend Vagrant::Util::Retryable
@@logger = Log4r::Logger.new("vagrant::synced_folders::sshfs_mount")

def self.sshfs_is_folder_mounted(machine, opts)
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(
Expand All @@ -34,7 +34,7 @@ def self.sshfs_is_folder_mounted(machine, opts)
return mounted
end

def self.sshfs_mount_folder(machine, opts)
def self.sshfs_forward_mount_folder(machine, opts)
# opts contains something like:
# { :type=>:sshfs,
# :guestpath=>"/sharedfolder",
Expand Down Expand Up @@ -76,7 +76,7 @@ def self.sshfs_mount_folder(machine, opts)
end
end

def self.sshfs_unmount_folder(machine, opts)
def self.sshfs_forward_unmount_folder(machine, opts)
# opts contains something like:
# { :type=>:sshfs,
# :guestpath=>"/sharedfolder",
Expand Down Expand Up @@ -199,7 +199,7 @@ def self.sshfs_slave_mount(machine, opts, hostpath, expanded_guest_path)
mounted = false
for i in 0..6
machine.ui.info("Checking Mount..")
if self.sshfs_is_folder_mounted(machine, opts)
if self.sshfs_forward_is_folder_mounted(machine, opts)
mounted = true
break
end
Expand Down
File renamed without changes.
176 changes: 176 additions & 0 deletions lib/vagrant-sshfs/cap/host/linux/sshfs_reverse_mount.rb
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions lib/vagrant-sshfs/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ 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
Expand Down
47 changes: 31 additions & 16 deletions lib/vagrant-sshfs/plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,68 +20,83 @@ class Plugin < Vagrant.plugin("2")
Command::SSHFS
end

guest_capability("linux", "sshfs_mount_folder") do
require_relative "cap/linux/sshfs_mount"
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_unmount_folder") do
require_relative "cap/linux/sshfs_mount"
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_is_folder_mounted") do
require_relative "cap/linux/sshfs_mount"
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/redhat/sshfs_client"
require_relative "cap/guest/redhat/sshfs_client"
VagrantPlugins::GuestRedHat::Cap::SSHFSClient
end

guest_capability("redhat", "sshfs_install") do
require_relative "cap/redhat/sshfs_client"
require_relative "cap/guest/redhat/sshfs_client"
VagrantPlugins::GuestRedHat::Cap::SSHFSClient
end

guest_capability("fedora", "sshfs_installed") do
require_relative "cap/fedora/sshfs_client"
require_relative "cap/guest/fedora/sshfs_client"
VagrantPlugins::GuestFedora::Cap::SSHFSClient
end

guest_capability("fedora", "sshfs_install") do
require_relative "cap/fedora/sshfs_client"
require_relative "cap/guest/fedora/sshfs_client"
VagrantPlugins::GuestFedora::Cap::SSHFSClient
end

guest_capability("debian", "sshfs_installed") do
require_relative "cap/debian/sshfs_client"
require_relative "cap/guest/debian/sshfs_client"
VagrantPlugins::GuestDebian::Cap::SSHFSClient
end

guest_capability("debian", "sshfs_install") do
require_relative "cap/debian/sshfs_client"
require_relative "cap/guest/debian/sshfs_client"
VagrantPlugins::GuestDebian::Cap::SSHFSClient
end

guest_capability("arch", "sshfs_installed") do
require_relative "cap/arch/sshfs_client"
require_relative "cap/guest/arch/sshfs_client"
VagrantPlugins::GuestArch::Cap::SSHFSClient
end

guest_capability("arch", "sshfs_install") do
require_relative "cap/arch/sshfs_client"
require_relative "cap/guest/arch/sshfs_client"
VagrantPlugins::GuestArch::Cap::SSHFSClient
end

guest_capability("suse", "sshfs_installed") do
require_relative "cap/suse/sshfs_client"
require_relative "cap/guest/suse/sshfs_client"
VagrantPlugins::GuestSUSE::Cap::SSHFSClient
end

guest_capability("suse", "sshfs_install") do
require_relative "cap/suse/sshfs_client"
require_relative "cap/guest/suse/sshfs_client"
VagrantPlugins::GuestSUSE::Cap::SSHFSClient
end

Expand Down
Loading

0 comments on commit f326e90

Please sign in to comment.