guests/linux: Properly escape and retry vbox shared folder mounting

This commit is contained in:
Seth Vargo 2016-06-24 21:19:58 -04:00
parent 4aaa600bd6
commit 7e88266999
No known key found for this signature in database
GPG Key ID: 905A90C2949E8787
3 changed files with 49 additions and 46 deletions

View File

@ -780,6 +780,10 @@ module Vagrant
error_key(:virtualbox_no_name)
end
class VirtualBoxMountFailed < VagrantError
error_key(:virtualbox_mount_failed)
end
class VirtualBoxNameExists < VagrantError
error_key(:virtualbox_name_exists)
end

View File

@ -1,12 +1,17 @@
require "shellwords"
require "vagrant/util/retryable"
module VagrantPlugins
module GuestLinux
module Cap
class MountVirtualBoxSharedFolder
def self.mount_virtualbox_shared_folder(machine, name, guestpath, options)
expanded_guest_path = machine.guest.capability(
:shell_expand_guest_path, guestpath)
extend Vagrant::Util::Retryable
mount_commands = []
def self.mount_virtualbox_shared_folder(machine, name, guestpath, options)
guest_path = Shellwords.escape(guestpath)
mount_commands = ["set -e"]
if options[:owner].is_a? Integer
mount_uid = options[:owner]
@ -25,73 +30,54 @@ module VagrantPlugins
# First mount command uses getent to get the group
mount_options = "-o uid=#{mount_uid},gid=#{mount_gid}"
mount_options += ",#{options[:mount_options].join(",")}" if options[:mount_options]
mount_commands << "mount -t vboxsf #{mount_options} #{name} #{expanded_guest_path}"
mount_commands << "mount -t vboxsf #{mount_options} #{name} #{guest_path}"
# Second mount command uses the old style `id -g`
mount_options = "-o uid=#{mount_uid},gid=#{mount_gid_old}"
mount_options += ",#{options[:mount_options].join(",")}" if options[:mount_options]
mount_commands << "mount -t vboxsf #{mount_options} #{name} #{expanded_guest_path}"
mount_commands << "mount -t vboxsf #{mount_options} #{name} #{guest_path}"
# Create the guest path if it doesn't exist
machine.communicate.sudo("mkdir -p #{expanded_guest_path}")
machine.communicate.sudo("mkdir -p #{guest_path}")
# Attempt to mount the folder. We retry here a few times because
# it can fail early on.
attempts = 0
while true
success = true
stderr = ""
mount_commands.each do |command|
no_such_device = false
stderr = ""
status = machine.communicate.sudo(command, error_check: false) do |type, data|
if type == :stderr
no_such_device = true if data =~ /No such device/i
stderr += data.to_s
end
end
success = status == 0 && !no_such_device
break if success
end
break if success
attempts += 1
if attempts > 10
raise Vagrant::Errors::LinuxMountFailed,
command: mount_commands.join("\n"),
output: stderr
end
sleep(2*attempts)
command = mount_commands.join("\n")
stderr = ""
retryable(on: Vagrant::Errors::VirtualBoxMountFailed, tries: 3, sleep: 5) do
machine.communicate.sudo(command,
error_class: Vagrant::Errors::VirtualBoxMountFailed,
error_key: :virtualbox_mount_failed,
command: command,
output: stderr,
) { |type, data| stderr = data if type == :stderr }
end
# Chown the directory to the proper user. We skip this if the
# mount options contained a readonly flag, because it won't work.
if !options[:mount_options] || !options[:mount_options].include?("ro")
chown_commands = []
chown_commands << "chown #{mount_uid}:#{mount_gid} #{expanded_guest_path}"
chown_commands << "chown #{mount_uid}:#{mount_gid_old} #{expanded_guest_path}"
chown_commands << "chown #{mount_uid}:#{mount_gid} #{guest_path}"
chown_commands << "chown #{mount_uid}:#{mount_gid_old} #{guest_path}"
exit_status = machine.communicate.sudo(chown_commands[0], error_check: false)
machine.communicate.sudo(chown_commands[1]) if exit_status != 0
end
# Emit an upstart event if we can
machine.communicate.sudo <<-SCRIPT
if command -v /sbin/init && /sbin/init --version | grep upstart; then
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
fi
SCRIPT
machine.communicate.sudo <<-EOH.gsub(/^ {12}/, "")
if command -v /sbin/init && /sbin/init --version | grep upstart; then
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guest_path}
fi
EOH
end
def self.unmount_virtualbox_shared_folder(machine, guestpath, options)
result = machine.communicate.sudo(
"umount #{guestpath}", error_check: false)
guest_path = Shellwords.escape(guestpath)
result = machine.communicate.sudo("umount #{guest_path}", error_check: false)
if result == 0
machine.communicate.sudo("rmdir #{guestpath}", error_check: false)
machine.communicate.sudo("rmdir #{guest_path}", error_check: false)
end
end
end

View File

@ -1342,6 +1342,19 @@ en:
VirtualBox is complaining that the installation is incomplete. Please
run `VBoxManage --version` to see the error message which should contain
instructions on how to fix this error.
virtualbox_mount_failed: |-
Vagrant was unable to mount VirtualBox shared folders. This is usually
because the filesystem "vboxsf" is not available. This filesystem is
made available via the VirtualBox Guest Additions and kernel module.
Please verify that these guest additions are properly installed in the
guest. This is not a bug in Vagrant and is usually caused by a faulty
Vagrant box. For context, the command attemped was:
%{command}
The error output from the command was:
%{output}
virtualbox_name_exists: |-
The name of your virtual machine couldn't be set because VirtualBox
is reporting another VM with that name already exists. Most of the