From 859d48d5f59400cc7dd653bb69d3d9d135af3c29 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Sat, 30 Jun 2018 09:58:19 -0700 Subject: [PATCH 1/3] Restrict synced folder access to DrvFs file systems only within WSL --- lib/vagrant/util/platform.rb | 35 +++++++++ plugins/kernel_v2/config/vm.rb | 28 +++++++ templates/locales/en.yml | 4 + test/unit/plugins/kernel_v2/config/vm_test.rb | 73 +++++++++++++++++++ test/unit/vagrant/util/platform_test.rb | 48 ++++++++++++ 5 files changed, 188 insertions(+) diff --git a/lib/vagrant/util/platform.rb b/lib/vagrant/util/platform.rb index 789d61ece..67a438578 100644 --- a/lib/vagrant/util/platform.rb +++ b/lib/vagrant/util/platform.rb @@ -483,6 +483,41 @@ module Vagrant path.to_s.start_with?(wsl_windows_accessible_path.to_s) end + # Mount pattern for extracting local mount information + MOUNT_PATTERN = /^(?.+?) on (?.+?) type (?.+?) \((?.+)\)/.freeze + + # Get list of local mount paths that are DrvFs file systems + # + # @return [Array] + def wsl_drvfs_mounts + if !defined?(@_wsl_drvfs_mounts) + @_wsl_drvfs_mounts = [] + if wsl? + result = Util::Subprocess.execute("mount") + result.stdout.each_line do |line| + info = line.match(MOUNT_PATTERN) + if info && info[:type] == "drvfs" + @_wsl_drvfs_mounts << info[:mount] + end + end + end + end + @_wsl_drvfs_mounts + end + + # Check if given path is located on DrvFs file system + # + # @param [String, Pathname] path Path to check + # @return [Boolean] + def wsl_drvfs_path?(path) + if wsl? + wsl_drvfs_mounts.each do |mount_path| + return true if path.to_s.start_with?(mount_path) + end + end + false + end + # If running within the Windows Subsystem for Linux, this will provide # simple setup to allow sharing of the user's VAGRANT_HOME directory # within the subsystem diff --git a/plugins/kernel_v2/config/vm.rb b/plugins/kernel_v2/config/vm.rb index b257ca97a..2214f42a5 100644 --- a/plugins/kernel_v2/config/vm.rb +++ b/plugins/kernel_v2/config/vm.rb @@ -3,6 +3,7 @@ require "securerandom" require "set" require "vagrant" +require "vagrant/action/builtin/mixin_synced_folders" require "vagrant/config/v2/util" require "vagrant/util/platform" require "vagrant/util/presence" @@ -757,6 +758,33 @@ module VagrantPlugins end end + # If running from the Windows Subsystem for Linux, validate that configured + # hostpaths for synced folders are on DrvFs file systems, or the synced + # folder implementation explicitly supports non-DrvFs file system types + # within the WSL + if Vagrant::Util::Platform.wsl? + # Create a helper that will with the synced folders mixin + # from the builtin action to get the correct implementation + # to be used for each folder + sf_helper = Class.new do + include Vagrant::Action::Builtin::MixinSyncedFolders + end.new + folders = sf_helper.synced_folders(machine, config: self) + folders.each do |impl_name, data| + data.each do |_, fs| + hostpath = File.expand_path(fs[:hostpath], machine.env.root_path) + if !Vagrant::Util::Platform.wsl_drvfs_path?(hostpath) + sf_klass = sf_helper.plugins[impl_name.to_sym].first + if sf_klass.respond_to?(:wsl_allow_non_drvfs?) && sf_klass.wsl_allow_non_drvfs? + next + end + errors["vm"] << I18n.t("vagrant.config.vm.shared_folder_wsl_not_drvfs", + path: fs[:hostpath]) + end + end + end + end + # Validate sub-VMs if there are any @__defined_vms.each do |name, _| if name =~ /[\[\]\{\}\/]/ diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 24ce2391f..8b102a8e8 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -1794,6 +1794,10 @@ en: be an array of options. shared_folder_requires_guestpath_or_name: |- Shared folder options must include a guestpath and/or name. + shared_folder_wsl_not_drvfs: |- + The host path of the shared folder is not supported from WSL. Host + path of the shared folder must be located on a file system with + DrvFs type. Host path: %{path} #------------------------------------------------------------------------------- # Translations for commands. e.g. `vagrant x` diff --git a/test/unit/plugins/kernel_v2/config/vm_test.rb b/test/unit/plugins/kernel_v2/config/vm_test.rb index 29919d672..f88d1329c 100644 --- a/test/unit/plugins/kernel_v2/config/vm_test.rb +++ b/test/unit/plugins/kernel_v2/config/vm_test.rb @@ -589,6 +589,79 @@ describe VagrantPlugins::Kernel_V2::VMConfig do sf = subject.synced_folders expect(sf["my-vagrant-folder"][:guestpath]).to be_nil end + + context "WSL host paths" do + let(:valid_path){ "/mnt/c/path" } + let(:invalid_path){ "/home/vagrant/path" } + let(:synced_folder_impl){ double("synced_folder_impl", new: double("synced_folder_inst", usable?: true)) } + let(:fs_config){ double("fs_config", vm: double("fs_vm", allowed_synced_folder_types: nil)) } + let(:plugin){ double("plugin", manager: manager) } + let(:manager){ double("manager", synced_folders: {sf_impl: [synced_folder_impl, 1]}) } + + let(:stub_pathname){ double("stub_pathname", directory?: true, relative?: false) } + + before do + allow(Pathname).to receive(:new).and_return(stub_pathname) + allow(stub_pathname).to receive(:expand_path).and_return(stub_pathname) + allow(Vagrant::Util::Platform).to receive(:wsl?).and_return(true) + allow(Vagrant::Util::Platform).to receive(:wsl_drvfs_path?).with(valid_path).and_return(true) + allow(Vagrant::Util::Platform).to receive(:wsl_drvfs_path?).with(invalid_path).and_return(false) + allow(machine).to receive(:config).and_return(fs_config) + allow(Vagrant).to receive(:plugin).with("2").and_return(plugin) + subject.synced_folder(".", "/vagrant", disabled: true) + end + + it "is valid when located on DrvFs" do + subject.synced_folder(valid_path, "/guest/path") + subject.finalize! + assert_valid + end + + it "is invalid when not located on DrvFs" do + subject.synced_folder(invalid_path, "/guest/path") + subject.finalize! + assert_invalid + end + + context "when synced folder defines support for non-DrvFs" do + let(:support_nondrvfs){ true } + + before do + allow(synced_folder_impl).to receive(:respond_to?).with(:wsl_allow_non_drvfs?).and_return(true) + allow(synced_folder_impl).to receive(:wsl_allow_non_drvfs?).and_return(support_nondrvfs) + end + + context "and is supported" do + it "is valid when located on DrvFs" do + subject.synced_folder(valid_path, "/guest/path") + subject.finalize! + assert_valid + end + + it "is valid when not located on DrvFs" do + subject.synced_folder(invalid_path, "/guest/path") + subject.finalize! + assert_valid + end + end + + context "and is not supported" do + let(:support_nondrvfs){ false } + + it "is valid when located on DrvFs" do + subject.synced_folder(valid_path, "/guest/path") + subject.finalize! + assert_valid + end + + it "is invalid when not located on DrvFs" do + subject.synced_folder(invalid_path, "/guest/path") + subject.finalize! + assert_invalid + end + end + end + end end describe "#usable_port_range" do diff --git a/test/unit/vagrant/util/platform_test.rb b/test/unit/vagrant/util/platform_test.rb index f43afd1d8..533f4461d 100644 --- a/test/unit/vagrant/util/platform_test.rb +++ b/test/unit/vagrant/util/platform_test.rb @@ -357,5 +357,53 @@ describe Vagrant::Util::Platform do end end end + + describe ".wsl_drvfs_mounts" do + let(:mount_output) { <<-EOF +rootfs on / type lxfs (rw,noatime) +sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,noatime) +proc on /proc type proc (rw,nosuid,nodev,noexec,noatime) +none on /dev type tmpfs (rw,noatime,mode=755) +devpts on /dev/pts type devpts (rw,nosuid,noexec,noatime) +none on /run type tmpfs (rw,nosuid,noexec,noatime,mode=755) +none on /run/lock type tmpfs (rw,nosuid,nodev,noexec,noatime) +none on /run/shm type tmpfs (rw,nosuid,nodev,noatime) +none on /run/user type tmpfs (rw,nosuid,nodev,noexec,noatime,mode=755) +binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,noatime) +C: on /mnt/c type drvfs (rw,noatime) +EOF + } + + before do + expect(Vagrant::Util::Subprocess).to receive(:execute).with("mount"). + and_return(Vagrant::Util::Subprocess::Result.new(0, mount_output, "")) + end + + it "should locate DrvFs mount path" do + expect(subject.wsl_drvfs_mounts).to eq(["/mnt/c"]) + end + + context "when no DrvFs mounts exist" do + let(:mount_output){ "" } + + it "should locate no paths" do + expect(subject.wsl_drvfs_mounts).to eq([]) + end + end + end + + describe ".wsl_drvfs_path?" do + before do + expect(subject).to receive(:wsl_drvfs_mounts).and_return(["/mnt/c"]) + end + + it "should return true when path prefix is found" do + expect(subject.wsl_drvfs_path?("/mnt/c/some/path")).to be_truthy + end + + it "should return false when path prefix is not found" do + expect(subject.wsl_drvfs_path?("/home/vagrant/some/path")).to be_falsey + end + end end end From 2667de163b0f135ceffd50ca435247f037611b2b Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Sat, 30 Jun 2018 10:00:33 -0700 Subject: [PATCH 2/3] Enable rsync synced folders for non-DrvFs file systems within WSL --- plugins/synced_folders/rsync/synced_folder.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/synced_folders/rsync/synced_folder.rb b/plugins/synced_folders/rsync/synced_folder.rb index 4e978f371..680870be8 100644 --- a/plugins/synced_folders/rsync/synced_folder.rb +++ b/plugins/synced_folders/rsync/synced_folder.rb @@ -48,6 +48,12 @@ module VagrantPlugins RsyncHelper.rsync_single(machine, ssh_info, folder_opts) end end + + # Enable rsync synced folders within WSL when in use + # on non-DrvFs file systems + def self.wsl_allow_non_drvfs? + true + end end end end From 1a67b59292253d649984c2cec073062c05585dd0 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Fri, 6 Jul 2018 14:07:39 -0700 Subject: [PATCH 3/3] Update WSL docs page --- website/source/docs/other/wsl.html.md | 72 ++++++++++++++------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/website/source/docs/other/wsl.html.md b/website/source/docs/other/wsl.html.md index 92b7a921c..87951a3f8 100644 --- a/website/source/docs/other/wsl.html.md +++ b/website/source/docs/other/wsl.html.md @@ -9,10 +9,10 @@ description: |- # Vagrant and Windows Subsystem for Linux -Windows has recently introduced a new feature called the Windows Subsystem -for Linux (WSL). This is a beta feature available in developer mode on recent -releases of Windows 10. It is important to note that this feature is still -in _beta_ on Windows, and Vagrant support should be considered _alpha_. +Recent versions of Windows 10 now include Windows Subsystem for Linux (WSL) as +an optional Windows feature. The WSL supports running a Linux environment +within Windows. Vagrant support for WSL is still in development and should +be considered _beta_.
Warning: Advanced Topic! Using Vagrant within the Windows @@ -21,43 +21,38 @@ in _beta_ on Windows, and Vagrant support should be considered _alpha_.
-# Installation - -Installation requires WSL, Ubuntu on Windows, and Vagrant. Read on for installation -instructions for each item. - -## Windows Subsystem for Linux and Ubuntu on Windows - -First install the Windows Subsystem for Linux, followed by Ubuntu on Windows. This guide -from Microsoft walks through the process: - -* https://msdn.microsoft.com/en-us/commandline/wsl/install_guide - ## Vagrant Installation -Vagrant _must_ be installed within Ubuntu on Windows. Even though the `vagrant.exe` -file can be executed from within the WSL, it will not function as expected. To -install Vagrant into the WSL, follow these steps: +Vagrant _must_ be installed within the Linux distribution used with WSL. While +the `vagrant.exe` executable provided by the Vagrant Windows installation is +accessible from within the WSL, it will not function as expected. -* Download the 64-bit Debian package from the downloads page. -* Open a `cmd` or `powershell` window -* Enter the command: `bash` -* Install vagrant: `sudo dpkg -i vagrant_VERSION_x86_64.deb` +Download the installer package for the Linux distribution from the releases +page and install Vagrant. -``` -C:\Users\vagrant> bash -vagrant@vagrant-10:/mnt/c/Users/vagrant$ sudo dpkg -i vagrant_VERSION_x86_64.deb -[sudo] password for vagrant: -(Reading database ... 31885 files and directories currently installed.) -Preparing to unpack vagrant_VERSION_x86_64.deb ... -Unpacking vagrant (1:VERSION) ... -Setting up vagrant (1:VERSION) ... -vagrant@vagrant-10:/mnt/c/Users/vagrant$ vagrant help -Usage: vagrant [options] [] -``` +__NOTE: When Vagrant is installed on the Windows system the version installed +within the Linux distribution *must* match._ # Vagrant Usage +## Windows Access + +By default Vagrant will not access features available on the Windows system +from within the WSL. This means the VirtualBox and Hyper-V providers will +not be available. To enable Windows access, which will also enable the +VirtualBox and Hyper-V providers, set the `VAGRANT_WSL_ENABLE_WINDOWS_ACCESS` +environment variable: + +```` +$ export VAGRANT_WSL_ENABLE_WINDOWS_ACCESS="1" +``` + +When Windows access is enabled Vagrant will automatically adjust `VAGRANT_HOME` +to be located on the Windows host. This is required to ensure `VAGRANT_HOME` +is located on a DrvFs file system. + +## PATH modifications + Vagrant will detect when it is being run within the WSL and adjust how it locates and executes third party executables. For example, when using the VirtualBox provider Vagrant will interact with VirtualBox installed on @@ -71,6 +66,15 @@ For example, when using the VirtualBox provider: export PATH="$PATH:/mnt/c/Program Files/Oracle/VirtualBox" ``` +## Synced Folders + +Support for synced folders within the WSL is implementation dependent. In +most cases synced folders will not be supported when running Vagrant within +WSL on a VolFs file system. Synced folder implementations must "opt-in" to +supporting usage from VolFs file systems. To use synced folders from within +the WSL that do not support VolFs file systems, move the Vagrant project +directory to a DrvFs file system location (/mnt/c/ prefixed path for example). + ## Windows Access Working within the WSL provides a layer of isolation from the actual