manageiq-smartstate 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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