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) error_key(:virtualbox_no_name)
end end
class VirtualBoxMountFailed < VagrantError
error_key(:virtualbox_mount_failed)
end
class VirtualBoxNameExists < VagrantError class VirtualBoxNameExists < VagrantError
error_key(:virtualbox_name_exists) error_key(:virtualbox_name_exists)
end end

View File

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

View File

@ -1342,6 +1342,19 @@ en:
VirtualBox is complaining that the installation is incomplete. Please VirtualBox is complaining that the installation is incomplete. Please
run `VBoxManage --version` to see the error message which should contain run `VBoxManage --version` to see the error message which should contain
instructions on how to fix this error. 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: |- virtualbox_name_exists: |-
The name of your virtual machine couldn't be set because VirtualBox The name of your virtual machine couldn't be set because VirtualBox
is reporting another VM with that name already exists. Most of the is reporting another VM with that name already exists. Most of the