PartitionEditorTable.Mod 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. MODULE PartitionEditorTable; (** AUTHOR "staubesv"; PURPOSE "Partition Table Abstraction"; *)
  2. IMPORT
  3. KernelLog, Plugins, Disks;
  4. CONST
  5. Ok* = Disks.Ok;
  6. DeviceNotFound* = 98;
  7. BlocksizeNotSupported* = 99;
  8. NoSignature* = 100;
  9. PartitionTableOffset = 01BEH;
  10. EntrySize = 16; (* bytes *)
  11. BlockSize = 512;
  12. (* procedure Changed: changeType parameter encoding *)
  13. SizeChanged* = 1;
  14. StartLbaChanged* = 2;
  15. StartChsChanged* = 3;
  16. EndLbaChanged* = 4;
  17. EndChsChanged* = 5;
  18. TYPE
  19. Buffer* = ARRAY BlockSize OF CHAR;
  20. Block* = RECORD
  21. lba* : LONGINT;
  22. cylinder*, head*, sector* : LONGINT;
  23. END;
  24. (** Datastructure representing a slot of a partition table *)
  25. Partition* = RECORD
  26. flag* : CHAR;
  27. type* : LONGINT;
  28. start*, end* : Block; (* note: end.lba is not stored in the partition table *)
  29. size* : LONGINT;
  30. END;
  31. PartitionTable* = ARRAY 4 OF Partition;
  32. DiskGeometry = RECORD
  33. cylinders, headsPerCylinder, sectorsPerTrack : LONGINT;
  34. END;
  35. (* get a named device, necessary for ReadBlock and WriteBlock *)
  36. PROCEDURE GetDevice(CONST devicename : ARRAY OF CHAR) : Disks.Device;
  37. VAR plugin : Plugins.Plugin;
  38. BEGIN
  39. plugin := Disks.registry.Get(devicename);
  40. IF (plugin # NIL) & (plugin IS Disks.Device) THEN
  41. RETURN plugin (Disks.Device);
  42. ELSE
  43. RETURN NIL;
  44. END;
  45. END GetDevice;
  46. (* Get the disk geometry of a named device *)
  47. PROCEDURE GetDiskGeometry(CONST devicename : ARRAY OF CHAR; VAR diskGeometry : DiskGeometry; VAR res : WORD);
  48. VAR device : Disks.Device; geometry : Disks.GetGeometryMsg; ignore : WORD;
  49. BEGIN
  50. device := GetDevice(devicename);
  51. IF (device # NIL) THEN
  52. device.Open(res);
  53. IF (res = Disks.Ok) THEN
  54. device.Handle(geometry, res);
  55. IF (res = Disks.Ok) THEN
  56. diskGeometry.cylinders := geometry.cyls;
  57. diskGeometry.headsPerCylinder := geometry.hds;
  58. diskGeometry.sectorsPerTrack := geometry.spt;
  59. END;
  60. device.Close(ignore);
  61. END;
  62. ELSE
  63. res := DeviceNotFound;
  64. END;
  65. END GetDiskGeometry;
  66. (* read a block from device with name devicename. Returns with res = Disks.Ok, if successful *)
  67. PROCEDURE ReadBlock*(CONST devicename : ARRAY OF CHAR; block : LONGINT; VAR buffer: Buffer; VAR res: WORD);
  68. VAR device : Disks.Device; ignore : WORD;
  69. BEGIN
  70. device := GetDevice(devicename);
  71. IF (device # NIL) THEN
  72. IF (device.blockSize = BlockSize) THEN
  73. device.Open(res);
  74. IF (res = Ok) THEN
  75. device.Transfer(Disks.Read, block, 1, buffer, 0, res);
  76. device.Close(ignore);
  77. END;
  78. ELSE
  79. res := BlocksizeNotSupported;
  80. END;
  81. ELSE
  82. res := DeviceNotFound;
  83. END;
  84. END ReadBlock;
  85. (* write a block to device with name devicename. Returns with res = Disks.Ok, if successful*)
  86. PROCEDURE WriteBlock*(CONST devicename : ARRAY OF CHAR; block : LONGINT; VAR buffer: Buffer; VAR res: WORD);
  87. VAR device : Disks.Device; ignore : WORD;
  88. BEGIN
  89. device := GetDevice(devicename);
  90. IF (device # NIL) THEN
  91. IF (device.blockSize = BlockSize) THEN
  92. device.Open(res);
  93. IF (res = Ok) THEN
  94. device.Transfer(Disks.Write, block, 1, buffer, 0, res);
  95. device.Close(ignore);
  96. END;
  97. ELSE
  98. res := BlocksizeNotSupported;
  99. END;
  100. ELSE
  101. res := DeviceNotFound;
  102. END;
  103. END WriteBlock;
  104. PROCEDURE HasSignature*(CONST buffer : Buffer) : BOOLEAN;
  105. BEGIN
  106. RETURN (buffer[510] = 055X) & (buffer[511] = 0AAX);
  107. END HasSignature;
  108. PROCEDURE WriteSignature(VAR buffer : Buffer);
  109. BEGIN
  110. buffer[510] := 055X;
  111. buffer[511] := 0AAX;
  112. END WriteSignature;
  113. PROCEDURE Get4(CONST buffer : ARRAY OF CHAR; offset : LONGINT): LONGINT;
  114. BEGIN
  115. RETURN ORD(buffer[offset]) + ASH(ORD(buffer[offset+1]), 8) +
  116. ASH(ORD(buffer[offset+2]), 16) + ASH(ORD(buffer[offset+3]), 24)
  117. END Get4;
  118. PROCEDURE Put4(VAR buffer : ARRAY OF CHAR; value, offset : LONGINT);
  119. VAR i : LONGINT;
  120. BEGIN
  121. FOR i := 0 TO 3 DO
  122. buffer[offset + i] := CHR(value MOD 256); value := value DIV 256;
  123. END;
  124. END Put4;
  125. (** This procedure is called by the Partition Editor when you press the Load button. It should load the block number <block> on the
  126. device <devicename> and extract the partition table inside if any *)
  127. PROCEDURE LoadPartitionTable*(CONST devicename : ARRAY OF CHAR; block : LONGINT; VAR res : WORD) : PartitionTable;
  128. VAR pt : PartitionTable; buffer: Buffer;
  129. BEGIN
  130. Clear(pt);
  131. ReadBlock(devicename,block,buffer,res);
  132. IF res = Disks.Ok THEN
  133. pt := ParseBuffer(buffer,res);
  134. END;
  135. RETURN pt;
  136. END LoadPartitionTable;
  137. PROCEDURE ParseBuffer*(CONST buffer : Buffer; VAR res : WORD) : PartitionTable;
  138. VAR pt : PartitionTable; entry, offset : LONGINT;
  139. PROCEDURE ParseCHS(CONST buffer : Buffer; offset : LONGINT; VAR cylinder, head, sector : LONGINT);
  140. BEGIN
  141. head := ORD(buffer[offset]);
  142. sector := ORD(buffer[offset+1]) MOD 64;
  143. cylinder := ORD(buffer[offset+2]);
  144. cylinder := cylinder + ((ORD(buffer[offset+1]) DIV 64) * 256)
  145. END ParseCHS;
  146. BEGIN
  147. IF HasSignature(buffer) THEN
  148. res := Ok;
  149. FOR entry := 0 TO 3 DO
  150. offset := PartitionTableOffset + entry * EntrySize;
  151. pt[entry].flag := buffer[offset + 0];
  152. ParseCHS(buffer, offset + 1, pt[entry].start.cylinder, pt[entry].start.head, pt[entry].start.sector);
  153. pt[entry].type := ORD(buffer[offset + 4]);
  154. ParseCHS(buffer, offset + 5, pt[entry].end.cylinder, pt[entry].end.head, pt[entry].end.sector);
  155. pt[entry].start.lba := Get4(buffer, offset + 8);
  156. pt[entry].size := Get4(buffer, offset + 12);
  157. (* fixup: LBA of end sector *)
  158. IF (pt[entry].size > 0) THEN
  159. pt[entry].end.lba := pt[entry].start.lba + pt[entry].size - 1;
  160. ELSE
  161. pt[entry].end.lba := 0;
  162. END;
  163. END;
  164. ELSE
  165. res := NoSignature;
  166. END;
  167. RETURN pt;
  168. END ParseBuffer;
  169. (** This procedure is called by the Partition Editor when you press the Store button. It shall encode the given partition table <pt> into a
  170. 512 bytes block and store this block on the named device at block number <block> *)
  171. PROCEDURE StorePartitionTable*(CONST devicename : ARRAY OF CHAR; block : LONGINT; CONST pt : PartitionTable; VAR res : WORD);
  172. VAR buffer : Buffer;
  173. BEGIN
  174. ReadBlock(devicename,block,buffer,res);
  175. IF res = Disks.Ok THEN
  176. WriteToBuffer(pt,buffer);
  177. WriteBlock(devicename,block,buffer,res);
  178. END;
  179. END StorePartitionTable;
  180. PROCEDURE WriteToBuffer*(CONST pt : PartitionTable; VAR buffer : Buffer);
  181. VAR entry, offset : LONGINT;
  182. PROCEDURE WriteChs(VAR buffer : Buffer; offset, cylinder, head, sector : LONGINT);
  183. BEGIN
  184. buffer[offset] := CHR(head);
  185. buffer[offset + 1] := CHR((sector MOD 64) + (cylinder DIV 64) * 64);
  186. buffer[offset + 2] := CHR(cylinder);
  187. END WriteChs;
  188. BEGIN
  189. WriteSignature(buffer);
  190. FOR entry := 0 TO 3 DO
  191. offset := PartitionTableOffset + entry * EntrySize;
  192. buffer[offset + 0] := pt[entry].flag;
  193. WriteChs(buffer, offset + 1, pt[entry].start.cylinder, pt[entry].start.head, pt[entry].start.sector);
  194. buffer[offset + 4] := CHR(pt[entry].type);
  195. WriteChs(buffer, offset + 5, pt[entry].end.cylinder, pt[entry].end.head, pt[entry].end.sector);
  196. Put4(buffer, pt[entry].start.lba, offset + 8);
  197. Put4(buffer, pt[entry].size, offset + 12);
  198. END;
  199. END WriteToBuffer;
  200. PROCEDURE Lba2Chs(lba : LONGINT; VAR c, h, s : LONGINT; geometry : DiskGeometry);
  201. VAR temp : LONGINT;
  202. BEGIN
  203. c := lba DIV (geometry.headsPerCylinder * geometry.sectorsPerTrack);
  204. temp := lba MOD (geometry.headsPerCylinder * geometry.sectorsPerTrack);
  205. h := temp DIV geometry.sectorsPerTrack;
  206. s := temp MOD geometry.sectorsPerTrack + 1;
  207. END Lba2Chs;
  208. PROCEDURE Chs2Lba(c, h, s : LONGINT; VAR lba : LONGINT; geometry : DiskGeometry);
  209. BEGIN
  210. lba := ((c * geometry.headsPerCylinder + h) * geometry.sectorsPerTrack) + s - 1;
  211. END Chs2Lba;
  212. (** This procedure is called by the Partition Editor when the user presses the enter key on an editor component.
  213. Dependent of the changeType, we should now fixup all other entries of the provide partition table entry, e.g.
  214. if the start LBA changed, we should adjust the start CHS and the partition size.
  215. The Partition Editor will visualize changes we do to the Partition record *)
  216. PROCEDURE Changed*(changeType : LONGINT; VAR partition : Partition; CONST devicename : ARRAY OF CHAR; VAR res : WORD);
  217. VAR diskGeometry : DiskGeometry; geometry : BOOLEAN;
  218. BEGIN
  219. GetDiskGeometry(devicename, diskGeometry, res);
  220. IF (res = Ok) THEN
  221. geometry := TRUE;
  222. KernelLog.String(devicename); KernelLog.String(": CHS ");
  223. KernelLog.Int(diskGeometry.cylinders, 0); KernelLog.String(" x "); KernelLog.Int(diskGeometry.headsPerCylinder, 0); KernelLog.String(" x ");
  224. KernelLog.Int(diskGeometry.sectorsPerTrack, 0); KernelLog.Ln;
  225. ELSE
  226. (* if res = Disks.Unsupported, we could try to fake a disk geometry here... *)
  227. geometry := FALSE;
  228. END;
  229. IF (changeType = StartLbaChanged) OR (changeType = EndLbaChanged) THEN
  230. partition.size := partition.end.lba - partition.start.lba + 1;
  231. IF geometry THEN
  232. Lba2Chs(partition.start.lba, partition.start.cylinder, partition.start.head, partition.start.sector, diskGeometry);
  233. Lba2Chs(partition.end.lba, partition.end.cylinder, partition.end.head, partition.end.sector, diskGeometry);
  234. END;
  235. ELSIF (changeType = SizeChanged) THEN
  236. partition.end.lba := partition.start.lba + partition.size - 1;
  237. IF geometry THEN
  238. Lba2Chs(partition.end.lba, partition.end.cylinder, partition.end.head, partition.end.sector, diskGeometry);
  239. END;
  240. ELSIF (changeType = StartChsChanged) OR (changeType = EndChsChanged)THEN
  241. IF geometry THEN
  242. Chs2Lba(partition.start.cylinder, partition.start.head, partition.start.sector, partition.start.lba, diskGeometry);
  243. Chs2Lba(partition.end.cylinder, partition.end.head, partition.end.sector, partition.end.lba, diskGeometry);
  244. partition.size := partition.end.lba - partition.start.lba + 1;
  245. END;
  246. END;
  247. res := Ok;
  248. END Changed;
  249. (** Set all entries the partition table <partitionTable> to zero, needed by editor component. Can also be used here. *)
  250. PROCEDURE Clear*(VAR partitionTable : PartitionTable);
  251. VAR i : LONGINT;
  252. PROCEDURE ClearBlock(VAR block : Block);
  253. BEGIN
  254. block.lba := 0;
  255. block.cylinder := 0; block.head := 0; block.sector := 0;
  256. END ClearBlock;
  257. BEGIN
  258. FOR i := 0 TO LEN(partitionTable)-1 DO
  259. partitionTable[i].type := 0;
  260. partitionTable[i].flag := 0X;
  261. ClearBlock(partitionTable[i].start);
  262. ClearBlock(partitionTable[i].end);
  263. END;
  264. END Clear;
  265. END PartitionEditorTable.