MODULE WinDisks;
IMPORT Kernel32, SYSTEM, Strings, KernelLog, Streams, Commands, Disks, Plugins, Modules, Machine, WinFS;
VAR
DeviceIoControl: PROCEDURE {WINAPI} ( hDevice: Kernel32.HANDLE; dwIoControlCode: LONGINT; VAR lpInBuffer: ARRAY OF SYSTEM.BYTE; nInBufferSize: LONGINT;
VAR lpOutBuffer: ARRAY OF SYSTEM.BYTE; nOutBufferSize: LONGINT; VAR lpBytesReturned: LONGINT; lpOverlapped: ANY ): Kernel32.BOOL;
SetFilePointer: PROCEDURE {WINAPI} ( hFile: Kernel32.HANDLE; lDistanceToMove: LONGINT; VAR lpDistanceToMoveHigh: LONGINT; dwMoveMethod: LONGINT ): LONGINT;
CONST
MaxExtents = 1;
BlockNumberInvalid* = 101; Error* = 102;
TYPE
DISK_GEOMETRY = RECORD
Cylinders: HUGEINT;
MediaType: LONGINT;
TracksPerCylinder: LONGINT;
SectorsPerTrack: LONGINT;
BytesPerSector: LONGINT;
END;
DISK_EXTENT = RECORD
DiskNumber: LONGINT;
padding: LONGINT;
StartingOffset: HUGEINT;
ExtentLength: HUGEINT;
END;
VOLUME_DISK_EXTENTS = RECORD
NumberOfDiskExtents: LONGINT;
padding: LONGINT;
extents: ARRAY MaxExtents OF DISK_EXTENT;
END;
CONST
Unknown = 0;
RemovableMedia = 11; FixedMedia = 12;
IOCTL_DISK_GET_DRIVE_GEOMETRY = 00070000H; VOLUME_GET_VOLUME_DISK_EXTENTS = 00560000H;
TYPE
VirtualDisk = OBJECT (Disks.Device)
VAR handle: Kernel32.HANDLE;
size: LONGINT;
next: VirtualDisk;
drive: LONGINT;
PROCEDURE Finish( VAR res: LONGINT );
BEGIN
Kernel32.CloseHandle( handle ); KernelLog.String("Disk closed"); KernelLog.Ln;
END Finish;
PROCEDURE Transfer( op, block, num: LONGINT; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR res: LONGINT );
VAR bool, n,err: LONGINT; pos: HUGEINT; poslow, poshigh: LONGINT; large: Kernel32.LargeInteger;
BEGIN {EXCLUSIVE}
IF (block < 0) OR (num < 1) OR (block + num > size) THEN res := BlockNumberInvalid; RETURN; END;
pos := LONG( block ) * LONG( blockSize );
poslow := SHORT( pos ); poshigh := SHORT( Machine.ASHH( pos, -32 ) );
large.LowPart := poslow; large.HighPart := poshigh;
bool := SetFilePointer( handle, poslow, poshigh, Kernel32.FileBegin );
IF bool = -1 THEN res := BlockNumberInvalid; HALT( 101 ); RETURN; END;
IF op = Disks.Read THEN
bool := Kernel32.ReadFile( handle, data[ofs], num * blockSize, n, NIL );
IF (bool > 0) & (num * blockSize = n) THEN
res := Disks.Ok;
ELSE
res := Error;
END;
ELSIF op = Disks.Write THEN
IF Disks.ReadOnly IN flags THEN
KernelLog.String("Write attempt on read-only mounted drive "); KernelLog.String(name); KernelLog.Ln;
res := Disks.Ok;
ELSE
bool := Kernel32.WriteFile(handle,data[ofs],num*blockSize,n,NIL);
IF (bool # 0) & (num * blockSize = n) THEN
res := Disks.Ok;
ELSE
res := Error;
err := Kernel32.GetLastError();
KernelLog.String("last error = "); KernelLog.Int(err,1); KernelLog.Ln;
END;
END;
ELSE res := Disks.Unsupported;
END;
IF Disks.Stats THEN
IF op = Disks.Read THEN
INC (NnofReads);
IF (res = Disks.Ok) THEN INC (NbytesRead, num * blockSize);
ELSE INC (NnofErrors);
END;
ELSIF op = Disks.Write THEN
INC (NnofWrites);
IF (res = Disks.Ok) THEN INC (NbytesWritten, num * blockSize);
ELSE INC (NnofErrors);
END;
ELSE
INC (NnofOthers);
END;
END;
END Transfer;
PROCEDURE GetSize( VAR size, res: LONGINT );
BEGIN
size := SELF.size; res := Disks.Ok;
END GetSize;
PROCEDURE Handle( VAR msg: Disks.Message; VAR res: LONGINT );
BEGIN
res := Disks.Unsupported;
END Handle;
PROCEDURE & New*( handle: Kernel32.HANDLE; CONST diskname: ARRAY OF CHAR; drive: LONGINT; flags: SET; blockSize, blocks: LONGINT );
BEGIN
ASSERT( handle > 0 ); SELF.handle := handle; SELF.blockSize := blockSize; SELF.size := blocks; SetName( diskname ); desc := "Windows Disk ";
SELF.drive := drive;
SELF.flags := flags;
END New;
END VirtualDisk;
VAR
disks: VirtualDisk;
PROCEDURE AddDisk( vd: VirtualDisk );
BEGIN {EXCLUSIVE}
vd.next := disks; disks := vd;
END AddDisk;
PROCEDURE RemoveDisk( vd: VirtualDisk );
VAR d: VirtualDisk;
BEGIN {EXCLUSIVE}
IF disks = vd THEN disks := disks.next;
ELSE
d := disks;
WHILE (d # NIL ) & (d.next # vd) DO d := d.next; END;
IF (d # NIL ) THEN d.next := d.next.next; END;
END;
END RemoveDisk;
PROCEDURE IsMounted( dev: Disks.Device ): BOOLEAN;
VAR i: LONGINT;
BEGIN
IF dev.table # NIL THEN
FOR i := 0 TO LEN( dev.table ) - 1 DO
IF Disks.Mounted IN dev.table[i].flags THEN RETURN TRUE END
END
END;
RETURN FALSE
END IsMounted;
PROCEDURE Uninstall*(context : Commands.Context);
VAR diskname: Plugins.Name; plugin: Plugins.Plugin; drive: LONGINT; v: VirtualDisk;
BEGIN
context.arg.SkipWhitespace;
context.arg.String(diskname);
plugin := Disks.registry.Get( diskname );
IF plugin = NIL THEN
IF diskname[1] = ":" THEN
drive := ORD(CAP(diskname[0]))-ORD("A");
v := disks;
WHILE(v#NIL) & (v.drive # drive) DO
v := v.next;
END;
plugin := v;
END;
END;
IF plugin # NIL THEN
IF ~IsMounted( plugin( VirtualDisk ) ) THEN
Disks.registry.Remove( plugin ); RemoveDisk( plugin( VirtualDisk ) );
context.out.String( diskname ); context.out.String( " removed" ); context.out.Ln;
ELSE
context.error.String( diskname ); context.error.String( " is mounted." ); context.error.Ln;
END;
ELSE
context.error.String( diskname ); context.error.String( " not found" ); context.error.Ln;
END;
END Uninstall;
PROCEDURE Cleanup;
VAR res: LONGINT;
BEGIN {EXCLUSIVE}
WHILE (disks # NIL ) DO disks.Finish( res ); Disks.registry.Remove( disks ); disks := disks.next; END;
END Cleanup;
PROCEDURE ReportDiskGeometry( VAR pdg: DISK_GEOMETRY; out : Streams.Writer );
VAR size: LONGREAL;
BEGIN
out.String( "Disk type: " );
CASE pdg.MediaType OF
| Unknown:
out.String( "unknown" );
| RemovableMedia:
out.String( "removable media" );
| FixedMedia:
out.String( "fixed media" );
ELSE out.String( "floppy" );
END;
out.Ln; out.String( "Cylinders = " ); out.Int( SHORT( pdg.Cylinders ), 1 ); out.Ln;
out.String( "TracksPerCylinder = " ); out.Int( pdg.TracksPerCylinder, 8 ); out.Ln;
out.String( "SectorsPerTrack = " ); out.Int( pdg.SectorsPerTrack, 8 ); out.Ln;
out.String( "BytesPerSector = " ); out.Int( pdg.BytesPerSector, 8 ); out.Ln;
size := pdg.Cylinders; size := size * pdg.TracksPerCylinder * pdg.SectorsPerTrack * pdg.BytesPerSector;
out.String( "DiskSize = " ); OutSize( size, out ); out.Ln;
END ReportDiskGeometry;
PROCEDURE GetDiskGeometry( handle: Kernel32.HANDLE; VAR pdg: DISK_GEOMETRY ): BOOLEAN;
VAR done, returned: LONGINT;
BEGIN
done := DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NIL , 0, pdg, SYSTEM.SIZEOF( DISK_GEOMETRY ), returned, NIL ); RETURN done > 0;
END GetDiskGeometry;
PROCEDURE AppendInt( VAR name: ARRAY OF CHAR; i: LONGINT );
VAR str: ARRAY 8 OF CHAR;
BEGIN
Strings.IntToStr( i, str ); Strings.Append( name, str );
END AppendInt;
PROCEDURE OpenVolume( VAR handle: Kernel32.HANDLE; flags: SET; CONST name: ARRAY OF CHAR; context : Commands.Context ): BOOLEAN;
VAR devname: ARRAY 256 OF CHAR; tflags: SET; errorcode : LONGINT;
BEGIN
Strings.Concat( "\\.\", name, devname );
IF Disks.ReadOnly IN flags THEN tflags := {Kernel32.GenericRead} ELSE tflags := {Kernel32.GenericWrite,Kernel32.GenericRead} END;
handle := Kernel32.CreateFile( devname, tflags, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.OpenExisting, {}, Kernel32.NULL );
IF (handle = Kernel32.InvalidHandleValue) THEN
errorcode := Kernel32.GetLastError();
context.error.String("Could not open '"); context.error.String(devname); context.error.String("' : ");
CASE errorcode OF
|Kernel32.ErrorFileNotFound:
context.error.String('Drive or physical volume not found (Use a drive specification like "A:" or a phyiscal volume like PhysicalDrive0)');
|Kernel32.ErrorAccessDenied:
context.error.String("Access denied (Administrator privileges required)");
ELSE
context.error.String("Windows Error Code: "); context.error.Int(errorcode, 0);
END;
context.error.Ln;
END;
RETURN handle # Kernel32.InvalidHandleValue;
END OpenVolume;
PROCEDURE OutSize( f: LONGREAL; out : Streams.Writer );
BEGIN
IF f > 1.E9 THEN out.FloatFix( f / 1024 / 1024 / 1024, 4, 3, 0 ); out.String( " GiB" );
ELSIF f > 1.E6 THEN out.FloatFix( f / 1024 / 1024, 4, 3, 0 ); out.String( " MiB" );
ELSIF f > 1.E3 THEN out.FloatFix( f / 1024, 4, 3, 0 ); out.String( " KiB" );
ELSE out.FloatFix( f, 4, 3, 0 ); out.String( " B" );
END;
END OutSize;
PROCEDURE GetPhysicalDrive( VAR handle: Kernel32.HANDLE; flags: SET; VAR name: ARRAY OF CHAR; context : Commands.Context );
VAR done, returned: LONGINT; extents: VOLUME_DISK_EXTENTS; drive: LONGINT; first, last: LONGREAL; bps: LONGREAL; pdg: DISK_GEOMETRY;
BEGIN
done := DeviceIoControl( handle, VOLUME_GET_VOLUME_DISK_EXTENTS, NIL , 0, extents, SYSTEM.SIZEOF( VOLUME_DISK_EXTENTS ), returned, NIL );
IF done > 0 THEN
IF extents.NumberOfDiskExtents = 0 THEN
context.error.String( "no disk extents used, probably the drive is physical already " ); context.error.Ln;
ELSIF extents.NumberOfDiskExtents = 1 THEN
drive := extents.extents[0].DiskNumber;
IF GetDiskGeometry( handle, pdg ) THEN bps := pdg.BytesPerSector ELSE bps := 512 END;
first := extents.extents[0].StartingOffset / bps; last := extents.extents[0].ExtentLength / bps; last := first + last;
context.out.String( "Partition from Block " ); context.out.Int( ENTIER( first ), 1 ); context.out.String( " to " ); context.out.Int( ENTIER( last ), 1 );
context.out.String( " in physical drive # " ); context.out.Int( drive, 1 ); OutSize( extents.extents[0].ExtentLength, context.out); context.out.Ln;
name := "PhysicalDrive"; AppendInt( name, drive ); context.out.String( "Mapping to drive : " ); context.out.String( name ); context.out.Ln;
IF ~OpenVolume( handle, flags,name, context) THEN HALT(100); END;
ELSE
context.error.String( "cannot handle volumes with more than one extent (yet) " ); context.error.Ln;
END;
ELSE
context.error.String( "GetPhyiscalDrive: no success, probably the drive is already physical" ); context.error.Ln;
END;
END GetPhysicalDrive;
PROCEDURE Install*(context : Commands.Context);
VAR
diskname, flagss: ARRAY 256 OF CHAR; flags: SET;
res: LONGINT; handle: Kernel32.HANDLE; pdg: DISK_GEOMETRY; size: LONGINT; vd: VirtualDisk; drive: LONGINT;
i: LONGINT;
BEGIN
context.arg.SkipWhitespace;
context.arg.String(diskname);
context.arg.SkipWhitespace;
flagss := ""; context.arg.String(flagss);
flags := {Disks.ReadOnly};
i := 0;
WHILE(flagss[i] # 0X) DO
IF flagss[i] = "W" THEN
EXCL(flags,Disks.ReadOnly);
END;
INC(i);
END;
IF diskname[1] = ":" THEN drive := ORD(CAP(diskname[0]))-ORD("A"); ELSE drive := -1 END;
IF OpenVolume( handle, flags, diskname, context) THEN
GetPhysicalDrive( handle, flags,diskname, context );
IF ~GetDiskGeometry( handle, pdg ) THEN
Kernel32.CloseHandle( handle );
context.error.String( "Could not determine disk geometry " ); context.error.Ln;
ELSE
ReportDiskGeometry( pdg, context.out);
IF pdg.MediaType = RemovableMedia THEN INCL(flags,Disks.Removable) END;
IF pdg.Cylinders > MAX( LONGINT ) THEN
HALT( 100 )
ELSE
size := SHORT( pdg.Cylinders ) * pdg.TracksPerCylinder * pdg.SectorsPerTrack;
END;
NEW( vd, handle, diskname, drive, flags, pdg.BytesPerSector, size ); Disks.registry.Add( vd, res );
IF res = Plugins.Ok THEN
AddDisk( vd );
context.out.String( diskname ); context.out.String( " registered." ); context.out.Ln;
ELSE
Kernel32.CloseHandle( handle );
context.out.String( "Could not register disk, res: " ); context.out.Int( res, 0 ); context.out.Ln;
END;
END;
END;
END Install;
PROCEDURE Notification(type: LONGINT; drives: SET);
VAR v: VirtualDisk; res: LONGINT;
BEGIN
IF type = WinFS.deviceArrival THEN
ELSIF type = WinFS.deviceRemove THEN
v := disks;
WHILE(v # NIL) DO
IF (v.drive >= 0) & (v.drive IN drives) THEN
IF IsMounted (v) THEN
KernelLog.String("Warning: Disk mounted but forcefully removed !"); KernelLog.Ln;
END;
Disks.registry.Remove( v ); v.Finish(res); RemoveDisk( v );
KernelLog.String( v.name ); KernelLog.String( " removed" ); KernelLog.Ln;
v := disks;
ELSE
v := v.next;
END;
END;
END;
END Notification;
PROCEDURE Init;
VAR mod: Kernel32.HMODULE;
str: ARRAY 64 OF CHAR;
BEGIN
str := "Kernel32.DLL"; mod := Kernel32.LoadLibrary( str ); str := "DeviceIoControl"; Kernel32.GetProcAddress( mod, str, SYSTEM.VAL( LONGINT, DeviceIoControl ) ); str := "SetFilePointer";
Kernel32.GetProcAddress( mod, str, SYSTEM.VAL( LONGINT, SetFilePointer ) );
IF DeviceIoControl = NIL THEN HALT( 100 ) END;
IF SetFilePointer = NIL THEN HALT( 100 ) END;
Modules.InstallTermHandler( Cleanup );
WinFS.RegisterNotification(Notification);
END Init;
BEGIN
Init();
END WinDisks.
short description
WinDisks is a module to access volumes and partitions under WinAos.
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.
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.
To add a windows disk to the Aos system use the command
WinDisks.Install DriveName ["RW"]
where
- DriveName can be one of "A:" to "Z:" or "PhysicalDriveX" where X has to be replaced by the physical drive number.
A drive name such as "C:" is matched to a PhysicalDriveX name, if appropriate.
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.
- A volume is inserted read-only unless the optional parameter "RW" is provided.
To access the partitions of the drive you may use the Partition Tool.
To uninstall an installed volume in Aos, use the command
WinDisks.Uninstall DriveName .
~
Examples
WinDisks.Install "f:"
WinDisks.Install "c:"
WinDisks.Install "PhysicalDrive1" "RW"
WinDisks.Uninstall "C:" ~
WinDisks.Uninstall "PhysicalDrive0" ~
WinDisks.Uninstall "F:" ~
WinDisks.Install "f:" "RW" ~
SystemTools.Free WinDisks ~