manageiq-smartstate 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +4 -0
- data/.rspec_ci +4 -0
- data/.travis.yml +15 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +202 -0
- data/README.md +45 -0
- data/Rakefile +23 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/MiqContainerGroup/MiqContainerGroup.rb +31 -0
- data/lib/MiqVm/MiqLocalVm.rb +50 -0
- data/lib/MiqVm/MiqRhevmVm.rb +179 -0
- data/lib/MiqVm/MiqVm.rb +355 -0
- data/lib/MiqVm/miq_azure_vm.rb +96 -0
- data/lib/MiqVm/miq_scvmm_vm.rb +38 -0
- data/lib/MiqVm/test/camcorder_fleece_test.rb +60 -0
- data/lib/MiqVm/test/localVm.rb +45 -0
- data/lib/MiqVm/test/partitionAlignmentCheck.rb +76 -0
- data/lib/MiqVm/test/remoteVm.rb +65 -0
- data/lib/MiqVm/test/rhevmNfsTest.rb +62 -0
- data/lib/MiqVm/test/rhevmNfsTest2.rb +66 -0
- data/lib/MiqVm/test/rhevmTest.rb +70 -0
- data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackCommon.rb +107 -0
- data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackImage.rb +67 -0
- data/lib/OpenStackExtract/MiqOpenStackVm/MiqOpenStackInstance.rb +182 -0
- data/lib/Scvmm/miq_hyperv_disk.rb +273 -0
- data/lib/Scvmm/miq_scvmm_parse_powershell.rb +75 -0
- data/lib/Scvmm/miq_scvmm_vm_ssa_info.rb +135 -0
- data/lib/Scvmm/test/miq_hyperv_disk_test.rb +33 -0
- data/lib/Scvmm/test/miq_scvmm_vm_ssa_info_test.rb +41 -0
- data/lib/VmLocalDiskAccess/test/localCfg.rb +97 -0
- data/lib/VolumeManager/LVM/logical_volume.rb +75 -0
- data/lib/VolumeManager/LVM/lv_segment.rb +43 -0
- data/lib/VolumeManager/LVM/lvm2disk.rb +158 -0
- data/lib/VolumeManager/LVM/parser.rb +138 -0
- data/lib/VolumeManager/LVM/physical_volume.rb +19 -0
- data/lib/VolumeManager/LVM/scanner.rb +156 -0
- data/lib/VolumeManager/LVM/thin/btree.rb +83 -0
- data/lib/VolumeManager/LVM/thin/constants.rb +86 -0
- data/lib/VolumeManager/LVM/thin/data_map.rb +44 -0
- data/lib/VolumeManager/LVM/thin/mapping_tree.rb +19 -0
- data/lib/VolumeManager/LVM/thin/space_maps.rb +58 -0
- data/lib/VolumeManager/LVM/thin/superblock.rb +136 -0
- data/lib/VolumeManager/LVM/thin.rb +6 -0
- data/lib/VolumeManager/LVM/volume_group.rb +97 -0
- data/lib/VolumeManager/LVM.rb +8 -0
- data/lib/VolumeManager/MiqLdm.rb +546 -0
- data/lib/VolumeManager/MiqLvm.rb +17 -0
- data/lib/VolumeManager/MiqNativeVolumeManager.rb +150 -0
- data/lib/VolumeManager/MiqVolumeManager.rb +277 -0
- data/lib/VolumeManager/VolMgrPlatformSupport.rb +18 -0
- data/lib/VolumeManager/VolMgrPlatformSupportLinux.rb +77 -0
- data/lib/VolumeManager/VolMgrPlatformSupportWin.rb +17 -0
- data/lib/VolumeManager/test/blockDevTest.rb +40 -0
- data/lib/VolumeManager/test/ldm.rb +97 -0
- data/lib/blackbox/VmBlackBox.rb +103 -0
- data/lib/blackbox/xmlStorage.rb +180 -0
- data/lib/db/MiqBdb/MiqBdb.rb +309 -0
- data/lib/db/MiqBdb/MiqBdbBtree.rb +219 -0
- data/lib/db/MiqBdb/MiqBdbHash.rb +199 -0
- data/lib/db/MiqBdb/MiqBdbPage.rb +159 -0
- data/lib/db/MiqBdb/MiqBdbUtil.rb +18 -0
- data/lib/db/MiqSqlite/MiqSqlite3.rb +330 -0
- data/lib/db/MiqSqlite/MiqSqlite3Cell.rb +167 -0
- data/lib/db/MiqSqlite/MiqSqlite3Page.rb +151 -0
- data/lib/db/MiqSqlite/MiqSqlite3Table.rb +124 -0
- data/lib/db/MiqSqlite/MiqSqlite3Util.rb +32 -0
- data/lib/disk/DiskProbe.rb +68 -0
- data/lib/disk/MiqDisk.rb +317 -0
- data/lib/disk/camcorder_test.rb +90 -0
- data/lib/disk/dos_mbr.img +0 -0
- data/lib/disk/modules/AzureBlobDisk.rb +101 -0
- data/lib/disk/modules/LocalDevMod.rb +47 -0
- data/lib/disk/modules/LocalDevProbe.rb +6 -0
- data/lib/disk/modules/MSCommon.rb +352 -0
- data/lib/disk/modules/MSVSDiffDisk.rb +91 -0
- data/lib/disk/modules/MSVSDiskProbe.rb +61 -0
- data/lib/disk/modules/MSVSDynamicDisk.rb +42 -0
- data/lib/disk/modules/MSVSFixedDisk.rb +45 -0
- data/lib/disk/modules/MiqLargeFile.rb +63 -0
- data/lib/disk/modules/MiqLargeFileWin32.rb +107 -0
- data/lib/disk/modules/QcowDisk.rb +692 -0
- data/lib/disk/modules/QcowDiskProbe.rb +34 -0
- data/lib/disk/modules/RawBlockIO.rb +116 -0
- data/lib/disk/modules/RawDisk.rb +45 -0
- data/lib/disk/modules/RawDiskProbe.rb +7 -0
- data/lib/disk/modules/RhevmDescriptor.rb +167 -0
- data/lib/disk/modules/RhevmDiskProbe.rb +52 -0
- data/lib/disk/modules/VMWareCowdDisk.rb +207 -0
- data/lib/disk/modules/VMWareDescriptor.rb +214 -0
- data/lib/disk/modules/VMWareDiskProbe.rb +74 -0
- data/lib/disk/modules/VMWareSparseDisk.rb +189 -0
- data/lib/disk/modules/VhdxDisk.rb +625 -0
- data/lib/disk/modules/VhdxDiskProbe.rb +46 -0
- data/lib/disk/modules/VixDiskMod.rb +54 -0
- data/lib/disk/modules/VixDiskProbe.rb +6 -0
- data/lib/disk/modules/miq_disk_cache.rb +135 -0
- data/lib/disk/modules/miq_dummy_disk.rb +41 -0
- data/lib/disk/modules/vhdx_bat_entry.rb +10 -0
- data/lib/disk/test.rb +66 -0
- data/lib/fs/MetakitFS/MetakitFS.rb +530 -0
- data/lib/fs/MetakitFS/test/Makefile +14 -0
- data/lib/fs/MetakitFS/test/MkCollectFiles.rb +165 -0
- data/lib/fs/MetakitFS/test/MkSelectFiles.rb +30 -0
- data/lib/fs/MetakitFS/test/collect_files.yaml +70 -0
- data/lib/fs/MetakitFS/test/init.rb +3 -0
- data/lib/fs/MetakitFS/test/mk2vmdk.rb +64 -0
- data/lib/fs/MetakitFS/test/mk4test.c +92 -0
- data/lib/fs/MetakitFS/test/mkFsTest.rb +113 -0
- data/lib/fs/MetakitFS/test/proto.rb +97 -0
- data/lib/fs/MiqFS/FsProbe.rb +39 -0
- data/lib/fs/MiqFS/MiqFS.rb +515 -0
- data/lib/fs/MiqFS/modules/AUFSProbe.rb +26 -0
- data/lib/fs/MiqFS/modules/Ext3.rb +305 -0
- data/lib/fs/MiqFS/modules/Ext3Probe.rb +25 -0
- data/lib/fs/MiqFS/modules/Ext4.rb +304 -0
- data/lib/fs/MiqFS/modules/Ext4Probe.rb +25 -0
- data/lib/fs/MiqFS/modules/Fat32.rb +318 -0
- data/lib/fs/MiqFS/modules/Fat32Probe.rb +30 -0
- data/lib/fs/MiqFS/modules/HFSProbe.rb +18 -0
- data/lib/fs/MiqFS/modules/Iso9660.rb +293 -0
- data/lib/fs/MiqFS/modules/Iso9660Probe.rb +18 -0
- data/lib/fs/MiqFS/modules/LocalFS.rb +105 -0
- data/lib/fs/MiqFS/modules/NTFS.rb +287 -0
- data/lib/fs/MiqFS/modules/NTFSProbe.rb +21 -0
- data/lib/fs/MiqFS/modules/NativeFS.rb +155 -0
- data/lib/fs/MiqFS/modules/ReFSProbe.rb +17 -0
- data/lib/fs/MiqFS/modules/RealFS.rb +79 -0
- data/lib/fs/MiqFS/modules/RealFSProbe.rb +6 -0
- data/lib/fs/MiqFS/modules/Reiser4Probe.rb +18 -0
- data/lib/fs/MiqFS/modules/ReiserFS.rb +315 -0
- data/lib/fs/MiqFS/modules/ReiserFSProbe.rb +42 -0
- data/lib/fs/MiqFS/modules/UnionFSProbe.rb +18 -0
- data/lib/fs/MiqFS/modules/WebDAV.rb +127 -0
- data/lib/fs/MiqFS/modules/WebDAVFile.rb +68 -0
- data/lib/fs/MiqFS/modules/XFS.rb +300 -0
- data/lib/fs/MiqFS/modules/XFSProbe.rb +26 -0
- data/lib/fs/MiqFS/modules/ZFSProbe.rb +18 -0
- data/lib/fs/MiqFS/test.rb +59 -0
- data/lib/fs/MiqFsUtil.rb +383 -0
- data/lib/fs/MiqMountManager.rb +209 -0
- data/lib/fs/MiqNativeMountManager.rb +101 -0
- data/lib/fs/MountManagerProbe.rb +29 -0
- data/lib/fs/ReiserFS/block.rb +209 -0
- data/lib/fs/ReiserFS/directory.rb +136 -0
- data/lib/fs/ReiserFS/directory_entry.rb +140 -0
- data/lib/fs/ReiserFS/file_data.rb +111 -0
- data/lib/fs/ReiserFS/superblock.rb +140 -0
- data/lib/fs/ReiserFS/utils.rb +95 -0
- data/lib/fs/VimDatastoreFS/VimDatastoreFS.rb +192 -0
- data/lib/fs/ext3/alloc_bitmap.rb +38 -0
- data/lib/fs/ext3/block_pointers_path.rb +130 -0
- data/lib/fs/ext3/directory.rb +51 -0
- data/lib/fs/ext3/directory_entry.rb +67 -0
- data/lib/fs/ext3/ex_attrib_header.rb +14 -0
- data/lib/fs/ext3/ex_attrib_name.rb +23 -0
- data/lib/fs/ext3/file_data.rb +130 -0
- data/lib/fs/ext3/group_descriptor_entry.rb +65 -0
- data/lib/fs/ext3/group_descriptor_table.rb +54 -0
- data/lib/fs/ext3/hash_tree_entry.rb +18 -0
- data/lib/fs/ext3/hash_tree_header.rb +15 -0
- data/lib/fs/ext3/inode.rb +228 -0
- data/lib/fs/ext3/posix_acl_entry.rb +29 -0
- data/lib/fs/ext3/posix_acl_header.rb +11 -0
- data/lib/fs/ext3/superblock.rb +406 -0
- data/lib/fs/ext3/test/tc_Ext3BlockPointersPath.rb +74 -0
- data/lib/fs/ext4/alloc_bitmap.rb +38 -0
- data/lib/fs/ext4/directory.rb +87 -0
- data/lib/fs/ext4/directory_entry.rb +77 -0
- data/lib/fs/ext4/ex_attrib_header.rb +14 -0
- data/lib/fs/ext4/ex_attrib_name.rb +23 -0
- data/lib/fs/ext4/extent.rb +35 -0
- data/lib/fs/ext4/extent_header.rb +40 -0
- data/lib/fs/ext4/extent_index.rb +33 -0
- data/lib/fs/ext4/group_descriptor_entry.rb +69 -0
- data/lib/fs/ext4/group_descriptor_table.rb +54 -0
- data/lib/fs/ext4/hash_tree_entry.rb +58 -0
- data/lib/fs/ext4/hash_tree_header.rb +35 -0
- data/lib/fs/ext4/inode.rb +465 -0
- data/lib/fs/ext4/posix_acl_entry.rb +29 -0
- data/lib/fs/ext4/posix_acl_header.rb +11 -0
- data/lib/fs/ext4/superblock.rb +412 -0
- data/lib/fs/fat32/boot_sect.rb +379 -0
- data/lib/fs/fat32/directory.rb +222 -0
- data/lib/fs/fat32/directory_entry.rb +540 -0
- data/lib/fs/fat32/file_data.rb +128 -0
- data/lib/fs/iso9660/boot_sector.rb +170 -0
- data/lib/fs/iso9660/directory.rb +90 -0
- data/lib/fs/iso9660/directory_entry.rb +147 -0
- data/lib/fs/iso9660/file_data.rb +78 -0
- data/lib/fs/iso9660/rock_ridge.rb +329 -0
- data/lib/fs/iso9660/util.rb +57 -0
- data/lib/fs/modules/LinuxMount.rb +300 -0
- data/lib/fs/modules/LinuxMountProbe.rb +29 -0
- data/lib/fs/modules/WinMount.rb +97 -0
- data/lib/fs/modules/WinMountProbe.rb +24 -0
- data/lib/fs/ntfs/attrib_attribute_list.rb +131 -0
- data/lib/fs/ntfs/attrib_bitmap.rb +26 -0
- data/lib/fs/ntfs/attrib_data.rb +74 -0
- data/lib/fs/ntfs/attrib_file_name.rb +110 -0
- data/lib/fs/ntfs/attrib_header.rb +194 -0
- data/lib/fs/ntfs/attrib_index_allocation.rb +19 -0
- data/lib/fs/ntfs/attrib_index_root.rb +247 -0
- data/lib/fs/ntfs/attrib_object_id.rb +40 -0
- data/lib/fs/ntfs/attrib_standard_information.rb +107 -0
- data/lib/fs/ntfs/attrib_type.rb +49 -0
- data/lib/fs/ntfs/attrib_volume_information.rb +53 -0
- data/lib/fs/ntfs/attrib_volume_name.rb +31 -0
- data/lib/fs/ntfs/boot_sect.rb +253 -0
- data/lib/fs/ntfs/data_run.rb +358 -0
- data/lib/fs/ntfs/directory_index_node.rb +114 -0
- data/lib/fs/ntfs/index_node_header.rb +69 -0
- data/lib/fs/ntfs/index_record_header.rb +85 -0
- data/lib/fs/ntfs/mft_entry.rb +288 -0
- data/lib/fs/ntfs/utils.rb +43 -0
- data/lib/fs/test/camcorder_fs_test.rb +108 -0
- data/lib/fs/test/collect_files_direct.yaml +22 -0
- data/lib/fs/test/collect_files_in.yaml +24 -0
- data/lib/fs/test/collect_files_in_nc.yaml +22 -0
- data/lib/fs/test/collect_files_out.yaml +6 -0
- data/lib/fs/test/collect_files_rm.yaml +6 -0
- data/lib/fs/test/copyTest.rb +126 -0
- data/lib/fs/test/fsTest.rb +87 -0
- data/lib/fs/test/updateTest.rb +184 -0
- data/lib/fs/xfs/allocation_group.rb +160 -0
- data/lib/fs/xfs/bmap_btree_block.rb +125 -0
- data/lib/fs/xfs/bmap_btree_record.rb +80 -0
- data/lib/fs/xfs/bmap_btree_root_node.rb +72 -0
- data/lib/fs/xfs/directory.rb +133 -0
- data/lib/fs/xfs/directory2_data_header.rb +27 -0
- data/lib/fs/xfs/directory3_data_header.rb +34 -0
- data/lib/fs/xfs/directory_block_tail.rb +22 -0
- data/lib/fs/xfs/directory_data_header.rb +46 -0
- data/lib/fs/xfs/directory_entry.rb +106 -0
- data/lib/fs/xfs/inode.rb +532 -0
- data/lib/fs/xfs/inode_map.rb +100 -0
- data/lib/fs/xfs/short_form_directory_entry.rb +91 -0
- data/lib/fs/xfs/short_form_header.rb +44 -0
- data/lib/fs/xfs/superblock.rb +556 -0
- data/lib/lib/tasks/azure.rake +52 -0
- data/lib/manageiq/smartstate/version.rb +5 -0
- data/lib/manageiq/smartstate.rb +7 -0
- data/lib/manageiq-smartstate.rb +1 -0
- data/lib/metadata/MIQExtract/MIQExtract.rb +297 -0
- data/lib/metadata/MIQExtract/test/extractTest.rb +41 -0
- data/lib/metadata/MIQExtract/test/full_extract_test.rb +68 -0
- data/lib/metadata/ScanProfile/HostScanItem.rb +4 -0
- data/lib/metadata/ScanProfile/HostScanProfile.rb +4 -0
- data/lib/metadata/ScanProfile/HostScanProfiles.rb +41 -0
- data/lib/metadata/ScanProfile/ScanItemBase.rb +63 -0
- data/lib/metadata/ScanProfile/ScanProfileBase.rb +51 -0
- data/lib/metadata/ScanProfile/ScanProfilesBase.rb +60 -0
- data/lib/metadata/ScanProfile/VmScanItem.rb +4 -0
- data/lib/metadata/ScanProfile/VmScanProfile.rb +4 -0
- data/lib/metadata/ScanProfile/VmScanProfiles.rb +38 -0
- data/lib/metadata/ScanProfile/modules/HostScanItemFile.rb +51 -0
- data/lib/metadata/ScanProfile/modules/HostScanItemNteventlog.rb +84 -0
- data/lib/metadata/ScanProfile/modules/VmScanItemFile.rb +39 -0
- data/lib/metadata/ScanProfile/modules/VmScanItemNteventlog.rb +34 -0
- data/lib/metadata/ScanProfile/modules/VmScanItemRegistry.rb +64 -0
- data/lib/metadata/VMMount/VMMount.rb +81 -0
- data/lib/metadata/VMMount/VMPlatformMount.rb +18 -0
- data/lib/metadata/VMMount/VMPlatformMountLinux.rb +75 -0
- data/lib/metadata/VMMount/VMPlatformMountWin.rb +13 -0
- data/lib/metadata/VmConfig/GetNativeCfg.rb +45 -0
- data/lib/metadata/VmConfig/VmConfig.rb +947 -0
- data/lib/metadata/VmConfig/cfgConfig.rb +45 -0
- data/lib/metadata/VmConfig/ovfConfig.rb +99 -0
- data/lib/metadata/VmConfig/test/GetVMwareCfgTest.rb +40 -0
- data/lib/metadata/VmConfig/vmcConfig.rb +116 -0
- data/lib/metadata/VmConfig/vmtxConfig.rb +4 -0
- data/lib/metadata/VmConfig/vmxConfig.rb +162 -0
- data/lib/metadata/VmConfig/xmlConfig.rb +79 -0
- data/lib/metadata/VmConfig/xmlMsHyperVConfig.rb +41 -0
- data/lib/metadata/linux/InitProcHash.rb +632 -0
- data/lib/metadata/linux/LinuxInitProcs.rb +142 -0
- data/lib/metadata/linux/LinuxOSInfo.rb +237 -0
- data/lib/metadata/linux/LinuxPackages.rb +209 -0
- data/lib/metadata/linux/LinuxSystemd.rb +130 -0
- data/lib/metadata/linux/LinuxUsers.rb +289 -0
- data/lib/metadata/linux/LinuxUtils.rb +197 -0
- data/lib/metadata/linux/MiqConaryPackages.rb +41 -0
- data/lib/metadata/linux/MiqRpmPackages.rb +160 -0
- data/lib/metadata/linux/test/Name +0 -0
- data/lib/metadata/linux/test/Packages +0 -0
- data/lib/metadata/linux/test/rpoTest.rb +5 -0
- data/lib/metadata/linux/test/tc_LinuxUtils.rb +4157 -0
- data/lib/metadata/util/event_log_filter.rb +61 -0
- data/lib/metadata/util/md5deep.rb +280 -0
- data/lib/metadata/util/win32/Win32Accounts.rb +764 -0
- data/lib/metadata/util/win32/Win32EventLog.rb +743 -0
- data/lib/metadata/util/win32/Win32Services.rb +86 -0
- data/lib/metadata/util/win32/Win32Software.rb +326 -0
- data/lib/metadata/util/win32/Win32System.rb +333 -0
- data/lib/metadata/util/win32/boot_info_win.rb +59 -0
- data/lib/metadata/util/win32/fleece_hives.rb +220 -0
- data/lib/metadata/util/win32/ms-registry.rb +650 -0
- data/lib/metadata/util/win32/peheader.rb +868 -0
- data/lib/metadata/util/win32/remote-registry.rb +142 -0
- data/lib/metadata/util/win32/system_path_win.rb +103 -0
- data/lib/metadata/util/win32/versioninfo.rb +17 -0
- data/manageiq-smartstate.gemspec +35 -0
- 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
|