Class: Win32::SystemPath

Inherits:
Object
  • Object
show all
Defined in:
lib/metadata/util/win32/boot_info_win.rb,
lib/metadata/util/win32/system_path_win.rb

Constant Summary collapse

SYSTEM_STORE_GUID =

BCD WMI Provider Enumerations (values) msdn.microsoft.com/en-us/library/cc441427(VS.85).aspx

'{9dea862c-5cdd-4e70-acc1-f32b344d4795}'
BCD_BOOTMGR_OBJECT_DEFAULTOBJECT =

0x23000003

'23000003'
BCD_OS_LOADER_DEVICE_OSDEVICE =

0x21000001

'21000001'
BCD_OS_LOADER_STRING_SYSTEMROOT =

0x22000002

'22000002'
BCD_LIBRARYSTRING_DESCRIPTION =

0x12000004

'12000004'

Class Method Summary collapse

Class Method Details

.driveAssignment(fs) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/metadata/util/win32/boot_info_win.rb', line 5

def self.driveAssignment(fs)
  log_header = "MIQ(SystemPath.driveAssignment)"
  drives = []
  regHnd = RemoteRegistry.new(fs, true)
  xml = regHnd.loadHive("system", ["MountedDevices"])

  # Find the MountedDevices node
  node = nil
  xml.elements.each { |e| node = e if e.name == :key && e.attributes[:keyname] == 'MountedDevices' }

  unless node.nil?
    node.each_element do |e|
      if e.attributes[:name].include?("DosDevices") && e.text.length <= 36
        data = e.text.split(",")

        # The partition signature is derived from the DiskID and the partition's starting
        # sector number. The DiskID (sometimes called the "NT serial number") is a group of
        # four bytes in the master boot sector (LBA 0) at location 01B8h. Each partition's
        # starting sector number is doubled and combined with the DiskID to form a unique
        # signature for that partition. For example, consider a disk with the serial number
        # 3D173D16h (hexadecimal) and a partition starting at LBA 44933868 (decimal). Double
        # the sector number (89867736) and convert to hexadecimal (055B45D8h). If this partition
        # were designated E:, the corresponding registry values would be:
        #
        # [HKEY_LOCAL_MACHINE\System\MountedDevices]
        # \??\Volume{...} = 16 3d 17 3d 00 d8 45 5b 05 00 00 00
        # \DosDevices\E:  = 16 3d 17 3d 00 d8 45 5b 05 00 00 00

        drives << {:device          => e.attributes[:name],
                   :name            => e.attributes[:name].split("\\")[-1],
                   :raw_data        => e.text,
                   :serial_num      => "0x#{data[3]}#{data[2]}#{data[1]}#{data[0]}".to_i(16),
                   :starting_sector => "0x#{data[8]}#{data[7]}#{data[6]}#{data[5]}".to_i(16) / 2}
      elsif e.attributes[:name].include?("DosDevices") && e.text.length <= 100
        $log.warn "#{log_header} Skipping disk #{e.attributes[:name]} - (#{e.text.length})#{e.text}"
      end
    end
  end

  # If we do not find this key we cannot map disks with the proper drive letter.
  # This is a good sign that the OS is in a sysprep state and not fully installed.
  if drives.empty?
    $log.warn "#{log_header} The registry does not contain a mounted device list.  [Possible cause: The OS is in a pre-installed state.]"
    xml.to_xml.write(xml_str = '', 0)
    $log.warn "#{log_header} HKLM\\SYSTEM\\MountedDevices - START\n#{xml_str}"
    $log.warn "#{log_header} HKLM\\SYSTEM\\MountedDevices - END"

    os_install_loc = Win32::SystemPath.systemIdentifier(fs, :debug => true)
    $log.warn "#{log_header} System Install location: <#{os_install_loc.inspect}>"
  end

  drives
end

.parse_bcd(fs) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/metadata/util/win32/system_path_win.rb', line 76

def self.parse_bcd(fs)
  result = {}
  reg = RemoteRegistry.new(fs, XmlHash, '/boot')
  xml = reg.loadBootHive

  # Find the GUID of the default Boot element
  default_guid_path = "HKEY_LOCAL_MACHINE\\BCD\\Objects\\#{SYSTEM_STORE_GUID}\\Elements\\#{BCD_BOOTMGR_OBJECT_DEFAULTOBJECT}\\Element"
  default_os = MIQRexml.findRegElement(default_guid_path, xml.root)
  default_os = MIQRexml.findRegElement("HKEY_LOCAL_MACHINE\\BCD\\Objects\\#{default_os.text}\\Elements", xml.root) if default_os

  [BCD_OS_LOADER_DEVICE_OSDEVICE, :os_device, BCD_OS_LOADER_STRING_SYSTEMROOT, :system_root,
   BCD_LIBRARYSTRING_DESCRIPTION, :os_description].each_slice(2) do |key_id, name|
    device = MIQRexml.findRegElement("#{key_id}\\Element", default_os) if default_os
    result[name] = device.text unless device.nil?
  end

  # Decode disk signature
  unless result[:os_device].nil?
    d = result[:os_device].split(',')
    result[:disk_sig] = (d[59] + d[58] + d[57] + d[56]).to_i(16) if d.size >= 60
  end
  result
end

.registryPath(fs = nil, root = nil) ⇒ Object



11
12
13
# File 'lib/metadata/util/win32/system_path_win.rb', line 11

def self.registryPath(fs = nil, root = nil)
  File.join(system32Path(fs, root), "config")
end

.system32Path(fs = nil, root = nil) ⇒ Object



15
16
17
18
# File 'lib/metadata/util/win32/system_path_win.rb', line 15

def self.system32Path(fs = nil, root = nil)
  return File.join(root, "system32") if root
  File.join(systemRoot(fs), "system32")
end

.systemIdentifier(fs = nil, options = {}) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/metadata/util/win32/system_path_win.rb', line 28

def self.systemIdentifier(fs = nil, options = {})
  # If we are not passed a fs handle return the %systemRoot% from the environment
  if fs.nil?
    raise(MiqException::MiqVmMountError, "System root not available through environment variables.") if ENV["SystemRoot"].nil?
    return File.expand_path(ENV["SystemRoot"].tr('\\', '/'))
  end

  # Use the boot.ini file to get the starting path to the Windows folder
  fn = [{:file => "/boot.ini", :type => :boot_ini}, {:file => "/boot/BCD", :type => :bcd}, {:file => '/Windows/System32/config/SYSTEM', :type => :registry_file}]
  drive_letter = fs.pwd.to_s[0, 2]
  if drive_letter[1, 1] == ':'
    fs.chdir("#{drive_letter}/")
    fn.each { |f| f[:file] = "#{drive_letter}#{f[:file]}" }
  end

  # Determine where the initial boot information is stored (boot.ini or BCD registry
  boot_cfg = fn.detect { |b| fs.fileExists?(b[:file]) }

  # If we did not find either identifier above raise an error.
  return {} if boot_cfg.nil?

  # Set default system root path
  boot_cfg[:system_root] = "/Windows"

  $log.debug "Boot info stored in: [#{boot_cfg.inspect}]" if $log
  case boot_cfg[:type]
  when :boot_ini
    begin
      if fs.kind_of?(MiqFS)
        boot_cfg_text = fs.fileOpen(boot_cfg[:file]).read
        $log.warn "Contents of <#{boot_cfg[:file]}>\n#{boot_cfg_text}" if $log && options[:debug] == true
        boot_cfg[:system_root] = $'.split("\n")[0].split("\\")[1].chomp if boot_cfg_text =~ /default=/
      end
      boot_cfg[:system_root].strip!
      boot_cfg[:system_root] = "/#{boot_cfg[:system_root]}"
    rescue RuntimeError => err
      $log.warn "Win32::SystemPath.systemRoot [#{err}]" if $log
    end
  when :bcd
    boot_cfg.merge!(parse_bcd(fs))
    boot_cfg[:system_root].tr!('\\', '/') unless boot_cfg[:system_root].nil?
  when :registry_files
    # TODO: Needs to change to support BCD on different partition.  This is just a work-around
  end

  boot_cfg
end

.systemRoot(fs = nil, si = nil) ⇒ Object

Raises:

  • (MiqException::MiqVmMountError)


20
21
22
23
24
25
26
# File 'lib/metadata/util/win32/system_path_win.rb', line 20

def self.systemRoot(fs = nil, si = nil)
  si = systemIdentifier(fs) if si.nil?
  raise(MiqException::MiqVmMountError, "Filesystem does not contain system root identifiers.") if si.blank?
  system32_dir = File.join(si[:system_root], 'system32')
  raise(MiqException::MiqVmMountError, "Filesystem does not contain system root folder (#{system32_dir}).") unless fs.fileDirectory?(system32_dir)
  si[:system_root]
end