Return to Snippet

Revision: 21614
at December 16, 2009 12:24 by pateman


Initial Code
//******************************************************************************
//
//    UDiskAccess - A low-level disk I/O stream class (tested under Vista).
//    Copyright © 2009 Patryk Nusbaum.
//    All rights reserved.
//
//    Last modified: 10.10.2009
//    Mail: [email protected]
//    WWW: www.pateman.net76.net
//
//    License
//    -------------------
//    Redistribution and use in source and binary forms, with or without
//    modification, are permitted provided that the following conditions are
//    met:
//
//      * Redistributions of source code must retain the above copyright notice,
//        this list of conditions and the following disclaimer.
//      * Redistributions in binary form must reproduce the above copyright
//        notice, this list of conditions and the following disclaimer in the
//        documentation and/or other materials provided with the distribution.
//      * Neither the name of Patryk Nusbaum nor the names of his contributors
//        may be used to endorse or promote products derived from this software
//        without specific prior written permission.
//
//    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
//    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
//    OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
//    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
//    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//******************************************************************************
unit UDiskAccess;

interface

uses
  Classes;

const
  NTDLLFile = 'ntdll.dll';

  FILE_SHARE_READ = $00000001;
  FILE_SHARE_WRITE = $00000002;
  FILE_READ_ACCESS = $0001;
  FILE_WRITE_ACCESS = $0002;
  FILE_OPEN = $00000001;
  FILE_SYNCHRONOUS_IO_NONALERT = $00000020;

  SYNCHRONIZE = $00100000;
  OBJ_CASE_INSENSITIVE = $00000040;

type
  LONG = Longint;
  LONGLONG = Int64;
  ULONG_PTR = Longword;
  USHORT = Word;
  DWORD = Longword;

  NTSTATUS = LONG;
  ACCESS_MASK = DWORD;

  PWSTR = PWideChar;
  LPCWSTR = PWideChar;

  PVOID = Pointer;

  { .: HANDLE :. }
  HANDLE = Cardinal;
  PHANDLE = ^HANDLE;

  { .: ULONG :. }
  ULONG = Longword;
  PULONG = ^ULONG;

  { .: IO_STATUS_BLOCK :. }
  _IO_STATUS_BLOCK = record
    Status: NTSTATUS;
    Information: ULONG_PTR;
  end;
  IO_STATUS_BLOCK = _IO_STATUS_BLOCK;
  PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;

  { .: UNICODE_STRING :. }
  _UNICODE_STRING = record
    Length: USHORT;
    MaximumLength: USHORT;
    Buffer: PWSTR;
  end;
  UNICODE_STRING = _UNICODE_STRING;
  PUNICODE_STRING = ^UNICODE_STRING;

  { .: OBJECT_ATTRIBUTES :. }
  _OBJECT_ATTRIBUTES = record
    Length: ULONG;
    RootDirectory: HANDLE;
    ObjectName: PUNICODE_STRING;
    Attributes: ULONG;
    SecurityDescriptor: PVOID;
    SecurityQualityOfService: PVOID;
  end;
  OBJECT_ATTRIBUTES = _OBJECT_ATTRIBUTES;
  POBJECT_ATTRIBUTES = ^OBJECT_ATTRIBUTES;

  { .: LARGE_INTEGER :. }
  _LARGE_INTEGER = record
    case Integer of
      0: (LowPart: DWORD;
          HighPart: LONG);
      1: (QuadPart: LONGLONG);
  end;
  LARGE_INTEGER = _LARGE_INTEGER;
  PLARGE_INTEGER = ^LARGE_INTEGER;

  { .: PIO_APC_ROUTINE :. }
  PIO_APC_ROUTINE = procedure(ApcContext: PVOID;
    IoStatusBlock: PIO_STATUS_BLOCK; Reserved: ULONG); stdcall;

  { .: FS_INFORMATION_CLASS :. }
  _FSINFOCLASS = (FileFsVolumeInformation = 1,
    FileFsLabelInformation,      // 2
    FileFsSizeInformation,       // 3
    FileFsDeviceInformation,     // 4
    FileFsAttributeInformation,  // 5
    FileFsControlInformation,    // 6
    FileFsFullSizeInformation,   // 7
    FileFsObjectIdInformation,   // 8
    FileFsDriverPathInformation, // 9
    FileFsVolumeFlagsInformation,// 10
    FileFsMaximumInformation);
  FS_INFORMATION_CLASS = _FSINFOCLASS;

  { .: FILE_FS_SIZE_INFORMATION :. }
  _FILE_FS_SIZE_INFORMATION = record
    TotalAllocationUnits: LARGE_INTEGER;
    AvailableAllocationUnits: LARGE_INTEGER;
    SectorsPerAllocationUnit: ULONG;
    BytesPerSector: ULONG;
  end;
  FILE_FS_SIZE_INFORMATION = _FILE_FS_SIZE_INFORMATION;
  PFILE_FS_SIZE_INFORMATION = ^FILE_FS_SIZE_INFORMATION;

  { .: TRawDiskAccessStream :. }
  TRawDiskAccessStream = class sealed(TStream)
  private
    { Private declarations }
    FHandleRead, FHandleWrite: HANDLE;
    FOffset: LARGE_INTEGER;
    FSize: Int64;
    FSectorCount: Cardinal;
    FSectorSize: Cardinal;
  protected
    { Protected declarations }
    function GetSize(): Int64; override;
  public
    { Public declarations }
    constructor Create(const ADriveLetter: Char);
    destructor Destroy(); override;

    function SectorOffset(const ASector: Cardinal): Int64;

    // the "Count" parameter is ignored
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;

    property SectorCount: Cardinal read FSectorCount;
    property SectorSize: Cardinal read FSectorSize;
  end;

procedure RtlInitUnicodeString(DestinationString: PUNICODE_STRING;
  SourceString: LPCWSTR); stdcall; external NTDLLFile name 'RtlInitUnicodeString';
function NtCreateFile(FileHandle: PHANDLE; DesiredAccess: ACCESS_MASK;
  ObjectAttributes: POBJECT_ATTRIBUTES; IoStatusBlock: PIO_STATUS_BLOCK;
  AllocationSize: PLARGE_INTEGER; FileAttributes: ULONG; ShareAccess: ULONG;
  CreateDisposition: ULONG; CreateOptions: ULONG; EaBuffer: PVOID;
  EaLength: ULONG): NTSTATUS; stdcall; external NTDLLFile name 'NtCreateFile';
function NtReadFile(FileHandle: HANDLE; Event: HANDLE;
  ApcRoutine: PIO_APC_ROUTINE; ApcContext: PVOID;
  IoStatusBlock: PIO_STATUS_BLOCK; Buffer: PVOID; Length: ULONG;
  ByteOffset: PLARGE_INTEGER; Key: PULONG): NTSTATUS; stdcall; external NTDLLFile name 'NtReadFile';
function NtWriteFile(FileHandle: HANDLE; Event: HANDLE;
  ApcRoutine: PIO_APC_ROUTINE; ApcContext: PVOID;
  IoStatusBlock: PIO_STATUS_BLOCK; Buffer: PVOID; Length: ULONG;
  ByteOffset: PLARGE_INTEGER; Key: PULONG): NTSTATUS; stdcall; external NTDLLFile name 'NtWriteFile';
function NtClose(Handle: HANDLE): NTSTATUS; stdcall; external NTDLLFile name 'NtClose';
function NtQueryVolumeInformationFile(FileHandle: HANDLE;
  IoStatusBlock: PIO_STATUS_BLOCK; FsInformation: PVOID; Length: ULONG;
  FsInformationClass: FS_INFORMATION_CLASS): NTSTATUS; stdcall; external NTDLLFile name 'NtQueryVolumeInformationFile';

const
  INVALID_HANDLE_VALUE = DWORD(-1); // from Windows.pas

implementation

{ .: NT_SUCCESS :. }
function NT_SUCCESS(const Status: NTSTATUS): Boolean;
begin
  Result := (Status >= 0);
end;

{ .: InitializeObjectAttributes :. }
procedure InitializeObjectAttributes(p: POBJECT_ATTRIBUTES; n: PUNICODE_STRING;
  a: ULONG; r: HANDLE; s: PVOID);
begin
  with p^ do
  begin
    Length := SizeOf(OBJECT_ATTRIBUTES);
    RootDirectory := r;
    Attributes := a;
    ObjectName := n;
    SecurityDescriptor := s;
    SecurityQualityOfService := nil;
  end;
end;

{ TRawDiskAccessStream }

constructor TRawDiskAccessStream.Create(const ADriveLetter: Char);
var
  DiskHandle: HANDLE;
  IOStatus: IO_STATUS_BLOCK;
  Status: NTSTATUS;
  ObjectAttr: OBJECT_ATTRIBUTES;
  DiskName: UNICODE_STRING;
  X: _FILE_FS_SIZE_INFORMATION;
begin
  inherited Create();

  FHandleRead := INVALID_HANDLE_VALUE;
  FHandleWrite := INVALID_HANDLE_VALUE;

  RtlInitUnicodeString(@DiskName, PChar('\DosDevices\' + ADriveLetter + ':'));
  InitializeObjectAttributes(@ObjectAttr, @DiskName, OBJ_CASE_INSENSITIVE, 0,
    nil);

  Status := NtCreateFile(@DiskHandle, SYNCHRONIZE or FILE_READ_ACCESS,
    @ObjectAttr, @IOStatus, nil, 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
    FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, nil, 0);
  if (NT_SUCCESS(Status)) then
    FHandleRead := DiskHandle;

  Status := NtCreateFile(@DiskHandle, SYNCHRONIZE or FILE_WRITE_ACCESS,
    @ObjectAttr, @IOStatus, nil, 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
    FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, nil, 0);
  if (NT_SUCCESS(Status)) then
  begin
    FHandleWrite := DiskHandle;

    Status := NtQueryVolumeInformationFile(FHandleRead, @IOStatus, @X,
      SizeOf(X), FileFsSizeInformation);

    FSectorCount := X.TotalAllocationUnits.QuadPart;
    FSectorSize := X.BytesPerSector;
    FSize := FSectorCount * FSectorSize;
  end;
end;

destructor TRawDiskAccessStream.Destroy;
begin
  if (FHandleRead <> INVALID_HANDLE_VALUE) and
    (FHandleWrite <> INVALID_HANDLE_VALUE) then
  begin
    NtClose(FHandleRead);
    NtClose(FHandleWrite);

    FHandleRead := INVALID_HANDLE_VALUE;
    FHandleWrite := INVALID_HANDLE_VALUE;
  end;

  inherited Destroy();
end;

function TRawDiskAccessStream.GetSize: Int64;
begin
  Result := FSize;
end;

function TRawDiskAccessStream.Read(var Buffer; Count: Integer): Longint;
var
  IOStatus: IO_STATUS_BLOCK;
  Status: NTSTATUS;
begin
  Result := -1;

  if (FHandleRead = INVALID_HANDLE_VALUE) then
    exit;

  Status := NtReadFile(FHandleRead, 0, nil, nil, @IOStatus, @Buffer,
    FSectorSize, @FOffset, nil);
  if (NT_SUCCESS(Status)) then
    Result := FSectorSize
  else
    Result := 0;
end;

function TRawDiskAccessStream.SectorOffset(const ASector: Cardinal): Int64;
begin
  if (ASector < FSectorCount) then
    Result := ASector * FSectorSize
  else
    Result := -1;
end;

function TRawDiskAccessStream.Seek(const Offset: Int64;
  Origin: TSeekOrigin): Int64;
var
  X: Int64;
begin
  case Origin of
    soBeginning:
      if (Offset <= GetSize()) then
        FOffset.QuadPart := Offset;
    soCurrent:
      begin
        X := FOffset.QuadPart + Offset;
        if (X <= GetSize()) then
          FOffset.QuadPart := X;
      end;
    soEnd:
      FOffset.QuadPart := GetSize() - Abs(Offset);
  end;

  Result := FOffset.QuadPart;
end;

function TRawDiskAccessStream.Write(const Buffer; Count: Integer): Longint;
var
  IOStatus: IO_STATUS_BLOCK;
  Status: NTSTATUS;
begin
  Result := -1;

  if (FHandleWrite = INVALID_HANDLE_VALUE) then
    exit;

  Status := NtWriteFile(FHandleWrite, 0, nil, nil, @IOStatus, @Buffer,
    FSectorSize, @FOffset, nil);
  if (NT_SUCCESS(Status)) then
    Result := FSectorSize
  else
    Result := 0;
end;

end.

Initial URL


Initial Description


Initial Title
Raw Disk Access Unit

Initial Tags


Initial Language
Delphi