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,379 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
|
3
|
+
require 'binary_struct'
|
4
|
+
require 'memory_buffer'
|
5
|
+
|
6
|
+
# ////////////////////////////////////////////////////////////////////////////
|
7
|
+
# // Data definitions.
|
8
|
+
|
9
|
+
module Fat32
|
10
|
+
BOOT_SECT = BinaryStruct.new([
|
11
|
+
'a3', 'jmp_boot', # Jump to boot loader.
|
12
|
+
'a8', 'oem_name', # OEM Name in ASCII.
|
13
|
+
'S', 'bytes_per_sec', # Bytes per sector: 512, 1024, 2048 or 4096.
|
14
|
+
'C', 'sec_per_clus', # Sectors per cluster, size must be < 32K.
|
15
|
+
'S', 'res_sec', # Reserved sectors.
|
16
|
+
'C', 'num_fats', # Typically 2, but can be 1.
|
17
|
+
'S', 'max_root', # Max files in root dir - 0 FOR FAT32.
|
18
|
+
'S', 'num_sec16', # 16-bit number of sectors in file system (0 if 32-bits needed).
|
19
|
+
'C', 'media_type', # Ususally F8, but can be F0 for removeable.
|
20
|
+
'S', 'fat_size16', # 16-bit number of sectors in FAT, 0 FOR FAT32.
|
21
|
+
'S', 'sec_per_track', # Sectors per track.
|
22
|
+
'S', 'num_heads', # Number of heads.
|
23
|
+
'L', 'pre_sec', # Sectors before the start of the partition.
|
24
|
+
'L', 'num_sec32', # 32-bit number of sectors in the file system (0 if 16-bit num used).
|
25
|
+
'L', 'fat_size32', # 32-bit number of sectors in FAT.
|
26
|
+
'S', 'fat_usage', # Describes how FATs are used: See FU_ below.
|
27
|
+
'S', 'version', # Major & minor version numbers.
|
28
|
+
'L', 'root_clus', # Cluster location of root directory.
|
29
|
+
'S', 'fsinfo_sec', # Sector location of FSINFO structure .
|
30
|
+
'S', 'boot_bkup', # Sector location of boot sector backup.
|
31
|
+
'a12', 'reserved1', # Reserved.
|
32
|
+
'C', 'drive_num', # INT13 drive number.
|
33
|
+
'C', 'unused1', # Unused.
|
34
|
+
'C', 'ex_sig', # If 0x29, then the next three values are valid.
|
35
|
+
'L', 'serial_num', # Volume serial number.
|
36
|
+
'a11', 'label', # Volume label.
|
37
|
+
'a8', 'fs_label', # File system type label, not required.
|
38
|
+
# NOTE: MS always uses "FAT32 ". For probe, seek to 66 & verify 0x29,
|
39
|
+
# then seek to 82 & read 8, compare with "FAT32 ".
|
40
|
+
'a420', nil, # Unused.
|
41
|
+
'S', 'signature', # 0xaa55
|
42
|
+
])
|
43
|
+
SIZEOF_BOOT_SECT = BOOT_SECT.size
|
44
|
+
|
45
|
+
DOS_SIGNATURE = 0xaa55
|
46
|
+
|
47
|
+
FSINFO = BinaryStruct.new([
|
48
|
+
'a4', 'sig1', # Signature - 0x41615252 (RRaA).
|
49
|
+
'a480', nil, # Unused.
|
50
|
+
'a4', 'sig2', # Signature - 0x61417272 (rrAa).
|
51
|
+
'L', 'free_clus', # Number of free clusters.
|
52
|
+
'L', 'next_free', # Next free cluster.
|
53
|
+
'a12', nil, # Unused.
|
54
|
+
'L', 'sig3', # Signature - 0xaa550000.
|
55
|
+
])
|
56
|
+
SIZEOF_FSINFO = FSINFO.size
|
57
|
+
|
58
|
+
FSINFO_SIG1 = "RRaA"
|
59
|
+
FSINFO_SIG2 = "rrAa"
|
60
|
+
FSINFO_SIG3 = 0xaa550000
|
61
|
+
|
62
|
+
# ////////////////////////////////////////////////////////////////////////////
|
63
|
+
# // Class.
|
64
|
+
|
65
|
+
class BootSect
|
66
|
+
FAT_ENTRY_SIZE = 4
|
67
|
+
|
68
|
+
FU_ONE_FAT = 0x0080
|
69
|
+
FU_MSK_ACTIVE_FAT = 0x000f
|
70
|
+
|
71
|
+
CC_NOT_ALLOCATED = 0
|
72
|
+
CC_DAMAGED = 0x0ffffff7
|
73
|
+
CC_END_OF_CHAIN = 0x0ffffff8
|
74
|
+
CC_END_MARK = 0x0fffffff
|
75
|
+
CC_VALUE_MASK = 0x0fffffff
|
76
|
+
|
77
|
+
# Members.
|
78
|
+
attr_accessor :bytesPerSector, :bytesPerCluster, :rootCluster
|
79
|
+
attr_reader :fatBase, :fatSize, :rootBase, :freeClusters
|
80
|
+
attr_reader :fsId, :volName
|
81
|
+
|
82
|
+
# Initialization
|
83
|
+
def initialize(stream)
|
84
|
+
raise "Nil stream" if stream.nil?
|
85
|
+
|
86
|
+
# Init all.
|
87
|
+
@bytesPerSector = 0; @bytesPerCluster = 0; @fatBase = 0
|
88
|
+
@fatSize = 0; @rootBase = 0; @mountable = false
|
89
|
+
|
90
|
+
# Buffer stream, read & decode boot sector.
|
91
|
+
@stream = stream
|
92
|
+
buf = stream.read(SIZEOF_BOOT_SECT)
|
93
|
+
raise "Couldn't read boot sector." if buf.nil?
|
94
|
+
@bs = BOOT_SECT.decode(buf)
|
95
|
+
raise "Couldn't decode boot sector." if @bs.nil?
|
96
|
+
|
97
|
+
# Bytes per sector must be 512, 1024, 2048 or 4096
|
98
|
+
bps = @bs['bytes_per_sec']
|
99
|
+
raise "Bytes per sector value invalid: #{bps}" if bps != 512 && bps != 1024 && bps != 2048 && bps != 4096
|
100
|
+
@bytesPerSector = bps
|
101
|
+
|
102
|
+
# Cluster size must be 32K or smaller.
|
103
|
+
bpc = @bs['sec_per_clus'] * bps
|
104
|
+
raise "Sectors per cluster value invalid: #{bpc} (#{bps}bps * #{@bs['sec_per_clus']}spc)" if bpc > 32768
|
105
|
+
@bytesPerCluster = bpc
|
106
|
+
|
107
|
+
# Get free clusters.
|
108
|
+
stream.seek(@bs['fsinfo_sec'] * @bytesPerSector)
|
109
|
+
@fsinfo = FSINFO.decode(stream.read(@bytesPerSector))
|
110
|
+
if @fsinfo['sig1'] == 'RRaA' && @fsinfo['sig2'] == 'rrAa' && @fsinfo['sig3'] == 0xaa550000
|
111
|
+
@freeClusters = @fsinfo['free_clus']
|
112
|
+
else
|
113
|
+
@freeClusters = 0
|
114
|
+
end
|
115
|
+
|
116
|
+
# Expose volume information.
|
117
|
+
@fsId = @bs['serial_num']
|
118
|
+
@volName = @bs['label']
|
119
|
+
|
120
|
+
# Verify FAT32 values according to Carrier.
|
121
|
+
raise "Maximum files in root dir invalid: #{@bs['max_root']}\nIs partition FAT12/16?" if @bs['max_root'] != 0
|
122
|
+
raise "Number of sectors in FAT invalid: #{@bs['fat_size32']}\nIs partition FAT12/16?" if @bs['fat_size32'] == 0
|
123
|
+
raise "Unknown number of sectors in file system." if @bs['num_sec16'] == 0 && @bs['num_sec32'] == 0
|
124
|
+
raise "Boot sector signature invalid: 0x#{'%04x' % @bs['signature']}" if @bs['signature'] != 0xaa55
|
125
|
+
|
126
|
+
# Calc location of the FAT & root dir.
|
127
|
+
@mountable = getLocs
|
128
|
+
end
|
129
|
+
|
130
|
+
# String rep.
|
131
|
+
def to_s
|
132
|
+
# NOTE: Non Microsoft tools may not set the file system label (i.e. Win emulators, linux, etc.)
|
133
|
+
@bs['fs_label']
|
134
|
+
end
|
135
|
+
|
136
|
+
# ////////////////////////////////////////////////////////////////////////////
|
137
|
+
# // Class helpers & accessors.
|
138
|
+
|
139
|
+
def isMountable?
|
140
|
+
return false if @mountable.nil?
|
141
|
+
@mountable
|
142
|
+
end
|
143
|
+
|
144
|
+
def oemName
|
145
|
+
@bs['oem_name']
|
146
|
+
end
|
147
|
+
|
148
|
+
# ////////////////////////////////////////////////////////////////////////////
|
149
|
+
# // Utility functions.
|
150
|
+
|
151
|
+
# Get absolute byte locations of the FAT & the root dir.
|
152
|
+
def getLocs
|
153
|
+
# Calculate the location of the (active) FAT (loc as absolute byte for seek).
|
154
|
+
@fatBase = @bs['res_sec'] * @bytesPerSector
|
155
|
+
@fatSize = @bs['fat_size32'] * @bytesPerSector
|
156
|
+
fu = @bs['fat_usage']
|
157
|
+
if fu & FU_ONE_FAT == FU_ONE_FAT
|
158
|
+
@fatBase += @fatSize * (fu & FU_MSK_ACTIVE_FAT)
|
159
|
+
end
|
160
|
+
return false if @fatBase == 0 || @fatSize == 0
|
161
|
+
|
162
|
+
# Calculate the location of the root dir (loc as absolute byte for seek).
|
163
|
+
@rootCluster = @bs['root_clus']
|
164
|
+
@rootBase = clusToByte(@rootCluster)
|
165
|
+
return false if @rootBase == 0
|
166
|
+
true
|
167
|
+
end
|
168
|
+
|
169
|
+
# Get data for the requested cluster.
|
170
|
+
def getCluster(clus)
|
171
|
+
raise "Cluster is nil" if clus.nil?
|
172
|
+
@stream.seek(clusToByte(clus))
|
173
|
+
@stream.read(@bytesPerCluster)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Write data to a cluster.
|
177
|
+
def putCluster(clus, buf)
|
178
|
+
@stream.seek(clusToByte(clus))
|
179
|
+
@stream.write(buf, @bytesPerCluster)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Gets data for the next cluster given current, or nil if end.
|
183
|
+
def getNextCluster(clus)
|
184
|
+
nxt = getFatEntry(clus)
|
185
|
+
return nil if nxt > CC_END_OF_CHAIN
|
186
|
+
raise "Damaged cluster in cluster chain" if nxt == CC_DAMAGED
|
187
|
+
[nxt, getCluster(nxt)]
|
188
|
+
end
|
189
|
+
|
190
|
+
# Return continuous data from a beginning cluster to limit bytes (or EOF).
|
191
|
+
def getToLimit(clus, limit)
|
192
|
+
# Init.
|
193
|
+
out = MemoryBuffer.create(limit)
|
194
|
+
pos = 0
|
195
|
+
cur_clus = clus
|
196
|
+
|
197
|
+
# How many clusters fill request.
|
198
|
+
num = limit.divmod(@bytesPerCluster)
|
199
|
+
num_clus = num[0]; num_clus += 1 if num[1] > 0
|
200
|
+
|
201
|
+
# Loop until done or EOF.
|
202
|
+
while num_clus > 0
|
203
|
+
|
204
|
+
# Find number of contiguous clusters & trim by num_clus.
|
205
|
+
contig = countContigClusters(cur_clus)
|
206
|
+
red_clus = num_clus > contig ? contig : num_clus
|
207
|
+
|
208
|
+
# Get data.
|
209
|
+
chunk = red_clus * @bytesPerCluster
|
210
|
+
@stream.seek(clusToByte(cur_clus))
|
211
|
+
out[pos, chunk] = @stream.read(chunk)
|
212
|
+
pos += chunk
|
213
|
+
|
214
|
+
# Inc current & dec number to read.
|
215
|
+
cur_clus += (red_clus - 1); num_clus -= red_clus
|
216
|
+
|
217
|
+
# Get next cluster & abort if end of chain.
|
218
|
+
cur_clus = getFatEntry(cur_clus)
|
219
|
+
break if cur_clus > CC_END_OF_CHAIN
|
220
|
+
end
|
221
|
+
|
222
|
+
# Return next cluster & data.
|
223
|
+
[cur_clus, out]
|
224
|
+
end
|
225
|
+
|
226
|
+
# Count the number of continuous clusters from some beginning cluster.
|
227
|
+
def countContigClusters(clus)
|
228
|
+
cur = clus; nxt = 0
|
229
|
+
loop do
|
230
|
+
nxt = getFatEntry(cur)
|
231
|
+
break if nxt != cur + 1
|
232
|
+
cur = nxt; redo
|
233
|
+
end
|
234
|
+
raise "Damaged cluster in cluster chain" if nxt == CC_DAMAGED
|
235
|
+
cur - clus + 1
|
236
|
+
end
|
237
|
+
|
238
|
+
# Allocate a number of clusters on a particular cluster chain or start a chain.
|
239
|
+
# Start can be anywhere on the chain, but most efficient when just before end.
|
240
|
+
# If start is 0 then start a chain (for file data).
|
241
|
+
def allocClusters(start, num = 1)
|
242
|
+
first = 0; clus = 0
|
243
|
+
if start == 0 # Start chain.
|
244
|
+
first = getNextAvailableCluster(@rootCluster)
|
245
|
+
putFatEntry(first, CC_END_MARK)
|
246
|
+
clus = first; num -= 1
|
247
|
+
else # Allocate on chain - seek to end.
|
248
|
+
clus = start
|
249
|
+
# while (nxt = getFatEntry(clus)) <= CC_END_OF_CHAIN do clus = nxt end
|
250
|
+
loop do
|
251
|
+
nxt = getFatEntry(clus)
|
252
|
+
break if nxt > CC_END_OF_CHAIN
|
253
|
+
clus = nxt
|
254
|
+
end
|
255
|
+
end
|
256
|
+
# Allocate num clusters, put end mark at end.
|
257
|
+
num.times do
|
258
|
+
nxt = getNextAvailableCluster(clus)
|
259
|
+
first = nxt if first == 0
|
260
|
+
putFatEntry(clus, nxt)
|
261
|
+
putCluster(nxt, MemoryBuffer.create(@bytesPerCluster))
|
262
|
+
clus = nxt
|
263
|
+
putFatEntry(clus, CC_END_MARK)
|
264
|
+
end
|
265
|
+
first
|
266
|
+
end
|
267
|
+
|
268
|
+
# Start from defined FAT entry and look for next available entry.
|
269
|
+
def getNextAvailableCluster(clus)
|
270
|
+
loop do
|
271
|
+
break if getFatEntry(clus) == 0
|
272
|
+
clus += 1
|
273
|
+
end
|
274
|
+
# while getFatEntry(clus) != 0 do clus += 1 end
|
275
|
+
clus
|
276
|
+
end
|
277
|
+
|
278
|
+
# Deallocate all clusters on a chain from a starting cluster number.
|
279
|
+
def wipeChain(clus)
|
280
|
+
loop do
|
281
|
+
nxt = getFatEntry(clus)
|
282
|
+
putFatEntry(clus, 0)
|
283
|
+
break if nxt == 0 # A 0 entry means FAT is inconsistent. Chkdsk may report lost clusters.
|
284
|
+
break if nxt == CC_DAMAGED # This should never happen but if it does allow clusters to become lost.
|
285
|
+
break if nxt > CC_END_OF_CHAIN
|
286
|
+
clus = nxt
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# Start from defined cluster number and write data, following allocated cluster chain.
|
291
|
+
def writeClusters(start, buf, len = buf.length)
|
292
|
+
clus = start; num, leftover = len.divmod(@bytesPerCluster); num += 1 if leftover > 0
|
293
|
+
0.upto(num - 1) do |offset|
|
294
|
+
local = buf[offset * @bytesPerCluster, @bytesPerCluster]
|
295
|
+
if local.length < @bytesPerCluster then local += ("\0" * (@bytesPerCluster - local.length)) end
|
296
|
+
@stream.seek(clusToByte(clus), IO::SEEK_SET)
|
297
|
+
@stream.write(local, @bytesPerCluster)
|
298
|
+
break if offset == num - 1 # ugly hack to prevent allocating more than needed.
|
299
|
+
nxt = getFatEntry(clus)
|
300
|
+
nxt = allocClusters(clus) if nxt > CC_END_OF_CHAIN
|
301
|
+
clus = nxt
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# Translate a cluster number to an absolute byte location.
|
306
|
+
def clusToByte(clus = @rootCluster)
|
307
|
+
raise "Cluster is nil" if clus.nil?
|
308
|
+
@bs['res_sec'] * @bytesPerSector + @fatSize * @bs['num_fats'] + (clus - 2) * @bytesPerCluster
|
309
|
+
end
|
310
|
+
|
311
|
+
# Return the FAT entry for a cluster.
|
312
|
+
def getFatEntry(clus)
|
313
|
+
@stream.seek(@fatBase + FAT_ENTRY_SIZE * clus)
|
314
|
+
@stream.read(FAT_ENTRY_SIZE).unpack('L')[0] & CC_VALUE_MASK
|
315
|
+
end
|
316
|
+
|
317
|
+
# Write a FAT entry for a cluster.
|
318
|
+
def putFatEntry(clus, value)
|
319
|
+
raise "DONT TOUCH THIS CLUSTER: #{clus}" if clus < 3
|
320
|
+
@stream.seek(@fatBase + FAT_ENTRY_SIZE * clus)
|
321
|
+
@stream.write([value].pack('L'), FAT_ENTRY_SIZE)
|
322
|
+
end
|
323
|
+
|
324
|
+
def mkClusterMap(clus)
|
325
|
+
map = []
|
326
|
+
if clus > 0
|
327
|
+
map << clus
|
328
|
+
loop do
|
329
|
+
nxt = getFatEntry(clus)
|
330
|
+
break if nxt > CC_END_OF_CHAIN
|
331
|
+
clus = nxt
|
332
|
+
map << clus
|
333
|
+
end
|
334
|
+
end
|
335
|
+
map
|
336
|
+
end
|
337
|
+
|
338
|
+
def dumpFat(numEnt)
|
339
|
+
out = ""
|
340
|
+
0.upto(numEnt - 1) do|i|
|
341
|
+
out += "#{i} #{'%08x' % getFatEntry(i)}\n"
|
342
|
+
end
|
343
|
+
out
|
344
|
+
end
|
345
|
+
|
346
|
+
# Dump object.
|
347
|
+
def dump
|
348
|
+
out = "\#<#{self.class}:0x#{'%08x' % object_id}>\n"
|
349
|
+
out += "Jump boot (hex) : #{'%02x %02x %02x' % @bs['jmp_boot'].unpack('C3')}\n"
|
350
|
+
out += "OEM Name : #{@bs['oem_name']}\n"
|
351
|
+
out += "Bytes per sector : #{@bs['bytes_per_sec']}\n"
|
352
|
+
out += "Sectors per clus : #{@bs['sec_per_clus']}\n"
|
353
|
+
out += "Reserved sectors : #{@bs['res_sec']}\n"
|
354
|
+
out += "Number of FATs : #{@bs['num_fats']}\n"
|
355
|
+
out += "Max files in root : #{@bs['max_root']}\n"
|
356
|
+
out += "Sectors in FS(16) : 0x#{'%04x' % @bs['num_sec16']}\n"
|
357
|
+
out += "Media type : 0x#{'%02x' % @bs['media_type']}\n"
|
358
|
+
out += "Sectors in FAT(16): 0x#{'%04x' % @bs['fat_size16']}\n"
|
359
|
+
out += "Sectors per track : #{@bs['sec_per_track']}\n"
|
360
|
+
out += "Number of heads : #{@bs['num_heads']}\n"
|
361
|
+
out += "Sectors pre start : #{@bs['pre_sec']}\n"
|
362
|
+
out += "Sectors in FS(32) : 0x#{'%08x' % @bs['num_sec32']}\n"
|
363
|
+
out += "Sectors in FAT(32): 0x#{'%08x' % @bs['fat_size32']}\n"
|
364
|
+
out += "FAT usage flags : 0x#{'%04x' % @bs['fat_usage']}\n"
|
365
|
+
out += "Version (MJ/MN) : 0x#{'%04x' % @bs['version']}\n"
|
366
|
+
out += "Root cluster : #{@bs['root_clus']}\n"
|
367
|
+
out += "FSINFO sector : #{@bs['fsinfo_sec']}\n"
|
368
|
+
out += "Backup boot sector: #{@bs['boot_bkup']}\n"
|
369
|
+
out += "Reserved : '#{@bs['reserved1']}'\n"
|
370
|
+
out += "Drive number : #{@bs['drive_num']}\n"
|
371
|
+
out += "Extended signature: 0x#{'%02x' % @bs['ex_sig']} (0x29?)\n"
|
372
|
+
out += "Serial number : 0x#{'%08x' % @bs['serial_num']}\n"
|
373
|
+
out += "Label : '#{@bs['label']}'\n"
|
374
|
+
out += "File Sys label : '#{@bs['fs_label']}'\n"
|
375
|
+
out += "Signature : 0x#{'%04x' % @bs['signature']} (0xaa55?)\n"
|
376
|
+
out
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end # module Fat32
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
require 'fs/fat32/directory_entry'
|
4
|
+
|
5
|
+
require 'memory_buffer'
|
6
|
+
|
7
|
+
# ////////////////////////////////////////////////////////////////////////////
|
8
|
+
# // Data definitions.
|
9
|
+
|
10
|
+
# A Directory is basically a helper for dealing with directories. It doesn't
|
11
|
+
# really have a structure of it's own, but builds structure when it needs to
|
12
|
+
# using DirectoryEntry instances.
|
13
|
+
|
14
|
+
# ////////////////////////////////////////////////////////////////////////////
|
15
|
+
# // Class.
|
16
|
+
|
17
|
+
module Fat32
|
18
|
+
class Directory
|
19
|
+
# Maximum LFN entry span in bytes (LFN entries *can* span clusters).
|
20
|
+
MAX_ENT_SIZE = 640
|
21
|
+
|
22
|
+
# Find entry flags.
|
23
|
+
FE_DIR = 0
|
24
|
+
FE_FILE = 1
|
25
|
+
FE_EITHER = 2
|
26
|
+
|
27
|
+
# Get free entry behaviors.
|
28
|
+
# Windows 98 returns the first deleted or unallocated entry.
|
29
|
+
# Windows XP returns the first unallocated entry.
|
30
|
+
# Advantage W98: less allocation, advantage WXP: deleted entries are not overwritten.
|
31
|
+
GF_W98 = 0
|
32
|
+
GF_WXP = 1
|
33
|
+
|
34
|
+
# Initialization
|
35
|
+
def initialize(bs, cluster = nil)
|
36
|
+
raise "Nil boot sector" if bs.nil?
|
37
|
+
cluster = bs.rootCluster if cluster.nil?
|
38
|
+
|
39
|
+
@bs = bs
|
40
|
+
# Allocate one cluster if cluster is zero.
|
41
|
+
cluster = @bs.allocClusters(0) if cluster == 0
|
42
|
+
@cluster = cluster
|
43
|
+
@data, @all_clusters = getDirData
|
44
|
+
end
|
45
|
+
|
46
|
+
# ////////////////////////////////////////////////////////////////////////////
|
47
|
+
# // Class helpers & accessors.
|
48
|
+
|
49
|
+
# Return all names in directory as a sorted string array.
|
50
|
+
def globNames
|
51
|
+
names = []
|
52
|
+
cluster = @cluster
|
53
|
+
mf = StringIO.new(@bs.getCluster(cluster))
|
54
|
+
loop do
|
55
|
+
(@bs.bytesPerCluster / DIR_ENT_SIZE - 1).times do
|
56
|
+
de = DirectoryEntry.new(mf.read)
|
57
|
+
break if de.name == ''
|
58
|
+
names << de.name.downcase if de.name !=
|
59
|
+
DirectoryEntry::AF_DELETED && de.name[0] != DirectoryEntry::AF_DELETED
|
60
|
+
mf = StringIO.new(de.unused)
|
61
|
+
break if mf.size == 0
|
62
|
+
end
|
63
|
+
data = @bs.getNextCluster(cluster)
|
64
|
+
break if data.nil?
|
65
|
+
cluster = data[0]
|
66
|
+
mf = StringIO.new(data[1])
|
67
|
+
end
|
68
|
+
names.sort!
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return a DirectoryEntry for a specific file (or subdirectory).
|
72
|
+
def findEntry(name, flags = FE_EITHER)
|
73
|
+
de = nil # found directory entry.
|
74
|
+
skip_next = found = false
|
75
|
+
offset = 0
|
76
|
+
|
77
|
+
# Look for appropriate records.
|
78
|
+
0.step(@data.length - 1, DIR_ENT_SIZE) do|offset|
|
79
|
+
# Check allocation status (ignore if deleted, done if not allocated).
|
80
|
+
alloc_flags = @data[offset]
|
81
|
+
next if alloc_flags == DirectoryEntry::AF_DELETED
|
82
|
+
break if alloc_flags == DirectoryEntry::AF_NOT_ALLOCATED
|
83
|
+
|
84
|
+
# Skip LFN entries unless it's the first (last iteration already chewed them all up).
|
85
|
+
attrib = @data[offset + ATTRIB_OFFSET]
|
86
|
+
if attrib == DirectoryEntry::FA_LFN && (alloc_flags & DirectoryEntry::AF_LFN_LAST != DirectoryEntry::AF_LFN_LAST)
|
87
|
+
# Also skip the next entry (it's the base entry for the last dir ent).
|
88
|
+
skip_next = true; next
|
89
|
+
end
|
90
|
+
if skip_next
|
91
|
+
skip_next = false; next
|
92
|
+
end
|
93
|
+
|
94
|
+
# If a specific type of record was requested, look for only that type.
|
95
|
+
# NOTE: You know, it's possible to look ahead and see what the base entry is.
|
96
|
+
if flags != FE_EITHER && attrib != DirectoryEntry::FA_LFN
|
97
|
+
next if flags == FE_DIR && (attrib & DirectoryEntry::FA_DIRECTORY == 0)
|
98
|
+
next if flags == FE_FILE && (attrib & DirectoryEntry::FA_DIRECTORY != 0)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Potential match... get a DirectoryEntry & stop if found.
|
102
|
+
de = DirectoryEntry.new(@data[offset, MAX_ENT_SIZE])
|
103
|
+
# TODO: - what if the name ends with a dot & there's another dot in the name?
|
104
|
+
if de.name.downcase == name.downcase || de.shortName.downcase == name.downcase
|
105
|
+
found = true
|
106
|
+
break
|
107
|
+
end
|
108
|
+
end
|
109
|
+
return nil unless found
|
110
|
+
parentLoc = offset.divmod(@bs.bytesPerCluster)
|
111
|
+
de.parentCluster = @all_clusters[parentLoc[0]].number
|
112
|
+
de.parentOffset = parentLoc[1]
|
113
|
+
de
|
114
|
+
end
|
115
|
+
|
116
|
+
def mkdir(name)
|
117
|
+
dir = createFile(name)
|
118
|
+
data = FileData.new(dir, @bs)
|
119
|
+
dir.setAttribute(DirectoryEntry::FA_ARCHIVE, false)
|
120
|
+
dir.setAttribute(DirectoryEntry::FA_DIRECTORY)
|
121
|
+
dir.writeEntry(@bs)
|
122
|
+
|
123
|
+
# Write dot and double dot directories.
|
124
|
+
dot = DirectoryEntry.new; dotdot = DirectoryEntry.new
|
125
|
+
dot.name = "."; dotdot.name = ".."
|
126
|
+
dot.setAttribute(DirectoryEntry::FA_ARCHIVE, false)
|
127
|
+
dot.setAttribute(DirectoryEntry::FA_DIRECTORY)
|
128
|
+
dotdot.setAttribute(DirectoryEntry::FA_ARCHIVE, false)
|
129
|
+
dotdot.setAttribute(DirectoryEntry::FA_DIRECTORY)
|
130
|
+
buf = dot.raw + dotdot.raw
|
131
|
+
data.write(buf)
|
132
|
+
dir.firstCluster = data.firstCluster
|
133
|
+
dir.writeEntry(@bs)
|
134
|
+
|
135
|
+
# Update firsCluster in . and .. (if .. is root then it's 0, not 2).
|
136
|
+
dot.firstCluster = dir.firstCluster
|
137
|
+
dotdot.firstCluster = dir.parentCluster == 2 ? 0 : dir.parentCluster
|
138
|
+
buf = dot.raw + dotdot.raw
|
139
|
+
data.rewind
|
140
|
+
data.write(buf)
|
141
|
+
end
|
142
|
+
|
143
|
+
def createFile(name)
|
144
|
+
de = DirectoryEntry.new; de.name = name
|
145
|
+
until findEntry(de.shortName).nil?
|
146
|
+
raise "Duplicate file name: #{de.shortName}" unless de.shortName.include?("~")
|
147
|
+
de.incShortName
|
148
|
+
end
|
149
|
+
de.parentOffset, de.parentCluster = getFirstFreeEntry(de.numEnts)
|
150
|
+
de.writeEntry(@bs)
|
151
|
+
@data, @all_clusters = getDirData
|
152
|
+
de
|
153
|
+
end
|
154
|
+
|
155
|
+
# ////////////////////////////////////////////////////////////////////////////
|
156
|
+
# // Utility functions.
|
157
|
+
|
158
|
+
# Get free entry or entries in directory data. If not exist, allocate cluster.
|
159
|
+
def getFirstFreeEntry(num_entries = 1, behavior = GF_W98)
|
160
|
+
0.step(@data.size - 1, DIR_ENT_SIZE) do |offset|
|
161
|
+
next if @data[offset] != DirectoryEntry::AF_NOT_ALLOCATED && @data[offset] != DirectoryEntry::AF_DELETED
|
162
|
+
num = countFreeEntries(behavior, @data[offset..-1])
|
163
|
+
return offset.divmod(@bs.bytesPerCluster)[1], getClusterStatus(offset).number if num >= num_entries
|
164
|
+
end
|
165
|
+
|
166
|
+
# Must allocate another cluster.
|
167
|
+
cluster = @bs.allocClusters(@cluster)
|
168
|
+
@data += MemoryBuffer.create(@bs.bytesPerCluster)
|
169
|
+
@all_clusters << mkClusterStatus(cluster, 0)
|
170
|
+
return 0, cluster
|
171
|
+
end
|
172
|
+
|
173
|
+
# Return the number of contiguous free entries starting at buf[0] according to behavior.
|
174
|
+
def countFreeEntries(behavior, buf)
|
175
|
+
num_free = 0
|
176
|
+
0.step(buf.size - 1, DIR_ENT_SIZE) do |offset|
|
177
|
+
if isFree(buf[offset], behavior)
|
178
|
+
num_free += 1
|
179
|
+
else
|
180
|
+
return num_free
|
181
|
+
end
|
182
|
+
end
|
183
|
+
num_free
|
184
|
+
end
|
185
|
+
|
186
|
+
def isFree(allocStatus, behavior)
|
187
|
+
if behavior == GF_W98
|
188
|
+
return true if allocStatus == DirectoryEntry::AF_NOT_ALLOCATED || allocStatus == DirectoryEntry::AF_DELETED
|
189
|
+
elsif behavior == GF_WXP
|
190
|
+
return true if allocStatus == DirectoryEntry::AF_NOT_ALLOCATED
|
191
|
+
else
|
192
|
+
raise "Fat32Directory#isFree: Unknown behavior: #{behavior}"
|
193
|
+
end
|
194
|
+
false
|
195
|
+
end
|
196
|
+
|
197
|
+
def getDirData
|
198
|
+
allClusters = []
|
199
|
+
clus = @cluster
|
200
|
+
allClusters << mkClusterStatus(clus, 0)
|
201
|
+
buf = @bs.getCluster(clus); data = nil
|
202
|
+
while (data = @bs.getNextCluster(clus)) != nil
|
203
|
+
clus = data[0]; buf += data[1]
|
204
|
+
allClusters << mkClusterStatus(clus, 0)
|
205
|
+
end
|
206
|
+
return buf, allClusters
|
207
|
+
end
|
208
|
+
|
209
|
+
# TODO: - Loose this idea.
|
210
|
+
def mkClusterStatus(num, dirty)
|
211
|
+
status = OpenStruct.new
|
212
|
+
status.number = num
|
213
|
+
status.dirty = dirty
|
214
|
+
status
|
215
|
+
end
|
216
|
+
|
217
|
+
def getClusterStatus(offset)
|
218
|
+
idx = offset.divmod(@bs.bytesPerCluster)[0]
|
219
|
+
@all_clusters[idx]
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end # module Fat32
|