class Macinbox::Actions::CreateVMDKFromImage

Public Class Methods

new(opts) click to toggle source
# File lib/macinbox/actions/create_vmdk_from_image.rb, line 17
def initialize(opts)
  @input_image       = opts[:image_path]    or raise ArgumentError.new(":image_path not specified")
  @output_path       = opts[:vmdk_path]     or raise ArgumentError.new(":vmdk_path not specified")
  @vmware_fusion_app = opts[:vmware_path]   or raise ArgumentError.new(":vmware_path not specified")
  @use_qemu          = opts[:use_qemu]

  @macos_version     = opts[:macos_version] or raise ArgumentError.new(":macos_version not specified")

  @collector         = opts[:collector]     or raise ArgumentError.new(":collector not specified")

  raise Macinbox::Error.new("input image not found")   unless File.exist? @input_image
  raise Macinbox::Error.new("VMware Fusion not found") unless File.exist? @vmware_fusion_app

  if @use_qemu
    raise Macinbox::Error.new("qemu-img not found") unless File.exist? "/usr/local/bin/qemu-img"
  end
end

Public Instance Methods

attach_image() click to toggle source
# File lib/macinbox/actions/create_vmdk_from_image.rb, line 58
def attach_image
  Logger.info "Attaching the image..." do
    @disk = VirtualDisk.new(@image)
    @collector.on_cleanup { @disk.detach! }
    @image_mountpoint = "#{@temp_dir}/image_mountpoint"
    FileUtils.mkdir @image_mountpoint
    @disk.attach
    @disk.mount(at: @image_mountpoint, owners: true)
  end
end
convert_image() click to toggle source
# File lib/macinbox/actions/create_vmdk_from_image.rb, line 139
def convert_image
  Logger.info "Converting the image to VMDK format#{@use_qemu ? " using QEMU" : ""}..." do
    if @use_qemu
      @disk.convert(outfile: "#{@temp_dir}/macinbox.dmg")
      Task.run %W[ /usr/local/bin/qemu-img convert -f dmg -O vmdk #{@temp_dir}/macinbox.dmg #{@temp_dir}/macinbox.vmdk ]
    else
      @disk.attach
      task_opts = $verbose ? {} : { :out => File::NULL }
      vdiskmanager = "#{@vmware_fusion_app}/Contents/Library/vmware-vdiskmanager"
      Dir.chdir(@temp_dir) do
        VMDK.create_raw_vmdk(@disk.device, "rawdisk.vmdk")
        begin
          Task.run %W[ #{vdiskmanager} -t 0 -r rawdisk.vmdk macinbox.vmdk ] + [task_opts]
        rescue Macinbox::Error => e
          Logger.error "If the conversion failed due to an AppleXPC error please see https://kb.vmware.com/s/article/65163 for a possible workaround."
          raise e
        end
      end
      @disk.eject
    end
  end
end
copy_input_image() click to toggle source
# File lib/macinbox/actions/create_vmdk_from_image.rb, line 51
def copy_input_image
  Logger.info "Copying the image..." do
    @image = "#{@temp_dir}/macinbox.sparseimage"
    Macinbox::copyfiles(from: @input_image, to: @image)
  end
end
create_temp_dir() click to toggle source
# File lib/macinbox/actions/create_vmdk_from_image.rb, line 46
def create_temp_dir
  @temp_dir = Task.backtick %W[ /usr/bin/mktemp -d -t create_vmdk_from_image ]
  @collector.add_temp_dir @temp_dir
end
eject_image() click to toggle source
# File lib/macinbox/actions/create_vmdk_from_image.rb, line 135
def eject_image
  @disk.eject
end
install_vmware_tools() click to toggle source
# File lib/macinbox/actions/create_vmdk_from_image.rb, line 69
def install_vmware_tools
  tools_image = "#{@vmware_fusion_app}/Contents/Library/isoimages/darwin.iso"

  unless File.exist? tools_image
    Logger.info "Downloading the VMware Tools..." do
      bundle_version = Task.backtick %W[ defaults read #{"/Applications/VMware Fusion.app/Contents/Info.plist"} CFBundleVersion ]
      bundle_short_version = Task.backtick %W[ defaults read #{"/Applications/VMware Fusion.app/Contents/Info.plist"} CFBundleShortVersionString ]
      darwin_iso_url = "http://softwareupdate.vmware.com/cds/vmw-desktop/fusion/#{bundle_short_version}/#{bundle_version}/packages/com.vmware.fusion.tools.darwin.zip.tar"
      Dir.chdir(@temp_dir) do
        Task.run %W[ /usr/bin/curl #{darwin_iso_url} -O ] + ($verbose ? [] : %W[ -s -S ])
        Task.run %W[ /usr/bin/tar -xf com.vmware.fusion.tools.darwin.zip.tar com.vmware.fusion.tools.darwin.zip ]
        Task.run %W[ /usr/bin/unzip ] + ($verbose ? [] : %W[ -qq ]) + %W[ com.vmware.fusion.tools.darwin.zip payload/darwin.iso ]
      end
      tools_image = "#{@temp_dir}/payload/darwin.iso"
    end
  end

  Logger.info "Installing the VMware Tools..." do
    tools_disk = VirtualDisk.new(tools_image)
    @collector.on_cleanup { tools_disk.detach! }
    tools_mountpoint = "#{@temp_dir}/tools_mountpoint"
    FileUtils.mkdir tools_mountpoint
    tools_disk.attach
    tools_disk.mount(at: tools_mountpoint)
    tools_package = "#{tools_mountpoint}/Install VMware Tools.app/Contents/Resources/VMware Tools.pkg"
    tools_package_dir = "#{@temp_dir}/tools_package"
    Task.run %W[ /usr/sbin/pkgutil --expand #{tools_package} #{tools_package_dir} ]
    Task.run %W[ /usr/bin/ditto -x -z #{tools_package_dir}/files.pkg/Payload #{@image_mountpoint} ]
    image_vmhgfs_filesystem_resources = "#{@image_mountpoint}/Library/Filesystems/vmhgfs.fs/Contents/Resources"
    FileUtils.mkdir_p image_vmhgfs_filesystem_resources
    FileUtils.ln_s "/Library/Application Support/VMware Tools/mount_vmhgfs", "#{image_vmhgfs_filesystem_resources}/"
    tools_disk.eject
  end
end
run() click to toggle source
# File lib/macinbox/actions/create_vmdk_from_image.rb, line 35
def run
  create_temp_dir
  copy_input_image
  attach_image
  set_spc_kextpolicy
  install_vmware_tools
  eject_image
  convert_image
  save_image
end
save_image() click to toggle source
# File lib/macinbox/actions/create_vmdk_from_image.rb, line 162
def save_image
  Logger.info "Moving the VMDK to the destination..." do
    FileUtils.chown ENV["SUDO_USER"], nil, "#{@temp_dir}/macinbox.vmdk"
    FileUtils.mv "#{@temp_dir}/macinbox.vmdk", @output_path
  end
end
set_spc_kextpolicy() click to toggle source
# File lib/macinbox/actions/create_vmdk_from_image.rb, line 104
      def set_spc_kextpolicy
        Logger.info "Setting the KextPolicy to allow loading the VMware kernel extensions..." do
          image_spc_kextpolicy = "#{@image_mountpoint}/private/var/db/SystemPolicyConfiguration/KextPolicy"
          unless File.exist? image_spc_kextpolicy
            Task.run_with_input %W[ /usr/bin/sqlite3 #{image_spc_kextpolicy} ] do |pipe|
              pipe.write <<~EOF
                PRAGMA foreign_keys=OFF;
                BEGIN TRANSACTION;
                CREATE TABLE kext_load_history_v3 ( path TEXT PRIMARY KEY, team_id TEXT, bundle_id TEXT, boot_uuid TEXT, created_at TEXT, last_seen TEXT, flags INTEGER );
                CREATE TABLE kext_policy ( team_id TEXT, bundle_id TEXT, allowed BOOLEAN, developer_name TEXT, flags INTEGER, PRIMARY KEY (team_id, bundle_id) );
                CREATE TABLE kext_policy_mdm ( team_id TEXT, bundle_id TEXT, allowed BOOLEAN, payload_uuid TEXT, PRIMARY KEY (team_id, bundle_id) );
                CREATE TABLE settings ( name TEXT, value TEXT, PRIMARY KEY (name) );
                COMMIT;
              EOF
            end
          end
          Task.run_with_input %W[ /usr/bin/sqlite3 #{image_spc_kextpolicy} ] do |pipe|
            pipe.write <<~EOF
              PRAGMA foreign_keys=OFF;
              BEGIN TRANSACTION;
              INSERT OR REPLACE INTO kext_load_history_v3 VALUES('/Library/Extensions/VMwareGfx.kext','EG7KH642X6','com.vmware.kext.VMwareGfx','F5E8FB21-EE9B-47F0-9C9C-FD6C4E41AC99','2020-04-13 16:29:03','2020-04-13 16:30:58',55);
              INSERT OR REPLACE INTO kext_load_history_v3 VALUES('/Library/Application Support/VMware Tools/vmhgfs.kext','EG7KH642X6','com.vmware.kext.vmhgfs','F5E8FB21-EE9B-47F0-9C9C-FD6C4E41AC99','2020-04-13 16:29:04','2020-04-13 16:29:45',53);
              INSERT OR REPLACE INTO kext_policy VALUES('EG7KH642X6','com.vmware.kext.VMwareGfx',1,'VMware, Inc.',1);
              INSERT OR REPLACE INTO kext_policy VALUES('EG7KH642X6','com.vmware.kext.vmmemctl',1,'VMware, Inc.',1);
              INSERT OR REPLACE INTO kext_policy VALUES('EG7KH642X6','com.vmware.kext.vmhgfs',1,'VMware, Inc.',1);
              COMMIT;
            EOF
          end
        end
      end