manageiq-smartstate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (305) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +4 -0
  4. data/.rspec_ci +4 -0
  5. data/.travis.yml +15 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE.txt +202 -0
  8. data/README.md +45 -0
  9. data/Rakefile +23 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/MiqContainerGroup/MiqContainerGroup.rb +31 -0
  13. data/lib/MiqVm/MiqLocalVm.rb +50 -0
  14. data/lib/MiqVm/MiqRhevmVm.rb +179 -0
  15. data/lib/MiqVm/MiqVm.rb +355 -0
  16. data/lib/MiqVm/miq_azure_vm.rb +96 -0
  17. data/lib/MiqVm/miq_scvmm_vm.rb +38 -0
  18. data/lib/MiqVm/test/camcorder_fleece_test.rb +60 -0
  19. data/lib/MiqVm/test/localVm.rb +45 -0
  20. data/lib/MiqVm/test/partitionAlignmentCheck.rb +76 -0
  21. data/lib/MiqVm/test/remoteVm.rb +65 -0
  22. data/lib/MiqVm/test/rhevmNfsTest.rb +62 -0
  23. data/lib/MiqVm/test/rhevmNfsTest2.rb +66 -0
  24. data/lib/MiqVm/test/rhevmTest.rb +70 -0
  25. data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackCommon.rb +107 -0
  26. data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackImage.rb +67 -0
  27. data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackInstance.rb +182 -0
  28. data/lib/Scvmm/miq_hyperv_disk.rb +273 -0
  29. data/lib/Scvmm/miq_scvmm_parse_powershell.rb +75 -0
  30. data/lib/Scvmm/miq_scvmm_vm_ssa_info.rb +135 -0
  31. data/lib/Scvmm/test/miq_hyperv_disk_test.rb +33 -0
  32. data/lib/Scvmm/test/miq_scvmm_vm_ssa_info_test.rb +41 -0
  33. data/lib/VmLocalDiskAccess/test/localCfg.rb +97 -0
  34. data/lib/VolumeManager/LVM/logical_volume.rb +75 -0
  35. data/lib/VolumeManager/LVM/lv_segment.rb +43 -0
  36. data/lib/VolumeManager/LVM/lvm2disk.rb +158 -0
  37. data/lib/VolumeManager/LVM/parser.rb +138 -0
  38. data/lib/VolumeManager/LVM/physical_volume.rb +19 -0
  39. data/lib/VolumeManager/LVM/scanner.rb +156 -0
  40. data/lib/VolumeManager/LVM/thin/btree.rb +83 -0
  41. data/lib/VolumeManager/LVM/thin/constants.rb +86 -0
  42. data/lib/VolumeManager/LVM/thin/data_map.rb +44 -0
  43. data/lib/VolumeManager/LVM/thin/mapping_tree.rb +19 -0
  44. data/lib/VolumeManager/LVM/thin/space_maps.rb +58 -0
  45. data/lib/VolumeManager/LVM/thin/superblock.rb +136 -0
  46. data/lib/VolumeManager/LVM/thin.rb +6 -0
  47. data/lib/VolumeManager/LVM/volume_group.rb +97 -0
  48. data/lib/VolumeManager/LVM.rb +8 -0
  49. data/lib/VolumeManager/MiqLdm.rb +546 -0
  50. data/lib/VolumeManager/MiqLvm.rb +17 -0
  51. data/lib/VolumeManager/MiqNativeVolumeManager.rb +150 -0
  52. data/lib/VolumeManager/MiqVolumeManager.rb +277 -0
  53. data/lib/VolumeManager/VolMgrPlatformSupport.rb +18 -0
  54. data/lib/VolumeManager/VolMgrPlatformSupportLinux.rb +77 -0
  55. data/lib/VolumeManager/VolMgrPlatformSupportWin.rb +17 -0
  56. data/lib/VolumeManager/test/blockDevTest.rb +40 -0
  57. data/lib/VolumeManager/test/ldm.rb +97 -0
  58. data/lib/blackbox/VmBlackBox.rb +103 -0
  59. data/lib/blackbox/xmlStorage.rb +180 -0
  60. data/lib/db/MiqBdb/MiqBdb.rb +309 -0
  61. data/lib/db/MiqBdb/MiqBdbBtree.rb +219 -0
  62. data/lib/db/MiqBdb/MiqBdbHash.rb +199 -0
  63. data/lib/db/MiqBdb/MiqBdbPage.rb +159 -0
  64. data/lib/db/MiqBdb/MiqBdbUtil.rb +18 -0
  65. data/lib/db/MiqSqlite/MiqSqlite3.rb +330 -0
  66. data/lib/db/MiqSqlite/MiqSqlite3Cell.rb +167 -0
  67. data/lib/db/MiqSqlite/MiqSqlite3Page.rb +151 -0
  68. data/lib/db/MiqSqlite/MiqSqlite3Table.rb +124 -0
  69. data/lib/db/MiqSqlite/MiqSqlite3Util.rb +32 -0
  70. data/lib/disk/DiskProbe.rb +68 -0
  71. data/lib/disk/MiqDisk.rb +317 -0
  72. data/lib/disk/camcorder_test.rb +90 -0
  73. data/lib/disk/dos_mbr.img +0 -0
  74. data/lib/disk/modules/AzureBlobDisk.rb +101 -0
  75. data/lib/disk/modules/LocalDevMod.rb +47 -0
  76. data/lib/disk/modules/LocalDevProbe.rb +6 -0
  77. data/lib/disk/modules/MSCommon.rb +352 -0
  78. data/lib/disk/modules/MSVSDiffDisk.rb +91 -0
  79. data/lib/disk/modules/MSVSDiskProbe.rb +61 -0
  80. data/lib/disk/modules/MSVSDynamicDisk.rb +42 -0
  81. data/lib/disk/modules/MSVSFixedDisk.rb +45 -0
  82. data/lib/disk/modules/MiqLargeFile.rb +63 -0
  83. data/lib/disk/modules/MiqLargeFileWin32.rb +107 -0
  84. data/lib/disk/modules/QcowDisk.rb +692 -0
  85. data/lib/disk/modules/QcowDiskProbe.rb +34 -0
  86. data/lib/disk/modules/RawBlockIO.rb +116 -0
  87. data/lib/disk/modules/RawDisk.rb +45 -0
  88. data/lib/disk/modules/RawDiskProbe.rb +7 -0
  89. data/lib/disk/modules/RhevmDescriptor.rb +167 -0
  90. data/lib/disk/modules/RhevmDiskProbe.rb +52 -0
  91. data/lib/disk/modules/VMWareCowdDisk.rb +207 -0
  92. data/lib/disk/modules/VMWareDescriptor.rb +214 -0
  93. data/lib/disk/modules/VMWareDiskProbe.rb +74 -0
  94. data/lib/disk/modules/VMWareSparseDisk.rb +189 -0
  95. data/lib/disk/modules/VhdxDisk.rb +625 -0
  96. data/lib/disk/modules/VhdxDiskProbe.rb +46 -0
  97. data/lib/disk/modules/VixDiskMod.rb +54 -0
  98. data/lib/disk/modules/VixDiskProbe.rb +6 -0
  99. data/lib/disk/modules/miq_disk_cache.rb +135 -0
  100. data/lib/disk/modules/miq_dummy_disk.rb +41 -0
  101. data/lib/disk/modules/vhdx_bat_entry.rb +10 -0
  102. data/lib/disk/test.rb +66 -0
  103. data/lib/fs/MetakitFS/MetakitFS.rb +530 -0
  104. data/lib/fs/MetakitFS/test/Makefile +14 -0
  105. data/lib/fs/MetakitFS/test/MkCollectFiles.rb +165 -0
  106. data/lib/fs/MetakitFS/test/MkSelectFiles.rb +30 -0
  107. data/lib/fs/MetakitFS/test/collect_files.yaml +70 -0
  108. data/lib/fs/MetakitFS/test/init.rb +3 -0
  109. data/lib/fs/MetakitFS/test/mk2vmdk.rb +64 -0
  110. data/lib/fs/MetakitFS/test/mk4test.c +92 -0
  111. data/lib/fs/MetakitFS/test/mkFsTest.rb +113 -0
  112. data/lib/fs/MetakitFS/test/proto.rb +97 -0
  113. data/lib/fs/MiqFS/FsProbe.rb +39 -0
  114. data/lib/fs/MiqFS/MiqFS.rb +515 -0
  115. data/lib/fs/MiqFS/modules/AUFSProbe.rb +26 -0
  116. data/lib/fs/MiqFS/modules/Ext3.rb +305 -0
  117. data/lib/fs/MiqFS/modules/Ext3Probe.rb +25 -0
  118. data/lib/fs/MiqFS/modules/Ext4.rb +304 -0
  119. data/lib/fs/MiqFS/modules/Ext4Probe.rb +25 -0
  120. data/lib/fs/MiqFS/modules/Fat32.rb +318 -0
  121. data/lib/fs/MiqFS/modules/Fat32Probe.rb +30 -0
  122. data/lib/fs/MiqFS/modules/HFSProbe.rb +18 -0
  123. data/lib/fs/MiqFS/modules/Iso9660.rb +293 -0
  124. data/lib/fs/MiqFS/modules/Iso9660Probe.rb +18 -0
  125. data/lib/fs/MiqFS/modules/LocalFS.rb +105 -0
  126. data/lib/fs/MiqFS/modules/NTFS.rb +287 -0
  127. data/lib/fs/MiqFS/modules/NTFSProbe.rb +21 -0
  128. data/lib/fs/MiqFS/modules/NativeFS.rb +155 -0
  129. data/lib/fs/MiqFS/modules/ReFSProbe.rb +17 -0
  130. data/lib/fs/MiqFS/modules/RealFS.rb +79 -0
  131. data/lib/fs/MiqFS/modules/RealFSProbe.rb +6 -0
  132. data/lib/fs/MiqFS/modules/Reiser4Probe.rb +18 -0
  133. data/lib/fs/MiqFS/modules/ReiserFS.rb +315 -0
  134. data/lib/fs/MiqFS/modules/ReiserFSProbe.rb +42 -0
  135. data/lib/fs/MiqFS/modules/UnionFSProbe.rb +18 -0
  136. data/lib/fs/MiqFS/modules/WebDAV.rb +127 -0
  137. data/lib/fs/MiqFS/modules/WebDAVFile.rb +68 -0
  138. data/lib/fs/MiqFS/modules/XFS.rb +300 -0
  139. data/lib/fs/MiqFS/modules/XFSProbe.rb +26 -0
  140. data/lib/fs/MiqFS/modules/ZFSProbe.rb +18 -0
  141. data/lib/fs/MiqFS/test.rb +59 -0
  142. data/lib/fs/MiqFsUtil.rb +383 -0
  143. data/lib/fs/MiqMountManager.rb +209 -0
  144. data/lib/fs/MiqNativeMountManager.rb +101 -0
  145. data/lib/fs/MountManagerProbe.rb +29 -0
  146. data/lib/fs/ReiserFS/block.rb +209 -0
  147. data/lib/fs/ReiserFS/directory.rb +136 -0
  148. data/lib/fs/ReiserFS/directory_entry.rb +140 -0
  149. data/lib/fs/ReiserFS/file_data.rb +111 -0
  150. data/lib/fs/ReiserFS/superblock.rb +140 -0
  151. data/lib/fs/ReiserFS/utils.rb +95 -0
  152. data/lib/fs/VimDatastoreFS/VimDatastoreFS.rb +192 -0
  153. data/lib/fs/ext3/alloc_bitmap.rb +38 -0
  154. data/lib/fs/ext3/block_pointers_path.rb +130 -0
  155. data/lib/fs/ext3/directory.rb +51 -0
  156. data/lib/fs/ext3/directory_entry.rb +67 -0
  157. data/lib/fs/ext3/ex_attrib_header.rb +14 -0
  158. data/lib/fs/ext3/ex_attrib_name.rb +23 -0
  159. data/lib/fs/ext3/file_data.rb +130 -0
  160. data/lib/fs/ext3/group_descriptor_entry.rb +65 -0
  161. data/lib/fs/ext3/group_descriptor_table.rb +54 -0
  162. data/lib/fs/ext3/hash_tree_entry.rb +18 -0
  163. data/lib/fs/ext3/hash_tree_header.rb +15 -0
  164. data/lib/fs/ext3/inode.rb +228 -0
  165. data/lib/fs/ext3/posix_acl_entry.rb +29 -0
  166. data/lib/fs/ext3/posix_acl_header.rb +11 -0
  167. data/lib/fs/ext3/superblock.rb +406 -0
  168. data/lib/fs/ext3/test/tc_Ext3BlockPointersPath.rb +74 -0
  169. data/lib/fs/ext4/alloc_bitmap.rb +38 -0
  170. data/lib/fs/ext4/directory.rb +87 -0
  171. data/lib/fs/ext4/directory_entry.rb +77 -0
  172. data/lib/fs/ext4/ex_attrib_header.rb +14 -0
  173. data/lib/fs/ext4/ex_attrib_name.rb +23 -0
  174. data/lib/fs/ext4/extent.rb +35 -0
  175. data/lib/fs/ext4/extent_header.rb +40 -0
  176. data/lib/fs/ext4/extent_index.rb +33 -0
  177. data/lib/fs/ext4/group_descriptor_entry.rb +69 -0
  178. data/lib/fs/ext4/group_descriptor_table.rb +54 -0
  179. data/lib/fs/ext4/hash_tree_entry.rb +58 -0
  180. data/lib/fs/ext4/hash_tree_header.rb +35 -0
  181. data/lib/fs/ext4/inode.rb +465 -0
  182. data/lib/fs/ext4/posix_acl_entry.rb +29 -0
  183. data/lib/fs/ext4/posix_acl_header.rb +11 -0
  184. data/lib/fs/ext4/superblock.rb +412 -0
  185. data/lib/fs/fat32/boot_sect.rb +379 -0
  186. data/lib/fs/fat32/directory.rb +222 -0
  187. data/lib/fs/fat32/directory_entry.rb +540 -0
  188. data/lib/fs/fat32/file_data.rb +128 -0
  189. data/lib/fs/iso9660/boot_sector.rb +170 -0
  190. data/lib/fs/iso9660/directory.rb +90 -0
  191. data/lib/fs/iso9660/directory_entry.rb +147 -0
  192. data/lib/fs/iso9660/file_data.rb +78 -0
  193. data/lib/fs/iso9660/rock_ridge.rb +329 -0
  194. data/lib/fs/iso9660/util.rb +57 -0
  195. data/lib/fs/modules/LinuxMount.rb +300 -0
  196. data/lib/fs/modules/LinuxMountProbe.rb +29 -0
  197. data/lib/fs/modules/WinMount.rb +97 -0
  198. data/lib/fs/modules/WinMountProbe.rb +24 -0
  199. data/lib/fs/ntfs/attrib_attribute_list.rb +131 -0
  200. data/lib/fs/ntfs/attrib_bitmap.rb +26 -0
  201. data/lib/fs/ntfs/attrib_data.rb +74 -0
  202. data/lib/fs/ntfs/attrib_file_name.rb +110 -0
  203. data/lib/fs/ntfs/attrib_header.rb +194 -0
  204. data/lib/fs/ntfs/attrib_index_allocation.rb +19 -0
  205. data/lib/fs/ntfs/attrib_index_root.rb +247 -0
  206. data/lib/fs/ntfs/attrib_object_id.rb +40 -0
  207. data/lib/fs/ntfs/attrib_standard_information.rb +107 -0
  208. data/lib/fs/ntfs/attrib_type.rb +49 -0
  209. data/lib/fs/ntfs/attrib_volume_information.rb +53 -0
  210. data/lib/fs/ntfs/attrib_volume_name.rb +31 -0
  211. data/lib/fs/ntfs/boot_sect.rb +253 -0
  212. data/lib/fs/ntfs/data_run.rb +358 -0
  213. data/lib/fs/ntfs/directory_index_node.rb +114 -0
  214. data/lib/fs/ntfs/index_node_header.rb +69 -0
  215. data/lib/fs/ntfs/index_record_header.rb +85 -0
  216. data/lib/fs/ntfs/mft_entry.rb +288 -0
  217. data/lib/fs/ntfs/utils.rb +43 -0
  218. data/lib/fs/test/camcorder_fs_test.rb +108 -0
  219. data/lib/fs/test/collect_files_direct.yaml +22 -0
  220. data/lib/fs/test/collect_files_in.yaml +24 -0
  221. data/lib/fs/test/collect_files_in_nc.yaml +22 -0
  222. data/lib/fs/test/collect_files_out.yaml +6 -0
  223. data/lib/fs/test/collect_files_rm.yaml +6 -0
  224. data/lib/fs/test/copyTest.rb +126 -0
  225. data/lib/fs/test/fsTest.rb +87 -0
  226. data/lib/fs/test/updateTest.rb +184 -0
  227. data/lib/fs/xfs/allocation_group.rb +160 -0
  228. data/lib/fs/xfs/bmap_btree_block.rb +125 -0
  229. data/lib/fs/xfs/bmap_btree_record.rb +80 -0
  230. data/lib/fs/xfs/bmap_btree_root_node.rb +72 -0
  231. data/lib/fs/xfs/directory.rb +133 -0
  232. data/lib/fs/xfs/directory2_data_header.rb +27 -0
  233. data/lib/fs/xfs/directory3_data_header.rb +34 -0
  234. data/lib/fs/xfs/directory_block_tail.rb +22 -0
  235. data/lib/fs/xfs/directory_data_header.rb +46 -0
  236. data/lib/fs/xfs/directory_entry.rb +106 -0
  237. data/lib/fs/xfs/inode.rb +532 -0
  238. data/lib/fs/xfs/inode_map.rb +100 -0
  239. data/lib/fs/xfs/short_form_directory_entry.rb +91 -0
  240. data/lib/fs/xfs/short_form_header.rb +44 -0
  241. data/lib/fs/xfs/superblock.rb +556 -0
  242. data/lib/lib/tasks/azure.rake +52 -0
  243. data/lib/manageiq/smartstate/version.rb +5 -0
  244. data/lib/manageiq/smartstate.rb +7 -0
  245. data/lib/manageiq-smartstate.rb +1 -0
  246. data/lib/metadata/MIQExtract/MIQExtract.rb +297 -0
  247. data/lib/metadata/MIQExtract/test/extractTest.rb +41 -0
  248. data/lib/metadata/MIQExtract/test/full_extract_test.rb +68 -0
  249. data/lib/metadata/ScanProfile/HostScanItem.rb +4 -0
  250. data/lib/metadata/ScanProfile/HostScanProfile.rb +4 -0
  251. data/lib/metadata/ScanProfile/HostScanProfiles.rb +41 -0
  252. data/lib/metadata/ScanProfile/ScanItemBase.rb +63 -0
  253. data/lib/metadata/ScanProfile/ScanProfileBase.rb +51 -0
  254. data/lib/metadata/ScanProfile/ScanProfilesBase.rb +60 -0
  255. data/lib/metadata/ScanProfile/VmScanItem.rb +4 -0
  256. data/lib/metadata/ScanProfile/VmScanProfile.rb +4 -0
  257. data/lib/metadata/ScanProfile/VmScanProfiles.rb +38 -0
  258. data/lib/metadata/ScanProfile/modules/HostScanItemFile.rb +51 -0
  259. data/lib/metadata/ScanProfile/modules/HostScanItemNteventlog.rb +84 -0
  260. data/lib/metadata/ScanProfile/modules/VmScanItemFile.rb +39 -0
  261. data/lib/metadata/ScanProfile/modules/VmScanItemNteventlog.rb +34 -0
  262. data/lib/metadata/ScanProfile/modules/VmScanItemRegistry.rb +64 -0
  263. data/lib/metadata/VMMount/VMMount.rb +81 -0
  264. data/lib/metadata/VMMount/VMPlatformMount.rb +18 -0
  265. data/lib/metadata/VMMount/VMPlatformMountLinux.rb +75 -0
  266. data/lib/metadata/VMMount/VMPlatformMountWin.rb +13 -0
  267. data/lib/metadata/VmConfig/GetNativeCfg.rb +45 -0
  268. data/lib/metadata/VmConfig/VmConfig.rb +947 -0
  269. data/lib/metadata/VmConfig/cfgConfig.rb +45 -0
  270. data/lib/metadata/VmConfig/ovfConfig.rb +99 -0
  271. data/lib/metadata/VmConfig/test/GetVMwareCfgTest.rb +40 -0
  272. data/lib/metadata/VmConfig/vmcConfig.rb +116 -0
  273. data/lib/metadata/VmConfig/vmtxConfig.rb +4 -0
  274. data/lib/metadata/VmConfig/vmxConfig.rb +162 -0
  275. data/lib/metadata/VmConfig/xmlConfig.rb +79 -0
  276. data/lib/metadata/VmConfig/xmlMsHyperVConfig.rb +41 -0
  277. data/lib/metadata/linux/InitProcHash.rb +632 -0
  278. data/lib/metadata/linux/LinuxInitProcs.rb +142 -0
  279. data/lib/metadata/linux/LinuxOSInfo.rb +237 -0
  280. data/lib/metadata/linux/LinuxPackages.rb +209 -0
  281. data/lib/metadata/linux/LinuxSystemd.rb +130 -0
  282. data/lib/metadata/linux/LinuxUsers.rb +289 -0
  283. data/lib/metadata/linux/LinuxUtils.rb +197 -0
  284. data/lib/metadata/linux/MiqConaryPackages.rb +41 -0
  285. data/lib/metadata/linux/MiqRpmPackages.rb +160 -0
  286. data/lib/metadata/linux/test/Name +0 -0
  287. data/lib/metadata/linux/test/Packages +0 -0
  288. data/lib/metadata/linux/test/rpoTest.rb +5 -0
  289. data/lib/metadata/linux/test/tc_LinuxUtils.rb +4157 -0
  290. data/lib/metadata/util/event_log_filter.rb +61 -0
  291. data/lib/metadata/util/md5deep.rb +280 -0
  292. data/lib/metadata/util/win32/Win32Accounts.rb +764 -0
  293. data/lib/metadata/util/win32/Win32EventLog.rb +743 -0
  294. data/lib/metadata/util/win32/Win32Services.rb +86 -0
  295. data/lib/metadata/util/win32/Win32Software.rb +326 -0
  296. data/lib/metadata/util/win32/Win32System.rb +333 -0
  297. data/lib/metadata/util/win32/boot_info_win.rb +59 -0
  298. data/lib/metadata/util/win32/fleece_hives.rb +220 -0
  299. data/lib/metadata/util/win32/ms-registry.rb +650 -0
  300. data/lib/metadata/util/win32/peheader.rb +868 -0
  301. data/lib/metadata/util/win32/remote-registry.rb +142 -0
  302. data/lib/metadata/util/win32/system_path_win.rb +103 -0
  303. data/lib/metadata/util/win32/versioninfo.rb +17 -0
  304. data/manageiq-smartstate.gemspec +35 -0
  305. metadata +486 -0
@@ -0,0 +1,625 @@
1
+ # encoding: US-ASCII
2
+
3
+ require 'util/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
+ # NOTE: All values are stored in network byte order.
12
+
13
+ VHDX_FILE_IDENTIFIER = BinaryStruct.new([
14
+ 'Q<', 'signature', # Always 'vhdxfile'.
15
+ 'A256', 'creator', # Parser that created the vhdx.
16
+ ])
17
+ SIZEOF_VHDX_FILE_IDENTIFIER = VHDX_FILE_IDENTIFIER.size
18
+ VHDX_FILE_IDENTIFIER_SIGNATURE = 0x656C696678646876
19
+
20
+ VHDX_HEADER = BinaryStruct.new([
21
+ 'I<', 'signature', # Always 'head'.
22
+ 'I<', 'checksum', # cfc-32c hash over entire 4 KB structure
23
+ 'Q<', 'sequence_number', #
24
+ 'A16', 'file_write_guid', # Identifies the file's contents
25
+ 'A16', 'data_write_guid', # Identifies the user visible data
26
+ 'A16', 'log_guid', # Determines validity of log entries
27
+ 'S<', 'log_version', # The version of the log format.
28
+ 'S<', 'version', # The version of the VHDX format.
29
+ 'I<', 'log_length', # Length of the log
30
+ 'I<', 'log_offset', # Byte offset of the log
31
+ 'A4016', 'reserved', # reserved
32
+ ])
33
+ SIZEOF_VHDX_HEADER = VHDX_HEADER.size
34
+ VHDX_HEADER_OFFSET = 64 * 1024
35
+ VHDX_HEADER_SIGNATURE = 0x64616568
36
+ VHDX_HEADER2_OFFSET = 2 * 64 * 1024
37
+
38
+ VHDX_REGION_TABLE_HEADER = BinaryStruct.new([
39
+ 'I<', 'signature', # Always 'regi'.
40
+ 'I<', 'checksum', # cfc-32c hash over entire 4 KB structure
41
+ 'I<', 'entry_count', # Number of Region Table entries
42
+ 'I<', 'reserved', # reserved
43
+ ])
44
+ SIZEOF_VHDX_REGION_TABLE_HEADER = VHDX_REGION_TABLE_HEADER.size
45
+ VHDX_REGION_TABLE_HEADER_OFFSET = 64 * 1024
46
+ VHDX_REGION_TABLE_HEADER_SIGNATURE = 0x69676572
47
+
48
+ VHDX_REGION_TABLE_ENTRY = BinaryStruct.new([
49
+ 'A16', 'guid', # GUID must be unique within the table
50
+ 'Q<', 'file_offset', # offset of the Region Table entry (1MB multiples)
51
+ 'I<', 'length', # Byte length of the object (1MB multiples)
52
+ 'I<', 'required', # 1st bit is required, 31 remaining bits reserved
53
+ ])
54
+ SIZEOF_VHDX_REGION_TABLE_ENTRY = VHDX_REGION_TABLE_ENTRY.size
55
+ REGION_TABLE_BAT_GUID = 0x2DC27766F62342009D64115E9BFD4A08
56
+ REGION_TABLE_METADATA_GUID = 0x8B7CA20647904B9AB8FE575F050F886E
57
+
58
+ VHDX_BAT_STATE_ENTRY = BinaryStruct.new([
59
+ 'B8', 'state', # least significant three bits only
60
+ 'B8', 'reserved',
61
+ 'B48', 'file_offset_mb' # the offset within the file in units of 1 MB
62
+ ])
63
+ VHDX_BAT_ENTRY = BinaryStruct.new([
64
+ 'Q<', 'file_offset_mb', # most significant 44 bits only
65
+ ])
66
+ SIZEOF_VHDX_BAT_ENTRY = VHDX_BAT_ENTRY.size
67
+ BAT_OFFSET_MASK = 0x00000FFFFFFFFFFF
68
+ BAT_OFFSET_SHIFT = 20
69
+ BAT_OFFSET_UNITS = 1024 * 1024 # units for fill_offset_mb field
70
+
71
+ PAYLOAD_BLOCK_NOT_PRESENT = 0
72
+ PAYLOAD_BLOCK_UNDEFINED = 1
73
+ PAYLOAD_BLOCK_ZERO = 2
74
+ PAYLOAD_BLOCK_UNMAPPED = 3
75
+ PAYLOAD_BLOCK_FULLY_PRESENT = 6
76
+ PAYLOAD_BLOCK_PARTIALLY_PRESENT = 7
77
+
78
+ SB_BLOCK_NOT_PRESENT = 0
79
+ SB_BLOCK_PRESENT = 6
80
+ SECTOR_BITMAP_BLOCKSIZE = 1024 * 1024
81
+
82
+ VHDX_METADATA_TABLE_HEADER = BinaryStruct.new([
83
+ 'Q<', 'signature', # Always 'metadata'
84
+ 'S<', 'reserved', #
85
+ 'S<', 'entry_count', # number of entries in the table
86
+ 'I5', 'reserved2', #
87
+ ])
88
+ SIZEOF_VHDX_METADATA_TABLE_HEADER = VHDX_METADATA_TABLE_HEADER.size
89
+ VHDX_METADATA_TABLE_HEADER_SIGNATURE = 0x617461646174656D
90
+
91
+ VHDX_METADATA_TABLE_ENTRY = BinaryStruct.new([
92
+ 'A16', 'item_id', # item_id and isuser pair must be unique within the table
93
+ 'I<', 'offset', # offset relative to beginning of the metadata region >= 64KB
94
+ 'I<', 'length', # < = 1MB
95
+ 'b32', 'bit_fields',
96
+ # 'B1', 'is_user', # system or user metadata
97
+ # 'B1', 'is_virtual_disk', # file or virtual disk metadata
98
+ # 'B1', 'is_required', #
99
+ # 'B29', 'reserved',
100
+ 'I<', 'reserved2'
101
+ ])
102
+ SIZEOF_VHDX_METADATA_TABLE_ENTRY = VHDX_METADATA_TABLE_ENTRY.size
103
+
104
+ FILE_PARAMETERS_GUID = 0xCAA16737FA364D43B3B633F0AA44E76B
105
+ VIRTUAL_DISK_SIZE_GUID = 0x2FA54224CD1B4876B2115DBED83BF4B8
106
+ PAGE_83_DATA_GUID = 0xBECA12ABB2E6452393EFC309E000C746
107
+ LOGICAL_SECTOR_SIZE_GUID = 0x8141BF1DA96F4709BA47F233A8FAAB5F
108
+ PHYSICAL_SECTOR_SIZE_GUID = 0xCDA348C7445D44719CC9E9885251C556
109
+ PARENT_LOCATOR_GUID = 0xA8D35F2DB30B454DABF7D3D84834AB0C
110
+
111
+ METADATA_OPS = {
112
+ FILE_PARAMETERS_GUID => :file_parameters,
113
+ VIRTUAL_DISK_SIZE_GUID => :virtual_disk_size,
114
+ PAGE_83_DATA_GUID => :page_83_data,
115
+ LOGICAL_SECTOR_SIZE_GUID => :logical_sector_size,
116
+ PHYSICAL_SECTOR_SIZE_GUID => :physical_sector_size,
117
+ PARENT_LOCATOR_GUID => :parent_locator_header
118
+ }
119
+
120
+ ALLOCATION_STATUS = {
121
+ PAYLOAD_BLOCK_NOT_PRESENT => PAYLOAD_BLOCK_NOT_PRESENT,
122
+ PAYLOAD_BLOCK_UNDEFINED => nil,
123
+ PAYLOAD_BLOCK_ZERO => nil,
124
+ PAYLOAD_BLOCK_UNMAPPED => nil,
125
+ PAYLOAD_BLOCK_FULLY_PRESENT => PAYLOAD_BLOCK_FULLY_PRESENT,
126
+ PAYLOAD_BLOCK_PARTIALLY_PRESENT => PAYLOAD_BLOCK_PARTIALLY_PRESENT
127
+ }
128
+
129
+ VHDX_FILE_PARAMETERS = BinaryStruct.new([
130
+ 'I<', 'block_size', # size of payload block in bytes between 1MB & 256MB
131
+ 'b32', 'bit_fields',
132
+ # 'B1', 'leave_blocks_allocated',
133
+ # 'B1', 'has_parent',
134
+ # 'B30', 'reserved',
135
+ ])
136
+ SIZEOF_VHDX_FILE_PARAMETERS = VHDX_FILE_PARAMETERS.size
137
+
138
+ VHDX_VIRTUAL_DISK_SIZE = BinaryStruct.new([
139
+ 'Q<', 'virtual_disk_size' # size in bytes. must be a multiple of logical sector size
140
+ ])
141
+ SIZEOF_VHDX_VIRTUAL_DISK_SIZE = VHDX_VIRTUAL_DISK_SIZE.size
142
+
143
+ VHDX_LOGICAL_SECTOR_SIZE = BinaryStruct.new([
144
+ 'I<', 'logical_sector_size' # size in bytes. must be 512 or 4096
145
+ ])
146
+ SIZEOF_VHDX_LOGICAL_SECTOR_SIZE = VHDX_LOGICAL_SECTOR_SIZE.size
147
+
148
+ VHDX_PHYSICAL_SECTOR_SIZE = BinaryStruct.new([
149
+ 'I<', 'physical_sector_size' # size in bytes. must be 512 or 4096
150
+ ])
151
+ SIZEOF_VHDX_PHYSICAL_SECTOR_SIZE = VHDX_PHYSICAL_SECTOR_SIZE.size
152
+
153
+ VHDX_PAGE_83_DATA = BinaryStruct.new([
154
+ 'A16', 'page_83_data' # unique guid
155
+ ])
156
+ SIZEOF_VHDX_PAGE_83_DATA = VHDX_PAGE_83_DATA.size
157
+
158
+ VHDX_PARENT_LOCATOR_HEADER = BinaryStruct.new([
159
+ 'A16', 'locator_type', # guid with type of the parent virtual disk
160
+ 'S<', 'reserved',
161
+ 'S<', 'key_value_count', # number of key-value pairs for the parent locator
162
+ ])
163
+ SIZEOF_VHDX_PARENT_LOCATOR_HEADER = VHDX_PARENT_LOCATOR_HEADER.size
164
+ VHDX_PARENT_LOCATOR_TYPE_GUID = 0xB04AEFB7D19E4A81B78925B8E9445913
165
+
166
+ VHDX_PARENT_LOCATOR_ENTRY = BinaryStruct.new([
167
+ 'I<', 'key_offset', # offset within the metadata item of key
168
+ 'I<', 'value_offset', # offset within the metadata item of value
169
+ 'S<', 'key_length', # length of the entry's key
170
+ 'S<', 'value_length', # length of the entry's value
171
+ ])
172
+ SIZEOF_VHDX_PARENT_LOCATOR_ENTRY = VHDX_PARENT_LOCATOR_ENTRY.size
173
+
174
+ attr_reader :file_identifier_signature, :vhdx_header_signature, :dInfo
175
+ def d_init
176
+ @diskType = "Vhdx"
177
+ @blockSize = 0
178
+ @virtual_disk_size = 0
179
+ @logical_sector_size = 0
180
+ @physical_sector_size = 0
181
+ @payload_block_size = 0
182
+ @has_parent = nil
183
+ @parent_locator = nil
184
+ @file_name = dInfo.fileName
185
+ @hyperv_connection = nil
186
+ @vhdx_file = connection_to_file(dInfo)
187
+ @converter = Encoding::Converter.new("UTF-16LE", "UTF-8")
188
+ header_section
189
+ end
190
+
191
+ def connection_to_file(dInfo)
192
+ if dInfo.mountMode.nil? || dInfo.mountMode == "r"
193
+ dInfo.mountMode = "r"
194
+ file_mode = "r"
195
+ elsif dInfo.mountMode == "rw"
196
+ file_mode = "r+"
197
+ else
198
+ raise "Unrecognized mountMode: #{dInfo.mountMode}"
199
+ end
200
+ if dInfo.hyperv_connection
201
+ @vhdx_file = connect_to_hyperv(dInfo)
202
+ else
203
+ @vhdx_file = MiqLargeFile.open(@file_name, file_mode)
204
+ end
205
+ @vhdx_file
206
+ end
207
+
208
+ def d_read(pos, len)
209
+ raise "VhdxDisk.d_read Invalid len #{len} #{@has_parent ? "Checkpoint Disk" : "Parent Disk"}\n #{caller}" if len < 0
210
+ $log.debug "VhdxDisk.d_read(#{pos}, #{len})"
211
+ buf = ""
212
+ return buf if len == 0
213
+ block_start, sector_start, byte_offset_start = block_pos(pos)
214
+ block_end, sector_end, _byte_offset_end = block_pos(pos + len - 1)
215
+ byte_offset = 0
216
+ this_len = @blockSize
217
+ (block_start..block_end).each do |block_number|
218
+ real_sector_start = (block_number == block_start) ? sector_start : 0
219
+ real_sector_end = (block_number == block_end) ? sector_end : @sectors_per_block - 1
220
+ allocation_status = get_allocation_status(block_number)
221
+ bat_offset = bat_offset(block_number)
222
+ (real_sector_start..real_sector_end).each do |sector_number|
223
+ if (block_start == block_end) && (sector_start == sector_end)
224
+ byte_offset = byte_offset_start
225
+ this_len = len
226
+ elsif (block_number == block_start) && (sector_number == sector_start)
227
+ byte_offset = byte_offset_start
228
+ this_len = @blockSize - byte_offset
229
+ elsif (block_number == block_end) && (sector_number == sector_end)
230
+ byte_offset = 0
231
+ this_len = len - buf.length
232
+ raise "Internal Error: Calculated read more than sector: #{this_len}" if this_len > @blockSize
233
+ else
234
+ byte_offset = 0
235
+ this_len = @blockSize
236
+ end
237
+ if allocation_status.nil? || allocation_status == PAYLOAD_BLOCK_NOT_PRESENT
238
+ buf << read_unallocated_buf(pos, this_len, buf, allocation_status == PAYLOAD_BLOCK_NOT_PRESENT)
239
+ else
240
+ if allocation_status == PAYLOAD_BLOCK_PARTIALLY_PRESENT
241
+ sector_status = get_sector_allocation_status(block_number, sector_number)
242
+ $log.debug "PAYLOAD_BLOCK_PARTIALLY_PRESENT & sector_status #{sector_status} for sector #{sector_number}"
243
+ if sector_status == false
244
+ buf << read_unallocated_buf(pos, this_len, buf, true)
245
+ next
246
+ end
247
+ end
248
+ byte_offset += sector_number * @logical_sector_size
249
+ @vhdx_file.seek(bat_offset + byte_offset, IO::SEEK_SET)
250
+ buf << @vhdx_file.read(this_len)
251
+ end
252
+ end
253
+ end
254
+ buf
255
+ end
256
+
257
+ def d_write(pos, buf, len)
258
+ @vhdx_file.seek(pos, IO::SEEK_SET)
259
+ @vhdx_file.write(buf, len)
260
+ end
261
+
262
+ def d_close
263
+ @vhdx_file.close
264
+ end
265
+
266
+ # Disk size in sectors.
267
+ def d_size
268
+ @virtual_disk_size / @logical_sector_size
269
+ end
270
+
271
+ def getBase
272
+ self
273
+ end
274
+
275
+ private
276
+
277
+ def read_unallocated_buf(pos, len, buf, status)
278
+ if status == true && @has_parent == true
279
+ return @parent.d_read(pos + buf.length, len)
280
+ else
281
+ return MemoryBuffer.create(len)
282
+ end
283
+ end
284
+
285
+ def header_section
286
+ @file_identifier = file_identifier
287
+ @vhdx_header = header(1)
288
+ unless valid_header_signature?
289
+ $log.info "Invalid VHDX Header Signature #{@vhdx_header_signature}"
290
+ @vhdx_header = header(2)
291
+ end
292
+ raise "Invalid VHDX Header Signature #{@vhdx_header_signature}" unless valid_header_signature?
293
+ @region_table_header = region_table(1)
294
+ unless valid_region_table_header?
295
+ $log.info "Invalid Region Table Header Signature #{@region_table_header_signature}"
296
+ @region_table_header = header(2)
297
+ end
298
+ raise "Invalid Region Table Header Signature #{@region_table_header_signature}" unless valid_region_table_header?
299
+ end
300
+
301
+ def file_identifier
302
+ @vhdx_file.seek(0, IO::SEEK_SET)
303
+ file_identifier = VHDX_FILE_IDENTIFIER.decode(@vhdx_file.read(SIZEOF_VHDX_FILE_IDENTIFIER))
304
+ @file_identifier_signature = file_identifier['signature']
305
+ raise "Invalid VHDX File Identifier Signature #{@file_identifier_signature}" unless valid_file_identifier_signature?
306
+ file_identifier
307
+ end
308
+
309
+ def valid_file_identifier_signature?
310
+ @file_identifier_signature == VHDX_FILE_IDENTIFIER_SIGNATURE
311
+ end
312
+
313
+ def header(header_number)
314
+ @vhdx_file.seek(header_number * VHDX_HEADER_OFFSET, IO::SEEK_SET)
315
+ vhdx_header = VHDX_HEADER.decode(@vhdx_file.read(SIZEOF_VHDX_HEADER))
316
+ @vhdx_header_signature = vhdx_header['signature']
317
+ vhdx_header
318
+ end
319
+
320
+ def valid_header_signature?
321
+ @vhdx_header_signature == VHDX_HEADER_SIGNATURE
322
+ end
323
+
324
+ def region_table(table_number)
325
+ table_offset = 2 * VHDX_HEADER_OFFSET + table_number * VHDX_REGION_TABLE_HEADER_OFFSET
326
+ header = region_table_header(table_offset)
327
+ return if header.nil?
328
+ (1..header['entry_count']).each do |count|
329
+ process_region_table_entry(table_offset, count)
330
+ end
331
+ process_metadata_table_header
332
+ process_bat
333
+ end
334
+
335
+ def region_table_header(table_offset)
336
+ @vhdx_file.seek(table_offset, IO::SEEK_SET)
337
+ region_table_header = VHDX_REGION_TABLE_HEADER.decode(@vhdx_file.read(SIZEOF_VHDX_REGION_TABLE_HEADER))
338
+ @region_table_header_signature = region_table_header['signature']
339
+ unless valid_region_table_header?
340
+ $log.info "Invalid Region Table Header #{@region_table_header_signature}"
341
+ return nil
342
+ end
343
+ unless region_table_header['entry_count'] > 0
344
+ $log.warn "Invalid Region Table Entry Count #{region_table_header['entry_count']}"
345
+ return nil
346
+ end
347
+ region_table_header
348
+ end
349
+
350
+ def valid_region_table_header?
351
+ @region_table_header_signature == VHDX_REGION_TABLE_HEADER_SIGNATURE
352
+ end
353
+
354
+ def process_region_table_entry(table_offset, count)
355
+ table_entry = region_table_entry(table_offset, count)
356
+ region_table_guid = table_entry['guid']
357
+ file_offset = table_entry['file_offset']
358
+ length = table_entry['length']
359
+ if guid_match?(region_table_guid, REGION_TABLE_BAT_GUID)
360
+ @bat_offset = file_offset
361
+ @bat_length = length
362
+ elsif guid_match?(region_table_guid, REGION_TABLE_METADATA_GUID)
363
+ @metadata_offset = file_offset
364
+ @metadata_length = length
365
+ else
366
+ raise "Invalid Region Table GUID Type #{region_table_guid}"
367
+ end
368
+ end
369
+
370
+ def region_table_entry(table_offset, count)
371
+ offset_to_entry = table_offset + SIZEOF_VHDX_REGION_TABLE_HEADER + (count - 1) * SIZEOF_VHDX_REGION_TABLE_ENTRY
372
+ @vhdx_file.seek(offset_to_entry, IO::SEEK_SET)
373
+ VHDX_REGION_TABLE_ENTRY.decode(@vhdx_file.read(SIZEOF_VHDX_REGION_TABLE_ENTRY))
374
+ end
375
+
376
+ def process_bat
377
+ @vhdx_file.seek(@bat_offset, IO::SEEK_SET)
378
+ @bat = []
379
+ 1.step(@total_bat_entries, 1) do |block_num|
380
+ encoded_bat_entry = @vhdx_file.read(SIZEOF_VHDX_BAT_ENTRY)
381
+
382
+ next_bat_state_entry = VHDX_BAT_STATE_ENTRY.decode(encoded_bat_entry)
383
+ state = next_bat_state_entry['state'][5..7].to_i(2) # 3 least significant bits
384
+
385
+ next_bat_entry = VHDX_BAT_ENTRY.decode(encoded_bat_entry)
386
+ file_offset_mb = (next_bat_entry['file_offset_mb'] >> BAT_OFFSET_SHIFT) & BAT_OFFSET_MASK
387
+
388
+ entry = VhdxBatEntry.new(block_num, state, file_offset_mb)
389
+ @bat << entry
390
+ end
391
+ end
392
+
393
+ def compute_bat_offset(offset_bits)
394
+ (offset_bits & BAT_OFFSET_MASK) >> BAT_OFFSET_SHIFT
395
+ end
396
+
397
+ def process_metadata_table_header
398
+ @vhdx_file.seek(@metadata_offset, IO::SEEK_SET)
399
+ metadata_table_header = VHDX_METADATA_TABLE_HEADER.decode(@vhdx_file.read(SIZEOF_VHDX_METADATA_TABLE_HEADER))
400
+ signature = metadata_table_header['signature']
401
+ count = metadata_table_header['entry_count']
402
+ raise "Invalid Metadata Header Signature #{signature}" unless signature == VHDX_METADATA_TABLE_HEADER_SIGNATURE
403
+ raise "Invalid Metadata Header Entry Count #{count}" unless count > 0
404
+ (1..count).each do |i|
405
+ process_metadata_table_entry(i)
406
+ end
407
+ raise "Invalid or Missing Metadata Table Entries" unless valid_metadata_table?
408
+ post_process_metadata
409
+ end
410
+
411
+ def post_process_metadata
412
+ @chunk_ratio = (2**23 * @logical_sector_size) / @payload_block_size
413
+ @data_blocks_count = (@virtual_disk_size.to_f / @payload_block_size).ceil
414
+ @sector_bitmap_blocks_count = (@data_blocks_count.to_f / @chunk_ratio).ceil
415
+ @sectors_per_block = @payload_block_size / @logical_sector_size
416
+ @total_bat_entries = if @has_parent
417
+ @sector_bitmap_blocks_count * (@chunk_ratio + 1)
418
+ else
419
+ @data_blocks_count + ((@data_blocks_count - 1) / @chunk_ratio).floor
420
+ end
421
+ end
422
+
423
+ def process_metadata_table_entry(entry_number)
424
+ entry_offset = @metadata_offset + SIZEOF_VHDX_METADATA_TABLE_HEADER +
425
+ (entry_number - 1) * SIZEOF_VHDX_METADATA_TABLE_ENTRY
426
+ @vhdx_file.seek(entry_offset, IO::SEEK_SET)
427
+ metadata_table_entry = VHDX_METADATA_TABLE_ENTRY.decode(@vhdx_file.read(SIZEOF_VHDX_METADATA_TABLE_ENTRY))
428
+ guid = metadata_table_entry['item_id']
429
+ offset = @metadata_offset + metadata_table_entry['offset']
430
+ length = metadata_table_entry['length']
431
+ bits = metadata_table_entry['bit_fields']
432
+ @is_user = bits[0].to_i(2)
433
+ @is_virtual_disk = bits[1].to_i(2)
434
+ @is_required = bits[2].to_i(2)
435
+ if length == 0
436
+ raise "Invalid Metadata Table Entry - Length Zero and Offset is #{offset}" if offset != 0
437
+ $log.debug "Metadata Table Entry Present but Empty"
438
+ return
439
+ end
440
+ @vhdx_file.seek(offset, IO::SEEK_SET)
441
+ guid_found = nil
442
+ METADATA_OPS.each do |meta_guid, op|
443
+ next unless guid_match?(guid, meta_guid)
444
+ method(op).call(offset)
445
+ guid_found = guid
446
+ break
447
+ end
448
+ raise "Invalid Metadata Table Entry GUID #{guid}" unless guid_found
449
+ end
450
+
451
+ def file_parameters(_offset)
452
+ file_parameters_entry = VHDX_FILE_PARAMETERS.decode(@vhdx_file.read(SIZEOF_VHDX_FILE_PARAMETERS))
453
+ @payload_block_size = file_parameters_entry['block_size']
454
+ bits = file_parameters_entry['bit_fields']
455
+ @leave_blocks_allocated = bits[0].to_i(2) == 1
456
+ @has_parent = bits[1].to_i(2) == 1
457
+ $log.debug "Payload Block Size #{@payload_block_size} Leave Blocks Allocated #{@leave_blocks_allocated}"
458
+ $log.debug "Has Parent #{@has_parent}"
459
+ end
460
+
461
+ def virtual_disk_size(_offset)
462
+ virtual_disk_metadata = VHDX_VIRTUAL_DISK_SIZE.decode(@vhdx_file.read(SIZEOF_VHDX_VIRTUAL_DISK_SIZE))
463
+ @virtual_disk_size = virtual_disk_metadata['virtual_disk_size']
464
+ $log.debug "Virtual Disk Size #{@virtual_disk_size}"
465
+ end
466
+
467
+ def page_83_data(_offset)
468
+ page_83_metadata = VHDX_PAGE_83_DATA.decode(@vhdx_file.read(SIZEOF_VHDX_PAGE_83_DATA))
469
+ @page_83_data = page_83_metadata['page_83_data']
470
+ $log.debug "Page 83 Data #{@page_83_data}"
471
+ end
472
+
473
+ def logical_sector_size(_offset)
474
+ logical_sector_metadata = VHDX_LOGICAL_SECTOR_SIZE.decode(@vhdx_file.read(SIZEOF_VHDX_LOGICAL_SECTOR_SIZE))
475
+ @logical_sector_size = logical_sector_metadata['logical_sector_size']
476
+ @blockSize = @logical_sector_size
477
+ $log.debug "Logical Sector Size (blockSize) #{@logical_sector_size}"
478
+ end
479
+
480
+ def physical_sector_size(_offset)
481
+ physical_sector_metadata = VHDX_PHYSICAL_SECTOR_SIZE.decode(@vhdx_file.read(SIZEOF_VHDX_PHYSICAL_SECTOR_SIZE))
482
+ @physical_sector_size = physical_sector_metadata['physical_sector_size']
483
+ $log.debug "Physical Sector Size #{@physical_sector_size}"
484
+ end
485
+
486
+ def parent_locator_header(offset)
487
+ raise "Inconsistent filesystem - \'Has_Parent\' Flag unset but Parent Locator Info Present" if @has_parent == false
488
+ @parent_locator = VHDX_PARENT_LOCATOR_HEADER.decode(@vhdx_file.read(SIZEOF_VHDX_PARENT_LOCATOR_HEADER))
489
+ @parent_locator_entries = {}
490
+ key_value_count = @parent_locator['key_value_count']
491
+ guid = @parent_locator['locator_type']
492
+ raise "Invalid Parent Locator Type Guid #{guid}" unless guid_match?(guid, VHDX_PARENT_LOCATOR_TYPE_GUID)
493
+ return if key_value_count == 0
494
+ (1..key_value_count).each do |i|
495
+ @vhdx_file.seek(offset + SIZEOF_VHDX_PARENT_LOCATOR_HEADER +
496
+ (i - 1) * SIZEOF_VHDX_PARENT_LOCATOR_ENTRY, IO::SEEK_SET)
497
+ process_parent_locator_entry(offset)
498
+ end
499
+ keys = @parent_locator_entries.keys
500
+ $log.warn "Missing \"parent_linkage\" Parent Locator Entry" if keys.index("parent_linkage").nil?
501
+ parent_locators_to_path(keys)
502
+ end
503
+
504
+ def parent_locators_to_path(keys)
505
+ if keys.index("absolute_win32_path")
506
+ parent_path = strip_path_prefix(@parent_locator_entries["absolute_win32_path"])
507
+ elsif keys.index("relative_path")
508
+ parent_path = File.dirname(@file_name) + '/' + @parent_locator_entries["relative_path"]
509
+ elsif keys.index("volume_path")
510
+ # TODO: Test Volume Path Parent Locator
511
+ parent_path = strip_path_prefix(@parent_locator_entries["volume_path"])
512
+ else
513
+ raise "Missing Parent Locator entries \"relative_path\", \"volume_path\", and \"absolute_win32_path\""
514
+ end
515
+ @parent = parent_disk(parent_path)
516
+ end
517
+
518
+ def parent_disk(path)
519
+ @parent_ostruct = OpenStruct.new
520
+ @parent_ostruct.fileName = path
521
+ @parent_ostruct.driveType = dInfo.driveType
522
+ @parent_ostruct.hyperv_connection = @hyperv_connection if @hyperv_connection
523
+ parent = MiqDisk.getDisk(@parent_ostruct)
524
+ raise "Unable to access parent disk #{path}" if parent.nil?
525
+ $log.debug "Got #{path} as Parent disk of #{@file_name}"
526
+ parent
527
+ end
528
+
529
+ def strip_path_prefix(path)
530
+ path[0, 4] == "\\\\?\\" ? path[4..-1] : path
531
+ end
532
+
533
+ def process_parent_locator_entry(offset)
534
+ entry = VHDX_PARENT_LOCATOR_ENTRY.decode(@vhdx_file.read(SIZEOF_VHDX_PARENT_LOCATOR_ENTRY))
535
+ @vhdx_file.seek(offset + entry['key_offset'], IO::SEEK_SET)
536
+ key = @converter.convert(@vhdx_file.read(entry['key_length'])).gsub(/\"/, '')
537
+ @vhdx_file.seek(offset + entry['value_offset'], IO::SEEK_SET)
538
+ value = @converter.convert(@vhdx_file.read(entry['value_length'])).gsub(/\"/, '')
539
+ @parent_locator_entries[key] = value
540
+ end
541
+
542
+ def valid_metadata_table?
543
+ if @virtual_disk_size > 0 && @payload_block_size > 0 && @logical_sector_size > 0 && @physical_sector_size > 0
544
+ return true
545
+ end
546
+ $log.warn "Disk Size #{@virtual_disk_size}"
547
+ $log.warn "Block Size #{@blockSize}"
548
+ $log.warn "Logical Sector Size #{@logical_sector_size}"
549
+ $log.warn "Physical Sector Size #{physical_sector_size}"
550
+ false
551
+ end
552
+
553
+ def guid_match?(input, guid)
554
+ new_guid = ""
555
+ input.unpack("I<S<S<C8").each do |x|
556
+ if x < 16
557
+ new_guid += "0#{x.to_s(16)}"
558
+ else
559
+ new_guid += x.to_s(16)
560
+ end
561
+ end
562
+ new_guid == guid.to_s(16)
563
+ end
564
+
565
+ def block_pos(pos)
566
+ raw_sector, byte_offset = pos.divmod(@blockSize)
567
+ block_number, sector_in_block = raw_sector.divmod(@sectors_per_block)
568
+ return block_number, sector_in_block, byte_offset
569
+ end
570
+
571
+ def get_allocation_status(block_number)
572
+ allocation_status = bat_status(block_number)
573
+ if allocation_status == PAYLOAD_BLOCK_PARTIALLY_PRESENT
574
+ raise "Invalid status PAYLOAD_BLOCK_PARTIALLY_PRESENT for BAT block number #{block_number}" unless @has_parent
575
+ end
576
+ ALLOCATION_STATUS[allocation_status]
577
+ end
578
+
579
+ def get_sector_allocation_status(block_number, sector_number)
580
+ sector_bitmap_status = bitmap_status(block_number)
581
+ raise "Invalid Sector Bitmap Status #{sector_bitmap_status}" unless sector_bitmap_status == SB_BLOCK_PRESENT
582
+ sector_bitmap_offset = bitmap_offset(block_number)
583
+ sector_byte, bit_offset = sector_number.divmod(8)
584
+ @vhdx_file.seek(sector_bitmap_offset + sector_byte, IO::SEEK_SET)
585
+ sector_mask = 0x80 >> bit_offset
586
+ sector_bitmap = @vhdx_file.read(1).unpack("C")[0]
587
+ sector_bitmap & sector_mask == sector_mask
588
+ end
589
+
590
+ def bat_status(block_number)
591
+ @bat[bat_entry_number(block_number)].state
592
+ end
593
+
594
+ def bat_offset(block_number)
595
+ @bat[bat_entry_number(block_number)].offset * BAT_OFFSET_UNITS
596
+ end
597
+
598
+ def bat_entry_number(block_number)
599
+ block_number + block_number / @chunk_ratio
600
+ end
601
+
602
+ def bitmap_status(block_number)
603
+ @bat[bitmap_entry_number(block_number)].state
604
+ end
605
+
606
+ def bitmap_offset(block_number)
607
+ @bat[bitmap_entry_number(block_number)].offset * BAT_OFFSET_UNITS
608
+ end
609
+
610
+ def bitmap_entry_number(block_number)
611
+ (block_number / @chunk_ratio) * (@chunk_ratio + 1) + @chunk_ratio
612
+ end
613
+
614
+ def connect_to_hyperv(disk_info)
615
+ connection = @hyperv_connection = disk_info.hyperv_connection
616
+ @network = disk_info.driveType == "Network"
617
+ hyperv_disk = MiqHyperVDisk.new(connection[:host],
618
+ connection[:user],
619
+ connection[:password],
620
+ connection[:port],
621
+ @network)
622
+ hyperv_disk.open(@file_name)
623
+ hyperv_disk
624
+ end
625
+ end
@@ -0,0 +1,46 @@
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