/Disks.pas
Pascal | 406 lines | 341 code | 43 blank | 22 comment | 10 complexity | 621adc4eaa53a59b3bcdb8607f535d8d MD5 | raw file
- {*******************************************************************************
- NT Disk access unit
- allows physical disks to be accessed as TStream
- by Miika Sihvola (smiika@mbnet.fi)
- *******************************************************************************}
- unit Disks;
-
- interface
-
- uses Classes, Windows, Dialogs;
-
- const PartitionTableOffset = $1BE; // 446 location inside master boot record
-
- const IOCTL_DISK_GET_DRIVE_GEOMETRY = (7 shl 16);
- IOCTL_STORAGE_CHECK_VERIFY = 2967552;
- MediaTypes : array [0..25] of string =
- ('Unknown', // Format is unknown
- 'F5_1Pt2_512', // 5.25", 1.2MB, 512 bytes/sector
- 'F3_1Pt44_512', // 3.5", 1.44MB, 512 bytes/sector
- 'F3_2Pt88_512', // 3.5", 2.88MB, 512 bytes/sector
- 'F3_20Pt8_512', // 3.5", 20.8MB, 512 bytes/sector
- 'F3_720_512', // 3.5", 720KB, 512 bytes/sector
- 'F5_360_512', // 5.25", 360KB, 512 bytes/sector
- 'F5_320_512', // 5.25", 320KB, 512 bytes/sector
- 'F5_320_1024', // 5.25", 320KB, 1024 bytes/sector
- 'F5_180_512', // 5.25", 180KB, 512 bytes/sector
- 'F5_160_512', // 5.25", 160KB, 512 bytes/sector
- 'REMOVABLE', //'RemovableMedia', // Removable media other than floppy
- 'FIXED', //FixedMedia', // Fixed hard disk media
- 'F3_120M_512', // 3.5", 120M Floppy
- 'F3_640_512', // 3.5" , 640KB, 512 bytes/sector
- 'F5_640_512', // 5.25", 640KB, 512 bytes/sector
- 'F5_720_512', // 5.25", 720KB, 512 bytes/sector
- 'F3_1Pt2_512', // 3.5" , 1.2Mb, 512 bytes/sector
- 'F3_1Pt23_1024', // 3.5" , 1.23Mb, 1024 bytes/sector
- 'F5_1Pt23_1024', // 5.25", 1.23MB, 1024 bytes/sector
- 'F3_128Mb_512', // 3.5" MO 128Mb 512 bytes/sector
- 'F3_230Mb_512', // 3.5" MO 230Mb 512 bytes/sector
- 'F8_256_128', // 8", 256KB, 128 bytes/sector
- 'F3_200Mb_512', // 3.5", 200M Floppy (HiFD)
- 'F3_240M_512', // 3.5", 240Mb Floppy (HiFD)
- 'F3_32M_512'); // 3.5", 32Mb Floppy
-
- type
- TDiskGeometry = packed record
- Cylinders: LARGE_INTEGER;
- MediaType: DWORD;
- TracksPerCylinder: dword;
- SectorsPerTrack: dword;
- BytesPerSector: dword;
- end;
-
- TDisk = class(TStream)
- public
- Number: Integer;
- Physical:boolean;
- Name: string;
- DiskHandle: THandle;
- DiskGeometry: TDiskGeometry;
- DiskSerial:String;
- Size: Int64;
- TotalSectors: Int64;
- SecBuf : array [0..511] of Byte;
-
- constructor Create(DiskNumber: Byte); overload;
- constructor Create(DriveLetter: string; DiskNumber:integer); overload;
- destructor Destroy(); override;
-
- function OpenVolumePhysical:boolean;
- function OpenVolumeByLetter(Letter:string):boolean;
- procedure CloseVolume;
-
- function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
- function Read(var Buffer; Count: Longint): Longint; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- function SizeStr: string;
- end;
-
- //partition entry inside partition table
- TPartDesc = record
- BootIndicator: Byte; //one of partitions have this value at 80 (which means partition is avtive), which is the partition that MBR code boots
- BeginCHS: array [1..3] of Byte;
- PartitionType: Byte; //important byte, tells the filesystem type
- EndCHS: array [1..3] of Byte;
- StartSect: Cardinal; //physical sector location where partition begins
- SizeSect: Cardinal; //how many sectors partition has (size in bytes = 512 * SizeSect)
- end;
- //partiton table structure inside MBR
- TPartitionTable = array [1..4] of TPartDesc;
-
- //MBR, the first sector of every disk, and in begin of extended partitions
- TMBR = packed record
- BootCode: array [$0..$1BD] of Byte; //x86 16-bit realmode code loaded to address 7E00 by BIOS, no boot code in extended partition's MBR
- Partitions: TPartitionTable; //starts at $1BE
- Signature: Word; //must be 55 AA
- end;
-
- //application specĂfic structure
- TPartInfo = record
- Disk: TDisk;
- StartSec: Int64;
- SizeSec: Int64;
- PartitionTable: TPartitionTable;
- sLetter: string;
- partno:Byte;
- sVolName: string;
- sFSName: string;
- sVolSerial: string;
- end;
- pPartInfo = ^TPartInfo;
-
- TPARTITION_INFORMATION = packed record
- StartingOffset:LARGE_INTEGER;
- PartitionLength:LARGE_INTEGER;
- HiddenSectors:DWORD;
- PartitionNumber:DWORD;
- PartitionType:BYTE;
- BootIndicator:BOOLEAN;
- RecognizedPartition:BOOLEAN;
- RewritePartition:BOOLEAN;
- end;
-
- TSTORAGE_DEVICE_NUMBER = packed record
- // The FILE_DEVICE_XXX type for this device.
- DeviceType:DWORD;
- // The number of this device
- DeviceNumber:DWORD;
- // If the device is partitionable, the partition number of the device. Otherwise -1
- PartitionNumber:DWORD;
- end;
-
- procedure GetPartitionInfo(driveletter: string;
- var sVolName: string; var sFSName: string; var sVolSerial: string);
- function GetFSName(pDesc: TPartDesc; Disk:TDisk): string;
-
- //var DiskHandle : THandle;
- //var Disk : TDisk;
- //var I : Byte;
-
- implementation
-
- uses SysUtils, DiskName, Main,
- NTFS, FAT32;
-
- constructor TDisk.Create(DiskNumber: Byte);
- var s1 : string;
- begin
- Physical:=true;
- Number:=DiskNumber;
- // get actual device name in future
- //s1:=ReadPhysicalDriveInNT(DiskNumber, nil); // diskname.pas
- DiskSerial:='-';
- s1:=GetDriveManufacturerAndModel(DiskNumber,DiskSerial,nil); // diskname.pas
- insert('-',DiskSerial,5);
- Name := 'Disk'+IntToStr(Number)+' '+ s1;
- OpenVolumePhysical;
- end;
-
- constructor TDisk.Create(DriveLetter: string; DiskNumber: integer);
- var VolName, FSName, VolSerial: string;
- begin
- Physical:=false;
- Number:=DiskNumber;
- Name := DriveLetter + ': nonPhysicalDrive' + IntToStr(Number);
- GetPartitionInfo(DriveLetter, VolName, FSName, VolSerial);
- OpenVolumeByLetter(Driveletter);
- end;
-
- destructor TDisk.Destroy;
- begin
- CloseVolume;
- inherited Destroy;
- end;
-
- procedure TDisk.CloseVolume;
- begin
- if Win32Platform=VER_PLATFORM_WIN32_NT then
- CloseHandle(DiskHandle)
- else
- DeleteFile('\\.\INT13EXT');
- DiskHandle:=0;
- end;
-
- function TDisk.OpenVolumePhysical:boolean;
- var i: cardinal;
- begin
- result:=false;
- if Win32Platform=VER_PLATFORM_WIN32_NT then
- begin
- DiskHandle := CreateFile(pchar('\\.\PhysicalDrive' + IntToStr(Number)), GENERIC_READ,
- FILE_SHARE_READ OR FILE_SHARE_WRITE, nil, OPEN_EXISTING,
- 0, //FILE_FLAG_SEQUENTIAL_SCAN OR FILE_FLAG_NO_BUFFERING, //0,
- 0)
- end else begin
- //Handle := CreateFile(pchar('\\.\PhysicalDrive' + IntToStr(DiskNumber)), GENERIC_READ, FILE_SHARE_READ , nil, OPEN_EXISTING, 0, 0);
- //Handle := CreateFile(pchar('\\.\INT13EXT.VXD'), 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);
- DiskHandle := CreateFile(pchar('\\.\vwin32'), 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);
- end;
-
- if not DeviceIoControl(DiskHandle, IOCTL_STORAGE_CHECK_VERIFY, nil, 0, nil, 0, I, nil)
- then begin
- // make sure media is loaded
- //CloseHandle(Handle);
- exit;
- //SysErrMsg(GetLastError, 'checkdisk');
- end;
-
- if (DiskHandle <> INVALID_HANDLE_VALUE) then
- begin
- result:=true;
- if DeviceIoControl(DiskHandle, IOCTL_DISK_GET_DRIVE_GEOMETRY, nil, 0, @DiskGeometry, SizeOf(DiskGeometry), I, nil) then
- begin
- TotalSectors := DiskGeometry.cylinders.lowpart * DiskGeometry.trackspercylinder * DiskGeometry.sectorspertrack;
- Size := TotalSectors*512; //DiskGeometry.BytesPerSector;
- end else begin
- CloseHandle(DiskHandle);
- raise EInOutError.CreateFmt({s1}'', ['1']); // ('Failed getting disk geometry.');
- end;
- end else begin
- CloseHandle(DiskHandle);
- raise EInOutError.CreateFmt({s1}'', ['1']); // ('Failed getting disk device handle.');
- end;
- end;
-
- function TDisk.OpenVolumeByLetter(Letter:string):boolean;
- var I : Cardinal;
- begin
- result:=false;
- if Win32Platform=VER_PLATFORM_WIN32_NT then
- begin
- DiskHandle := CreateFile(pchar('\\.\'+Letter+':'), GENERIC_READ,
- FILE_SHARE_READ OR FILE_SHARE_WRITE, nil, OPEN_EXISTING,
- FILE_FLAG_SEQUENTIAL_SCAN OR FILE_FLAG_NO_BUFFERING, 0)
- end else begin
- //Handle := CreateFile(pchar('\\.\PhysicalDrive' + IntToStr(DiskNumber)), GENERIC_READ, FILE_SHARE_READ , nil, OPEN_EXISTING, 0, 0);
- //Handle := CreateFile(pchar('\\.\INT13EXT.VXD'), 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);
- //Handle := CreateFile(pchar('\\.\vwin32'), 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);
- end;
-
- if not DeviceIoControl(DiskHandle, IOCTL_STORAGE_CHECK_VERIFY, nil, 0, nil, 0, I, nil) then
- begin
- //CloseHandle(Handle);
- exit;
- end;
-
- if (DiskHandle <> INVALID_HANDLE_VALUE) then
- begin
- if DeviceIoControl(DiskHandle, IOCTL_DISK_GET_DRIVE_GEOMETRY, nil, 0, @DiskGeometry, SizeOf(DiskGeometry), I, nil) then
- begin
- TotalSectors := DiskGeometry.cylinders.lowpart * DiskGeometry.trackspercylinder * DiskGeometry.sectorspertrack;
- Size := TotalSectors * 512; //DiskGeometry.BytesPerSector;
- result:=true;
- end else begin
- CloseHandle(DiskHandle);
- raise EInOutError.CreateFmt(name, ['1']); // ('Failed getting disk geometry.');
- end;
- end else begin
- CloseHandle(DiskHandle);
- raise EInOutError.CreateFmt(name, ['1']); // ('Failed getting disk device handle.');
- end;
- end;
-
- ////////////////////////////////////////////////////////////////////////////////
-
- function TDisk.Read(var Buffer; Count: Integer): Longint;
- var BytesRead: Cardinal;
- begin
- if Count = 0 then begin result:=0; exit; end;
- if Count mod 512 <> 0 then
- raise EInOutError.Create('Number of bytes to read must be aligned to 512 bytes.');
- if not ReadFile(DiskHandle, Buffer, Count, BytesRead, nil) then
- //RaiseLastWin32Error;
- SysErrMsg(GetLastError, 'DiskRead: ');
- Result := BytesRead;
- end;
-
- function TDisk.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
- var
- HighOffset: Integer;
- begin
- if Offset mod 512 <> 0 then
- raise EInOutError.Create('Number of bytes to read must be aligned to 512 bytes.');
- HighOffset := (Offset shr 32) and $00000000FFFFFFFF;
- case Origin of
- soBeginning:
- Result := SetFilePointer(DiskHandle, Integer(Offset and $00000000FFFFFFFF), @HighOffset, FILE_BEGIN);
- soCurrent:
- Result := SetFilePointer(DiskHandle, Integer(Offset and $00000000FFFFFFFF), @HighOffset, FILE_CURRENT);
- soEnd:
- Result := SetFilePointer(DiskHandle, Integer(Offset and $00000000FFFFFFFF), @HighOffset, FILE_END);
- else
- Result := 0;
- end;
- end;
-
- function TDisk.SizeStr: string;
- begin
- Result := IntToSize(Size);
- end;
-
- function TDisk.Write(const Buffer; Count: Integer): Longint;
- begin
- raise EInOutError.Create('Writing to physical disks is not implemented.');
- end;
-
- procedure GetPartitionInfo(driveletter: string; var sVolName, sFSName, sVolSerial: string);
- var MaximumComponentLength,
- VOLSERIAL, FileSystemFlags: DWORD;
- VOLNAME: array[0..31] of char;
- FSNAME: array[0..15] of char;
- Size : integer;
- begin
- Size := SizeOf(FSNAME);
- FillChar(FSNAME, Size, 0);
- GetVolumeInformation(PAnsiChar(driveletter + ':\'),
- VOLNAME,32,
- @VOLSERIAL,MaximumComponentLength,
- FileSystemFlags,
- FSNAME, 16);
- sVolName:=StrPas(@VOLNAME[0]);
- sFSName:=StrPas(@FSNAME[0]);
- sVolSerial := IntToHex(HiWord(VOLSERIAL), 4) + '-' +
- IntToHex(LoWord(VOLSERIAL), 4);
- end;
-
- function GetFSName(pDesc: TPartDesc; Disk:TDisk): string;
- begin
- Disk.Position := pDesc.StartSect * 512;
- Disk.Read(Disk.SecBuf,512);
- case pDesc.PartitionType of
- $00: Result := 'Free';
- $05: Result := 'Extended DOS-partition';
- $0F: Result := 'Extended XINT13';
-
- $01: Result := 'FAT12';
- $02: Result := 'Xenix root';
- $03: Result := 'Xenix user';
- $04: Result := 'FAT16 < 32M';
- $06: Result := 'FAT16 > 32M MSDOSV4 HUGE';
-
- $07: Result := 'NTFS';
- $08: Result := 'AIX';
- $09: Result := 'AIX boot';
- $0A: Result := 'OS/2 boot Manager';
-
- $0B: Result := 'FAT32';
- $0C: Result := 'FAT32 XINT13';
- $0E: Result := 'FAT16 XINT13';
- $11: Result := 'Hidden FAT12';
-
- $12: Result := 'Compaq diagnostics';
-
- $14: Result := 'Hidden FAT16 (<32MB)';
- $16: Result := 'Hidden FAT16B (>=32MB)';
- $17: Result := 'Hidden NTFS';
- $1B: Result := 'Hidden FAT32';
- $1C: Result := 'Hidden FAT32 XINT13';
- $1E: Result := 'Hidden FAT16 XINT13';
-
- $2C: Result := 'WildFile/Adaptec GOBack';
- $3C: Result := 'PowerQuest Recoverable Partition';
-
- $40: Result := 'VENIX 80286';
- $41: Result := 'PowerPC BOOT';
- $42: Result := 'Veritas Logical Disk Manager';
-
- $51: Result := 'Novell';
- $52: Result := 'MICROPORT';
- $63: Result := 'Unix';
- $64: Result := 'Novell NetWare 286';
- $65: Result := 'Novell NetWare (3.11 and 4.1)';
- $66: Result := 'Novell NetWare 386';
-
- $75: Result := 'PC/IX';
- $80: Result := 'OLD Minix';
-
- $81: Result := 'Linux/Minix v1.4b+';
- $82: Result := 'Linux Swap Partition';
- $83: Result := 'Linux Ext2/3/Rsr';
- $84: Result := 'OS/2 hiding type 04h partition';
- $85: Result := 'Linux Extended';
-
- $86: Result := 'NT FAT volume set';
- $87: Result := 'NT IFS volume set';
-
- $93: Result := 'Amoeba/Hidden Linux native FS (Reiser/Ext3/2)';
- $94: Result := 'Amoeba BBT';
- $A5: Result := 'BSD / 386';
- $AF: Result := 'APPLE_HFS';
- $B7: Result := 'BSDI fs';
- $B8: Result := 'BSDI swap';
-
- $C6: Result := 'SYRINX / Disabled NT FAT volume set';
- $C7: Result := 'SYRINX / Disabled NT IFS volume set';
-
- $DB: Result := 'CP/M';
- $DE: Result := 'Dell Corporation diagnostic partition';
-
- $E1: Result := 'DOS ACCESS';
- $F2: Result := 'DOS SECONDARY';
- $FF: Result := 'BBT';
- else Result := 'Unknown (' + IntToHex(pDesc.PartitionType,2) + ')';
- end;
- end;
-
- end.