VirtualDisks.Mod 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. MODULE VirtualDisks; (** AUTHOR "staubesv"; PURPOSE "Virtual disk driver"; *)
  2. (**
  3. * The virtual disk driver installs disk images as virtual disk drives.
  4. *
  5. * Usage:
  6. *
  7. * VirtualDisks.Create [Options] filename nbrOfBlocks ~ creates a empty file for being use as file disk
  8. *
  9. * VirtualDisks.Install [Options] diskname filename ~ installs file <filename> as file disk
  10. * VirtualDisks.InstallRamdisk [Options] diskname size ~ installs and creates a ram disk
  11. * VirtualDisks.Uninstall diskname ~
  12. *
  13. * System.Free VirtualDisks ~
  14. *
  15. *)
  16. IMPORT
  17. SYSTEM,
  18. Commands, Options, Plugins, Modules, Streams, Disks, Files, Strings;
  19. CONST
  20. BlockNumberInvalid* = 101;
  21. ShortTransfer* = 102;
  22. DefaultBlocksize = 512;
  23. TYPE
  24. VirtualDisk = OBJECT(Disks.Device)
  25. VAR
  26. size : LONGINT;
  27. (* virtual disk geometry CHS *)
  28. cyls, hds, spt : LONGINT;
  29. PROCEDURE Transfer*(op, block, num: LONGINT; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR res: WORD);
  30. BEGIN {EXCLUSIVE}
  31. IF (block < 0) OR (num < 1) OR (block + num > size) THEN res := BlockNumberInvalid; RETURN; END;
  32. ASSERT((ofs >= 0) & (ofs + num * blockSize <= LEN(data)));
  33. ASSERT( num * blockSize > 0);
  34. TransferOperation(op, block, num, data, ofs, res);
  35. IF Disks.Stats THEN
  36. IF op = Disks.Read THEN
  37. INC(NnofReads);
  38. IF (res = Disks.Ok) THEN INC(NbytesRead, num * blockSize);
  39. ELSE INC(NnofErrors);
  40. END;
  41. ELSIF op = Disks.Write THEN
  42. INC(NnofWrites);
  43. IF (res = Disks.Ok) THEN INC(NbytesWritten, num * blockSize);
  44. ELSE INC(NnofErrors);
  45. END;
  46. ELSE
  47. INC(NnofOthers);
  48. END;
  49. END;
  50. END Transfer;
  51. PROCEDURE TransferOperation(op, block, num: LONGINT; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR res: WORD);
  52. BEGIN
  53. HALT(301); (* abstract *)
  54. END TransferOperation;
  55. PROCEDURE GetSize*(VAR size, res: LONGINT);
  56. BEGIN
  57. size := SELF.size; res := Disks.Ok;
  58. END GetSize;
  59. PROCEDURE Handle*(VAR msg : Disks.Message; VAR res : WORD);
  60. BEGIN
  61. IF (msg IS Disks.GetGeometryMsg) & (cyls > 0) THEN
  62. WITH msg: Disks.GetGeometryMsg DO
  63. msg.cyls := SELF.cyls; msg.hds := SELF.hds; msg.spt := SELF.spt; res := Disks.Ok
  64. END
  65. ELSE
  66. res := Disks.Unsupported
  67. END
  68. END Handle;
  69. PROCEDURE &Init(CONST name : ARRAY OF CHAR; blockSize, cyls, hds, spt : LONGINT);
  70. BEGIN
  71. SELF.blockSize := blockSize;
  72. SELF.cyls := cyls; SELF.hds := hds; SELF.spt := spt;
  73. INCL(flags, Disks.Removable);
  74. SetName(name);
  75. END Init;
  76. END VirtualDisk;
  77. TYPE
  78. FileDisk = OBJECT(VirtualDisk)
  79. VAR
  80. file : Files.File;
  81. rider : Files.Rider;
  82. PROCEDURE TransferOperation(op, block, num: LONGINT; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR res: WORD);
  83. BEGIN
  84. file.Set(rider, SYSTEM.VAL (LONGINT, block * blockSize));
  85. IF rider.res # Files.Ok THEN res := BlockNumberInvalid; RETURN; END;
  86. IF op = Disks.Read THEN
  87. file.ReadBytes(rider, data, ofs, num * blockSize);
  88. IF rider.res # 0 THEN res := ShortTransfer; ELSE res := Disks.Ok; END;
  89. ELSIF op = Disks.Write THEN
  90. file.WriteBytes(rider, data, ofs, num * blockSize);
  91. file.Update;
  92. IF rider.res # 0 THEN res := ShortTransfer; ELSE res := Disks.Ok; END;
  93. ELSE
  94. res := Disks.Unsupported;
  95. END;
  96. END TransferOperation;
  97. PROCEDURE &New*(file : Files.File; CONST name, filename : ARRAY OF CHAR; blockSize, cyls, hds, spt : LONGINT);
  98. BEGIN
  99. ASSERT(file # NIL);
  100. ASSERT(file.Length() MOD blockSize = 0);
  101. Init(name, blockSize, cyls, hds, spt);
  102. SELF.file := file;
  103. SELF.size := file.Length() DIV blockSize;
  104. desc := "Virtual Disk for file "; Strings.Append(desc, filename);
  105. END New;
  106. END FileDisk;
  107. TYPE
  108. MemoryBlock = POINTER TO ARRAY OF CHAR;
  109. RamDisk = OBJECT(VirtualDisk)
  110. VAR
  111. memory : MemoryBlock;
  112. PROCEDURE TransferOperation(op, block, num: LONGINT; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR res: WORD);
  113. BEGIN
  114. IF op = Disks.Read THEN
  115. ASSERT((block + num) * blockSize <= LEN(memory));
  116. SYSTEM.MOVE(ADDRESSOF(memory[0]) + block * blockSize, ADDRESSOF(data[ofs]), num * blockSize);
  117. res := Disks.Ok;
  118. ELSIF op = Disks.Write THEN
  119. ASSERT((block + num) * blockSize <=LEN(memory));
  120. SYSTEM.MOVE(ADDRESSOF(data[ofs]), ADDRESSOF(memory[0]) + block * blockSize, num * blockSize);
  121. res := Disks.Ok;
  122. ELSE
  123. res := Disks.Unsupported;
  124. END;
  125. END TransferOperation;
  126. PROCEDURE &New*(memory : MemoryBlock; CONST name : ARRAY OF CHAR; blockSize, cyls, hds, spt : LONGINT);
  127. BEGIN
  128. ASSERT(memory # NIL);
  129. ASSERT(LEN(memory) MOD blockSize = 0);
  130. Init(name, blockSize, cyls, hds, spt);
  131. SELF.memory := memory;
  132. SELF.size := LEN(memory) DIV blockSize;
  133. desc := "Ramdisk";
  134. END New;
  135. END RamDisk;
  136. (** Create an empty virtual disk *)
  137. PROCEDURE Create*(context : Commands.Context); (** [Options] filename nbrOfBlocks ~ *)
  138. VAR
  139. options : Options.Options;
  140. filename : ARRAY 256 OF CHAR; nbrOfBlocks, blocksize : LONGINT;
  141. file : Files.File; rider : Files.Rider;
  142. buffer : POINTER TO ARRAY OF CHAR;
  143. i : LONGINT;
  144. BEGIN
  145. NEW(options);
  146. options.Add("b", "blocksize", Options.Integer);
  147. IF options.Parse(context.arg, context.error) THEN
  148. context.arg.SkipWhitespace; context.arg.String(filename);
  149. context.arg.SkipWhitespace; context.arg.Int(nbrOfBlocks, FALSE);
  150. IF ~options.GetInteger("blocksize", blocksize) THEN blocksize := DefaultBlocksize; END;
  151. IF (filename # "") THEN
  152. IF (nbrOfBlocks > 0) THEN
  153. file := Files.New(filename);
  154. IF file # NIL THEN
  155. context.out.String("Creating virtual disk '"); context.out.String(filename); context.out.String("' ... ");
  156. context.out.Update;
  157. NEW(buffer, blocksize);
  158. file.Set(rider, 0);
  159. FOR i := 0 TO nbrOfBlocks - 1 DO
  160. file.WriteBytes(rider, buffer^, 0, blocksize);
  161. IF rider.res # 0 THEN
  162. context.error.String("Error: Could not write bytes to file"); context.error.Ln;
  163. context.result := Commands.CommandError;
  164. RETURN;
  165. END;
  166. END;
  167. Files.Register(file);
  168. context.out.String("done."); context.out.Ln;
  169. ELSE
  170. context.error.String("Could not create file '"); context.error.String(filename); context.error.String("'"); context.error.Ln;
  171. context.result := Commands.CommandError;
  172. END;
  173. ELSE
  174. context.error.String("nbrOfBlocks parameter expected."); context.error.Ln;
  175. context.result := Commands.CommandParseError;
  176. END;
  177. ELSE
  178. context.error.String("filename parameter expected."); context.error.Ln;
  179. context.result := Commands.CommandParseError;
  180. END;
  181. ELSE
  182. context.result := Commands.CommandParseError;
  183. END;
  184. END Create;
  185. PROCEDURE GetOptions(context : Commands.Context; VAR blocksize, cylinders, heads, sectors : LONGINT) : BOOLEAN;
  186. VAR options : Options.Options;
  187. BEGIN
  188. NEW(options);
  189. options.Add("b", "blocksize", Options.Integer);
  190. options.Add("c", "cylinders", Options.Integer);
  191. options.Add("h", "heads", Options.Integer);
  192. options.Add("s", "sectors", Options.Integer);
  193. IF options.Parse(context.arg, context.error) THEN
  194. (* disk geometry in CHS *)
  195. IF ~options.GetInteger("blocksize", blocksize) THEN blocksize := DefaultBlocksize; END;
  196. IF ~options.GetInteger("cylinders", cylinders) THEN cylinders := 0; END;
  197. IF ~options.GetInteger("heads", heads) THEN heads := 0; END;
  198. IF ~options.GetInteger("sectors", sectors) THEN sectors := 0; END;
  199. RETURN TRUE;
  200. ELSE
  201. RETURN FALSE;
  202. END;
  203. END GetOptions;
  204. (** Add file as virtual disk *)
  205. PROCEDURE Install*(context : Commands.Context); (** [Options] diskname filename ~ *)
  206. VAR
  207. diskname, filename : ARRAY 256 OF CHAR;
  208. blocksize, c, h, s, res : LONGINT;
  209. file : Files.File;
  210. disk : FileDisk;
  211. PROCEDURE ShowUsage(out : Streams.Writer);
  212. BEGIN
  213. out.String("VirtualDisks.Install [Options] diskname filename ~"); out.Ln;
  214. END ShowUsage;
  215. BEGIN
  216. IF GetOptions(context, blocksize, c, h, s) THEN
  217. diskname := "";
  218. context.arg.SkipWhitespace; context.arg.String(diskname);
  219. filename := "";
  220. context.arg.SkipWhitespace; context.arg.String(filename);
  221. IF (diskname = "") OR (filename = "") THEN ShowUsage(context.out); RETURN; END;
  222. file := Files.Old(filename);
  223. IF file # NIL THEN
  224. IF file.Length() MOD blocksize # 0 THEN
  225. context.error.String("File size must be multiple of blocksize"); context.error.Ln;
  226. RETURN;
  227. END;
  228. NEW(disk, file, diskname, filename, blocksize, c, h, s);
  229. Disks.registry.Add(disk, res);
  230. IF res = Plugins.Ok THEN
  231. context.out.String("Disk "); context.out.String(diskname); context.out.String(" registered");
  232. IF (s # 0) THEN
  233. context.out.String(" (CHS: ");
  234. context.out.Int(c, 0); context.out.Char("x"); context.out.Int(h, 0); context.out.Char("x");
  235. context.out.Int(s, 0); context.out.Char(")");
  236. END;
  237. context.out.Ln;
  238. ELSE
  239. context.error.String("Could not register disk, res: "); context.error.Int(res, 0); context.error.Ln;
  240. context.result := Commands.CommandError;
  241. END;
  242. ELSE
  243. context.error.String(filename); context.error.String(" not found"); context.out.Ln;
  244. context.result := Commands.CommandError;
  245. END;
  246. ELSE
  247. context.result := Commands.CommandParseError;
  248. END;
  249. END Install;
  250. (** Add file as virtual disk *)
  251. PROCEDURE InstallRamdisk*(context : Commands.Context); (** [Options] diskname size ~ *)
  252. VAR
  253. diskname : ARRAY 256 OF CHAR;
  254. size, blocksize, c, h, s, res : LONGINT;
  255. memory : MemoryBlock;
  256. disk : RamDisk;
  257. PROCEDURE ShowUsage(out : Streams.Writer);
  258. BEGIN
  259. out.String("VirtualDisks.InstallRamdisk [Options] diskname size ~"); out.Ln;
  260. END ShowUsage;
  261. BEGIN
  262. IF GetOptions(context, blocksize, c, h, s) THEN
  263. diskname := "";
  264. context.arg.SkipWhitespace; context.arg.String(diskname);
  265. context.arg.SkipWhitespace; context.arg.Int(size, FALSE);
  266. IF (diskname = "") OR (size < 10) THEN ShowUsage(context.out); RETURN; END;
  267. NEW(memory, size * blocksize);
  268. NEW(disk, memory, diskname, blocksize, c, h, s);
  269. Disks.registry.Add(disk, res);
  270. IF res = Plugins.Ok THEN
  271. context.out.String("Disk "); context.out.String(diskname); context.out.String(" registered");
  272. IF (s # 0) THEN
  273. context.out.String(" (CHS: ");
  274. context.out.Int(c, 0); context.out.Char("x"); context.out.Int(h, 0); context.out.Char("x");
  275. context.out.Int(s, 0); context.out.Char(")");
  276. END;
  277. context.out.Ln;
  278. ELSE
  279. context.error.String("Could not register disk, res: "); context.error.Int(res, 0); context.error.Ln;
  280. context.result := Commands.CommandError;
  281. END;
  282. ELSE
  283. context.result := Commands.CommandParseError;
  284. END;
  285. END InstallRamdisk;
  286. (** Remove virtual disk *)
  287. PROCEDURE Uninstall*(context : Commands.Context); (** diskname ~ *)
  288. VAR diskname : Plugins.Name; plugin : Plugins.Plugin;
  289. PROCEDURE IsMounted(dev: Disks.Device): BOOLEAN;
  290. VAR i: LONGINT;
  291. BEGIN
  292. IF dev.table # NIL THEN
  293. FOR i := 0 TO LEN(dev.table)-1 DO
  294. IF Disks.Mounted IN dev.table[i].flags THEN RETURN TRUE END
  295. END
  296. END;
  297. RETURN FALSE
  298. END IsMounted;
  299. BEGIN
  300. context.arg.SkipWhitespace;
  301. context.arg.String(diskname); context.out.String(diskname);
  302. plugin := Disks.registry.Get(diskname);
  303. IF plugin # NIL THEN
  304. IF ~IsMounted(plugin(VirtualDisk)) THEN
  305. Disks.registry.Remove(plugin);
  306. context.out.String(" removed");
  307. ELSE
  308. context.out.String(" is mounted.");
  309. END;
  310. ELSE
  311. context.out.String(" not found");
  312. context.result := Commands.CommandError;
  313. END;
  314. context.out.Ln;
  315. END Uninstall;
  316. PROCEDURE Cleanup;
  317. VAR disks : Plugins.Table; i : LONGINT;
  318. BEGIN
  319. Disks.registry.GetAll(disks);
  320. IF (disks # NIL) THEN
  321. FOR i := 0 TO LEN(disks)-1 DO
  322. IF (disks[i] IS VirtualDisk) THEN
  323. Disks.registry.Remove(disks[i]);
  324. END;
  325. END;
  326. END;
  327. END Cleanup;
  328. BEGIN
  329. Modules.InstallTermHandler(Cleanup);
  330. END VirtualDisks.
  331. VirtualDisks.Create Test.Dsk 163840 ~
  332. VirtualDisks.Install VDISK0 Test.Dsk 512 ~
  333. VirtualDisks.Uninstall VDISK0 ~
  334. VirtualDisks.InstallRamdisk RAMDISK 120000 ~
  335. VirtualDisks.Uninstall RAMDISK ~
  336. VirtualDisks.Install AosCD.iso 2048 ~
  337. System.Free VirtualDisks ~