MiTec/Common/MiTeC_Disk.pas
2024-07-06 22:30:25 +02:00

283 lines
7.8 KiB
ObjectPascal

{*******************************************************}
{ MiTeC Common Routines }
{ Low-Level Disk Access }
{ }
{ Copyright (c) 1997-2016 Michal Mutl }
{ }
{*******************************************************}
{$INCLUDE Compilers.inc}
unit MiTeC_Disk;
interface
uses {$IFDEF RAD9PLUS}
WinAPI.Windows, System.SysUtils, System.Classes,
{$ELSE}
Windows, SysUtils, Classes,
{$ENDIF}
MiTeC_WinIOCTL, MiTeC_Storage, MiTeC_Windows;
const
PartitionTableOffset = $1BE;
type
TPartitionDescriptor = record
BootIndicator: Byte;
BeginCHS: array [1..3] of Byte;
PartitionType: Byte;
EndCHS: array [1..3] of Byte;
StartSector: Cardinal;
SectorCount: Cardinal;
end;
TPartitionTable = array [1..4] of TPartitionDescriptor;
TDiskAccess = class(TStream)
private
FDH: Cardinal;
FDN: Byte;
FSize,FTS: Int64;
FPT: TPartitionTable;
FBPS: Cardinal;
public
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;
constructor Create(ADiskNumber: Byte);
destructor Destroy; override;
property Handle: Cardinal read FDH;
property DiskNumber: Byte read FDN;
property TotalSize: Int64 read FSize;
property TotalSectors: Int64 read FTS;
property BytesPerSector: Cardinal read FBPS;
property Partitions: TPartitionTable read FPT;
end;
TFAT32BootRecord = packed record
BootCode: array [0..2] of Byte;
OemName: array [0..7] of Char;
BytesPerSec: Word;
SecPerClust: Byte;
ReservedSectors: Word;
NumOfFatCopies: Byte;
MaxRootEntries: Word;
SectorsOld: Word;
MediaDescriptor: Byte;
SectorsPerFatOld: Word;
SectorsPerTrack: Word;
NumOfHeads: Word;
HiddenSectors: Cardinal;
SectorsSize: Cardinal;
SectorsPerFat: Cardinal;
Flags: Word;
Version: Word;
RootDirStartClust: Cardinal;
FileSystemInfoSec: Word;
BackupSec: Word;
Reserved: array [0..11] of Byte;
DriveNumber: Word;
ExtraSignature: Byte;
SerialNumber: Cardinal;
VolumeName: array [0..10] of Char;
FATName: array [0..7] of Char;
Code: array [0..419] of Byte;
BootSignature: Word;
end;
TFAT = (fat16, fat32);
TFAT32 = class
private
FDA: TDiskAccess;
FBS: TFAT32BootRecord;
FFAT: TFAT;
FSS,FDSS: Cardinal;
public
constructor Create(ADiskNumber: Byte);
destructor Destroy; override;
procedure ReadCluster(ANumber: Cardinal; AStream: TMemoryStream);
function NextCluster(ACurrent: Cardinal): Cardinal;
property FAT: TFAT read FFAT;
end;
TNTFSBootRecord = packed record
BootCode: array [0..2] of Byte;
OemName: array [0..7] of Char;
BytesPerSec: Word;
SecPerClust: Byte;
ReservedSectors: Word;
Unused0: array [0..4] of Byte;
MediaDescriptor: Byte;
Unused1: Word;
SectorsPerTrack: Word;
NumOfHeads: Word;
HiddenSectors: Cardinal;
Unused2: array [0..7] of Byte;
SectorSize: Int64;
MTFClust: Int64;
MTFMirrotClust: Int64;
ClustersPerFileRec: Cardinal;
ClustersPerIndexBlock: Cardinal;
SerialNumber: Int64;
Checksum: Cardinal;
Code: array [0..425] of Byte;
BootSignature: Word;
end;
TNTFS = class
private
FDA: TDiskAccess;
FBS: TNTFSBootRecord;
public
constructor Create(ADiskNumber: Byte);
destructor Destroy; override;
procedure ReadClusters(AStart,ACount: Cardinal; AStream: TMemoryStream);
end;
implementation
var
SectorBuffer: TBytes;
constructor TDiskAccess.Create(ADiskNumber: Byte);
var
DG: TDiskGeometry;
n: Cardinal;
begin
FDH:=GetHandle_PhysicalDrive(ADiskNumber);
if (FDH=INVALID_HANDLE_VALUE) and (ADiskNumber>64) then
FDH:=GetHandle_LogicalDrive(ADiskNumber);
if FDH<>INVALID_HANDLE_VALUE then begin
//Position:=0;
FBPS:=bBytesPerSector;
if DeviceIoControl(FDH,IOCTL_DISK_GET_DRIVE_GEOMETRY,nil,0,@DG,SizeOf(DG),n,nil) then begin
FTS:=DG.Cylinders.LowPart*DG.TracksPerCylinder*DG.SectorsPerTrack;
FSize:=FTS*DG.BytesPerSector;
FBPS:=DG.BytesPerSector;
end else begin
CloseHandle(FDH);
raise EInOutError.Create('Cannot get disk geometry.');
end;
SetLength(SectorBuffer,FBPS);
Read(SectorBuffer,Length(SectorBuffer));
Move(SectorBuffer[PartitionTableOffset],FPT,SizeOf(TPartitionTable));
end else begin
CloseHandle(FDH);
raise EInOutError.Create('Cannot get disk access.');
end;
end;
destructor TDiskAccess.Destroy;
begin
CloseHandle(FDH);
end;
function TDiskAccess.Read(var Buffer; Count: Integer): Longint;
var
BytesRead: Cardinal;
begin
if Count mod FBPS<>0 then
raise EInOutError.Create('Number of bytes to read must be aligned to 512 bytes.');
if not ReadFile(FDH,Buffer,Count,BytesRead,nil) then
RaiseLastOSError;
Result:=BytesRead;
end;
function TDiskAccess.Seek(const Offset: Int64; Origin: TSeekOrigin): int64;
var
HighOffset: Integer;
begin
if Offset mod FBPS<>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(FDH,Integer(Offset and $00000000FFFFFFFF),@HighOffset,FILE_BEGIN);
soCurrent: Result:=SetFilePointer(FDH,Integer(Offset and $00000000FFFFFFFF),@HighOffset,FILE_CURRENT);
soEnd: Result:=SetFilePointer(FDH,Integer(Offset and $00000000FFFFFFFF),@HighOffset,FILE_END);
else Result:=0;
end;
end;
function TDiskAccess.Write(const Buffer; Count: Integer): Longint;
begin
raise EInOutError.Create('Read-only access granted.');
end;
{ TFAT32 }
constructor TFAT32.Create(ADiskNumber: Byte);
begin
FDA:=TDiskAccess.Create(ADiskNumber);
FSS:=FBS.ReservedSectors;
if Pos('FAT32',FBS.FATName)=1 then begin
FFAT:=fat32;
FDSS:=FBS.ReservedSectors+(FBS.NumOfFatCopies*FBS.SectorsPerFat);
end else begin
FFAT:=fat16;
FDSS:=FBS.ReservedSectors+(FBS.NumOfFatCopies*FBS.SectorsPerFatOld)+(FBS.MaxRootEntries*32 div FDA.BytesPerSector);
end;
end;
destructor TFAT32.Destroy;
begin
FDA.Free;
inherited;
end;
function TFAT32.NextCluster(ACurrent: Cardinal): Cardinal;
var
Sector,
Offset: Cardinal;
begin
if FFAT=fat32 then begin
Sector:=ACurrent div 128;
Offset:=(ACurrent mod 128)*4;
FDA.Position:=Int64(FSS+Sector)*Int64(FDA.BytesPerSector);
FDA.Read(SectorBuffer,Length(SectorBuffer));
Move(SectorBuffer[Offset],Result,4);
end else begin
Sector:=ACurrent div 256;
Offset:=(ACurrent mod 256)*2;
FDA.Position:=Int64(FSS+Sector)*Int64(FDA.BytesPerSector);
FDA.Read(SectorBuffer,Length(SectorBuffer));
Move(SectorBuffer[Offset],Result,2);
end;
end;
procedure TFAT32.ReadCluster(ANumber: Cardinal; AStream: TMemoryStream);
begin
AStream.SetSize(Int64(FBS.SecPerClust)*FDA.BytesPerSector);
FDA.Position:=Int64(FDSS+(ANumber*FBS.SecPerClust)-(2*FBS.SecPerClust))*FDA.BytesPerSector;
AStream.CopyFrom(FDA,Int64(FBS.SecPerClust)*FDA.BytesPerSector);
AStream.Position:=0;
end;
{ TNTFS }
constructor TNTFS.Create(ADiskNumber: Byte);
begin
FDA:=TDiskAccess.Create(ADiskNumber);
end;
destructor TNTFS.Destroy;
begin
FDA.Free;
inherited;
end;
procedure TNTFS.ReadClusters(AStart, ACount: Cardinal; AStream: TMemoryStream);
begin
if AStart>FBS.SectorSize div FBS.SecPerClust then
raise EInOutError.Create('Tried to read cluster out of range.');
AStream.SetSize(Int64(FBS.SecPerClust*ACount)*FDA.BytesPerSector);
FDA.Position:=Int64(FBS.SecPerClust*AStart)*FDA.BytesPerSector;
AStream.CopyFrom(FDA,Int64(FBS.SecPerClust*ACount)*FDA.BytesPerSector);
AStream.Position:=0;
end;
end.