283 lines
7.8 KiB
ObjectPascal
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.
|