PageRenderTime 36ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/Disks.pas

https://bitbucket.org/reiniero/diskbuddy
Pascal | 406 lines | 341 code | 43 blank | 22 comment | 10 complexity | 621adc4eaa53a59b3bcdb8607f535d8d MD5 | raw file
  1. {*******************************************************************************
  2. NT Disk access unit
  3. allows physical disks to be accessed as TStream
  4. by Miika Sihvola (smiika@mbnet.fi)
  5. *******************************************************************************}
  6. unit Disks;
  7. interface
  8. uses Classes, Windows, Dialogs;
  9. const PartitionTableOffset = $1BE; // 446 location inside master boot record
  10. const IOCTL_DISK_GET_DRIVE_GEOMETRY = (7 shl 16);
  11. IOCTL_STORAGE_CHECK_VERIFY = 2967552;
  12. MediaTypes : array [0..25] of string =
  13. ('Unknown', // Format is unknown
  14. 'F5_1Pt2_512', // 5.25", 1.2MB, 512 bytes/sector
  15. 'F3_1Pt44_512', // 3.5", 1.44MB, 512 bytes/sector
  16. 'F3_2Pt88_512', // 3.5", 2.88MB, 512 bytes/sector
  17. 'F3_20Pt8_512', // 3.5", 20.8MB, 512 bytes/sector
  18. 'F3_720_512', // 3.5", 720KB, 512 bytes/sector
  19. 'F5_360_512', // 5.25", 360KB, 512 bytes/sector
  20. 'F5_320_512', // 5.25", 320KB, 512 bytes/sector
  21. 'F5_320_1024', // 5.25", 320KB, 1024 bytes/sector
  22. 'F5_180_512', // 5.25", 180KB, 512 bytes/sector
  23. 'F5_160_512', // 5.25", 160KB, 512 bytes/sector
  24. 'REMOVABLE', //'RemovableMedia', // Removable media other than floppy
  25. 'FIXED', //FixedMedia', // Fixed hard disk media
  26. 'F3_120M_512', // 3.5", 120M Floppy
  27. 'F3_640_512', // 3.5" , 640KB, 512 bytes/sector
  28. 'F5_640_512', // 5.25", 640KB, 512 bytes/sector
  29. 'F5_720_512', // 5.25", 720KB, 512 bytes/sector
  30. 'F3_1Pt2_512', // 3.5" , 1.2Mb, 512 bytes/sector
  31. 'F3_1Pt23_1024', // 3.5" , 1.23Mb, 1024 bytes/sector
  32. 'F5_1Pt23_1024', // 5.25", 1.23MB, 1024 bytes/sector
  33. 'F3_128Mb_512', // 3.5" MO 128Mb 512 bytes/sector
  34. 'F3_230Mb_512', // 3.5" MO 230Mb 512 bytes/sector
  35. 'F8_256_128', // 8", 256KB, 128 bytes/sector
  36. 'F3_200Mb_512', // 3.5", 200M Floppy (HiFD)
  37. 'F3_240M_512', // 3.5", 240Mb Floppy (HiFD)
  38. 'F3_32M_512'); // 3.5", 32Mb Floppy
  39. type
  40. TDiskGeometry = packed record
  41. Cylinders: LARGE_INTEGER;
  42. MediaType: DWORD;
  43. TracksPerCylinder: dword;
  44. SectorsPerTrack: dword;
  45. BytesPerSector: dword;
  46. end;
  47. TDisk = class(TStream)
  48. public
  49. Number: Integer;
  50. Physical:boolean;
  51. Name: string;
  52. DiskHandle: THandle;
  53. DiskGeometry: TDiskGeometry;
  54. DiskSerial:String;
  55. Size: Int64;
  56. TotalSectors: Int64;
  57. SecBuf : array [0..511] of Byte;
  58. constructor Create(DiskNumber: Byte); overload;
  59. constructor Create(DriveLetter: string; DiskNumber:integer); overload;
  60. destructor Destroy(); override;
  61. function OpenVolumePhysical:boolean;
  62. function OpenVolumeByLetter(Letter:string):boolean;
  63. procedure CloseVolume;
  64. function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
  65. function Read(var Buffer; Count: Longint): Longint; override;
  66. function Write(const Buffer; Count: Longint): Longint; override;
  67. function SizeStr: string;
  68. end;
  69. //partition entry inside partition table
  70. TPartDesc = record
  71. BootIndicator: Byte; //one of partitions have this value at 80 (which means partition is avtive), which is the partition that MBR code boots
  72. BeginCHS: array [1..3] of Byte;
  73. PartitionType: Byte; //important byte, tells the filesystem type
  74. EndCHS: array [1..3] of Byte;
  75. StartSect: Cardinal; //physical sector location where partition begins
  76. SizeSect: Cardinal; //how many sectors partition has (size in bytes = 512 * SizeSect)
  77. end;
  78. //partiton table structure inside MBR
  79. TPartitionTable = array [1..4] of TPartDesc;
  80. //MBR, the first sector of every disk, and in begin of extended partitions
  81. TMBR = packed record
  82. 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
  83. Partitions: TPartitionTable; //starts at $1BE
  84. Signature: Word; //must be 55 AA
  85. end;
  86. //application specĂ­fic structure
  87. TPartInfo = record
  88. Disk: TDisk;
  89. StartSec: Int64;
  90. SizeSec: Int64;
  91. PartitionTable: TPartitionTable;
  92. sLetter: string;
  93. partno:Byte;
  94. sVolName: string;
  95. sFSName: string;
  96. sVolSerial: string;
  97. end;
  98. pPartInfo = ^TPartInfo;
  99. TPARTITION_INFORMATION = packed record
  100. StartingOffset:LARGE_INTEGER;
  101. PartitionLength:LARGE_INTEGER;
  102. HiddenSectors:DWORD;
  103. PartitionNumber:DWORD;
  104. PartitionType:BYTE;
  105. BootIndicator:BOOLEAN;
  106. RecognizedPartition:BOOLEAN;
  107. RewritePartition:BOOLEAN;
  108. end;
  109. TSTORAGE_DEVICE_NUMBER = packed record
  110. // The FILE_DEVICE_XXX type for this device.
  111. DeviceType:DWORD;
  112. // The number of this device
  113. DeviceNumber:DWORD;
  114. // If the device is partitionable, the partition number of the device. Otherwise -1
  115. PartitionNumber:DWORD;
  116. end;
  117. procedure GetPartitionInfo(driveletter: string;
  118. var sVolName: string; var sFSName: string; var sVolSerial: string);
  119. function GetFSName(pDesc: TPartDesc; Disk:TDisk): string;
  120. //var DiskHandle : THandle;
  121. //var Disk : TDisk;
  122. //var I : Byte;
  123. implementation
  124. uses SysUtils, DiskName, Main,
  125. NTFS, FAT32;
  126. constructor TDisk.Create(DiskNumber: Byte);
  127. var s1 : string;
  128. begin
  129. Physical:=true;
  130. Number:=DiskNumber;
  131. // get actual device name in future
  132. //s1:=ReadPhysicalDriveInNT(DiskNumber, nil); // diskname.pas
  133. DiskSerial:='-';
  134. s1:=GetDriveManufacturerAndModel(DiskNumber,DiskSerial,nil); // diskname.pas
  135. insert('-',DiskSerial,5);
  136. Name := 'Disk'+IntToStr(Number)+' '+ s1;
  137. OpenVolumePhysical;
  138. end;
  139. constructor TDisk.Create(DriveLetter: string; DiskNumber: integer);
  140. var VolName, FSName, VolSerial: string;
  141. begin
  142. Physical:=false;
  143. Number:=DiskNumber;
  144. Name := DriveLetter + ': nonPhysicalDrive' + IntToStr(Number);
  145. GetPartitionInfo(DriveLetter, VolName, FSName, VolSerial);
  146. OpenVolumeByLetter(Driveletter);
  147. end;
  148. destructor TDisk.Destroy;
  149. begin
  150. CloseVolume;
  151. inherited Destroy;
  152. end;
  153. procedure TDisk.CloseVolume;
  154. begin
  155. if Win32Platform=VER_PLATFORM_WIN32_NT then
  156. CloseHandle(DiskHandle)
  157. else
  158. DeleteFile('\\.\INT13EXT');
  159. DiskHandle:=0;
  160. end;
  161. function TDisk.OpenVolumePhysical:boolean;
  162. var i: cardinal;
  163. begin
  164. result:=false;
  165. if Win32Platform=VER_PLATFORM_WIN32_NT then
  166. begin
  167. DiskHandle := CreateFile(pchar('\\.\PhysicalDrive' + IntToStr(Number)), GENERIC_READ,
  168. FILE_SHARE_READ OR FILE_SHARE_WRITE, nil, OPEN_EXISTING,
  169. 0, //FILE_FLAG_SEQUENTIAL_SCAN OR FILE_FLAG_NO_BUFFERING, //0,
  170. 0)
  171. end else begin
  172. //Handle := CreateFile(pchar('\\.\PhysicalDrive' + IntToStr(DiskNumber)), GENERIC_READ, FILE_SHARE_READ , nil, OPEN_EXISTING, 0, 0);
  173. //Handle := CreateFile(pchar('\\.\INT13EXT.VXD'), 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);
  174. DiskHandle := CreateFile(pchar('\\.\vwin32'), 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);
  175. end;
  176. if not DeviceIoControl(DiskHandle, IOCTL_STORAGE_CHECK_VERIFY, nil, 0, nil, 0, I, nil)
  177. then begin
  178. // make sure media is loaded
  179. //CloseHandle(Handle);
  180. exit;
  181. //SysErrMsg(GetLastError, 'checkdisk');
  182. end;
  183. if (DiskHandle <> INVALID_HANDLE_VALUE) then
  184. begin
  185. result:=true;
  186. if DeviceIoControl(DiskHandle, IOCTL_DISK_GET_DRIVE_GEOMETRY, nil, 0, @DiskGeometry, SizeOf(DiskGeometry), I, nil) then
  187. begin
  188. TotalSectors := DiskGeometry.cylinders.lowpart * DiskGeometry.trackspercylinder * DiskGeometry.sectorspertrack;
  189. Size := TotalSectors*512; //DiskGeometry.BytesPerSector;
  190. end else begin
  191. CloseHandle(DiskHandle);
  192. raise EInOutError.CreateFmt({s1}'', ['1']); // ('Failed getting disk geometry.');
  193. end;
  194. end else begin
  195. CloseHandle(DiskHandle);
  196. raise EInOutError.CreateFmt({s1}'', ['1']); // ('Failed getting disk device handle.');
  197. end;
  198. end;
  199. function TDisk.OpenVolumeByLetter(Letter:string):boolean;
  200. var I : Cardinal;
  201. begin
  202. result:=false;
  203. if Win32Platform=VER_PLATFORM_WIN32_NT then
  204. begin
  205. DiskHandle := CreateFile(pchar('\\.\'+Letter+':'), GENERIC_READ,
  206. FILE_SHARE_READ OR FILE_SHARE_WRITE, nil, OPEN_EXISTING,
  207. FILE_FLAG_SEQUENTIAL_SCAN OR FILE_FLAG_NO_BUFFERING, 0)
  208. end else begin
  209. //Handle := CreateFile(pchar('\\.\PhysicalDrive' + IntToStr(DiskNumber)), GENERIC_READ, FILE_SHARE_READ , nil, OPEN_EXISTING, 0, 0);
  210. //Handle := CreateFile(pchar('\\.\INT13EXT.VXD'), 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);
  211. //Handle := CreateFile(pchar('\\.\vwin32'), 0, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);
  212. end;
  213. if not DeviceIoControl(DiskHandle, IOCTL_STORAGE_CHECK_VERIFY, nil, 0, nil, 0, I, nil) then
  214. begin
  215. //CloseHandle(Handle);
  216. exit;
  217. end;
  218. if (DiskHandle <> INVALID_HANDLE_VALUE) then
  219. begin
  220. if DeviceIoControl(DiskHandle, IOCTL_DISK_GET_DRIVE_GEOMETRY, nil, 0, @DiskGeometry, SizeOf(DiskGeometry), I, nil) then
  221. begin
  222. TotalSectors := DiskGeometry.cylinders.lowpart * DiskGeometry.trackspercylinder * DiskGeometry.sectorspertrack;
  223. Size := TotalSectors * 512; //DiskGeometry.BytesPerSector;
  224. result:=true;
  225. end else begin
  226. CloseHandle(DiskHandle);
  227. raise EInOutError.CreateFmt(name, ['1']); // ('Failed getting disk geometry.');
  228. end;
  229. end else begin
  230. CloseHandle(DiskHandle);
  231. raise EInOutError.CreateFmt(name, ['1']); // ('Failed getting disk device handle.');
  232. end;
  233. end;
  234. ////////////////////////////////////////////////////////////////////////////////
  235. function TDisk.Read(var Buffer; Count: Integer): Longint;
  236. var BytesRead: Cardinal;
  237. begin
  238. if Count = 0 then begin result:=0; exit; end;
  239. if Count mod 512 <> 0 then
  240. raise EInOutError.Create('Number of bytes to read must be aligned to 512 bytes.');
  241. if not ReadFile(DiskHandle, Buffer, Count, BytesRead, nil) then
  242. //RaiseLastWin32Error;
  243. SysErrMsg(GetLastError, 'DiskRead: ');
  244. Result := BytesRead;
  245. end;
  246. function TDisk.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  247. var
  248. HighOffset: Integer;
  249. begin
  250. if Offset mod 512 <> 0 then
  251. raise EInOutError.Create('Number of bytes to read must be aligned to 512 bytes.');
  252. HighOffset := (Offset shr 32) and $00000000FFFFFFFF;
  253. case Origin of
  254. soBeginning:
  255. Result := SetFilePointer(DiskHandle, Integer(Offset and $00000000FFFFFFFF), @HighOffset, FILE_BEGIN);
  256. soCurrent:
  257. Result := SetFilePointer(DiskHandle, Integer(Offset and $00000000FFFFFFFF), @HighOffset, FILE_CURRENT);
  258. soEnd:
  259. Result := SetFilePointer(DiskHandle, Integer(Offset and $00000000FFFFFFFF), @HighOffset, FILE_END);
  260. else
  261. Result := 0;
  262. end;
  263. end;
  264. function TDisk.SizeStr: string;
  265. begin
  266. Result := IntToSize(Size);
  267. end;
  268. function TDisk.Write(const Buffer; Count: Integer): Longint;
  269. begin
  270. raise EInOutError.Create('Writing to physical disks is not implemented.');
  271. end;
  272. procedure GetPartitionInfo(driveletter: string; var sVolName, sFSName, sVolSerial: string);
  273. var MaximumComponentLength,
  274. VOLSERIAL, FileSystemFlags: DWORD;
  275. VOLNAME: array[0..31] of char;
  276. FSNAME: array[0..15] of char;
  277. Size : integer;
  278. begin
  279. Size := SizeOf(FSNAME);
  280. FillChar(FSNAME, Size, 0);
  281. GetVolumeInformation(PAnsiChar(driveletter + ':\'),
  282. VOLNAME,32,
  283. @VOLSERIAL,MaximumComponentLength,
  284. FileSystemFlags,
  285. FSNAME, 16);
  286. sVolName:=StrPas(@VOLNAME[0]);
  287. sFSName:=StrPas(@FSNAME[0]);
  288. sVolSerial := IntToHex(HiWord(VOLSERIAL), 4) + '-' +
  289. IntToHex(LoWord(VOLSERIAL), 4);
  290. end;
  291. function GetFSName(pDesc: TPartDesc; Disk:TDisk): string;
  292. begin
  293. Disk.Position := pDesc.StartSect * 512;
  294. Disk.Read(Disk.SecBuf,512);
  295. case pDesc.PartitionType of
  296. $00: Result := 'Free';
  297. $05: Result := 'Extended DOS-partition';
  298. $0F: Result := 'Extended XINT13';
  299. $01: Result := 'FAT12';
  300. $02: Result := 'Xenix root';
  301. $03: Result := 'Xenix user';
  302. $04: Result := 'FAT16 < 32M';
  303. $06: Result := 'FAT16 > 32M MSDOSV4 HUGE';
  304. $07: Result := 'NTFS';
  305. $08: Result := 'AIX';
  306. $09: Result := 'AIX boot';
  307. $0A: Result := 'OS/2 boot Manager';
  308. $0B: Result := 'FAT32';
  309. $0C: Result := 'FAT32 XINT13';
  310. $0E: Result := 'FAT16 XINT13';
  311. $11: Result := 'Hidden FAT12';
  312. $12: Result := 'Compaq diagnostics';
  313. $14: Result := 'Hidden FAT16 (<32MB)';
  314. $16: Result := 'Hidden FAT16B (>=32MB)';
  315. $17: Result := 'Hidden NTFS';
  316. $1B: Result := 'Hidden FAT32';
  317. $1C: Result := 'Hidden FAT32 XINT13';
  318. $1E: Result := 'Hidden FAT16 XINT13';
  319. $2C: Result := 'WildFile/Adaptec GOBack';
  320. $3C: Result := 'PowerQuest Recoverable Partition';
  321. $40: Result := 'VENIX 80286';
  322. $41: Result := 'PowerPC BOOT';
  323. $42: Result := 'Veritas Logical Disk Manager';
  324. $51: Result := 'Novell';
  325. $52: Result := 'MICROPORT';
  326. $63: Result := 'Unix';
  327. $64: Result := 'Novell NetWare 286';
  328. $65: Result := 'Novell NetWare (3.11 and 4.1)';
  329. $66: Result := 'Novell NetWare 386';
  330. $75: Result := 'PC/IX';
  331. $80: Result := 'OLD Minix';
  332. $81: Result := 'Linux/Minix v1.4b+';
  333. $82: Result := 'Linux Swap Partition';
  334. $83: Result := 'Linux Ext2/3/Rsr';
  335. $84: Result := 'OS/2 hiding type 04h partition';
  336. $85: Result := 'Linux Extended';
  337. $86: Result := 'NT FAT volume set';
  338. $87: Result := 'NT IFS volume set';
  339. $93: Result := 'Amoeba/Hidden Linux native FS (Reiser/Ext3/2)';
  340. $94: Result := 'Amoeba BBT';
  341. $A5: Result := 'BSD / 386';
  342. $AF: Result := 'APPLE_HFS';
  343. $B7: Result := 'BSDI fs';
  344. $B8: Result := 'BSDI swap';
  345. $C6: Result := 'SYRINX / Disabled NT FAT volume set';
  346. $C7: Result := 'SYRINX / Disabled NT IFS volume set';
  347. $DB: Result := 'CP/M';
  348. $DE: Result := 'Dell Corporation diagnostic partition';
  349. $E1: Result := 'DOS ACCESS';
  350. $F2: Result := 'DOS SECONDARY';
  351. $FF: Result := 'BBT';
  352. else Result := 'Unknown (' + IntToHex(pDesc.PartitionType,2) + ')';
  353. end;
  354. end;
  355. end.