diff --git a/plugins/guests/bsd/cap/virtualbox.rb b/plugins/guests/bsd/cap/virtualbox.rb index 14406c534..fdd93269a 100644 --- a/plugins/guests/bsd/cap/virtualbox.rb +++ b/plugins/guests/bsd/cap/virtualbox.rb @@ -1,14 +1,78 @@ +require_relative "../../../synced_folders/unix_mount_helpers" + module VagrantPlugins module GuestBSD module Cap class VirtualBox + extend SyncedFolder::UnixMountHelpers # BSD-based guests do not currently support VirtualBox synced folders. # Instead of raising an error about a missing capability, this defines # the capability and then provides a more detailed error message, # linking to sources on the Internet where the problem is # better-described. def self.mount_virtualbox_shared_folder(machine, name, guestpath, options) - raise Vagrant::Errors::VirtualBoxMountNotSupportedBSD + guest_path = Shellwords.escape(guestpath) + + @@logger.debug("Mounting #{name} (#{options[:hostpath]} to #{guestpath})") + + builtin_mount_type = "-cit vboxsf" + addon_mount_type = "-t vboxsf" + + mount_options = options.fetch(:mount_options, []) + detected_ids = detect_owner_group_ids(machine, guest_path, mount_options, options) + mount_uid = detected_ids[:uid] + mount_gid = detected_ids[:gid] + + mount_options << "uid=#{mount_uid}" + mount_options << "gid=#{mount_gid}" + mount_options = mount_options.join(',') + mount_command = "mount #{addon_mount_type} -o #{mount_options} #{name} #{guest_path}" + + # Create the guest path if it doesn't exist + machine.communicate.sudo("mkdir -p #{guest_path}") + + stderr = "" + result = machine.communicate.sudo(mount_command, error_check: false) do |type, data| + stderr << data if type == :stderr + end + + if result != 0 + if stderr.include?("-cit") + @@logger.info("Detected builtin vboxsf module, modifying mount command") + mount_command.sub!(addon_mount_type, builtin_mount_type) + end + + # Attempt to mount the folder. We retry here a few times because + # it can fail early on. + stderr = "" + retryable(on: Vagrant::Errors::VirtualBoxMountFailed, tries: 3, sleep: 5) do + machine.communicate.sudo(mount_command, + error_class: Vagrant::Errors::VirtualBoxMountFailed, + error_key: :virtualbox_mount_failed, + command: mount_command, + output: stderr, + ) { |type, data| stderr = data if type == :stderr } + end + 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_command = "chown #{mount_uid}:#{mount_gid} #{guest_path}" + machine.communicate.sudo(chown_command) + end + + emit_upstart_notification(machine, guest_path) + end + + + def self.unmount_virtualbox_shared_folder(machine, guestpath, options) + guest_path = Shellwords.escape(guestpath) + + result = machine.communicate.sudo("umount #{guest_path}", error_check: false) + if result == 0 + machine.communicate.sudo("rmdir #{guest_path}", error_check: false) + end end end end