From 3c44ce97428fe97f7155136ae82975a8da3078f1 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Fri, 12 May 2017 13:11:53 -0700 Subject: [PATCH] Support vbox provider within WSL rootfs. Make Windows access easier. Enables proper setup of VMs started from within WSL rootfs paths. Updates setup for Windows access when working within the WSL to auto-detect settings instead of relying on user defined environment variables. --- lib/vagrant/errors.rb | 4 + lib/vagrant/util.rb | 4 +- lib/vagrant/util/platform.rb | 98 +++++++++++++++++-- plugins/providers/virtualbox/driver/base.rb | 4 + .../virtualbox/driver/version_5_0.rb | 7 +- templates/locales/en.yml | 7 ++ website/source/docs/other/wsl.html.md | 59 +++++------ 7 files changed, 138 insertions(+), 45 deletions(-) diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index d8d4ba44b..627038eb9 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -899,5 +899,9 @@ module Vagrant class WSLVagrantAccessError < VagrantError error_key(:wsl_vagrant_access_error) end + + class WSLVirtualBoxWindowsAccessError < VagrantError + error_key(:wsl_virtualbox_windows_access) + end end end diff --git a/lib/vagrant/util.rb b/lib/vagrant/util.rb index 0adc5689e..05547415a 100644 --- a/lib/vagrant/util.rb +++ b/lib/vagrant/util.rb @@ -4,15 +4,17 @@ module Vagrant autoload :CommandDeprecation, 'vagrant/util/command_deprecation' autoload :Counter, 'vagrant/util/counter' autoload :CredentialScrubber, 'vagrant/util/credential_scrubber' + autoload :DeepMerge, 'vagrant/util/deep_merge' autoload :Env, 'vagrant/util/env' autoload :HashWithIndifferentAccess, 'vagrant/util/hash_with_indifferent_access' autoload :GuestInspection, 'vagrant/util/guest_inspection' autoload :Platform, 'vagrant/util/platform' autoload :Retryable, 'vagrant/util/retryable' autoload :SafeExec, 'vagrant/util/safe_exec' + autoload :SilenceWarnings, 'vagrant/util/silence_warnings' autoload :StackedProcRunner, 'vagrant/util/stacked_proc_runner' - autoload :TemplateRenderer, 'vagrant/util/template_renderer' autoload :StringBlockEditor, 'vagrant/util/string_block_editor' autoload :Subprocess, 'vagrant/util/subprocess' + autoload :TemplateRenderer, 'vagrant/util/template_renderer' end end diff --git a/lib/vagrant/util/platform.rb b/lib/vagrant/util/platform.rb index 8996b3f80..cd0e87f7d 100644 --- a/lib/vagrant/util/platform.rb +++ b/lib/vagrant/util/platform.rb @@ -249,13 +249,52 @@ module Vagrant wsl? && !path.to_s.downcase.start_with?("/mnt/") end + # Convert a WSL path to the local Windows path. This is useful + # for conversion when calling out to Windows executables from + # the WSL + # + # @param [String, Pathname] path Path to convert + # @return [String] + def wsl_to_windows_path(path) + if wsl? + if wsl_path?(path) + parts = path.split("/") + parts.delete_if(&:empty?) + [wsl_windows_appdata_local, "lxss", *parts].join("\\") + else + path = path.to_s.sub("/mnt/", "") + parts = path.split("/") + parts.first << ":" + path = parts.join("\\") + path + end + else + path + end + end + + # Automatically convert a given path to a Windows path. Will only + # be applied if running on a Windows host. If running on Windows + # host within the WSL, the actual Windows path will be returned. + # + # @param [Pathname, String] path Path to convert + # @return [String] + def windows_path(path) + path = cygwin_windows_path(path) + path = wsl_to_windows_path(path) + if windows? || wsl? + path = windows_unc_path(path) + end + path + end + # Allow Vagrant to access Vagrant managed machines outside the # Windows Subsystem for Linux # # @return [Boolean] def wsl_windows_access? if !defined?(@_wsl_windows_access) - @_wsl_windows_access = wsl? && ENV["VAGRANT_WSL_ACCESS_WINDOWS_USER"] + @_wsl_windows_access = wsl? && ENV["VAGRANT_WSL_ENABLE_WINDOWS_ACCESS"] end @_wsl_windows_access end @@ -266,8 +305,12 @@ module Vagrant # @return [Pathname] def wsl_windows_accessible_path if !defined?(@_wsl_windows_accessible_path) - access_path = ENV.fetch("VAGRANT_WSL_ACCESS_WINDOWS_USER_HOME_PATH", - "/mnt/c/Users/#{ENV["VAGRANT_WSL_ACCESS_WINDOWS_USER"]}") + access_path = ENV["VAGRANT_WSL_WINDOWS_ACCESS_USER_HOME_PATH"] + if access_path.to_s.empty? + access_path = wsl_windows_home.gsub("\\", "/").sub(":", "") + access_path[0] = access_path[0].downcase + access_path = "/mnt/#{access_path}" + end @_wsl_windows_accessible_path = Pathname.new(access_path) end @_wsl_windows_accessible_path @@ -290,9 +333,12 @@ module Vagrant # @param [Logger] logger Optional logger to display information def wsl_init(env, logger=nil) if wsl? - if ENV["VAGRANT_WSL_ACCESS_WINDOWS_USER"] + if ENV["VAGRANT_WSL_ENABLE_WINDOWS_ACCESS"] wsl_validate_matching_vagrant_versions! - shared_user = ENV["VAGRANT_WSL_ACCESS_WINDOWS_USER"] + shared_user = ENV["VAGRANT_WSL_WINDOWS_ACCESS_USER"] + if shared_user.to_s.empty? + shared_user = wsl_windows_username + end if logger logger.warn("Windows Subsystem for Linux detected. Allowing access to user: #{shared_user}") logger.warn("Vagrant will be allowed to control Vagrant managed machines within the user's home path.") @@ -300,11 +346,12 @@ module Vagrant if ENV["VAGRANT_HOME"] || ENV["VAGRANT_WSL_DISABLE_VAGRANT_HOME"] logger.warn("VAGRANT_HOME environment variable already set. Not overriding!") if logger else - home_path = wsl_windows_accessible_path + home_path = wsl_windows_accessible_path.to_s ENV["VAGRANT_HOME"] = File.join(home_path, ".vagrant.d") if logger logger.info("Overriding VAGRANT_HOME environment variable to configured windows user. (#{ENV["VAGRANT_HOME"]})") end + true end else if env.local_data_path.to_s.start_with?("/mnt/") @@ -314,6 +361,45 @@ module Vagrant end end + # Fetch the Windows username currently in use + # + # @return [String, Nil] + def wsl_windows_username + if !@_wsl_windows_username + result = Util::Subprocess.execute("cmd.exe", "/c", "echo %USERNAME%") + if result.exit_code == 0 + @_wsl_windows_username = result.stdout.strip + end + end + @_wsl_windows_username + end + + # Fetch the Windows user home directory + # + # @return [String, Nil] + def wsl_windows_home + if !@_wsl_windows_home + result = Util::Subprocess.execute("cmd.exe", "/c" "echo %USERPROFILE%") + if result.exit_code == 0 + @_wsl_windows_home = result.stdout.gsub("\"", "").strip + end + end + @_wsl_windows_home + end + + # Fetch the Windows user local app data directory + # + # @return [String, Nil] + def wsl_windows_appdata_local + if !@_wsl_windows_appdata_local + result = Util::Subprocess.execute("cmd.exe", "/c", "echo %LOCALAPPDATA%") + if result.exit_code == 0 + @_wsl_windows_appdata_local = result.stdout.gsub("\"", "").strip + end + end + @_wsl_windows_appdata_local + end + # Confirm Vagrant versions installed within the WSL and the Windows system # are the same. Raise error if they do not match. def wsl_validate_matching_vagrant_versions! diff --git a/plugins/providers/virtualbox/driver/base.rb b/plugins/providers/virtualbox/driver/base.rb index 12c01eebe..76f08487e 100644 --- a/plugins/providers/virtualbox/driver/base.rb +++ b/plugins/providers/virtualbox/driver/base.rb @@ -67,6 +67,10 @@ module VagrantPlugins end end elsif Vagrant::Util::Platform.wsl? + if !Vagrant::Util::Platform.wsl_windows_access? + @logger.error("No user Windows access defined for the Windows Subsystem for Linux. This is required for VirtualBox.") + raise Vagrant::Errors::WSLVirtualBoxWindowsAccessError + end @logger.debug("Linux platform detected but executing within WSL. Locating VBoxManage.") @vboxmanage_path = Vagrant::Util::Which.which("VBoxManage") || Vagrant::Util::Which.which("VBoxManage.exe") if !@vboxmanage_path diff --git a/plugins/providers/virtualbox/driver/version_5_0.rb b/plugins/providers/virtualbox/driver/version_5_0.rb index aadabffd3..6544e8785 100644 --- a/plugins/providers/virtualbox/driver/version_5_0.rb +++ b/plugins/providers/virtualbox/driver/version_5_0.rb @@ -270,7 +270,7 @@ module VagrantPlugins end def import(ovf) - ovf = Vagrant::Util::Platform.cygwin_windows_path(ovf) + ovf = Vagrant::Util::Platform.windows_path(ovf) output = "" total = "" @@ -614,10 +614,7 @@ module VagrantPlugins def share_folders(folders) folders.each do |folder| - hostpath = folder[:hostpath] - if Vagrant::Util::Platform.windows? - hostpath = Vagrant::Util::Platform.windows_unc_path(hostpath) - end + hostpath = Vagrant::Util::Platform.windows_path(folder[:hostpath]) args = ["--name", folder[:name], "--hostpath", diff --git a/templates/locales/en.yml b/templates/locales/en.yml index be8e38c75..287079eb5 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -1538,7 +1538,14 @@ en: please refer to the Vagrant documentation: https://www.vagrantup.com/docs/other/wsl + wsl_virtualbox_windows_access: |- + Vagrant is unable to use the VirtualBox provider from the Windows Subsystem for + Linux without access to the Windows environment. Enabling this access must be + done with caution and an understanding of the implications. For more information + on enabing Windows access and using VirtualBox from the Windows Subsystem for + Linux, please refer to the Vagrant documentation: + https://www.vagrantup.com/docs/other/wsl #------------------------------------------------------------------------------- # Translations for config validation errors #------------------------------------------------------------------------------- diff --git a/website/source/docs/other/wsl.html.md b/website/source/docs/other/wsl.html.md index 6e3d88ee9..a765a2884 100644 --- a/website/source/docs/other/wsl.html.md +++ b/website/source/docs/other/wsl.html.md @@ -68,43 +68,36 @@ Vagrant to access them. ## Windows Access Working within the WSL provides a layer of isolation from the actual -Windows system. In some cases, a user may be using Vagrant in a regular -Windows environment, and then transition to using Vagrant within the -WSL. Using Vagrant within the WSL will appear to be isolated from -the Windows system. A new `VAGRANT_HOME` directory will be created within -the WSL (meaning all boxes will require re-downloading). Vagrant will also -lose the ability to control Vagrant managed machines within Windows (due -to user ID mismatches). - -Vagrant supports enabling user access to provide seamless behavior and -control between Vagrant on Windows and Vagrant on WSL. By setting the -`VAGRANT_WSL_ACCESS_WINDOWS_USER` environment variable, Vagrant will -allow access to Vagrant managed machines in that user's home path in -Windows (`C:\Users\vagrant` for example), as well as share the `VAGRANT_HOME` -directory. Below is a demonstration of the behavior: +Windows system. In most cases Vagrant will need access to the actual +Windows system to function correctly. As most Vagrant providers will +need to be installed on Windows directly (not within the WSL) Vagrant +will require Windows access. Access to the Windows system is controlled +via an environment variable: `VAGRANT_WSL_ENABLE_WINDOWS_ACCESS`. If +this environment variable is set, Vagrant will access the Windows system +to run executables and enable things like synced folders. When running +in a bash shell within WSL, the environment variable can be setup like so: ``` -C:\Users\vagrant> bash -vagrant@vagrant-10:/mnt/c/Users/vagrant$ mkdir test -vagrant@vagrant-10:/mnt/c/Users/vagrant$ cd test -vagrant@vagrant-10:/mnt/c/Users/vagrant/test$ vagrant init hashicorp/precisec4 -vagrant@vagrant-10:/mnt/c/Users/vagrant$ vagrant up -Vagrant will not operate outside the Windows Subsystem for Linux unless explicitly -instructed. Due to the inability to enforce expected Linux file ownership and -permissions on the Windows system, Vagrant will not make modifications to prevent -unexpected errors. To learn more about this, and the options that are available, -please refer to the Vagrant documentation: - - https://www.vagrantup.com/docs/other/wsl -vagrant@vagrant-10:/mnt/c/Users/vagrant$ export VAGRANT_WSL_ACCESS_WINDOWS_USER=vagrant -vagrant@vagrant-10:/mnt/c/Users/vagrant$ vagrant up -Bringing machine 'default' up with 'virtualbox' provider... +$ export VAGRANT_WSL_ENABLE_WINDOWS_ACCESS="1" ``` -It is important to note that file permissions cannot be enforced when Vagrant -modifies the Windows file system. It is for this reason that you must explicitly -enable this functionality with the express knowledge of the implication. If you -are unsure of how this may affect your system, do not enable this feature. +This will enable Vagrant to access the Windows system outside of the +WSL and properly interact with Windows executables. This will automatically +modify the `VAGRANT_HOME` environment variable if it is not already defined, +setting it to be within the user's home directory on Windows. + +It is important to note that paths shared with the Windows system will +not have Linux permissions enforced. For example, when a directory within +the WSL is synced to a guest using the VirtualBox provider, any local +permissions defined on that directory (or its contents) will not be +visible from the guest. Likewise, any files created from the guest within +the synced folder will be world readable/writeable in WSL. + +Other useful WSL related environment variables: + +* `VAGRANT_WSL_WINDOWS_ACCESS_USER` - Override current Windows username +* `VAGRANT_WSL_DISABLE_VAGRANT_HOME` - Do not modify the `VAGRANT_HOME` variable +* `VAGRANT_WSL_WINDOWS_ACCESS_USER_HOME_PATH` - Custom Windows system home path ## Using Docker