diff --git a/plugins/guests/darwin/cap/darwin_version.rb b/plugins/guests/darwin/cap/darwin_version.rb new file mode 100644 index 000000000..5168c6f55 --- /dev/null +++ b/plugins/guests/darwin/cap/darwin_version.rb @@ -0,0 +1,40 @@ +module VagrantPlugins + module GuestDarwin + module Cap + class DarwinVersion + + VERSION_REGEX = /\d+.\d+.?\d*/.freeze + + # Get the darwin version + # + # @param [Machine] + # @return [String] version of drawin + def self.darwin_version(machine) + output = "" + machine.communicate.sudo("sysctl kern.osrelease") do |_, data| + output = data + end + output.scan(VERSION_REGEX).first + end + + # Get the darwin major version + # + # @param [Machine] + # @return [int] major version of drawin (nil if version is not detected) + def self.darwin_major_version(machine) + output = "" + machine.communicate.sudo("sysctl kern.osrelease") do |_, data| + output = data + end + version_string = output.scan(VERSION_REGEX).first + if version_string + major_version = version_string.split(".").first.to_i + else + major_version = nil + end + major_version + end + end + end + end +end diff --git a/plugins/guests/darwin/cap/mount_vmware_shared_folder.rb b/plugins/guests/darwin/cap/mount_vmware_shared_folder.rb index 29f2dac79..d4c6e2413 100644 --- a/plugins/guests/darwin/cap/mount_vmware_shared_folder.rb +++ b/plugins/guests/darwin/cap/mount_vmware_shared_folder.rb @@ -4,6 +4,9 @@ module VagrantPlugins module GuestDarwin module Cap class MountVmwareSharedFolder + + MACOS_BIGSUR_DARWIN_VERSION = 20.freeze + # Entry point for hook to called delayed actions # which finalizing the synced folders setup on # the guest @@ -81,9 +84,16 @@ module VagrantPlugins content = @apply_firmlinks[machine.id][:content].join("\n") # Write out the synthetic file comm.sudo("echo -e #{content.inspect} > /etc/synthetic.conf") - if @apply_firmlinks[:bootstrap] + if @apply_firmlinks[machine.id][:bootstrap] + if machine.guest.capability("darwin_major_version").to_i < MACOS_BIGSUR_DARWIN_VERSION + apfs_bootstrap_flag = "-B" + expected_rc = 0 + else + apfs_bootstrap_flag = "-t" + expected_rc = 253 + end # Re-bootstrap the root container to pick up firmlink updates - comm.sudo("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B") + comm.sudo("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util #{apfs_bootstrap_flag}", good_exit: [expected_rc]) end end end diff --git a/plugins/guests/darwin/plugin.rb b/plugins/guests/darwin/plugin.rb index afda31843..d61a2d890 100644 --- a/plugins/guests/darwin/plugin.rb +++ b/plugins/guests/darwin/plugin.rb @@ -31,6 +31,16 @@ module VagrantPlugins Cap::ConfigureNetworks end + guest_capability(:darwin, :darwin_version) do + require_relative "cap/darwin_version" + Cap::DarwinVersion + end + + guest_capability(:darwin, :darwin_major_version) do + require_relative "cap/darwin_version" + Cap::DarwinVersion + end + guest_capability(:darwin, :halt) do require_relative "cap/halt" Cap::Halt diff --git a/test/unit/plugins/guests/darwin/cap/darwin_version_test.rb b/test/unit/plugins/guests/darwin/cap/darwin_version_test.rb new file mode 100644 index 000000000..5f1d9790a --- /dev/null +++ b/test/unit/plugins/guests/darwin/cap/darwin_version_test.rb @@ -0,0 +1,49 @@ +require_relative "../../../../base" + +describe "VagrantPlugins::GuestDarwin::Cap::DarwinVersion" do + let(:caps) do + VagrantPlugins::GuestDarwin::Plugin + .components + .guest_capabilities[:darwin] + end + + let(:machine) { double("machine") } + let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + + before do + allow(machine).to receive(:communicate).and_return(comm) + end + + after do + comm.verify_expectations! + end + + describe ".darwin_version" do + let(:cap) { caps.get(:darwin_version) } + + { + "kern.osrelease: 19.6.0" => "19.6.0", + "kern.osrelease: 20.1.10" => "20.1.10", + }.each do |str, expected| + it "returns #{expected} for #{str}" do + comm.stub_command("sysctl kern.osrelease", stdout: str) + expect(cap.darwin_version(machine)).to eq(expected) + end + end + end + + describe ".darwin_major_version" do + let(:cap) { caps.get(:darwin_major_version) } + + { + "kern.osrelease: 19.6.0" => 19, + "kern.osrelease: 20.1.10" => 20, + "" => nil + }.each do |str, expected| + it "returns #{expected} for #{str}" do + comm.stub_command("sysctl kern.osrelease", stdout: str) + expect(cap.darwin_major_version(machine)).to eq(expected) + end + end + end +end diff --git a/test/unit/plugins/guests/darwin/cap/mount_vmware_shared_folder_test.rb b/test/unit/plugins/guests/darwin/cap/mount_vmware_shared_folder_test.rb index a706caa21..44302cb53 100644 --- a/test/unit/plugins/guests/darwin/cap/mount_vmware_shared_folder_test.rb +++ b/test/unit/plugins/guests/darwin/cap/mount_vmware_shared_folder_test.rb @@ -8,7 +8,8 @@ describe "VagrantPlugins::GuestDarwin::Cap::MountVmwareSharedFolder" do .get(:mount_vmware_shared_folder) end - let(:machine) { double("machine", communicate: communicator, id: "MACHINE_ID") } + let(:machine) { double("machine", communicate: communicator, id: "MACHINE_ID", guest: guest) } + let(:guest) {double("guest")} let(:communicator) { double("communicator") } before do @@ -27,8 +28,9 @@ describe "VagrantPlugins::GuestDarwin::Cap::MountVmwareSharedFolder" do described_class.reset! end - after { described_class. - mount_vmware_shared_folder(machine, name, guestpath, options) } + after { + described_class.mount_vmware_shared_folder(machine, name, guestpath, options) + } context "with APFS root container" do before do @@ -73,6 +75,21 @@ describe "VagrantPlugins::GuestDarwin::Cap::MountVmwareSharedFolder" do expect(communicator).to receive(:sudo).with(%r{ln -s .+/System/Volumes/Data.+}) end + { + 19 => "-B", + 20 => "-t", + 21 => "-t", + nil => "-B" + }.each do |version, expected_flag| + it "should re-bootstrap root dir for darwin version #{version}" do + expect(communicator).to receive(:sudo).with(/apfs.util #{expected_flag}/, any_args) + expect(guest).to receive(:capability).with("darwin_major_version").and_return(version) + + described_class.mount_vmware_shared_folder(machine, name, guestpath, options) + described_class.apfs_firmlinks_delayed[machine.id].call + end + end + context "when firmlink is provided by the system" do before { expect(described_class).to receive(:system_firmlink?).and_return(true) }