Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DMG mounting. #2772

Merged
merged 2 commits into from
Jul 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Library/Homebrew/cask/lib/hbc/artifact/nested_container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def extract(container_relative_path)
end

ohai "Extracting nested container #{source.basename}"
container.new(@cask, source, @command).extract
container.new(@cask, source, @command, verbose: verbose?).extract
FileUtils.remove_entry_secure(source)
end
end
Expand Down
9 changes: 7 additions & 2 deletions Library/Homebrew/cask/lib/hbc/container/base.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
module Hbc
class Container
class Base
def initialize(cask, path, command, nested: false)
def initialize(cask, path, command, nested: false, verbose: false)
@cask = cask
@path = path
@command = command
@nested = nested
@verbose = verbose
end

def verbose?
@verbose
end

def extract_nested_inside(dir)
Expand All @@ -32,7 +37,7 @@ def extract_nested_container(source)
return false unless container

ohai "Extracting nested container #{source.basename}"
container.new(@cask, source, @command, nested: true).extract
container.new(@cask, source, @command, nested: true, verbose: verbose?).extract

true
end
Expand Down
105 changes: 58 additions & 47 deletions Library/Homebrew/cask/lib/hbc/container/dmg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,61 +13,76 @@ def self.me?(criteria)
print_stderr: false).stdout.empty?
end

attr_reader :mounts
def initialize(*args)
super(*args)
@mounts = []
end

def extract
mount!
assert_mounts_found
extract_mounts
ensure
eject!
mount do |mounts|
begin
raise CaskError, "No mounts found in '#{@path}'; perhaps it is a bad DMG?" if mounts.empty?
mounts.each(&method(:extract_mount))
ensure
mounts.each(&method(:eject))
end
end
end

def mount!
plist = @command.run!("/usr/bin/hdiutil",
# realpath is a failsafe against unusual filenames
args: %w[mount -plist -nobrowse -readonly -noidme -mountrandom /tmp] + [Pathname.new(@path).realpath],
input: "y\n")
.plist
@mounts = mounts_from_plist(plist)
end
def mount
# realpath is a failsafe against unusual filenames
path = Pathname.new(@path).realpath

def eject!
@mounts.each do |mount|
# realpath is a failsafe against unusual filenames
mountpath = Pathname.new(mount).realpath
next unless mountpath.exist?
Dir.mktmpdir do |unpack_dir|
cdr_path = Pathname.new(unpack_dir).join("#{path.basename(".dmg")}.cdr")

begin
tries ||= 3
if tries > 1
@command.run("/usr/sbin/diskutil",
args: ["eject", mountpath],
print_stderr: false)
else
@command.run("/usr/sbin/diskutil",
args: ["unmount", "force", mountpath],
print_stderr: false)
without_eula = @command.run("/usr/bin/hdiutil",
args: ["attach", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", unpack_dir, path],
input: "qn\n",
print_stderr: false)

# If mounting without agreeing to EULA succeeded, there is none.
plist = if without_eula.success?
without_eula.plist
else
@command.run!("/usr/bin/hdiutil", args: ["convert", "-quiet", "-format", "UDTO", "-o", cdr_path, path])

with_eula = @command.run!("/usr/bin/hdiutil",
args: ["attach", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", unpack_dir, cdr_path])

if verbose? && !(eula_text = without_eula.stdout).empty?
ohai "Software License Agreement for '#{path}':"
puts eula_text
end
raise CaskError, "Failed to eject #{mountpath}" if mountpath.exist?
rescue CaskError => e
raise e if (tries -= 1).zero?
sleep 1
retry

with_eula.plist
end

yield mounts_from_plist(plist)
end
end

private

def extract_mounts
@mounts.each(&method(:extract_mount))
def eject(mount)
# realpath is a failsafe against unusual filenames
mountpath = Pathname.new(mount).realpath
return unless mountpath.exist?

begin
tries ||= 3
if tries > 1
@command.run("/usr/sbin/diskutil",
args: ["eject", mountpath],
print_stderr: false)
else
@command.run("/usr/sbin/diskutil",
args: ["unmount", "force", mountpath],
print_stderr: false)
end
raise CaskError, "Failed to eject #{mountpath}" if mountpath.exist?
rescue CaskError => e
raise e if (tries -= 1).zero?
sleep 1
retry
end
end

private

def extract_mount(mount)
Tempfile.open(["", ".bom"]) do |bomfile|
bomfile.close
Expand Down Expand Up @@ -124,10 +139,6 @@ def mounts_from_plist(plist)
return [] unless plist.respond_to?(:fetch)
plist.fetch("system-entities", []).map { |e| e["mount-point"] }.compact
end

def assert_mounts_found
raise CaskError, "No mounts found in '#{@path}'; perhaps it is a bad DMG?" if @mounts.empty?
end
end
end
end
2 changes: 1 addition & 1 deletion Library/Homebrew/cask/lib/hbc/installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def extract_primary_container
end

odebug "Using container class #{container} for #{@downloaded_path}"
container.new(@cask, @downloaded_path, @command).extract
container.new(@cask, @downloaded_path, @command, verbose: verbose?).extract
end

def install_artifacts
Expand Down
11 changes: 6 additions & 5 deletions Library/Homebrew/test/cask/container/dmg_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
Hbc::SystemCommand,
)

begin
dmg.mount!
expect(dmg.mounts).not_to include nil
ensure
dmg.eject!
dmg.mount do |mounts|
begin
expect(mounts).not_to include nil
ensure
mounts.each(&dmg.public_method(:eject))
end
end
end
end
Expand Down