Windows.WinDisks.Mod 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. MODULE WinDisks; (** AUTHOR "fof"; PURPOSE "module to access partitions under Windows"; **)
  2. IMPORT Kernel32, SYSTEM, Strings, KernelLog, Streams, Commands, Disks, Plugins, Modules, WinFS;
  3. VAR
  4. DeviceIoControl: PROCEDURE {WINAPI} ( hDevice: Kernel32.HANDLE; dwIoControlCode: LONGINT; VAR lpInBuffer: ARRAY OF SYSTEM.BYTE; nInBufferSize: LONGINT;
  5. VAR lpOutBuffer: ARRAY OF SYSTEM.BYTE; nOutBufferSize: LONGINT; VAR lpBytesReturned: LONGINT; lpOverlapped: ANY ): Kernel32.BOOL;
  6. SetFilePointer: PROCEDURE {WINAPI} ( hFile: Kernel32.HANDLE; lDistanceToMove: LONGINT; VAR lpDistanceToMoveHigh: LONGINT; dwMoveMethod: LONGINT ): LONGINT;
  7. CONST
  8. MaxExtents = 1; (* do not handle more than one extents (yet?) *)
  9. BlockNumberInvalid* = 101; Error* = 102;
  10. TYPE
  11. DISK_GEOMETRY = RECORD
  12. Cylinders: HUGEINT;
  13. MediaType: LONGINT;
  14. TracksPerCylinder: LONGINT;
  15. SectorsPerTrack: LONGINT;
  16. BytesPerSector: LONGINT;
  17. END;
  18. DISK_EXTENT = RECORD (* immer auf größten Member aligniert *)
  19. DiskNumber: LONGINT;
  20. padding: LONGINT;
  21. StartingOffset: HUGEINT;
  22. ExtentLength: HUGEINT;
  23. END;
  24. VOLUME_DISK_EXTENTS = RECORD (* immer auf größten Member aligniert: hugeint *)
  25. NumberOfDiskExtents: LONGINT; (* the msdn reports something different (LONGINT) but it works this way only *)
  26. padding: LONGINT;
  27. extents: ARRAY MaxExtents OF DISK_EXTENT; (* should be dynamic *)
  28. END;
  29. CONST
  30. (* Media Types *)
  31. Unknown = 0;
  32. (* 1 - 10: Floppy *)
  33. RemovableMedia = 11; FixedMedia = 12;
  34. (* 13-..: Floopy *)
  35. IOCTL_DISK_GET_DRIVE_GEOMETRY = 00070000H; VOLUME_GET_VOLUME_DISK_EXTENTS = 00560000H;
  36. FSCTL_LOCK_VOLUME = 90018H; FSCTL_UNLOCK_VOLUME = 9001CH; FSCTL_DISMOUNT_VOLUME = 90020H;
  37. TYPE
  38. VirtualDisk = OBJECT (Disks.Device)
  39. VAR handle: Kernel32.HANDLE;
  40. size: LONGINT;
  41. next: VirtualDisk;
  42. drive: LONGINT;
  43. PROCEDURE Finish( VAR res: WORD );
  44. BEGIN
  45. Kernel32.CloseHandle( handle ); KernelLog.String("Disk closed"); KernelLog.Ln;
  46. END Finish;
  47. PROCEDURE Transfer*( op, block, num: LONGINT; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR res: WORD );
  48. VAR bool, n,err: LONGINT; pos: HUGEINT; poslow, poshigh: LONGINT; large: Kernel32.LargeInteger;
  49. BEGIN {EXCLUSIVE}
  50. IF (block < 0) OR (num < 1) OR (block + num > size) THEN res := BlockNumberInvalid; RETURN; END;
  51. pos := LONG( block ) * LONG( blockSize );
  52. poslow := SHORT( pos ); poshigh := SHORT( ASH( pos, -32 ) );
  53. large.LowPart := poslow; large.HighPart := poshigh;
  54. IF ~LockVolume(handle) THEN END;
  55. bool := SetFilePointer( handle, poslow, poshigh, Kernel32.FileBegin );
  56. IF bool = -1 THEN res := BlockNumberInvalid; HALT( 101 ); RETURN; END;
  57. IF op = Disks.Read THEN
  58. bool := Kernel32.ReadFile( handle, data[ofs], num * blockSize, n, NIL );
  59. IF (bool > 0) & (num * blockSize = n) THEN
  60. res := Disks.Ok;
  61. ELSE
  62. res := Error;
  63. END;
  64. ELSIF op = Disks.Write THEN
  65. IF Disks.ReadOnly IN flags THEN
  66. KernelLog.String("Write attempt on read-only mounted drive "); KernelLog.String(name); KernelLog.Ln;
  67. res := Disks.Ok; (* readonly *)
  68. ELSE
  69. bool := Kernel32.WriteFile(handle,data[ofs],num*blockSize,n,NIL);
  70. IF (bool # 0) & (num * blockSize = n) THEN
  71. res := Disks.Ok;
  72. ELSE
  73. res := Error;
  74. err := Kernel32.GetLastError();
  75. KernelLog.String("last error = "); KernelLog.Int(err,1); KernelLog.Ln;
  76. END;
  77. END;
  78. ELSE res := Disks.Unsupported;
  79. END;
  80. IF ~UnlockVolume(handle) THEN END;
  81. IF Disks.Stats THEN
  82. IF op = Disks.Read THEN
  83. INC (NnofReads);
  84. IF (res = Disks.Ok) THEN INC (NbytesRead, num * blockSize);
  85. ELSE INC (NnofErrors);
  86. END;
  87. ELSIF op = Disks.Write THEN
  88. INC (NnofWrites);
  89. IF (res = Disks.Ok) THEN INC (NbytesWritten, num * blockSize);
  90. ELSE INC (NnofErrors);
  91. END;
  92. ELSE
  93. INC (NnofOthers);
  94. END;
  95. END;
  96. END Transfer;
  97. PROCEDURE GetSize*( VAR size, res: LONGINT );
  98. BEGIN
  99. size := SELF.size; res := Disks.Ok;
  100. END GetSize;
  101. PROCEDURE Handle*( VAR msg: Disks.Message; VAR res: WORD );
  102. BEGIN
  103. res := Disks.Unsupported;
  104. END Handle;
  105. PROCEDURE & New*( handle: Kernel32.HANDLE; CONST diskname: ARRAY OF CHAR; drive: LONGINT; flags: SET; blockSize, blocks: LONGINT );
  106. BEGIN
  107. ASSERT( handle > 0 ); SELF.handle := handle; SELF.blockSize := blockSize; SELF.size := blocks; SetName( diskname ); desc := "Windows Disk ";
  108. SELF.drive := drive;
  109. SELF.flags := flags;
  110. END New;
  111. END VirtualDisk;
  112. VAR
  113. disks: VirtualDisk; (* to enable cleanup when unloading module *)
  114. PROCEDURE AddDisk( vd: VirtualDisk );
  115. BEGIN {EXCLUSIVE}
  116. vd.next := disks; disks := vd;
  117. END AddDisk;
  118. PROCEDURE RemoveDisk( vd: VirtualDisk );
  119. VAR d: VirtualDisk;
  120. BEGIN {EXCLUSIVE}
  121. IF disks = vd THEN disks := disks.next;
  122. ELSE
  123. d := disks;
  124. WHILE (d # NIL ) & (d.next # vd) DO d := d.next; END;
  125. IF (d # NIL ) THEN d.next := d.next.next; END;
  126. END;
  127. END RemoveDisk;
  128. PROCEDURE IsMounted( dev: Disks.Device ): BOOLEAN;
  129. VAR i: LONGINT;
  130. BEGIN
  131. IF dev.table # NIL THEN
  132. FOR i := 0 TO LEN( dev.table ) - 1 DO
  133. IF Disks.Mounted IN dev.table[i].flags THEN RETURN TRUE END
  134. END
  135. END;
  136. RETURN FALSE
  137. END IsMounted;
  138. (** Remove virtual disk *)
  139. PROCEDURE Uninstall*(context : Commands.Context); (** diskname ~ *)
  140. VAR diskname: Plugins.Name; plugin: Plugins.Plugin; drive: LONGINT; v: VirtualDisk;
  141. BEGIN
  142. context.arg.SkipWhitespace;
  143. context.arg.String(diskname);
  144. plugin := Disks.registry.Get( diskname );
  145. IF plugin = NIL THEN (* try to map disk name *)
  146. IF diskname[1] = ":" THEN
  147. drive := ORD(CAP(diskname[0]))-ORD("A");
  148. v := disks;
  149. WHILE(v#NIL) & (v.drive # drive) DO
  150. v := v.next;
  151. END;
  152. plugin := v;
  153. END;
  154. END;
  155. IF plugin # NIL THEN
  156. IF ~IsMounted( plugin( VirtualDisk ) ) THEN
  157. Disks.registry.Remove( plugin ); RemoveDisk( plugin( VirtualDisk ) );
  158. context.out.String( diskname ); context.out.String( " removed" ); context.out.Ln;
  159. ELSE
  160. context.error.String( diskname ); context.error.String( " is mounted." ); context.error.Ln;
  161. END;
  162. ELSE
  163. context.error.String( diskname ); context.error.String( " not found" ); context.error.Ln;
  164. context.result := Commands.CommandError;
  165. END;
  166. END Uninstall;
  167. PROCEDURE Cleanup;
  168. VAR res: WORD;
  169. BEGIN {EXCLUSIVE}
  170. WHILE (disks # NIL ) DO disks.Finish( res ); Disks.registry.Remove( disks ); disks := disks.next; END;
  171. END Cleanup;
  172. PROCEDURE ReportDiskGeometry( VAR pdg: DISK_GEOMETRY; out : Streams.Writer );
  173. VAR size: LONGREAL;
  174. BEGIN
  175. out.String( "Disk type: " );
  176. CASE pdg.MediaType OF
  177. | Unknown:
  178. out.String( "unknown" );
  179. | RemovableMedia:
  180. out.String( "removable media" );
  181. | FixedMedia:
  182. out.String( "fixed media" );
  183. ELSE out.String( "floppy" );
  184. END;
  185. out.Ln; out.String( "Cylinders = " ); out.Int( SHORT( pdg.Cylinders ), 1 ); out.Ln;
  186. out.String( "TracksPerCylinder = " ); out.Int( pdg.TracksPerCylinder, 8 ); out.Ln;
  187. out.String( "SectorsPerTrack = " ); out.Int( pdg.SectorsPerTrack, 8 ); out.Ln;
  188. out.String( "BytesPerSector = " ); out.Int( pdg.BytesPerSector, 8 ); out.Ln;
  189. size := pdg.Cylinders; size := size * pdg.TracksPerCylinder * pdg.SectorsPerTrack * pdg.BytesPerSector;
  190. out.String( "DiskSize = " ); OutSize( size, out ); out.Ln;
  191. END ReportDiskGeometry;
  192. PROCEDURE GetDiskGeometry( handle: Kernel32.HANDLE; VAR pdg: DISK_GEOMETRY ): BOOLEAN;
  193. VAR done, returned: LONGINT;
  194. BEGIN
  195. done := DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NIL , 0, pdg, SIZEOF( DISK_GEOMETRY ), returned, NIL ); RETURN done > 0;
  196. END GetDiskGeometry;
  197. (* lock, unlock and dismount only works on volumes specified as "X:". It does not work with PhysicalDrive.
  198. *)
  199. PROCEDURE LockVolume(handle: Kernel32.HANDLE): BOOLEAN;
  200. VAR done,returned: LONGINT;
  201. BEGIN
  202. done := DeviceIoControl(handle, FSCTL_LOCK_VOLUME, NIL,0, NIL, 0, returned, NIL);
  203. RETURN done > 0;
  204. END LockVolume;
  205. PROCEDURE UnlockVolume(handle: Kernel32.HANDLE): BOOLEAN;
  206. VAR done,returned: LONGINT;
  207. BEGIN
  208. done := DeviceIoControl(handle, FSCTL_UNLOCK_VOLUME, NIL,0, NIL, 0, returned, NIL);
  209. RETURN done > 0;
  210. END UnlockVolume;
  211. PROCEDURE DismountVolume(handle: Kernel32.HANDLE): BOOLEAN;
  212. VAR done,returned: LONGINT;
  213. BEGIN
  214. done := DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, NIL,0, NIL, 0, returned, NIL);
  215. RETURN done > 0;
  216. END DismountVolume;
  217. PROCEDURE AppendInt( VAR name: ARRAY OF CHAR; i: LONGINT );
  218. VAR str: ARRAY 8 OF CHAR;
  219. BEGIN
  220. Strings.IntToStr( i, str ); Strings.Append( name, str );
  221. END AppendInt;
  222. PROCEDURE OpenVolume( VAR handle: Kernel32.HANDLE; flags: SET; CONST name: ARRAY OF CHAR; context : Commands.Context ): BOOLEAN;
  223. VAR devname: ARRAY 256 OF CHAR; tflags: WORDSET; errorcode : LONGINT;
  224. BEGIN
  225. Strings.Concat( "\\.\", name, devname );
  226. IF Disks.ReadOnly IN flags THEN tflags := {Kernel32.GenericRead} ELSE tflags := {(*2 (* Kernel32.GenericDelete *), *)Kernel32.GenericWrite,Kernel32.GenericRead} END;
  227. handle := Kernel32.CreateFile( devname, tflags, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL, Kernel32.OpenExisting, {}, Kernel32.NULL );
  228. IF (handle = Kernel32.InvalidHandleValue) THEN
  229. errorcode := Kernel32.GetLastError();
  230. context.error.String("Could not open '"); context.error.String(devname); context.error.String("' : ");
  231. CASE errorcode OF
  232. |Kernel32.ErrorFileNotFound:
  233. context.error.String('Drive or physical volume not found (Use a drive specification like "A:" or a phyiscal volume like PhysicalDrive0)');
  234. |Kernel32.ErrorAccessDenied:
  235. context.error.String("Access denied (Administrator privileges required)");
  236. ELSE
  237. context.error.String("Windows Error Code: "); context.error.Int(errorcode, 0);
  238. END;
  239. context.error.Ln; context.result := Commands.CommandError;
  240. END;
  241. RETURN handle # Kernel32.InvalidHandleValue;
  242. END OpenVolume;
  243. PROCEDURE OutSize( f: LONGREAL; out : Streams.Writer );
  244. BEGIN
  245. IF f > 1.E9 THEN out.FloatFix( f / 1024 / 1024 / 1024, 4, 3, 0 ); out.String( " GiB" );
  246. ELSIF f > 1.E6 THEN out.FloatFix( f / 1024 / 1024, 4, 3, 0 ); out.String( " MiB" );
  247. ELSIF f > 1.E3 THEN out.FloatFix( f / 1024, 4, 3, 0 ); out.String( " KiB" );
  248. ELSE out.FloatFix( f, 4, 3, 0 ); out.String( " B" );
  249. END;
  250. END OutSize;
  251. PROCEDURE GetPhysicalDrive( VAR handle: Kernel32.HANDLE; flags: SET; VAR name: ARRAY OF CHAR; context : Commands.Context );
  252. VAR done, returned: LONGINT; extents: VOLUME_DISK_EXTENTS; drive: LONGINT; first, last: LONGREAL; bps: LONGREAL; pdg: DISK_GEOMETRY; (* number of first and last block used *)
  253. BEGIN
  254. done := DeviceIoControl( handle, VOLUME_GET_VOLUME_DISK_EXTENTS, NIL , 0, extents, SIZEOF( VOLUME_DISK_EXTENTS ), returned, NIL );
  255. IF done > 0 THEN
  256. IF extents.NumberOfDiskExtents = 0 THEN
  257. context.error.String( "no disk extents used, probably the drive is physical already " ); context.error.Ln;
  258. context.result := Commands.CommandError;
  259. ELSIF extents.NumberOfDiskExtents = 1 THEN
  260. drive := extents.extents[0].DiskNumber;
  261. IF GetDiskGeometry( handle, pdg ) THEN bps := pdg.BytesPerSector ELSE bps := 512 END;
  262. first := extents.extents[0].StartingOffset / bps; last := extents.extents[0].ExtentLength / bps; last := first + last;
  263. context.out.String( "Partition from Block " ); context.out.Int( ENTIER( first ), 1 ); context.out.String( " to " ); context.out.Int( ENTIER( last ), 1 );
  264. context.out.String( " in physical drive # " ); context.out.Int( drive, 1 ); OutSize( extents.extents[0].ExtentLength, context.out); context.out.Ln;
  265. name := "PhysicalDrive"; AppendInt( name, drive ); context.out.String( "Mapping to drive : " ); context.out.String( name ); context.out.Ln;
  266. IF ~OpenVolume( handle, flags,name, context) THEN
  267. context.error.String( "volume could not be opened. " ); context.error.Ln;
  268. context.result := Commands.CommandError;
  269. END;
  270. ELSE
  271. context.error.String( "cannot handle volumes with more than one extent (yet) " ); context.error.Ln;
  272. context.result := Commands.CommandError;
  273. END;
  274. ELSE
  275. context.error.String( "GetPhysicalDrive: no success, probably the drive is already physical" ); context.error.Ln;
  276. context.result := Commands.CommandError;
  277. END;
  278. END GetPhysicalDrive;
  279. (** Add file as virtual disk *)
  280. PROCEDURE Install*(context : Commands.Context); (** diskname filename [blocksize] ~ *)
  281. VAR
  282. diskname, flagss: ARRAY 256 OF CHAR; flags: SET;
  283. res: WORD; handle: Kernel32.HANDLE; pdg: DISK_GEOMETRY; size: LONGINT; vd: VirtualDisk; drive: LONGINT;
  284. i: LONGINT;
  285. BEGIN
  286. context.arg.SkipWhitespace;
  287. context.arg.String(diskname);
  288. context.arg.SkipWhitespace;
  289. flagss := ""; context.arg.String(flagss);
  290. flags := {Disks.ReadOnly};
  291. i := 0;
  292. WHILE(flagss[i] # 0X) DO
  293. IF flagss[i] = "W" THEN
  294. EXCL(flags,Disks.ReadOnly);
  295. END;
  296. INC(i);
  297. END;
  298. IF diskname[1] = ":" THEN drive := ORD(CAP(diskname[0]))-ORD("A"); ELSE drive := -1 END;
  299. IF OpenVolume( handle, flags, diskname, context) THEN
  300. IF (diskname[1] = ":") & DismountVolume(handle) THEN
  301. END;
  302. GetPhysicalDrive( handle, flags,diskname, context );
  303. IF ~GetDiskGeometry( handle, pdg ) THEN
  304. Kernel32.CloseHandle( handle );
  305. context.error.String( "Could not determine disk geometry " ); context.error.Ln;
  306. context.result := Commands.CommandError;
  307. ELSE
  308. ReportDiskGeometry( pdg, context.out);
  309. IF pdg.MediaType = RemovableMedia THEN INCL(flags,Disks.Removable) END;
  310. IF pdg.Cylinders > MAX( LONGINT ) THEN
  311. HALT( 100 )
  312. ELSE
  313. size := SHORT( pdg.Cylinders ) * pdg.TracksPerCylinder * pdg.SectorsPerTrack;
  314. END;
  315. NEW( vd, handle, diskname, drive, flags, pdg.BytesPerSector, size ); Disks.registry.Add( vd, res );
  316. IF res = Plugins.Ok THEN
  317. AddDisk( vd );
  318. context.out.String( diskname ); context.out.String( " registered." ); context.out.Ln;
  319. ELSE
  320. Kernel32.CloseHandle( handle );
  321. context.error.String( "Could not register disk, res: " ); context.error.Int( res, 0 ); context.error.Ln;
  322. context.result := Commands.CommandError;
  323. END;
  324. END;
  325. END;
  326. END Install;
  327. PROCEDURE Notification(type: LONGINT; drives: SET);
  328. VAR v: VirtualDisk; res: WORD;
  329. BEGIN
  330. IF type = WinFS.deviceArrival THEN
  331. ELSIF type = WinFS.deviceRemove THEN
  332. v := disks;
  333. WHILE(v # NIL) DO
  334. IF (v.drive >= 0) & (v.drive IN drives) THEN
  335. IF IsMounted (v) THEN
  336. KernelLog.String("Warning: Disk mounted but forcefully removed !"); KernelLog.Ln;
  337. END;
  338. Disks.registry.Remove( v ); v.Finish(res); RemoveDisk( v );
  339. KernelLog.String( v.name ); KernelLog.String( " removed" ); KernelLog.Ln;
  340. v := disks;
  341. ELSE
  342. v := v.next;
  343. END;
  344. END;
  345. END;
  346. END Notification;
  347. PROCEDURE Init;
  348. VAR mod: Kernel32.HMODULE;
  349. str: ARRAY 64 OF CHAR;
  350. BEGIN
  351. str := "Kernel32.DLL"; mod := Kernel32.LoadLibrary( str ); str := "DeviceIoControl"; Kernel32.GetProcAddress( mod, str, SYSTEM.VAL( ADDRESS, DeviceIoControl ) ); str := "SetFilePointer";
  352. Kernel32.GetProcAddress( mod, str, SYSTEM.VAL( ADDRESS, SetFilePointer ) );
  353. IF DeviceIoControl = NIL THEN HALT( 100 ) END;
  354. IF SetFilePointer = NIL THEN HALT( 100 ) END;
  355. Modules.InstallTermHandler( Cleanup );
  356. WinFS.RegisterNotification(Notification);
  357. END Init;
  358. BEGIN
  359. Init();
  360. END WinDisks.
  361. short description
  362. WinDisks is a module to access volumes and partitions under WinAos.
  363. One purpose is the access of file systems that are supported by Aos but not by Windows. The other is the partitioning of hard disks within WinAos.
  364. It may thus also be used to install an entire native Aos system on a partition on the hard drive or USB memory stick etc.
  365. To add a windows disk to the Aos system use the command
  366. WinDisks.Install DriveName ["RW"]
  367. where
  368. - DriveName can be one of "A:" to "Z:" or "PhysicalDriveX" where X has to be replaced by the physical drive number.
  369. A drive name such as "C:" is matched to a PhysicalDriveX name, if appropriate.
  370. It is better to use the "X:" format because the system can in general then perform an automatic unregistering if the device is becoming unavailable in Windows.
  371. - If "PhysicalDriveX" is used to specify the drive, the logical volumes associated with it will not be dismounted, which can mean that write operations are unsuccesful
  372. - A volume is inserted read-only unless the optional parameter "RW" is provided.
  373. To access the partitions of the drive you may use the Partition Tool.
  374. To uninstall an installed volume in Aos, use the command
  375. WinDisks.Uninstall DriveName .
  376. ~
  377. Examples
  378. WinDisks.Install "f:"
  379. WinDisks.Install "c:"
  380. WinDisks.Install "PhysicalDrive1" "RW"
  381. WinDisks.Uninstall "C:" ~
  382. WinDisks.Uninstall "PhysicalDrive0" ~
  383. WinDisks.Uninstall "F:" ~
  384. WinDisks.Install "f:" "RW" ~
  385. System.Free WinDisks ~