manageiq-smartstate 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yaml +31 -0
- data/.rspec +0 -1
- data/.whitesource +3 -0
- data/Gemfile +1 -1
- data/README.md +1 -2
- data/lib/MiqVm/MiqRhevmVm.rb +1 -0
- data/lib/MiqVm/MiqVm.rb +0 -7
- data/lib/blackbox/VmBlackBox.rb +1 -0
- data/lib/db/MiqBdb/MiqBdb.rb +1 -0
- data/lib/db/MiqBdb/MiqBdbBtree.rb +1 -0
- data/lib/db/MiqBdb/MiqBdbHash.rb +1 -0
- data/lib/db/MiqBdb/MiqBdbPage.rb +1 -0
- data/lib/disk/DiskProbe.rb +2 -1
- data/lib/disk/modules/MiqLargeFile.rb +1 -0
- data/lib/fs/ntfs/attrib_file_name.rb +1 -0
- data/lib/fs/ntfs/attrib_standard_information.rb +1 -0
- data/lib/manageiq/smartstate/version.rb +1 -1
- data/lib/metadata/MIQExtract/MIQExtract.rb +1 -0
- data/lib/metadata/ScanProfile/ScanProfilesBase.rb +1 -0
- data/lib/metadata/VmConfig/GetNativeCfg.rb +1 -0
- data/lib/metadata/VmConfig/VmConfig.rb +1 -0
- data/lib/metadata/VmConfig/ovfConfig.rb +1 -0
- data/lib/metadata/VmConfig/vmcConfig.rb +1 -0
- data/lib/metadata/VmConfig/xmlConfig.rb +1 -6
- data/lib/metadata/linux/LinuxInitProcs.rb +1 -0
- data/lib/metadata/linux/LinuxOSInfo.rb +1 -0
- data/lib/metadata/linux/LinuxPackages.rb +1 -0
- data/lib/metadata/linux/LinuxSystemd.rb +1 -0
- data/lib/metadata/linux/LinuxUsers.rb +1 -0
- data/lib/metadata/linux/MiqConaryPackages.rb +1 -0
- data/lib/metadata/linux/MiqRpmPackages.rb +1 -0
- data/lib/metadata/util/md5deep.rb +1 -0
- data/lib/metadata/util/win32/Win32Accounts.rb +1 -0
- data/lib/metadata/util/win32/Win32EventLog.rb +1 -0
- data/lib/metadata/util/win32/Win32Services.rb +1 -0
- data/lib/metadata/util/win32/Win32Software.rb +1 -0
- data/lib/metadata/util/win32/Win32System.rb +1 -0
- data/lib/metadata/util/win32/fleece_hives.rb +1 -0
- data/lib/metadata/util/win32/ms-registry.rb +1 -0
- data/lib/metadata/util/win32/remote-registry.rb +1 -0
- data/manageiq-smartstate.gemspec +4 -5
- metadata +7 -32
- data/.travis.yml +0 -15
- data/lib/MiqVm/miq_scvmm_vm.rb +0 -39
- data/lib/Scvmm/miq_hyperv_disk.rb +0 -274
- data/lib/Scvmm/miq_scvmm_parse_powershell.rb +0 -75
- data/lib/Scvmm/miq_scvmm_vm_ssa_info.rb +0 -135
- data/lib/disk/modules/MSCommon.rb +0 -354
- data/lib/disk/modules/MSVSDiffDisk.rb +0 -85
- data/lib/disk/modules/MSVSDiskProbe.rb +0 -61
- data/lib/disk/modules/MSVSDynamicDisk.rb +0 -36
- data/lib/disk/modules/MSVSFixedDisk.rb +0 -39
- data/lib/disk/modules/VhdxDisk.rb +0 -629
- data/lib/disk/modules/VhdxDiskProbe.rb +0 -46
- data/lib/metadata/VmConfig/xmlMsHyperVConfig.rb +0 -41
@@ -1,629 +0,0 @@
|
|
1
|
-
# encoding: US-ASCII
|
2
|
-
|
3
|
-
require 'miq_unicode'
|
4
|
-
require 'binary_struct'
|
5
|
-
require 'disk/MiqDisk'
|
6
|
-
require 'memory_buffer'
|
7
|
-
require 'disk/modules/MiqLargeFile'
|
8
|
-
require 'disk/modules/vhdx_bat_entry'
|
9
|
-
|
10
|
-
module VhdxDisk
|
11
|
-
using ManageIQ::UnicodeString
|
12
|
-
|
13
|
-
# NOTE: All values are stored in network byte order.
|
14
|
-
|
15
|
-
VHDX_FILE_IDENTIFIER = BinaryStruct.new([
|
16
|
-
'Q<', 'signature', # Always 'vhdxfile'.
|
17
|
-
'A256', 'creator', # Parser that created the vhdx.
|
18
|
-
])
|
19
|
-
SIZEOF_VHDX_FILE_IDENTIFIER = VHDX_FILE_IDENTIFIER.size
|
20
|
-
VHDX_FILE_IDENTIFIER_SIGNATURE = 0x656C696678646876
|
21
|
-
|
22
|
-
VHDX_HEADER = BinaryStruct.new([
|
23
|
-
'I<', 'signature', # Always 'head'.
|
24
|
-
'I<', 'checksum', # cfc-32c hash over entire 4 KB structure
|
25
|
-
'Q<', 'sequence_number', #
|
26
|
-
'A16', 'file_write_guid', # Identifies the file's contents
|
27
|
-
'A16', 'data_write_guid', # Identifies the user visible data
|
28
|
-
'A16', 'log_guid', # Determines validity of log entries
|
29
|
-
'S<', 'log_version', # The version of the log format.
|
30
|
-
'S<', 'version', # The version of the VHDX format.
|
31
|
-
'I<', 'log_length', # Length of the log
|
32
|
-
'I<', 'log_offset', # Byte offset of the log
|
33
|
-
'A4016', 'reserved', # reserved
|
34
|
-
])
|
35
|
-
SIZEOF_VHDX_HEADER = VHDX_HEADER.size
|
36
|
-
VHDX_HEADER_OFFSET = 64 * 1024
|
37
|
-
VHDX_HEADER_SIGNATURE = 0x64616568
|
38
|
-
VHDX_HEADER2_OFFSET = 2 * 64 * 1024
|
39
|
-
|
40
|
-
VHDX_REGION_TABLE_HEADER = BinaryStruct.new([
|
41
|
-
'I<', 'signature', # Always 'regi'.
|
42
|
-
'I<', 'checksum', # cfc-32c hash over entire 4 KB structure
|
43
|
-
'I<', 'entry_count', # Number of Region Table entries
|
44
|
-
'I<', 'reserved', # reserved
|
45
|
-
])
|
46
|
-
SIZEOF_VHDX_REGION_TABLE_HEADER = VHDX_REGION_TABLE_HEADER.size
|
47
|
-
VHDX_REGION_TABLE_HEADER_OFFSET = 64 * 1024
|
48
|
-
VHDX_REGION_TABLE_HEADER_SIGNATURE = 0x69676572
|
49
|
-
|
50
|
-
VHDX_REGION_TABLE_ENTRY = BinaryStruct.new([
|
51
|
-
'A16', 'guid', # GUID must be unique within the table
|
52
|
-
'Q<', 'file_offset', # offset of the Region Table entry (1MB multiples)
|
53
|
-
'I<', 'length', # Byte length of the object (1MB multiples)
|
54
|
-
'I<', 'required', # 1st bit is required, 31 remaining bits reserved
|
55
|
-
])
|
56
|
-
SIZEOF_VHDX_REGION_TABLE_ENTRY = VHDX_REGION_TABLE_ENTRY.size
|
57
|
-
REGION_TABLE_BAT_GUID = 0x2DC27766F62342009D64115E9BFD4A08
|
58
|
-
REGION_TABLE_METADATA_GUID = 0x8B7CA20647904B9AB8FE575F050F886E
|
59
|
-
|
60
|
-
VHDX_BAT_STATE_ENTRY = BinaryStruct.new([
|
61
|
-
'B8', 'state', # least significant three bits only
|
62
|
-
'B8', 'reserved',
|
63
|
-
'B48', 'file_offset_mb' # the offset within the file in units of 1 MB
|
64
|
-
])
|
65
|
-
VHDX_BAT_ENTRY = BinaryStruct.new([
|
66
|
-
'Q<', 'file_offset_mb', # most significant 44 bits only
|
67
|
-
])
|
68
|
-
SIZEOF_VHDX_BAT_ENTRY = VHDX_BAT_ENTRY.size
|
69
|
-
BAT_OFFSET_MASK = 0x00000FFFFFFFFFFF
|
70
|
-
BAT_OFFSET_SHIFT = 20
|
71
|
-
BAT_OFFSET_UNITS = 1024 * 1024 # units for fill_offset_mb field
|
72
|
-
|
73
|
-
PAYLOAD_BLOCK_NOT_PRESENT = 0
|
74
|
-
PAYLOAD_BLOCK_UNDEFINED = 1
|
75
|
-
PAYLOAD_BLOCK_ZERO = 2
|
76
|
-
PAYLOAD_BLOCK_UNMAPPED = 3
|
77
|
-
PAYLOAD_BLOCK_FULLY_PRESENT = 6
|
78
|
-
PAYLOAD_BLOCK_PARTIALLY_PRESENT = 7
|
79
|
-
|
80
|
-
SB_BLOCK_NOT_PRESENT = 0
|
81
|
-
SB_BLOCK_PRESENT = 6
|
82
|
-
SECTOR_BITMAP_BLOCKSIZE = 1024 * 1024
|
83
|
-
|
84
|
-
VHDX_METADATA_TABLE_HEADER = BinaryStruct.new([
|
85
|
-
'Q<', 'signature', # Always 'metadata'
|
86
|
-
'S<', 'reserved', #
|
87
|
-
'S<', 'entry_count', # number of entries in the table
|
88
|
-
'I5', 'reserved2', #
|
89
|
-
])
|
90
|
-
SIZEOF_VHDX_METADATA_TABLE_HEADER = VHDX_METADATA_TABLE_HEADER.size
|
91
|
-
VHDX_METADATA_TABLE_HEADER_SIGNATURE = 0x617461646174656D
|
92
|
-
|
93
|
-
VHDX_METADATA_TABLE_ENTRY = BinaryStruct.new([
|
94
|
-
'A16', 'item_id', # item_id and isuser pair must be unique within the table
|
95
|
-
'I<', 'offset', # offset relative to beginning of the metadata region >= 64KB
|
96
|
-
'I<', 'length', # < = 1MB
|
97
|
-
'b32', 'bit_fields',
|
98
|
-
# 'B1', 'is_user', # system or user metadata
|
99
|
-
# 'B1', 'is_virtual_disk', # file or virtual disk metadata
|
100
|
-
# 'B1', 'is_required', #
|
101
|
-
# 'B29', 'reserved',
|
102
|
-
'I<', 'reserved2'
|
103
|
-
])
|
104
|
-
SIZEOF_VHDX_METADATA_TABLE_ENTRY = VHDX_METADATA_TABLE_ENTRY.size
|
105
|
-
|
106
|
-
FILE_PARAMETERS_GUID = 0xCAA16737FA364D43B3B633F0AA44E76B
|
107
|
-
VIRTUAL_DISK_SIZE_GUID = 0x2FA54224CD1B4876B2115DBED83BF4B8
|
108
|
-
PAGE_83_DATA_GUID = 0xBECA12ABB2E6452393EFC309E000C746
|
109
|
-
LOGICAL_SECTOR_SIZE_GUID = 0x8141BF1DA96F4709BA47F233A8FAAB5F
|
110
|
-
PHYSICAL_SECTOR_SIZE_GUID = 0xCDA348C7445D44719CC9E9885251C556
|
111
|
-
PARENT_LOCATOR_GUID = 0xA8D35F2DB30B454DABF7D3D84834AB0C
|
112
|
-
|
113
|
-
METADATA_OPS = {
|
114
|
-
FILE_PARAMETERS_GUID => :file_parameters,
|
115
|
-
VIRTUAL_DISK_SIZE_GUID => :virtual_disk_size,
|
116
|
-
PAGE_83_DATA_GUID => :page_83_data,
|
117
|
-
LOGICAL_SECTOR_SIZE_GUID => :logical_sector_size,
|
118
|
-
PHYSICAL_SECTOR_SIZE_GUID => :physical_sector_size,
|
119
|
-
PARENT_LOCATOR_GUID => :parent_locator_header
|
120
|
-
}
|
121
|
-
|
122
|
-
ALLOCATION_STATUS = {
|
123
|
-
PAYLOAD_BLOCK_NOT_PRESENT => PAYLOAD_BLOCK_NOT_PRESENT,
|
124
|
-
PAYLOAD_BLOCK_UNDEFINED => nil,
|
125
|
-
PAYLOAD_BLOCK_ZERO => nil,
|
126
|
-
PAYLOAD_BLOCK_UNMAPPED => nil,
|
127
|
-
PAYLOAD_BLOCK_FULLY_PRESENT => PAYLOAD_BLOCK_FULLY_PRESENT,
|
128
|
-
PAYLOAD_BLOCK_PARTIALLY_PRESENT => PAYLOAD_BLOCK_PARTIALLY_PRESENT
|
129
|
-
}
|
130
|
-
|
131
|
-
VHDX_FILE_PARAMETERS = BinaryStruct.new([
|
132
|
-
'I<', 'block_size', # size of payload block in bytes between 1MB & 256MB
|
133
|
-
'b32', 'bit_fields',
|
134
|
-
# 'B1', 'leave_blocks_allocated',
|
135
|
-
# 'B1', 'has_parent',
|
136
|
-
# 'B30', 'reserved',
|
137
|
-
])
|
138
|
-
SIZEOF_VHDX_FILE_PARAMETERS = VHDX_FILE_PARAMETERS.size
|
139
|
-
|
140
|
-
VHDX_VIRTUAL_DISK_SIZE = BinaryStruct.new([
|
141
|
-
'Q<', 'virtual_disk_size' # size in bytes. must be a multiple of logical sector size
|
142
|
-
])
|
143
|
-
SIZEOF_VHDX_VIRTUAL_DISK_SIZE = VHDX_VIRTUAL_DISK_SIZE.size
|
144
|
-
|
145
|
-
VHDX_LOGICAL_SECTOR_SIZE = BinaryStruct.new([
|
146
|
-
'I<', 'logical_sector_size' # size in bytes. must be 512 or 4096
|
147
|
-
])
|
148
|
-
SIZEOF_VHDX_LOGICAL_SECTOR_SIZE = VHDX_LOGICAL_SECTOR_SIZE.size
|
149
|
-
|
150
|
-
VHDX_PHYSICAL_SECTOR_SIZE = BinaryStruct.new([
|
151
|
-
'I<', 'physical_sector_size' # size in bytes. must be 512 or 4096
|
152
|
-
])
|
153
|
-
SIZEOF_VHDX_PHYSICAL_SECTOR_SIZE = VHDX_PHYSICAL_SECTOR_SIZE.size
|
154
|
-
|
155
|
-
VHDX_PAGE_83_DATA = BinaryStruct.new([
|
156
|
-
'A16', 'page_83_data' # unique guid
|
157
|
-
])
|
158
|
-
SIZEOF_VHDX_PAGE_83_DATA = VHDX_PAGE_83_DATA.size
|
159
|
-
|
160
|
-
VHDX_PARENT_LOCATOR_HEADER = BinaryStruct.new([
|
161
|
-
'A16', 'locator_type', # guid with type of the parent virtual disk
|
162
|
-
'S<', 'reserved',
|
163
|
-
'S<', 'key_value_count', # number of key-value pairs for the parent locator
|
164
|
-
])
|
165
|
-
SIZEOF_VHDX_PARENT_LOCATOR_HEADER = VHDX_PARENT_LOCATOR_HEADER.size
|
166
|
-
VHDX_PARENT_LOCATOR_TYPE_GUID = 0xB04AEFB7D19E4A81B78925B8E9445913
|
167
|
-
|
168
|
-
VHDX_PARENT_LOCATOR_ENTRY = BinaryStruct.new([
|
169
|
-
'I<', 'key_offset', # offset within the metadata item of key
|
170
|
-
'I<', 'value_offset', # offset within the metadata item of value
|
171
|
-
'S<', 'key_length', # length of the entry's key
|
172
|
-
'S<', 'value_length', # length of the entry's value
|
173
|
-
])
|
174
|
-
SIZEOF_VHDX_PARENT_LOCATOR_ENTRY = VHDX_PARENT_LOCATOR_ENTRY.size
|
175
|
-
|
176
|
-
attr_reader :file_identifier_signature, :vhdx_header_signature, :dInfo
|
177
|
-
def d_init
|
178
|
-
@diskType = "Vhdx"
|
179
|
-
@blockSize = 0
|
180
|
-
@virtual_disk_size = 0
|
181
|
-
@logical_sector_size = 0
|
182
|
-
@physical_sector_size = 0
|
183
|
-
@payload_block_size = 0
|
184
|
-
@has_parent = nil
|
185
|
-
@parent_locator = nil
|
186
|
-
@file_name = dInfo.fileName
|
187
|
-
@scvmm = dInfo.scvmm
|
188
|
-
@hyperv_connection = nil
|
189
|
-
@vhdx_file = connection_to_file(dInfo)
|
190
|
-
@converter = Encoding::Converter.new("UTF-16LE", "UTF-8")
|
191
|
-
header_section
|
192
|
-
end
|
193
|
-
|
194
|
-
def connection_to_file(dInfo)
|
195
|
-
if dInfo.mountMode.nil? || dInfo.mountMode == "r"
|
196
|
-
dInfo.mountMode = "r"
|
197
|
-
file_mode = "r"
|
198
|
-
elsif dInfo.mountMode == "rw"
|
199
|
-
file_mode = "r+"
|
200
|
-
else
|
201
|
-
raise "Unrecognized mountMode: #{dInfo.mountMode}"
|
202
|
-
end
|
203
|
-
if dInfo.hyperv_connection
|
204
|
-
@vhdx_file = connect_to_hyperv(dInfo)
|
205
|
-
else
|
206
|
-
@vhdx_file = MiqLargeFile.open(@file_name, file_mode)
|
207
|
-
end
|
208
|
-
@vhdx_file
|
209
|
-
end
|
210
|
-
|
211
|
-
def d_read(pos, len)
|
212
|
-
raise "VhdxDisk.d_read Invalid len #{len} #{@has_parent ? "Checkpoint Disk" : "Parent Disk"}\n #{caller}" if len < 0
|
213
|
-
$log.debug "VhdxDisk.d_read(#{pos}, #{len})"
|
214
|
-
buf = ""
|
215
|
-
return buf if len == 0
|
216
|
-
block_start, sector_start, byte_offset_start = block_pos(pos)
|
217
|
-
block_end, sector_end, _byte_offset_end = block_pos(pos + len - 1)
|
218
|
-
byte_offset = 0
|
219
|
-
this_len = @blockSize
|
220
|
-
(block_start..block_end).each do |block_number|
|
221
|
-
real_sector_start = (block_number == block_start) ? sector_start : 0
|
222
|
-
real_sector_end = (block_number == block_end) ? sector_end : @sectors_per_block - 1
|
223
|
-
allocation_status = get_allocation_status(block_number)
|
224
|
-
bat_offset = bat_offset(block_number)
|
225
|
-
(real_sector_start..real_sector_end).each do |sector_number|
|
226
|
-
if (block_start == block_end) && (sector_start == sector_end)
|
227
|
-
byte_offset = byte_offset_start
|
228
|
-
this_len = len
|
229
|
-
elsif (block_number == block_start) && (sector_number == sector_start)
|
230
|
-
byte_offset = byte_offset_start
|
231
|
-
this_len = @blockSize - byte_offset
|
232
|
-
elsif (block_number == block_end) && (sector_number == sector_end)
|
233
|
-
byte_offset = 0
|
234
|
-
this_len = len - buf.length
|
235
|
-
raise "Internal Error: Calculated read more than sector: #{this_len}" if this_len > @blockSize
|
236
|
-
else
|
237
|
-
byte_offset = 0
|
238
|
-
this_len = @blockSize
|
239
|
-
end
|
240
|
-
if allocation_status.nil? || allocation_status == PAYLOAD_BLOCK_NOT_PRESENT
|
241
|
-
buf << read_unallocated_buf(pos, this_len, buf, allocation_status == PAYLOAD_BLOCK_NOT_PRESENT)
|
242
|
-
else
|
243
|
-
if allocation_status == PAYLOAD_BLOCK_PARTIALLY_PRESENT
|
244
|
-
sector_status = get_sector_allocation_status(block_number, sector_number)
|
245
|
-
$log.debug "PAYLOAD_BLOCK_PARTIALLY_PRESENT & sector_status #{sector_status} for sector #{sector_number}"
|
246
|
-
if sector_status == false
|
247
|
-
buf << read_unallocated_buf(pos, this_len, buf, true)
|
248
|
-
next
|
249
|
-
end
|
250
|
-
end
|
251
|
-
byte_offset += sector_number * @logical_sector_size
|
252
|
-
@vhdx_file.seek(bat_offset + byte_offset, IO::SEEK_SET)
|
253
|
-
buf << @vhdx_file.read(this_len)
|
254
|
-
end
|
255
|
-
end
|
256
|
-
end
|
257
|
-
buf
|
258
|
-
end
|
259
|
-
|
260
|
-
def d_write(pos, buf, len)
|
261
|
-
@vhdx_file.seek(pos, IO::SEEK_SET)
|
262
|
-
@vhdx_file.write(buf, len)
|
263
|
-
end
|
264
|
-
|
265
|
-
def d_close
|
266
|
-
@vhdx_file.close
|
267
|
-
end
|
268
|
-
|
269
|
-
# Disk size in sectors.
|
270
|
-
def d_size
|
271
|
-
@virtual_disk_size / @logical_sector_size
|
272
|
-
end
|
273
|
-
|
274
|
-
def getBase
|
275
|
-
self
|
276
|
-
end
|
277
|
-
|
278
|
-
private
|
279
|
-
|
280
|
-
def read_unallocated_buf(pos, len, buf, status)
|
281
|
-
if status == true && @has_parent == true
|
282
|
-
return @parent.d_read(pos + buf.length, len)
|
283
|
-
else
|
284
|
-
return MemoryBuffer.create(len)
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
def header_section
|
289
|
-
@file_identifier = file_identifier
|
290
|
-
@vhdx_header = header(1)
|
291
|
-
unless valid_header_signature?
|
292
|
-
$log.info "Invalid VHDX Header Signature #{@vhdx_header_signature}"
|
293
|
-
@vhdx_header = header(2)
|
294
|
-
end
|
295
|
-
raise "Invalid VHDX Header Signature #{@vhdx_header_signature}" unless valid_header_signature?
|
296
|
-
@region_table_header = region_table(1)
|
297
|
-
unless valid_region_table_header?
|
298
|
-
$log.info "Invalid Region Table Header Signature #{@region_table_header_signature}"
|
299
|
-
@region_table_header = header(2)
|
300
|
-
end
|
301
|
-
raise "Invalid Region Table Header Signature #{@region_table_header_signature}" unless valid_region_table_header?
|
302
|
-
end
|
303
|
-
|
304
|
-
def file_identifier
|
305
|
-
@vhdx_file.seek(0, IO::SEEK_SET)
|
306
|
-
file_identifier = VHDX_FILE_IDENTIFIER.decode(@vhdx_file.read(SIZEOF_VHDX_FILE_IDENTIFIER))
|
307
|
-
@file_identifier_signature = file_identifier['signature']
|
308
|
-
raise "Invalid VHDX File Identifier Signature #{@file_identifier_signature}" unless valid_file_identifier_signature?
|
309
|
-
file_identifier
|
310
|
-
end
|
311
|
-
|
312
|
-
def valid_file_identifier_signature?
|
313
|
-
@file_identifier_signature == VHDX_FILE_IDENTIFIER_SIGNATURE
|
314
|
-
end
|
315
|
-
|
316
|
-
def header(header_number)
|
317
|
-
@vhdx_file.seek(header_number * VHDX_HEADER_OFFSET, IO::SEEK_SET)
|
318
|
-
vhdx_header = VHDX_HEADER.decode(@vhdx_file.read(SIZEOF_VHDX_HEADER))
|
319
|
-
@vhdx_header_signature = vhdx_header['signature']
|
320
|
-
vhdx_header
|
321
|
-
end
|
322
|
-
|
323
|
-
def valid_header_signature?
|
324
|
-
@vhdx_header_signature == VHDX_HEADER_SIGNATURE
|
325
|
-
end
|
326
|
-
|
327
|
-
def region_table(table_number)
|
328
|
-
table_offset = 2 * VHDX_HEADER_OFFSET + table_number * VHDX_REGION_TABLE_HEADER_OFFSET
|
329
|
-
header = region_table_header(table_offset)
|
330
|
-
return if header.nil?
|
331
|
-
(1..header['entry_count']).each do |count|
|
332
|
-
process_region_table_entry(table_offset, count)
|
333
|
-
end
|
334
|
-
process_metadata_table_header
|
335
|
-
process_bat
|
336
|
-
end
|
337
|
-
|
338
|
-
def region_table_header(table_offset)
|
339
|
-
@vhdx_file.seek(table_offset, IO::SEEK_SET)
|
340
|
-
region_table_header = VHDX_REGION_TABLE_HEADER.decode(@vhdx_file.read(SIZEOF_VHDX_REGION_TABLE_HEADER))
|
341
|
-
@region_table_header_signature = region_table_header['signature']
|
342
|
-
unless valid_region_table_header?
|
343
|
-
$log.info "Invalid Region Table Header #{@region_table_header_signature}"
|
344
|
-
return nil
|
345
|
-
end
|
346
|
-
unless region_table_header['entry_count'] > 0
|
347
|
-
$log.warn "Invalid Region Table Entry Count #{region_table_header['entry_count']}"
|
348
|
-
return nil
|
349
|
-
end
|
350
|
-
region_table_header
|
351
|
-
end
|
352
|
-
|
353
|
-
def valid_region_table_header?
|
354
|
-
@region_table_header_signature == VHDX_REGION_TABLE_HEADER_SIGNATURE
|
355
|
-
end
|
356
|
-
|
357
|
-
def process_region_table_entry(table_offset, count)
|
358
|
-
table_entry = region_table_entry(table_offset, count)
|
359
|
-
region_table_guid = table_entry['guid']
|
360
|
-
file_offset = table_entry['file_offset']
|
361
|
-
length = table_entry['length']
|
362
|
-
if guid_match?(region_table_guid, REGION_TABLE_BAT_GUID)
|
363
|
-
@bat_offset = file_offset
|
364
|
-
@bat_length = length
|
365
|
-
elsif guid_match?(region_table_guid, REGION_TABLE_METADATA_GUID)
|
366
|
-
@metadata_offset = file_offset
|
367
|
-
@metadata_length = length
|
368
|
-
else
|
369
|
-
raise "Invalid Region Table GUID Type #{region_table_guid}"
|
370
|
-
end
|
371
|
-
end
|
372
|
-
|
373
|
-
def region_table_entry(table_offset, count)
|
374
|
-
offset_to_entry = table_offset + SIZEOF_VHDX_REGION_TABLE_HEADER + (count - 1) * SIZEOF_VHDX_REGION_TABLE_ENTRY
|
375
|
-
@vhdx_file.seek(offset_to_entry, IO::SEEK_SET)
|
376
|
-
VHDX_REGION_TABLE_ENTRY.decode(@vhdx_file.read(SIZEOF_VHDX_REGION_TABLE_ENTRY))
|
377
|
-
end
|
378
|
-
|
379
|
-
def process_bat
|
380
|
-
@vhdx_file.seek(@bat_offset, IO::SEEK_SET)
|
381
|
-
@bat = []
|
382
|
-
1.step(@total_bat_entries, 1) do |block_num|
|
383
|
-
encoded_bat_entry = @vhdx_file.read(SIZEOF_VHDX_BAT_ENTRY)
|
384
|
-
|
385
|
-
next_bat_state_entry = VHDX_BAT_STATE_ENTRY.decode(encoded_bat_entry)
|
386
|
-
state = next_bat_state_entry['state'][5..7].to_i(2) # 3 least significant bits
|
387
|
-
|
388
|
-
next_bat_entry = VHDX_BAT_ENTRY.decode(encoded_bat_entry)
|
389
|
-
file_offset_mb = (next_bat_entry['file_offset_mb'] >> BAT_OFFSET_SHIFT) & BAT_OFFSET_MASK
|
390
|
-
|
391
|
-
entry = VhdxBatEntry.new(block_num, state, file_offset_mb)
|
392
|
-
@bat << entry
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
def compute_bat_offset(offset_bits)
|
397
|
-
(offset_bits & BAT_OFFSET_MASK) >> BAT_OFFSET_SHIFT
|
398
|
-
end
|
399
|
-
|
400
|
-
def process_metadata_table_header
|
401
|
-
@vhdx_file.seek(@metadata_offset, IO::SEEK_SET)
|
402
|
-
metadata_table_header = VHDX_METADATA_TABLE_HEADER.decode(@vhdx_file.read(SIZEOF_VHDX_METADATA_TABLE_HEADER))
|
403
|
-
signature = metadata_table_header['signature']
|
404
|
-
count = metadata_table_header['entry_count']
|
405
|
-
raise "Invalid Metadata Header Signature #{signature}" unless signature == VHDX_METADATA_TABLE_HEADER_SIGNATURE
|
406
|
-
raise "Invalid Metadata Header Entry Count #{count}" unless count > 0
|
407
|
-
(1..count).each do |i|
|
408
|
-
process_metadata_table_entry(i)
|
409
|
-
end
|
410
|
-
raise "Invalid or Missing Metadata Table Entries" unless valid_metadata_table?
|
411
|
-
post_process_metadata
|
412
|
-
end
|
413
|
-
|
414
|
-
def post_process_metadata
|
415
|
-
@chunk_ratio = (2**23 * @logical_sector_size) / @payload_block_size
|
416
|
-
@data_blocks_count = (@virtual_disk_size.to_f / @payload_block_size).ceil
|
417
|
-
@sector_bitmap_blocks_count = (@data_blocks_count.to_f / @chunk_ratio).ceil
|
418
|
-
@sectors_per_block = @payload_block_size / @logical_sector_size
|
419
|
-
@total_bat_entries = if @has_parent
|
420
|
-
@sector_bitmap_blocks_count * (@chunk_ratio + 1)
|
421
|
-
else
|
422
|
-
@data_blocks_count + ((@data_blocks_count - 1) / @chunk_ratio).floor
|
423
|
-
end
|
424
|
-
end
|
425
|
-
|
426
|
-
def process_metadata_table_entry(entry_number)
|
427
|
-
entry_offset = @metadata_offset + SIZEOF_VHDX_METADATA_TABLE_HEADER +
|
428
|
-
(entry_number - 1) * SIZEOF_VHDX_METADATA_TABLE_ENTRY
|
429
|
-
@vhdx_file.seek(entry_offset, IO::SEEK_SET)
|
430
|
-
metadata_table_entry = VHDX_METADATA_TABLE_ENTRY.decode(@vhdx_file.read(SIZEOF_VHDX_METADATA_TABLE_ENTRY))
|
431
|
-
guid = metadata_table_entry['item_id']
|
432
|
-
offset = @metadata_offset + metadata_table_entry['offset']
|
433
|
-
length = metadata_table_entry['length']
|
434
|
-
bits = metadata_table_entry['bit_fields']
|
435
|
-
@is_user = bits[0].to_i(2)
|
436
|
-
@is_virtual_disk = bits[1].to_i(2)
|
437
|
-
@is_required = bits[2].to_i(2)
|
438
|
-
if length == 0
|
439
|
-
raise "Invalid Metadata Table Entry - Length Zero and Offset is #{offset}" if offset != 0
|
440
|
-
$log.debug "Metadata Table Entry Present but Empty"
|
441
|
-
return
|
442
|
-
end
|
443
|
-
@vhdx_file.seek(offset, IO::SEEK_SET)
|
444
|
-
guid_found = nil
|
445
|
-
METADATA_OPS.each do |meta_guid, op|
|
446
|
-
next unless guid_match?(guid, meta_guid)
|
447
|
-
method(op).call(offset)
|
448
|
-
guid_found = guid
|
449
|
-
break
|
450
|
-
end
|
451
|
-
raise "Invalid Metadata Table Entry GUID #{guid}" unless guid_found
|
452
|
-
end
|
453
|
-
|
454
|
-
def file_parameters(_offset)
|
455
|
-
file_parameters_entry = VHDX_FILE_PARAMETERS.decode(@vhdx_file.read(SIZEOF_VHDX_FILE_PARAMETERS))
|
456
|
-
@payload_block_size = file_parameters_entry['block_size']
|
457
|
-
bits = file_parameters_entry['bit_fields']
|
458
|
-
@leave_blocks_allocated = bits[0].to_i(2) == 1
|
459
|
-
@has_parent = bits[1].to_i(2) == 1
|
460
|
-
$log.debug "Payload Block Size #{@payload_block_size} Leave Blocks Allocated #{@leave_blocks_allocated}"
|
461
|
-
$log.debug "Has Parent #{@has_parent}"
|
462
|
-
end
|
463
|
-
|
464
|
-
def virtual_disk_size(_offset)
|
465
|
-
virtual_disk_metadata = VHDX_VIRTUAL_DISK_SIZE.decode(@vhdx_file.read(SIZEOF_VHDX_VIRTUAL_DISK_SIZE))
|
466
|
-
@virtual_disk_size = virtual_disk_metadata['virtual_disk_size']
|
467
|
-
$log.debug "Virtual Disk Size #{@virtual_disk_size}"
|
468
|
-
end
|
469
|
-
|
470
|
-
def page_83_data(_offset)
|
471
|
-
page_83_metadata = VHDX_PAGE_83_DATA.decode(@vhdx_file.read(SIZEOF_VHDX_PAGE_83_DATA))
|
472
|
-
@page_83_data = page_83_metadata['page_83_data']
|
473
|
-
$log.debug "Page 83 Data #{@page_83_data}"
|
474
|
-
end
|
475
|
-
|
476
|
-
def logical_sector_size(_offset)
|
477
|
-
logical_sector_metadata = VHDX_LOGICAL_SECTOR_SIZE.decode(@vhdx_file.read(SIZEOF_VHDX_LOGICAL_SECTOR_SIZE))
|
478
|
-
@logical_sector_size = logical_sector_metadata['logical_sector_size']
|
479
|
-
@blockSize = @logical_sector_size
|
480
|
-
$log.debug "Logical Sector Size (blockSize) #{@logical_sector_size}"
|
481
|
-
end
|
482
|
-
|
483
|
-
def physical_sector_size(_offset)
|
484
|
-
physical_sector_metadata = VHDX_PHYSICAL_SECTOR_SIZE.decode(@vhdx_file.read(SIZEOF_VHDX_PHYSICAL_SECTOR_SIZE))
|
485
|
-
@physical_sector_size = physical_sector_metadata['physical_sector_size']
|
486
|
-
$log.debug "Physical Sector Size #{@physical_sector_size}"
|
487
|
-
end
|
488
|
-
|
489
|
-
def parent_locator_header(offset)
|
490
|
-
raise "Inconsistent filesystem - \'Has_Parent\' Flag unset but Parent Locator Info Present" if @has_parent == false
|
491
|
-
@parent_locator = VHDX_PARENT_LOCATOR_HEADER.decode(@vhdx_file.read(SIZEOF_VHDX_PARENT_LOCATOR_HEADER))
|
492
|
-
@parent_locator_entries = {}
|
493
|
-
key_value_count = @parent_locator['key_value_count']
|
494
|
-
guid = @parent_locator['locator_type']
|
495
|
-
raise "Invalid Parent Locator Type Guid #{guid}" unless guid_match?(guid, VHDX_PARENT_LOCATOR_TYPE_GUID)
|
496
|
-
return if key_value_count == 0
|
497
|
-
(1..key_value_count).each do |i|
|
498
|
-
@vhdx_file.seek(offset + SIZEOF_VHDX_PARENT_LOCATOR_HEADER +
|
499
|
-
(i - 1) * SIZEOF_VHDX_PARENT_LOCATOR_ENTRY, IO::SEEK_SET)
|
500
|
-
process_parent_locator_entry(offset)
|
501
|
-
end
|
502
|
-
keys = @parent_locator_entries.keys
|
503
|
-
$log.warn "Missing \"parent_linkage\" Parent Locator Entry" if keys.index("parent_linkage").nil?
|
504
|
-
parent_locators_to_path(keys)
|
505
|
-
end
|
506
|
-
|
507
|
-
def parent_locators_to_path(keys)
|
508
|
-
if keys.index("absolute_win32_path")
|
509
|
-
parent_path = strip_path_prefix(@parent_locator_entries["absolute_win32_path"])
|
510
|
-
elsif keys.index("relative_path")
|
511
|
-
parent_path = File.dirname(@file_name) + '/' + @parent_locator_entries["relative_path"]
|
512
|
-
elsif keys.index("volume_path")
|
513
|
-
# TODO: Test Volume Path Parent Locator
|
514
|
-
parent_path = strip_path_prefix(@parent_locator_entries["volume_path"])
|
515
|
-
else
|
516
|
-
raise "Missing Parent Locator entries \"relative_path\", \"volume_path\", and \"absolute_win32_path\""
|
517
|
-
end
|
518
|
-
@parent = parent_disk(parent_path)
|
519
|
-
end
|
520
|
-
|
521
|
-
def parent_disk(path)
|
522
|
-
@parent_ostruct = OpenStruct.new
|
523
|
-
@parent_ostruct.fileName = path
|
524
|
-
@parent_ostruct.scvmm = @scvmm
|
525
|
-
@parent_ostruct.driveType = @scvmm.get_drivetype(path)
|
526
|
-
@parent_ostruct.hyperv_connection = @hyperv_connection if @hyperv_connection
|
527
|
-
parent = MiqDisk.getDisk(@parent_ostruct)
|
528
|
-
raise "Unable to access parent disk #{path}" if parent.nil?
|
529
|
-
$log.debug "Got #{path} as Parent disk of #{@file_name}"
|
530
|
-
parent
|
531
|
-
end
|
532
|
-
|
533
|
-
def strip_path_prefix(path)
|
534
|
-
path[0, 4] == "\\\\?\\" ? path[4..-1] : path
|
535
|
-
end
|
536
|
-
|
537
|
-
def process_parent_locator_entry(offset)
|
538
|
-
entry = VHDX_PARENT_LOCATOR_ENTRY.decode(@vhdx_file.read(SIZEOF_VHDX_PARENT_LOCATOR_ENTRY))
|
539
|
-
@vhdx_file.seek(offset + entry['key_offset'], IO::SEEK_SET)
|
540
|
-
key = @converter.convert(@vhdx_file.read(entry['key_length'])).gsub(/\"/, '')
|
541
|
-
@vhdx_file.seek(offset + entry['value_offset'], IO::SEEK_SET)
|
542
|
-
value = @converter.convert(@vhdx_file.read(entry['value_length'])).gsub(/\"/, '')
|
543
|
-
@parent_locator_entries[key] = value
|
544
|
-
end
|
545
|
-
|
546
|
-
def valid_metadata_table?
|
547
|
-
if @virtual_disk_size > 0 && @payload_block_size > 0 && @logical_sector_size > 0 && @physical_sector_size > 0
|
548
|
-
return true
|
549
|
-
end
|
550
|
-
$log.warn "Disk Size #{@virtual_disk_size}"
|
551
|
-
$log.warn "Block Size #{@blockSize}"
|
552
|
-
$log.warn "Logical Sector Size #{@logical_sector_size}"
|
553
|
-
$log.warn "Physical Sector Size #{physical_sector_size}"
|
554
|
-
false
|
555
|
-
end
|
556
|
-
|
557
|
-
def guid_match?(input, guid)
|
558
|
-
new_guid = ""
|
559
|
-
input.unpack("I<S<S<C8").each do |x|
|
560
|
-
if x < 16
|
561
|
-
new_guid += "0#{x.to_s(16)}"
|
562
|
-
else
|
563
|
-
new_guid += x.to_s(16)
|
564
|
-
end
|
565
|
-
end
|
566
|
-
new_guid == guid.to_s(16)
|
567
|
-
end
|
568
|
-
|
569
|
-
def block_pos(pos)
|
570
|
-
raw_sector, byte_offset = pos.divmod(@blockSize)
|
571
|
-
block_number, sector_in_block = raw_sector.divmod(@sectors_per_block)
|
572
|
-
return block_number, sector_in_block, byte_offset
|
573
|
-
end
|
574
|
-
|
575
|
-
def get_allocation_status(block_number)
|
576
|
-
allocation_status = bat_status(block_number)
|
577
|
-
if allocation_status == PAYLOAD_BLOCK_PARTIALLY_PRESENT
|
578
|
-
raise "Invalid status PAYLOAD_BLOCK_PARTIALLY_PRESENT for BAT block number #{block_number}" unless @has_parent
|
579
|
-
end
|
580
|
-
ALLOCATION_STATUS[allocation_status]
|
581
|
-
end
|
582
|
-
|
583
|
-
def get_sector_allocation_status(block_number, sector_number)
|
584
|
-
sector_bitmap_status = bitmap_status(block_number)
|
585
|
-
raise "Invalid Sector Bitmap Status #{sector_bitmap_status}" unless sector_bitmap_status == SB_BLOCK_PRESENT
|
586
|
-
sector_bitmap_offset = bitmap_offset(block_number)
|
587
|
-
sector_byte, bit_offset = sector_number.divmod(8)
|
588
|
-
@vhdx_file.seek(sector_bitmap_offset + sector_byte, IO::SEEK_SET)
|
589
|
-
sector_mask = 0x80 >> bit_offset
|
590
|
-
sector_bitmap = @vhdx_file.read(1).unpack("C")[0]
|
591
|
-
sector_bitmap & sector_mask == sector_mask
|
592
|
-
end
|
593
|
-
|
594
|
-
def bat_status(block_number)
|
595
|
-
@bat[bat_entry_number(block_number)].state
|
596
|
-
end
|
597
|
-
|
598
|
-
def bat_offset(block_number)
|
599
|
-
@bat[bat_entry_number(block_number)].offset * BAT_OFFSET_UNITS
|
600
|
-
end
|
601
|
-
|
602
|
-
def bat_entry_number(block_number)
|
603
|
-
block_number + block_number / @chunk_ratio
|
604
|
-
end
|
605
|
-
|
606
|
-
def bitmap_status(block_number)
|
607
|
-
@bat[bitmap_entry_number(block_number)].state
|
608
|
-
end
|
609
|
-
|
610
|
-
def bitmap_offset(block_number)
|
611
|
-
@bat[bitmap_entry_number(block_number)].offset * BAT_OFFSET_UNITS
|
612
|
-
end
|
613
|
-
|
614
|
-
def bitmap_entry_number(block_number)
|
615
|
-
(block_number / @chunk_ratio) * (@chunk_ratio + 1) + @chunk_ratio
|
616
|
-
end
|
617
|
-
|
618
|
-
def connect_to_hyperv(disk_info)
|
619
|
-
connection = @hyperv_connection = disk_info.hyperv_connection
|
620
|
-
@network = disk_info.driveType == "Network"
|
621
|
-
hyperv_disk = MiqHyperVDisk.new(connection[:host],
|
622
|
-
connection[:user],
|
623
|
-
connection[:password],
|
624
|
-
connection[:port],
|
625
|
-
@network)
|
626
|
-
hyperv_disk.open(@file_name)
|
627
|
-
hyperv_disk
|
628
|
-
end
|
629
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# encoding: US-ASCII
|
2
|
-
|
3
|
-
require 'Scvmm/miq_hyperv_disk'
|
4
|
-
|
5
|
-
VHDX_DISK = "VhdxDisk"
|
6
|
-
VHDX_SIGNATURE = "vhdxfile"
|
7
|
-
|
8
|
-
module VhdxDiskProbe
|
9
|
-
def self.probe(ostruct)
|
10
|
-
return nil unless ostruct.fileName
|
11
|
-
# If file not VHD then not Microsoft.
|
12
|
-
# Allow ".miq" also.
|
13
|
-
extended = false
|
14
|
-
ext = File.extname(ostruct.fileName).downcase
|
15
|
-
extended = true if ext == ".vhdx" || ext == ".avhdx"
|
16
|
-
return nil unless extended
|
17
|
-
|
18
|
-
if ostruct.hyperv_connection
|
19
|
-
vhdx_disk_file = connect_to_hyperv(ostruct)
|
20
|
-
else
|
21
|
-
vhdx_disk_file = File.new(ostruct.fileName, "rb")
|
22
|
-
end
|
23
|
-
rv = do_probe(vhdx_disk_file)
|
24
|
-
vhdx_disk_file.close
|
25
|
-
rv
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.do_probe(io)
|
29
|
-
io.seek(0)
|
30
|
-
magic = io.read(8)
|
31
|
-
return VHDX_DISK if magic == VHDX_SIGNATURE
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.connect_to_hyperv(ostruct)
|
36
|
-
connection = ostruct.hyperv_connection
|
37
|
-
network = ostruct.driveType == "Network"
|
38
|
-
hyperv_disk = MiqHyperVDisk.new(connection[:host],
|
39
|
-
connection[:user],
|
40
|
-
connection[:password],
|
41
|
-
connection[:port],
|
42
|
-
network)
|
43
|
-
hyperv_disk.open(ostruct.fileName)
|
44
|
-
hyperv_disk
|
45
|
-
end
|
46
|
-
end
|