MODULE Disks;
IMPORT SYSTEM, KernelLog, Modules, Plugins;
CONST
Read* = 0; Write* = 1;
Ok* = 0;
MediaChanged* = 2501;
WriteProtected* = 2502;
Unsupported* = 2503;
DeviceInUse* = 2504;
MediaMissing* = 2505;
ReadOnly* = 0;
Removable* = 1;
Mounted* = 0;
Primary* = 1;
Boot* = 2;
Valid* = 3;
BS = 512;
Trace = FALSE;
TraceBoot = FALSE;
Stats* = TRUE;
TYPE
Message* = RECORD END;
Partition* = RECORD
type*: LONGINT;
start*, size*: LONGINT;
flags*: SET;
ptblock*: LONGINT;
ptoffset*: LONGINT;
END;
PartitionTable* = POINTER TO ARRAY OF Partition;
Device* = OBJECT (Plugins.Plugin)
VAR
blockSize*: LONGINT;
flags*: SET;
table*: PartitionTable;
openCount*: LONGINT;
NbytesRead*, NbytesWritten*,
NnofReads*, NnofWrites*, NnofOthers*,
NnofErrors* : HUGEINT;
PROCEDURE Transfer*(op, block, num: LONGINT; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR res: LONGINT);
BEGIN
res := Unsupported
END Transfer;
PROCEDURE GetSize*(VAR size, res: LONGINT);
BEGIN
res := Unsupported
END GetSize;
PROCEDURE Handle*(VAR msg: Message; VAR res: LONGINT);
BEGIN
res := Unsupported
END Handle;
PROCEDURE Open*(VAR res: LONGINT);
VAR lockMsg: LockMsg; unlockMsg: UnlockMsg; ignore: LONGINT;
BEGIN
res := Ok;
IF openCount = 0 THEN
Handle(lockMsg, res);
IF TraceBoot THEN
KernelLog.Enter; KernelLog.String("LockMsg = "); KernelLog.Int(res, 1); KernelLog.Exit
END;
IF (res = Ok) OR (res = Unsupported) THEN
UpdatePartitionTable(SELF, res);
IF res # Ok THEN Handle(unlockMsg, ignore) END
END
END;
IF res = Ok THEN INC(openCount) END
END Open;
PROCEDURE Close*(VAR res: LONGINT);
VAR unlockMsg: UnlockMsg;
BEGIN
res := Ok; ASSERT(openCount > 0);
DEC(openCount);
IF openCount = 0 THEN
Handle(unlockMsg, res);
IF TraceBoot THEN
KernelLog.Enter; KernelLog.String("UnlockMsg = "); KernelLog.Int(res, 1); KernelLog.Exit
END;
IF res = Unsupported THEN res := Ok END;
table := NIL
END
END Close;
END Device;
EjectMsg* = RECORD (Message) END;
LockMsg* = RECORD (Message) END;
UnlockMsg* = RECORD (Message) END;
SavePowerMsg* = RECORD (Message) END;
GetGeometryMsg* = RECORD (Message)
cyls*, hds*, spt*: LONGINT
END;
ShutdownMsg* = RECORD (Message) END;
DiskBlock = ARRAY BS OF CHAR;
VAR
registry*: Plugins.Registry;
PROCEDURE InitDevice*(d: Device);
BEGIN
d.desc := "";
d.blockSize := BS; d.flags := {}; d.table := NIL; d.openCount := 0
END InitDevice;
PROCEDURE Resize(VAR p: PartitionTable; n: LONGINT);
VAR old: PartitionTable; i, len: LONGINT;
BEGIN
len := LEN(p); WHILE len < n DO len := 2*len END;
old := p; NEW(p, len);
FOR i := 0 TO LEN(old)-1 DO p[i] := old[i] END
END Resize;
PROCEDURE Get4(VAR b: ARRAY OF CHAR; i: LONGINT): LONGINT;
BEGIN
RETURN ORD(b[i]) + ASH(ORD(b[i+1]), 8) + ASH(ORD(b[i+2]), 16) + ASH(ORD(b[i+3]), 24)
END Get4;
PROCEDURE Extended(type: LONGINT): BOOLEAN;
BEGIN
RETURN (type = 5) OR (type = 15)
END Extended;
PROCEDURE ValidFlag(f: CHAR): BOOLEAN;
BEGIN
RETURN (f = 0X) OR (f = 80X) OR (f = 81X)
END ValidFlag;
PROCEDURE ReadPrimary(VAR b: DiskBlock; dev: Device; VAR p: PartitionTable; VAR n, res: LONGINT; VAR valid: BOOLEAN);
VAR e, size, i: LONGINT;
BEGIN
n := 0;
dev.Transfer(Read, 0, 1, b, 0, res);
IF (res = Ok) & (b[510] = 055X) & (b[511] = 0AAX) THEN
valid := ValidFlag(b[01BEH]) & ValidFlag(b[01BEH+16]) & ValidFlag(b[01BEH+32]) & ValidFlag(b[01BEH+48]);
IF valid THEN
FOR i := 0 TO 3 DO
e := 01BEH + 16*i; size := Get4(b, e+12);
IF (b[e+4] # 0X) & (size # 0) THEN
Resize(p, n+1); p[n].type := ORD(b[e+4]);
p[n].start := Get4(b, e+8); p[n].size := size; p[n].flags := {Valid, Primary};
IF b[e] # 0X THEN INCL(p[n].flags, Boot) END;
p[n].ptblock := 0; p[n].ptoffset := e;
INC(n)
END
END
END
ELSE
IF Trace THEN
KernelLog.String("Disks: ReadPrimary = "); KernelLog.Int(res, 1);
KernelLog.String(" on "); KernelLog.String(dev.name); KernelLog.Ln;
IF res = 0 THEN KernelLog.Memory(SYSTEM.ADR(b[0]), BS) END
END
END
END ReadPrimary;
PROCEDURE ReadLogical(VAR b: DiskBlock; dev: Device; first: LONGINT; VAR p: PartitionTable; VAR n, res: LONGINT);
VAR e, sec, size, i: LONGINT; found: BOOLEAN;
BEGIN
sec := first;
REPEAT
found := FALSE;
dev.Transfer(Read, sec, 1, b, 0, res);
IF (res = Ok) & (b[510] = 055X) & (b[511] = 0AAX) THEN
FOR i := 0 TO 3 DO
e := 01BEH + 16*i; size := Get4(b, e+12);
IF (b[e+4] # 0X) & ~Extended(ORD(b[e+4])) & (size # 0) THEN
Resize(p, n+1); p[n].type := ORD(b[e+4]);
p[n].start := sec + Get4(b, e+8); p[n].size := size; p[n].flags := {Valid};
IF b[e] # 0X THEN INCL(p[n].flags, Boot) END;
p[n].ptblock := sec; p[n].ptoffset := e;
INC(n)
END
END;
i := 0;
WHILE (i # 4) & ~found DO
e := 01BEH + 16*i; size := Get4(b, e+12);
IF Extended(ORD(b[e+4])) & (size # 0) THEN
sec := first + Get4(b, e+8);
i := 4; found := TRUE
ELSE
INC(i)
END
END
ELSE
IF Trace THEN
KernelLog.String("Disks: ReadLogical = "); KernelLog.Int(res, 1);
KernelLog.String(" on "); KernelLog.String(dev.name);
KernelLog.String(" sector "); KernelLog.Int(sec, 1); KernelLog.Ln
END
END
UNTIL ~found
END ReadLogical;
PROCEDURE UpdatePartitionTable*(dev: Device; VAR res: LONGINT);
VAR p, t: PartitionTable; i, pn, tn, size: LONGINT; buf: DiskBlock; valid: BOOLEAN;
BEGIN
IF dev.openCount = 0 THEN
tn := 0; res := Ok;
dev.table := NIL;
dev.GetSize(size, res);
IF (res = Ok) & (size = 0) THEN res := MediaMissing END;
IF (res = Ok) & (dev.blockSize = BS) THEN
NEW(p, 4); NEW(t, 8);
ReadPrimary(buf, dev, p, pn, res, valid);
i := 0;
WHILE valid & (i # pn) & (res = Ok) DO
Resize(t, tn+1); t[tn] := p[i]; INC(tn);
IF Extended(p[i].type) THEN
ReadLogical(buf, dev, p[i].start, t, tn, res)
END;
INC(i)
END
END;
IF res = Ok THEN
NEW(dev.table, tn+1);
dev.table[0].type := 256;
IF valid THEN dev.table[0].flags := {Valid} ELSE dev.table[0].flags := {} END;
dev.table[0].start := 0; dev.table[0].size := size;
FOR i := 1 TO tn DO dev.table[i] := t[i-1] END
END
ELSE
res := DeviceInUse
END;
IF TraceBoot THEN
KernelLog.Enter; KernelLog.String("UpdatePartitionTable = "); KernelLog.Int(res, 1); KernelLog.Exit
END
END UpdatePartitionTable;
PROCEDURE Cleanup;
VAR dev: Plugins.Table; i, res: LONGINT; msg: ShutdownMsg;
BEGIN
registry.GetAll(dev);
IF dev # NIL THEN
FOR i := 0 TO LEN(dev^)-1 DO
dev[i](Device).Handle(msg, res)
END
END
END Cleanup;
BEGIN
NEW(registry, "Disks", "Disk drivers");
Modules.InstallTermHandler(Cleanup)
END Disks.
(*
29.10.1999 pjm Started
19.01.1999 pjm 32-bit block numbers again
22.01.1999 pjm Hash call error in Acquire fixed
16.02.1999 pjm LRU design error fixed
04.05.1999 pjm Ported over new things from Native version, added partitioning
20.10.2000 pjm Added Cleanup and Valid from Native
26.03.2007 staubesv Added Device.NnofReads, Device.NnofWrites, Device.NnofOthers and Device.NnofErrors
Block size 512 and LONGINT block number limit us to 1TB = 1024GB.
*)