diff --git a/bin/vagrant b/bin/vagrant index cd924873e..19df75033 100755 --- a/bin/vagrant +++ b/bin/vagrant @@ -121,7 +121,11 @@ begin # some extra setup to allow access to Vagrant managed machines # outside the subsystem if Vagrant::Util::Platform.wsl? - Vagrant::Util::Platform.wsl_init(env, logger) + recreate_env = Vagrant::Util::Platform.wsl_init(env, logger) + if recreate_env + logger.info("Re-creating Vagrant environment due to WSL modifications.") + env = Vagrant::Environment.new(opts) + end end if !Vagrant.in_installer? && !Vagrant.very_quiet? 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..313e1bd19 100644 --- a/lib/vagrant/util.rb +++ b/lib/vagrant/util.rb @@ -4,15 +4,18 @@ 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' + autoload :Which, 'vagrant/util/which' end end diff --git a/lib/vagrant/util/platform.rb b/lib/vagrant/util/platform.rb index 8996b3f80..c86c127d2 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? && wsl_windows_access? + 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/docker/driver.rb b/plugins/providers/docker/driver.rb index f7a06f195..c845ed2a9 100644 --- a/plugins/providers/docker/driver.rb +++ b/plugins/providers/docker/driver.rb @@ -45,7 +45,19 @@ module VagrantPlugins run_cmd += expose.map { |p| ['--expose', "#{p}"] } run_cmd += links.map { |k, v| ['--link', "#{k}:#{v}"] } run_cmd += ports.map { |p| ['-p', p.to_s] } - run_cmd += volumes.map { |v| ['-v', v.to_s] } + run_cmd += volumes.map { |v| + v = v.to_s + if v.include?(":") && (Vagrant::Util::Platform.windows? || Vagrant::Util::Platform.wsl?) + host, guest = v.split(":", 2) + host = Vagrant::Util::Platform.windows_path(host) + # NOTE: Docker does not support UNC style paths (which also + # means that there's no long path support). Hopefully this + # will be fixed someday and the gsub below can be removed. + host.gsub!(/^[^A-Za-z]+/, "") + v = [host, guest].join(":") + end + ['-v', v.to_s] + } run_cmd += %W(--privileged) if params[:privileged] run_cmd += %W(-h #{params[:hostname]}) if params[:hostname] run_cmd << "-t" if params[:pty] 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