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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +31 -0
  3. data/.rspec +0 -1
  4. data/.whitesource +3 -0
  5. data/Gemfile +1 -1
  6. data/README.md +1 -2
  7. data/lib/MiqVm/MiqRhevmVm.rb +1 -0
  8. data/lib/MiqVm/MiqVm.rb +0 -7
  9. data/lib/blackbox/VmBlackBox.rb +1 -0
  10. data/lib/db/MiqBdb/MiqBdb.rb +1 -0
  11. data/lib/db/MiqBdb/MiqBdbBtree.rb +1 -0
  12. data/lib/db/MiqBdb/MiqBdbHash.rb +1 -0
  13. data/lib/db/MiqBdb/MiqBdbPage.rb +1 -0
  14. data/lib/disk/DiskProbe.rb +2 -1
  15. data/lib/disk/modules/MiqLargeFile.rb +1 -0
  16. data/lib/fs/ntfs/attrib_file_name.rb +1 -0
  17. data/lib/fs/ntfs/attrib_standard_information.rb +1 -0
  18. data/lib/manageiq/smartstate/version.rb +1 -1
  19. data/lib/metadata/MIQExtract/MIQExtract.rb +1 -0
  20. data/lib/metadata/ScanProfile/ScanProfilesBase.rb +1 -0
  21. data/lib/metadata/VmConfig/GetNativeCfg.rb +1 -0
  22. data/lib/metadata/VmConfig/VmConfig.rb +1 -0
  23. data/lib/metadata/VmConfig/ovfConfig.rb +1 -0
  24. data/lib/metadata/VmConfig/vmcConfig.rb +1 -0
  25. data/lib/metadata/VmConfig/xmlConfig.rb +1 -6
  26. data/lib/metadata/linux/LinuxInitProcs.rb +1 -0
  27. data/lib/metadata/linux/LinuxOSInfo.rb +1 -0
  28. data/lib/metadata/linux/LinuxPackages.rb +1 -0
  29. data/lib/metadata/linux/LinuxSystemd.rb +1 -0
  30. data/lib/metadata/linux/LinuxUsers.rb +1 -0
  31. data/lib/metadata/linux/MiqConaryPackages.rb +1 -0
  32. data/lib/metadata/linux/MiqRpmPackages.rb +1 -0
  33. data/lib/metadata/util/md5deep.rb +1 -0
  34. data/lib/metadata/util/win32/Win32Accounts.rb +1 -0
  35. data/lib/metadata/util/win32/Win32EventLog.rb +1 -0
  36. data/lib/metadata/util/win32/Win32Services.rb +1 -0
  37. data/lib/metadata/util/win32/Win32Software.rb +1 -0
  38. data/lib/metadata/util/win32/Win32System.rb +1 -0
  39. data/lib/metadata/util/win32/fleece_hives.rb +1 -0
  40. data/lib/metadata/util/win32/ms-registry.rb +1 -0
  41. data/lib/metadata/util/win32/remote-registry.rb +1 -0
  42. data/manageiq-smartstate.gemspec +4 -5
  43. metadata +7 -32
  44. data/.travis.yml +0 -15
  45. data/lib/MiqVm/miq_scvmm_vm.rb +0 -39
  46. data/lib/Scvmm/miq_hyperv_disk.rb +0 -274
  47. data/lib/Scvmm/miq_scvmm_parse_powershell.rb +0 -75
  48. data/lib/Scvmm/miq_scvmm_vm_ssa_info.rb +0 -135
  49. data/lib/disk/modules/MSCommon.rb +0 -354
  50. data/lib/disk/modules/MSVSDiffDisk.rb +0 -85
  51. data/lib/disk/modules/MSVSDiskProbe.rb +0 -61
  52. data/lib/disk/modules/MSVSDynamicDisk.rb +0 -36
  53. data/lib/disk/modules/MSVSFixedDisk.rb +0 -39
  54. data/lib/disk/modules/VhdxDisk.rb +0 -629
  55. data/lib/disk/modules/VhdxDiskProbe.rb +0 -46
  56. 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