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,53 @@
1
+ require 'binary_struct'
2
+
3
+ module NTFS
4
+ #
5
+ # VOLUME_INFORMATION - Attribute: Volume information (0x70).
6
+ #
7
+ # NOTE: Always resident.
8
+ # NOTE: Present only in FILE_Volume.
9
+ # NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses NTFS 1.2.
10
+ #
11
+
12
+ ATTRIB_VOLUME_INFORMATION = BinaryStruct.new([
13
+ 'Q', nil, # No information.
14
+ 'C1', 'ver_major', # File system major version number.
15
+ 'C1', 'ver_minor', # File system minor version number.
16
+ 'S', 'flags', # Volume flags (see VF_ below).
17
+ ])
18
+
19
+ class VolumeInformation
20
+ attr_reader :version, :flags
21
+
22
+ VF_IS_DIRTY = 0x0001
23
+ VF_RESIZE_LOG_FILE = 0x0002
24
+ VF_UPGRADE_ON_MOUNT = 0x0004
25
+ VF_MOUNTED_ON_NT4 = 0x0008
26
+ VF_DELETE_USN_UNDERWAY = 0x0010
27
+ VF_REPAIR_OBJECT_ID = 0x0020
28
+ VF_CHKDSK_UNDERWAY = 0x4000
29
+ VF_MODIFIED_BY_CHKDSK = 0x8000
30
+
31
+ def initialize(buf)
32
+ raise "MIQ(NTFS::VolumeInformation.initialize) Nil buffer" if buf.nil?
33
+ buf = buf.read(buf.length) if buf.kind_of?(DataRun)
34
+ @avi = ATTRIB_VOLUME_INFORMATION.decode(buf)
35
+
36
+ # Get accessor data.
37
+ @version = @avi['ver_major'].to_s + "." + @avi['ver_minor'].to_s
38
+ @flags = @avi['flags']
39
+ end
40
+
41
+ def to_s
42
+ @version
43
+ end
44
+
45
+ def dump
46
+ out = "\#<#{self.class}:0x#{'%08x' % object_id}>\n"
47
+ out << " Major ver: #{@avi['ver_major'].to_i}\n"
48
+ out << " Minor ver: #{@avi['ver_minor'].to_i}\n"
49
+ out << " Flags : 0x#{'%04x' % @flags}\n"
50
+ out << "---\n"
51
+ end
52
+ end
53
+ end # module NTFS
@@ -0,0 +1,31 @@
1
+ require 'util/miq-unicode'
2
+
3
+ module NTFS
4
+ #
5
+ # VOLUME_NAME - Attribute: Volume name (0x60).
6
+ #
7
+ # NOTE: Always resident.
8
+ # NOTE: Present only in FILE_Volume.
9
+ #
10
+ # Data of this class is not structured.
11
+ #
12
+
13
+ class VolumeName
14
+ attr_reader :name
15
+
16
+ def initialize(buf)
17
+ buf = buf.read(buf.length) if buf.kind_of?(DataRun)
18
+ @name = buf.UnicodeToUtf8
19
+ end
20
+
21
+ def to_s
22
+ @name
23
+ end
24
+
25
+ def dump
26
+ out = "\#<#{self.class}:0x#{'%08x' % object_id}>\n "
27
+ out << @name
28
+ out << "---\n"
29
+ end
30
+ end
31
+ end # module NTFS
@@ -0,0 +1,253 @@
1
+ # encoding: US-ASCII
2
+
3
+ require 'binary_struct'
4
+ require 'fs/ntfs/mft_entry'
5
+ require 'rufus/lru'
6
+
7
+ #######################################################################################
8
+ # A good source of disk-layout information is in open-source ntfs-3g/include/ntfs-3g/layout.h
9
+ # A good source of understanding how to use this data is in Brian Carrier's File System Forensic Analysis
10
+ #######################################################################################
11
+
12
+ module NTFS
13
+ # The boot parameters block, sector 0 byte 0 of a bootable volume.
14
+ BOOT_PARAMETERS_BLOCK = BinaryStruct.new([
15
+ 'a3', 'jmp_boot_loader', # Jump to boot loader
16
+ 'a8', 'oem_name', # OEM Name (should be 'NTFS ')
17
+
18
+ # BIOS Parameter Block
19
+ 'S', 'bytes_per_sector', # Bytes per sector. The size of a hardware sector. For most disks used in the United States, the value of this field is 512.
20
+ 'C1', 'sectors_per_cluster', # Sectors per cluster
21
+ 'S', 'reserved_sectors', # Reserved sectors. Always 0 because NTFS places the boot sector at the beginning of the partition. If the value is not 0, NTFS fails to mount the volume.
22
+ 'C1', 'fats', # Value must be 0 or NTFS fails to mount the volume
23
+ 'S', 'root_entries', # Value must be 0 or NTFS fails to mount the volume
24
+ 'S', 'sectors', # Value must be 0 or NTFS fails to mount the volume
25
+ 'C1', 'media_descriptor', # Provides information about the media being used. A value of 0xF8 indicates a hard disk and 0xF0 indicates a high-density 3.5-inch floppy disk. Media descriptor entries are a legacy of MS-DOS FAT16 disks and are not used in Windows Server 2003.
26
+ 'S', 'sectors_per_fat', # Value must be 0 or NTFS fails to mount the volume
27
+ 'S', 'sectors_per_track', # Required to boot Windows
28
+ 'S', 'number_of_heads', # Required to boot Windows
29
+ 'L', 'hidden_sectors', # Offset to the start of the partition relative to the disk in sectors. Required to boot Windows
30
+ 'L', 'large_sectors', # Must be 0
31
+
32
+ # Extended BIOS Parameter Block
33
+ 'C1', 'physical_drive', # 0x00 floppy, 0x80 hard disk
34
+ 'C1', 'current_head', # Must be 0
35
+ 'C1', 'extended_boot_signature', # 0x80
36
+ 'C1', 'reserved2',
37
+ 'Q', 'sectors_per_volume', # Number of sectors in volume. Gives maximum volume size of 2^63 sectors. Assuming standard sector size of 512 bytes, the maximum byte size is approx. 4.7x10^21 bytes.
38
+ 'Q', 'mft_lcn', # Logical Cluster Number for the File $MFT. Identifies the location of the MFT by using its logical cluster number.
39
+ 'Q', 'mftmirr_lcn', # Logical Cluster Number for the File $MFTMirr. Identifies the location of the mirrored copy of the MFT by using its logical cluster number.
40
+ 'c1', 'clusters_per_mft_record', # Mft record size in clusters. NTFS creates a file record for each file and a folder record for each folder that is created on an NTFS volume. Files and folders smaller than this size are contained within the MFT. If this number is positive (up to 7F), then it represents clusters per MFT record. If the number is negative (80 to FF), then the size of the file record is 2 raised to the absolute value of this number.
41
+ 'a3', 'reserved0',
42
+ 'c1', 'clusters_per_index_record', # Index block size in clusters. The size of each index buffer, which is used to allocate space for directories. If this number is positive (up to 7F), then it represents clusters per MFT record. If the number is negative (80 to FF), then the size of the file record is 2 raised to the absolute value of this number.
43
+ 'a3', 'reserved1',
44
+ 'Q', 'volume_serial_number', # Seems like only the low 32-bits are used
45
+ 'L', 'checksum', # Boot sector checksum
46
+
47
+ 'a426', 'boot_code', # Boot loader
48
+ 'S', 'signature', # Sanity check: always 0xaa55
49
+ ])
50
+ SIZEOF_BOOT_PARAMETERS_BLOCK = BOOT_PARAMETERS_BLOCK.size
51
+
52
+ NTFS_MAGIC = 0xaa55
53
+
54
+ # BootSect represents a volume boot sector.
55
+ class BootSect
56
+ attr_reader :stream, :bytesPerSector, :sectorsPerCluster, :mediaDescriptor
57
+ attr_reader :totalCapacity, :bytesPerFileRec, :bytesPerIndexRec, :serialNumber
58
+ attr_reader :signature, :bytesPerCluster
59
+
60
+ attr_accessor :version, :volumeInfo
61
+
62
+ def initialize(stream)
63
+ raise "MIQ(NTFS::BootSect.initialize) Nil stream" if stream.nil?
64
+
65
+ # Buffer stream & get enough data to fill BPB.
66
+ @stream = stream
67
+ buf = stream.read(SIZEOF_BOOT_PARAMETERS_BLOCK)
68
+ @bpb = BOOT_PARAMETERS_BLOCK.decode(buf)
69
+
70
+ # Always check magic number first.
71
+ @signature = @bpb['signature']
72
+ raise "MIQ(NTFS::BootSect.initialize) Boot sector is not NTFS: 0x#{'%04x' % signature}" if signature != NTFS_MAGIC
73
+
74
+ # Get accessor values.
75
+ @bytesPerSector = @bpb['bytes_per_sector']
76
+ @bytesPerCluster = @bpb['sectors_per_cluster'] * @bytesPerSector
77
+ @sectorsPerCluster = @bpb['sectors_per_cluster']
78
+ @mediaDescriptor = @bpb['media_descriptor']
79
+ @totalCapacity = @bpb['sectors_per_volume'] * @bytesPerSector
80
+ @bytesPerFileRec = bytesPerRec(@bpb['clusters_per_mft_record'])
81
+ @bytesPerIndexRec = bytesPerRec(@bpb['clusters_per_index_record'])
82
+ @serialNumber = @bpb['volume_serial_number']
83
+
84
+ # MFTs in-memory
85
+ @sys_mfts = {}
86
+ @mfts = LruHash.new(NTFS::DEF_CACHE_SIZE)
87
+ end
88
+
89
+ # Convert to string (just return OEM name).
90
+ def to_s
91
+ @bpb['oem_name'].strip
92
+ end
93
+
94
+ # NTFS has an interesting shorthand...
95
+ def bytesPerRec(size)
96
+ (size < 0) ? 2**size.abs : size * bytesPerCluster
97
+ end
98
+
99
+ # Return the absolute byte position of the MFT.
100
+ def mftLoc
101
+ @bpb.nil? ? 0 : lcn2abs(@bpb['mft_lcn'])
102
+ end
103
+
104
+ def fragTable
105
+ @fragTable || @rootFragTable
106
+ end
107
+
108
+ def maxMft
109
+ return getMaxMft if @fragTable.nil?
110
+ @maxMft ||= getMaxMft
111
+ end
112
+
113
+ def setup
114
+ @rootFragTable = mftEntry(0).rootAttributeData.data.runSpec
115
+
116
+ @sys_mfts.clear
117
+ @mfts.clear
118
+
119
+ # MFT Entry 0 ==> Prepare a fragment table.
120
+ @fragTable = mftEntry(0).attributeData.data.runSpec # Get the data runs for the MFT itself.
121
+
122
+ # MFT Entry 3 ==> Volume Information
123
+ @volumeInfo = getVolumeInfo
124
+ @version = @volumeInfo["version"].to_i
125
+ end
126
+
127
+ ################################################################################
128
+ # From "File System Forensic Analysis" by Brian Carrier
129
+ #
130
+ # The $Bitmap file, which is located in MFT entry 6, has a $DATA attribute that is used
131
+ # to manage the allocation status of clusters. The bitmap data are organized into 1-byte
132
+ # values, and the least significant bit of each byte corresponds to the cluster that follows
133
+ # the cluster that the most significant bit of the previous byte corresponds to.
134
+ ################################################################################
135
+ def clusterInfo
136
+ return @clusterInfo unless @clusterInfo.nil?
137
+
138
+ # MFT Entry 6 ==> BITMAP Information
139
+ ad = mftEntry(6).attributeData
140
+ data = ad.read(ad.length)
141
+ ad.rewind
142
+
143
+ c = data.unpack("b#{data.length * 8}")[0]
144
+ nclusters = c.length
145
+ on = c.count("1")
146
+ uclusters = on
147
+ fclusters = c.length - on
148
+
149
+ @clusterInfo = {"total" => nclusters, "free" => fclusters, "used" => uclusters}
150
+ end
151
+
152
+ # Returns free space on file system in bytes.
153
+ def freeBytes
154
+ clusterInfo["free"] * @bytesPerCluster
155
+ end
156
+
157
+ def getVolumeInfo
158
+ mft = mftEntry(3)
159
+ vi = {}
160
+
161
+ if nameAttrib = mft.getFirstAttribute(AT_VOLUME_NAME)
162
+ vi["name"] = nameAttrib.name
163
+ end
164
+
165
+ if objectidAttrib = mft.getFirstAttribute(AT_OBJECT_ID)
166
+ vi["objectId"] = objectidAttrib.objectId.to_s
167
+ vi["birthVolumeId"] = objectidAttrib.birthVolumeId.to_s
168
+ vi["birthObjectId"] = objectidAttrib.birthObjectId.to_s
169
+ vi["domainId"] = objectidAttrib.domainId.to_s
170
+ end
171
+
172
+ if infoAttrib = mft.getFirstAttribute(AT_VOLUME_INFORMATION)
173
+ vi["version"] = infoAttrib.version
174
+ vi["flags"] = infoAttrib.flags
175
+ end
176
+
177
+ vi
178
+ end
179
+
180
+ def numFrags
181
+ fragTable.size / 2
182
+ end
183
+
184
+ # Iterate all run lengths & return how many entries fit.
185
+ def getMaxMft
186
+ total_clusters = 0
187
+ fragTable.each_slice(2) { |_vcn, len| total_clusters += len }
188
+ total_clusters * @bytesPerCluster / @bytesPerFileRec
189
+ end
190
+
191
+ def rootDir
192
+ @rootDir ||= mftEntry(5).indexRoot
193
+ end
194
+
195
+ def mftEntry(recordNumber)
196
+ if recordNumber < 12
197
+ @sys_mfts[recordNumber] = MftEntry.new(self, recordNumber) unless @sys_mfts.key?(recordNumber)
198
+ return @sys_mfts[recordNumber]
199
+ end
200
+
201
+ if @mfts.key?(recordNumber)
202
+ mft = @mfts[recordNumber]
203
+ mft.attributeData.rewind unless mft.attributeData.nil?
204
+ return mft
205
+ end
206
+ @mfts[recordNumber] = MftEntry.new(self, recordNumber)
207
+ end
208
+
209
+ # Quick check to see if volume is mountable.
210
+ def isMountable?
211
+ return false if @bpb.nil?
212
+ b = @bpb['reserved_sectors'] == 0
213
+ b &= @bpb['unused1'] == "\0" * 5
214
+ b &= @bpb['unused2'] == 0
215
+ b &= @bpb['unused4'] == 0
216
+ b &= @bpb['signature'] == 0xaa55
217
+ end
218
+
219
+ # Convert a logical cluster number to an absolute byte position.
220
+ def lcn2abs(lcn)
221
+ lcn * bytesPerCluster
222
+ end
223
+
224
+ # Convert a virtual cluster number to an absolute byte position.
225
+ def vcn2abs(vcn)
226
+ lcn2abs(vcn)
227
+ end
228
+
229
+ # Use data run to convert mft record number to byte pos.
230
+ def mftRecToBytePos(recno)
231
+ # Return start of mft if rec 0 (no point in the rest of this).
232
+ return mftLoc if recno == 0
233
+
234
+ # Find which fragment contains the target mft record.
235
+ start = fragTable[0]; last_clusters = 0; target_cluster = recno * @bytesPerFileRec / @bytesPerCluster
236
+ if (recno > @bytesPerCluster / @bytesPerFileRec) && (fragTable.size > 2)
237
+ total_clusters = 0
238
+ fragTable.each_slice(2) do |vcn, len|
239
+ start = vcn # These are now absolute clusters, not offsets.
240
+ total_clusters += len
241
+ break if total_clusters > target_cluster
242
+ last_clusters += len
243
+ end
244
+ # Toss if we haven't found the fragment.
245
+ raise "MIQ(NTFS::BootSect.mftRecToBytePos) Can't find MFT record #{recno} in data run.\ntarget = #{target_cluster}\ntbl = #{fragTable.inspect}" if total_clusters < target_cluster
246
+ end
247
+
248
+ # Calculate offset in target cluster & final byte position.
249
+ offset = (recno - (last_clusters * @bytesPerCluster / @bytesPerFileRec)) * @bytesPerFileRec
250
+ start * @bytesPerCluster + offset
251
+ end
252
+ end
253
+ end # module NTFS
@@ -0,0 +1,358 @@
1
+ require 'memory_buffer'
2
+
3
+ module NTFS
4
+ class DataRun
5
+ DEBUG_TRACE_READS = false
6
+
7
+ OffsetCode = {
8
+ 1 => [0xffffff00, 'L', 'l'],
9
+ 2 => [0xffff0000, 'L', 'l'],
10
+ 3 => [0xff000000, 'L', 'l'],
11
+ 4 => [0x00000000, 'L', 'l'],
12
+ 5 => [0xffffff0000000000, 'Q', 'q'],
13
+ 6 => [0xffff000000000000, 'Q', 'q'],
14
+ 7 => [0xff00000000000000, 'Q', 'q'],
15
+ 8 => [0x0000000000000000, 'Q', 'q']
16
+ }
17
+
18
+ attr_reader :runSpec, :boot_sector, :length, :pos
19
+
20
+ def initialize(bs, buf, header)
21
+ raise "MIQ(NTFS::DataRun.initialize) Nil boot sector" if bs.nil?
22
+ raise "MIQ(NTFS::DataRun.initialize) Nil buffer" if buf.nil?
23
+
24
+ # Buffer boot sector & start spec array.
25
+ @boot_sector = bs
26
+ @bytesPerCluster = bs.bytesPerCluster
27
+ @header = header
28
+ @runSpec = []
29
+ rewind
30
+
31
+ # Read bytes until 0.
32
+ # puts "specification is:"
33
+ # buf.hex_dump(:obj => STDOUT, :meth => :puts, :newline => false)
34
+
35
+ last_lcn = 0
36
+ spec_pos = 0
37
+ total_clusters = 0
38
+ while buf[spec_pos, 1].ord != 0
39
+ # print "spec 0x#{'%02x' % buf[spec_pos]}\n"
40
+
41
+ # Size of offset is hi nibble, size of length is lo nibble.
42
+ size_of_offset, size_of_length = buf[spec_pos, 1].ord.divmod(16)
43
+ spec_pos += 1
44
+ # puts "size_of_length 0x#{'%08x' % size_of_length}"
45
+ # puts "size_of_offset 0x#{'%08x' % size_of_offset}"
46
+
47
+ # Get length of run (number of clusters).
48
+ run_length = suckBytes(buf[spec_pos, size_of_length])
49
+ spec_pos += size_of_length
50
+ # puts "length 0x#{'%08x' % run_length}"
51
+
52
+ # Get offset (offset from previous cluster).
53
+ run_offset = suckBytes(buf[spec_pos, size_of_offset])
54
+ spec_pos += size_of_offset
55
+ # puts "offset 0x#{'%08x' % run_offset}"
56
+
57
+ # Offset is signed (only check if size gt 0. 0 size means 0 offset means sparse run).
58
+ if size_of_offset > 0
59
+ hi_bit = 2**(8 * size_of_offset - 1) # 1 << (8 * size_of_offset - 1)
60
+ # If this goofy number is negative, make it negative.
61
+ if run_offset & hi_bit == hi_bit
62
+ run_offset |= OffsetCode[size_of_offset][0]
63
+ run_offset = [run_offset].pack(OffsetCode[size_of_offset][1]).unpack(OffsetCode[size_of_offset][2])[0]
64
+ end
65
+ end
66
+ # puts "offset 0x#{'%08x' % run_offset}"
67
+
68
+ # Not Sparse
69
+ if size_of_offset > 0
70
+ lcn = run_offset + last_lcn
71
+ last_lcn = lcn
72
+ else
73
+ lcn = nil
74
+ end
75
+
76
+ # Store run spec.
77
+ total_clusters += run_length
78
+ @runSpec << lcn
79
+ @runSpec << run_length
80
+ end
81
+ @length = header.specific['data_size']
82
+ @length = total_clusters * @bytesPerCluster if @length == 0
83
+
84
+ # Cache the clusters we've already read.
85
+ @clusters = {}
86
+ end
87
+
88
+ def to_s
89
+ # @current_run
90
+ end
91
+
92
+ def addRun(r)
93
+ @runSpec += r.runSpec
94
+ @length += r.length
95
+ end
96
+
97
+ def [](offset, len)
98
+ seek(offset)
99
+ read(len)
100
+ end
101
+
102
+ def rewind
103
+ @pos = 0
104
+ end
105
+
106
+ def seek(offset, method = IO::SEEK_SET)
107
+ @pos = case method
108
+ when IO::SEEK_SET then offset
109
+ when IO::SEEK_CUR then @pos + offset
110
+ when IO::SEEK_END then @length - offset
111
+ end
112
+ @pos = 0 if @pos < 0
113
+ @pos = @length if @pos > @length
114
+ @pos
115
+ end
116
+
117
+ def seekToVcn(vcn)
118
+ seek(vcn * @bytesPerCluster)
119
+ end
120
+
121
+ def read(bytes = @length)
122
+ return nil if @pos >= @length
123
+ $log.info "#{self.class} #{object_id}: Reading #{bytes} bytes @ #{@pos}" if DEBUG_TRACE_READS
124
+
125
+ startCluster, startOffset = @pos.divmod(@bytesPerCluster)
126
+ endCluster, endOffset = (@pos + (bytes - 1)).divmod(@bytesPerCluster)
127
+
128
+ ret = getClusters(startCluster, endCluster)
129
+ ret = ret[startOffset..endOffset - @bytesPerCluster]
130
+ @pos += ret.length
131
+
132
+ ret
133
+ end
134
+
135
+ def getClusters(start_vcn, end_vcn = nil)
136
+ end_vcn = start_vcn if end_vcn.nil?
137
+
138
+ # Single cluster
139
+ if start_vcn == end_vcn && @clusters.key?(start_vcn)
140
+ $log.info "#{self.class} #{object_id}: Reading clusters [#{start_vcn}, 1, true]" if DEBUG_TRACE_READS
141
+ return readCachedClusters(start_vcn, 1)[0]
142
+ end
143
+
144
+ # Multiple clusters (part of which may be cached)
145
+ num = end_vcn - start_vcn + 1
146
+ ret = MemoryBuffer.create(num * @bytesPerCluster)
147
+ offset = 0
148
+
149
+ to_read = findCachedClusters(start_vcn, end_vcn)
150
+ $log.info "#{self.class} #{object_id}: Reading clusters #{to_read.inspect}" if DEBUG_TRACE_READS
151
+ to_read.each_slice(3) do |vcn, len, cached|
152
+ clusters = cached ? readCachedClusters(vcn, len) : readRawClusters(vcn, len)
153
+ clusters.each do |c|
154
+ len = c.length
155
+ ret[offset, len] = c
156
+ offset += len
157
+ end
158
+ end
159
+
160
+ addClusterCache(start_vcn, ret)
161
+
162
+ ret
163
+ end
164
+
165
+ def findCachedClusters(start_vcn, end_vcn)
166
+ to_read = []
167
+ cur = run = last_cached = nil
168
+
169
+ start_vcn.upto(end_vcn) do |vcn|
170
+ check_last_cached = @clusters.key?(vcn)
171
+ if last_cached == check_last_cached
172
+ run += 1
173
+ else
174
+ to_read << cur << run << last_cached unless last_cached.nil?
175
+ last_cached = check_last_cached
176
+ cur = vcn
177
+ run = 1
178
+ end
179
+ end
180
+ to_read << cur << run << last_cached
181
+
182
+ to_read
183
+ end
184
+
185
+ def readCachedClusters(vcn, num)
186
+ ret = []
187
+
188
+ while num > 0
189
+ data, start_vcn, end_vcn, data_len, offset = getCacheInfo(vcn)
190
+
191
+ len = data_len - offset
192
+ len = num if num < len
193
+
194
+ ret << data[offset * @bytesPerCluster, len * @bytesPerCluster]
195
+
196
+ num -= len
197
+ vcn += len
198
+ end
199
+
200
+ ret
201
+ end
202
+
203
+ def addClusterCache(start_vcn, data)
204
+ end_vcn = start_vcn + (data.length / @bytesPerCluster) - 1
205
+
206
+ has_start = @clusters.key?(start_vcn)
207
+ start_data, start_data_vcn, = getCacheInfo(start_vcn) if has_start
208
+
209
+ has_end = @clusters.key?(end_vcn)
210
+ end_data, end_data_vcn, end_data_end_vcn, end_data_len, end_offset = getCacheInfo(end_vcn) if has_end
211
+
212
+ # Determine if we are adding an existing item or sub-item back into the cache
213
+ return if has_start && has_end && start_data_vcn == end_data_vcn
214
+
215
+ # Determine if we are overlapping an existing cached item at the start
216
+ if has_start && start_data_vcn != start_vcn
217
+ leftover_len = start_vcn - start_data_vcn
218
+ leftover = start_data[0, leftover_len * @bytesPerCluster]
219
+
220
+ # Recache only the leftover portion
221
+ @clusters[start_data_vcn] = leftover
222
+ end
223
+
224
+ # Determine if we are overlapping an existing cached item at the end
225
+ if has_end && end_data_end_vcn != end_vcn
226
+ leftover_start_vcn = end_vcn + 1
227
+ leftover = end_data[(end_offset + 1) * @bytesPerCluster..-1]
228
+
229
+ # Recache only the leftover portion
230
+ @clusters[leftover_start_vcn] = leftover
231
+ (leftover_start_vcn + 1..end_data_end_vcn).each { |i| @clusters[i] = leftover_start_vcn }
232
+ end
233
+
234
+ # Cache the data
235
+ @clusters[start_vcn] = data
236
+ (start_vcn + 1..end_vcn).each { |i| @clusters[i] = start_vcn }
237
+ end
238
+
239
+ def getCacheInfo(vcn)
240
+ data = @clusters[vcn]
241
+ offset = 0
242
+ start_vcn = vcn
243
+ if data.kind_of?(Integer)
244
+ start_vcn = data
245
+ offset = vcn - start_vcn
246
+ data = @clusters[data]
247
+ end
248
+
249
+ len = data.length / @bytesPerCluster
250
+ end_vcn = start_vcn + len - 1
251
+
252
+ return data, start_vcn, end_vcn, len, offset
253
+ end
254
+
255
+ def readRawClusters(vcn, num)
256
+ ret = []
257
+ offset = 0
258
+
259
+ lcns = getLCNs(vcn, num)
260
+ lcns.each_slice(2) do |lcn, len|
261
+ len *= @bytesPerCluster
262
+
263
+ clusters = unless lcn.nil?
264
+ @boot_sector.stream.seek(@boot_sector.lcn2abs(lcn))
265
+ @boot_sector.stream.read(len)
266
+ else
267
+ MemoryBuffer.create(len)
268
+ end
269
+
270
+ ret << clusters
271
+ offset += len
272
+ end
273
+
274
+ ret
275
+ end
276
+
277
+ def getLCNs(start_vcn, num)
278
+ lcns = []
279
+ end_vcn = start_vcn + num - 1
280
+ vcn = start_vcn
281
+ total_clusters = 0
282
+
283
+ @runSpec.each_slice(2) do |lcn, len|
284
+ total_clusters += len
285
+ next unless total_clusters > start_vcn
286
+
287
+ start = lcn + (vcn - (total_clusters - len))
288
+ count = len - (start - lcn)
289
+ count = count >= num ? num : count
290
+ lcns << start << count
291
+
292
+ vcn += count
293
+ num -= count
294
+ break if num <= 0
295
+ end
296
+
297
+ lcns << nil << end_vcn - vcn + 1 if vcn <= end_vcn
298
+
299
+ lcns
300
+ end
301
+
302
+ def suckBytes(buf)
303
+ return buf[0, 1].ord if buf.size == 1
304
+ val = 0
305
+ (buf.size - 1).downto(0) { |i| val *= 256; val += buf[i, 1].ord }
306
+ val
307
+ end
308
+
309
+ # Return true if a particular compression unit is compressed.
310
+ def isUnitCompr?(unit = 0)
311
+ return false unless header.isCompressd?
312
+ mkComprUnits if @compr_units.nil?
313
+ @compr_units[unit].isCompressed?
314
+ end
315
+
316
+ # Organize run list into compression units.
317
+ def mkComprUnits
318
+ end
319
+
320
+ def dump
321
+ out = "\#<#{self.class}:0x#{'%08x' % object_id}>\n"
322
+ out << " Length : #{@length}\n"
323
+ out << " Position : #{@pos}\n"
324
+ out << " Run List : #{@runSpec.inspect}\n"
325
+ out << " Cache Ranges : #{dumpCacheRanges}\n"
326
+ out << "---\n"
327
+ out
328
+ end
329
+
330
+ def dumpRunList
331
+ @runSpec.each_slice(2) { |lcn, len| puts "lcn = #{lcn.nil? ? 0 : lcn}, len = #{len}" }
332
+ end
333
+
334
+ def dumpCacheRanges
335
+ str = k_start = k_prev = nil
336
+ invalid = true
337
+ out = []
338
+ @clusters.keys.sort.each do |k|
339
+ if @clusters[k].kind_of?(String)
340
+ out << "#{invalid ? "*(#{str}) " : ''}#{k_start}..#{k_prev}" unless k_start.nil?
341
+ str = k
342
+ k_start = k
343
+ invalid = false
344
+ elsif @clusters[k] != str
345
+ out << "#{invalid ? "*(#{str}) " : ''}#{k_start}..#{k_prev}" unless k_start.nil?
346
+ str = @clusters[k]
347
+ k_start = k
348
+ invalid = true
349
+ elsif k != k_prev + 1
350
+ out << "#{k}^"
351
+ end
352
+ k_prev = k
353
+ end
354
+ out << "#{invalid ? "*(#{str}) " : ''}#{k_start}..#{k_prev}" unless k_start.nil?
355
+ out.inspect
356
+ end
357
+ end # class
358
+ end # module NTFS