FATVolumes.Mod 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE FATVolumes; (** AUTHOR "be"; PURPOSE "FAT file system volumes" *)
  3. (* Files.Volume implementation for [V]FAT volumes based on Disks *)
  4. IMPORT SYSTEM, Kernel, Plugins, Streams, Disks, Files, Strings, KernelLog;
  5. CONST
  6. Ok* = Disks.Ok;
  7. BS* = 512; (** supported block size *)
  8. ErrReadOnly* = 2901;
  9. ErrDiskFull* = 2902;
  10. ErrInvalidParams* = 2903;
  11. ErrIOError* = 2904;
  12. Override = 3; (* volume property flag *)
  13. FREE* = 0;
  14. EOC* = -1;
  15. BAD* = -2;
  16. IOERROR* = -3;
  17. (* Cache *)
  18. FAT* = 0;
  19. Data* = 0;
  20. FATCacheEnabled = TRUE;
  21. FATCacheSize = 127;
  22. FATWriteBehind = TRUE;
  23. DfltDataCacheSize = 256; (* default # of data cache blocks that are allocated for every mounted FAT volume, unless otherwise specified *)
  24. CacheUpdateTime = 5*1000; (* ms *)
  25. TYPE
  26. Address = Files.Address;
  27. BPB = ARRAY BS OF CHAR;
  28. CacheElement = RECORD
  29. adr: Address;
  30. valid, dirty: BOOLEAN;
  31. END;
  32. Cache = POINTER TO RECORD
  33. data: POINTER TO ARRAY OF CHAR;
  34. index: POINTER TO ARRAY OF CacheElement;
  35. startAdr: LONGINT; dataAdr: ADDRESS;
  36. blockSize, numBlocks: LONGINT;
  37. writeBehind, dirty: BOOLEAN;
  38. END;
  39. Volume* = OBJECT(Files.Volume)
  40. VAR
  41. dev-: Disks.Device;
  42. start-, (* first sector of partition *)
  43. startFAT-, (* first sector of the first FAT copy *)
  44. endFAT-, (* last sector of FAT area (includes root directory on FAT12/FAT16 volumes *)
  45. startData-, (* first physical sector of cluster 0 (although clusters 0 and 1 do not exist) *)
  46. maxClusters-, (* total number of clusters. First data cluster = 2, last data cluster = maxClusters+1 *)
  47. freeCluster: Address; (* # of cluster where search for free clusters begins *)
  48. freeCount, (* # of free clusters, -1 if unknown *)
  49. sectorsPC-, (* # of sectors per cluster *)
  50. clusterSize-, (* # of bytes per cluster *)
  51. fatSize-, (* # of sectors occupied by one FAT *)
  52. numFATs-: LONGINT; (* # of FAT copies *)
  53. ioError: BOOLEAN;
  54. label: ARRAY 12 OF CHAR; (* volume label *)
  55. unsafe*, quit, syncNow, dead: BOOLEAN;
  56. fatCache, dataCache: Cache;
  57. timer: Kernel.Timer;
  58. NreadSector*, NwriteSector*, NreadCluster*, NwriteCluster*, NreadFAT*, NwriteFAT*, NallocCluster*: LONGINT; (* stats *)
  59. PROCEDURE Init*(flags: SET; size, reserved: LONGINT);
  60. BEGIN HALT(301) (* abstract *)
  61. END Init;
  62. PROCEDURE AllocBlock*(hint: Address; VAR adr: Address);
  63. BEGIN HALT(301) (* not available on FAT volumes *)
  64. END AllocBlock;
  65. PROCEDURE FreeBlock*(adr: Address);
  66. BEGIN HALT(301) (* not available on FAT volumes *)
  67. END FreeBlock;
  68. PROCEDURE MarkBlock*(adr: Address);
  69. BEGIN HALT(301) (* not available on FAT volumes *)
  70. END MarkBlock;
  71. PROCEDURE Marked*(adr: Address): BOOLEAN;
  72. BEGIN HALT(301) (* not available on FAT volumes *)
  73. END Marked;
  74. PROCEDURE Initialize(VAR bpb: BPB; MaxClusters: LONGINT): BOOLEAN;
  75. BEGIN (* must be exclusive in overridden methods *)
  76. (* check BPB signature *)
  77. IF ~((bpb[510]=055X) & (bpb[511]=0AAX)) THEN RETURN FALSE END;
  78. IF ~(((bpb[0] = 0EBX) & (bpb[2] = 090X)) OR (bpb[0] = 0E9X)) THEN RETURN FALSE END;
  79. sectorsPC := ORD(bpb[13]);
  80. numFATs := ORD(bpb[16]);
  81. startFAT := GetUnsignedInteger(bpb, 14);
  82. maxClusters := MaxClusters;
  83. clusterSize := sectorsPC * BS;
  84. unsafe := FALSE; quit := FALSE; syncNow := FALSE;
  85. fatCache := NIL;
  86. dataCache := NIL;
  87. NreadSector := 0; NwriteSector := 0; NreadCluster := 0; NwriteCluster := 0; NallocCluster := 0; NreadFAT := 0; NwriteFAT := 0;
  88. RETURN TRUE
  89. END Initialize;
  90. (** This procedure is intended to provide low level access to FAT volume objects to disk utilities.
  91. Don't use it for mounting volumes! Initialise should be called before calling this procedure *)
  92. PROCEDURE InitLowLevel*(bpbin : ARRAY OF CHAR; numClusters : LONGINT; dev : Disks.Device; start, size, blockSize : LONGINT) : BOOLEAN;
  93. VAR bpb : BPB;
  94. BEGIN
  95. ASSERT((LEN(bpbin)=512) & (bpbin[510]=055X) & (bpbin[511]=0AAX));
  96. SYSTEM.MOVE(ADDRESSOF(bpbin[0]), ADDRESSOF(bpb[0]), 512);
  97. SELF.dev := dev; SELF.start := start; SELF.size := size; SELF.blockSize := BS;
  98. flags := {};
  99. IF Initialize(bpb, numClusters) THEN
  100. RETURN TRUE;
  101. ELSE
  102. RETURN FALSE;
  103. END;
  104. END InitLowLevel;
  105. PROCEDURE Finalize*;
  106. VAR i, j, res: LONGINT; ptable: Disks.PartitionTable;
  107. BEGIN (* must be exclusive in overridden methods *)
  108. quit := TRUE;
  109. timer.Wakeup;
  110. IF (fatCache # NIL) THEN FlushCache(fatCache); fatCache := NIL END;
  111. IF (dataCache # NIL) THEN FlushCache(dataCache); dataCache := NIL END;
  112. i := 0; j := -1; ptable := dev.table; (* todo: fix race! *)
  113. WHILE i # LEN(ptable) DO
  114. IF (start = ptable[i].start) THEN j := i END;
  115. INC(i)
  116. END;
  117. IF j # -1 THEN
  118. ASSERT(Disks.Mounted IN ptable[j].flags);
  119. EXCL(ptable[j].flags, Disks.Mounted)
  120. END;
  121. dev.Close(res); (* ignore res *)
  122. dev := NIL;
  123. Finalize^ (* see note in Files *)
  124. END Finalize;
  125. PROCEDURE Available*(): LONGINT;
  126. VAR i: Address;
  127. BEGIN {EXCLUSIVE}
  128. IF (freeCount = -1) THEN
  129. freeCount := 0;
  130. FOR i := 2 TO maxClusters+1 DO
  131. IF (ReadFATEntryX(i) = FREE) THEN INC(freeCount) END
  132. END
  133. END;
  134. RETURN freeCount*sectorsPC
  135. END Available;
  136. PROCEDURE SetCache*(CacheType, NumBlocks: LONGINT; WriteBehind: BOOLEAN);
  137. BEGIN
  138. IF (CacheType = FAT) THEN
  139. InitCache(fatCache, start, BS, NumBlocks, WriteBehind)
  140. ELSIF (CacheType = Data) THEN
  141. InitCache(dataCache, startData, clusterSize, NumBlocks, WriteBehind)
  142. END
  143. END SetCache;
  144. PROCEDURE InitCache(VAR cache: Cache; StartAdr, BlockSize, NumBlocks: LONGINT; WriteBehind: BOOLEAN);
  145. VAR i: LONGINT;
  146. BEGIN
  147. IF (cache # NIL) THEN
  148. IF cache.writeBehind THEN FlushCache(cache) END
  149. ELSIF (NumBlocks > 0) THEN
  150. NEW(cache)
  151. END;
  152. IF (NumBlocks > 0) THEN
  153. cache.startAdr := StartAdr; cache.blockSize := BlockSize;
  154. cache.numBlocks := NumBlocks; cache.writeBehind := WriteBehind;
  155. NEW(cache.data, BlockSize*NumBlocks); cache.dataAdr := ADDRESSOF(cache.data[0]);
  156. NEW(cache.index, NumBlocks);
  157. FOR i := 0 TO NumBlocks-1 DO
  158. cache.index[i].adr := -1;
  159. cache.index[i].valid := FALSE;
  160. cache.index[i].dirty := FALSE
  161. END
  162. END
  163. END InitCache;
  164. PROCEDURE FlushCache(cache: Cache);
  165. VAR i, firstDirty, lastDirty, adr, num, ofs, res: LONGINT; start: LONGINT;
  166. BEGIN
  167. IF (cache # NIL) & cache.dirty THEN
  168. cache.dirty := FALSE;
  169. i := 0; firstDirty := -1; lastDirty := -1;
  170. WHILE (i < cache.numBlocks) DO
  171. IF (cache.index[i].dirty) THEN
  172. cache.index[i].dirty := FALSE;
  173. firstDirty := i; lastDirty := i; adr := cache.index[i].adr;
  174. INC(i);
  175. WHILE (i < cache.numBlocks) & (cache.index[i].adr = adr+1) & (cache.index[i].valid) DO
  176. IF cache.index[i].dirty THEN cache.index[i].dirty := FALSE; lastDirty := i END;
  177. INC(adr); INC(i)
  178. END;
  179. start := cache.startAdr + cache.index[firstDirty].adr*(cache.blockSize DIV BS);
  180. num := (cache.blockSize DIV BS)*(lastDirty-firstDirty+1);
  181. ofs := firstDirty*cache.blockSize;
  182. dev.Transfer(Disks.Write, start, num, cache.data^, ofs, res)
  183. ELSE
  184. INC(i)
  185. END
  186. END
  187. END
  188. END FlushCache;
  189. (** ReadSector: reads a sector from the FAT area. Use ReadCluster to read a data cluster *)
  190. PROCEDURE ReadSector*(adr: Address; VAR data: ARRAY OF CHAR; VAR res: LONGINT);
  191. BEGIN {EXCLUSIVE} ReadSectorX(adr, data, 0, res)
  192. END ReadSector;
  193. PROCEDURE ReadSectorX(adr: Address; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR res: LONGINT);
  194. VAR block, idx: LONGINT;
  195. BEGIN
  196. INC(NreadSector);
  197. ASSERT((adr >= 0) & (adr < endFAT) & (ofs >= 0) & (LEN(data) >= ofs + BS), ErrInvalidParams);
  198. block := start + adr;
  199. IF (fatCache # NIL) THEN
  200. idx := adr MOD fatCache.numBlocks;
  201. IF (fatCache.index[idx].adr = adr) & (fatCache.index[idx].valid) THEN
  202. SYSTEM.MOVE(fatCache.dataAdr + idx*BS, ADDRESSOF(data[ofs]), BS);
  203. res := Ok
  204. ELSE
  205. IF fatCache.index[idx].dirty THEN FlushCache(fatCache) END;
  206. fatCache.index[idx].adr := adr; fatCache.index[idx].valid := TRUE; fatCache.index[idx].dirty := FALSE;
  207. dev.Transfer(Disks.Read, block, 1, fatCache.data^, idx*BS, res);
  208. SYSTEM.MOVE(fatCache.dataAdr + idx*BS, ADDRESSOF(data[ofs]), BS)
  209. END
  210. ELSE dev.Transfer(Disks.Read, block, 1, data, ofs, res)
  211. END;
  212. ioError := ioError OR (res # Ok)
  213. END ReadSectorX;
  214. (** WriteSector: writes a sector to the FAT area. Use WriteCluster to write a data cluster *)
  215. PROCEDURE WriteSector*(adr: Address; VAR data: ARRAY OF CHAR; VAR res: LONGINT);
  216. BEGIN {EXCLUSIVE} WriteSectorX(adr, data, 0, res)
  217. END WriteSector;
  218. PROCEDURE WriteSectorX(adr: Address; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR res: LONGINT);
  219. VAR block, idx: LONGINT;
  220. BEGIN
  221. INC(NwriteSector);
  222. ASSERT((adr >= 0) & (adr < endFAT) & (ofs >= 0) & (LEN(data) >= ofs + BS), ErrInvalidParams);
  223. IF Files.ReadOnly IN flags THEN HALT(ErrReadOnly) END; (* TODO pass as res *)
  224. block := start + adr;
  225. IF (fatCache # NIL) THEN
  226. idx := adr MOD fatCache.numBlocks;
  227. IF fatCache.writeBehind & (fatCache.index[idx].adr # adr) & (fatCache.index[idx].dirty) THEN
  228. FlushCache(fatCache)
  229. END;
  230. fatCache.index[idx].adr := adr; fatCache.index[idx].valid := TRUE;
  231. fatCache.index[idx].dirty := fatCache.writeBehind; fatCache.dirty := fatCache.writeBehind;
  232. SYSTEM.MOVE(ADDRESSOF(data[ofs]), fatCache.dataAdr + idx*BS, BS);
  233. IF ~fatCache.writeBehind THEN
  234. dev.Transfer(Disks.Write, block, 1, data, ofs, res)
  235. END
  236. ELSE dev.Transfer(Disks.Write, block, 1, data, ofs, res)
  237. END;
  238. ioError := ioError OR (res # Ok)
  239. END WriteSectorX;
  240. PROCEDURE ReadFATEntry*(adr: Address): Address;
  241. BEGIN {EXCLUSIVE} RETURN ReadFATEntryX(adr)
  242. END ReadFATEntry;
  243. PROCEDURE ReadFATEntryX(adr: Address): Address;
  244. BEGIN HALT(301) (* abstract *)
  245. END ReadFATEntryX;
  246. PROCEDURE WriteFATEntry*(adr, link: Address; VAR res: LONGINT);
  247. BEGIN {EXCLUSIVE} WriteFATEntryX(adr, link, res)
  248. END WriteFATEntry;
  249. PROCEDURE WriteFATEntryX(adr, link: Address; VAR res: LONGINT);
  250. BEGIN HALT(301) (* abstract *)
  251. END WriteFATEntryX;
  252. (** ReadCluster: reads a cluster from the data area. Use ReadSector to read a sector from the FAT area *)
  253. PROCEDURE ReadCluster*(adr: Address; VAR data: ARRAY OF CHAR; VAR res: LONGINT);
  254. BEGIN {EXCLUSIVE} ReadClusterX(adr, data, res)
  255. END ReadCluster;
  256. PROCEDURE ReadClusterX(adr: Address; VAR data: ARRAY OF CHAR; VAR res: LONGINT);
  257. VAR block, idx: LONGINT;
  258. BEGIN
  259. INC(NreadCluster);
  260. IF (adr < 2) OR (adr > maxClusters+1) THEN HALT(ErrInvalidParams) END;
  261. block := startData + (adr * sectorsPC);
  262. IF (dataCache # NIL) THEN
  263. idx := adr MOD dataCache.numBlocks;
  264. IF (dataCache.index[idx].adr = adr) & (dataCache.index[idx].valid) THEN
  265. SYSTEM.MOVE(dataCache.dataAdr + idx*clusterSize, ADDRESSOF(data[0]), clusterSize);
  266. res := Ok
  267. ELSE
  268. IF dataCache.index[idx].dirty THEN FlushCache(dataCache) END;
  269. dataCache.index[idx].adr := adr; dataCache.index[idx].valid := TRUE; dataCache.index[idx].dirty := FALSE;
  270. dev.Transfer(Disks.Read, block, sectorsPC, dataCache.data^, idx*clusterSize, res);
  271. SYSTEM.MOVE(dataCache.dataAdr + idx*clusterSize, ADDRESSOF(data[0]), clusterSize)
  272. END
  273. ELSE dev.Transfer(Disks.Read, block, sectorsPC, data, 0, res)
  274. END;
  275. ioError := ioError OR (res # Ok)
  276. END ReadClusterX;
  277. (** WriteCluster: writes a cluster to the data area. Use WriteSector to write a sector to the FAT area *)
  278. PROCEDURE WriteCluster*(adr: Address; VAR data: ARRAY OF CHAR; VAR res: LONGINT);
  279. BEGIN {EXCLUSIVE} WriteClusterX(adr, data, res)
  280. END WriteCluster;
  281. PROCEDURE WriteClusterX(adr: Address; VAR data: ARRAY OF CHAR; VAR res: LONGINT);
  282. VAR block, idx: LONGINT;
  283. BEGIN
  284. INC(NwriteCluster);
  285. IF (adr < 2) OR (adr > maxClusters+1) THEN HALT(ErrInvalidParams) END;
  286. IF Files.ReadOnly IN flags THEN HALT(ErrReadOnly) END;
  287. block := startData + (adr * sectorsPC);
  288. IF (dataCache # NIL) THEN
  289. idx := adr MOD dataCache.numBlocks;
  290. IF dataCache.writeBehind & (dataCache.index[idx].adr # adr) & (dataCache.index[idx].dirty) THEN
  291. FlushCache(dataCache)
  292. END;
  293. dataCache.index[idx].adr := adr; dataCache.index[idx].valid := TRUE;
  294. dataCache.index[idx].dirty := dataCache.writeBehind; dataCache.dirty := dataCache.writeBehind;
  295. SYSTEM.MOVE(ADDRESSOF(data[0]), dataCache.dataAdr + idx*clusterSize, clusterSize);
  296. IF ~dataCache.writeBehind THEN
  297. dev.Transfer(Disks.Write, block, sectorsPC, data, 0, res)
  298. END
  299. ELSE dev.Transfer(Disks.Write, block, sectorsPC, data, 0, res)
  300. END;
  301. ioError := ioError OR (res # Ok)
  302. END WriteClusterX;
  303. PROCEDURE AllocCluster*(link: Address; VAR res: LONGINT): Address;
  304. VAR c, cnt, entry: Address;
  305. BEGIN {EXCLUSIVE}
  306. INC(NallocCluster);
  307. IF Files.ReadOnly IN flags THEN res := ErrReadOnly; RETURN BAD END;
  308. c := freeCluster; entry := ReadFATEntryX(c); cnt := 1;
  309. WHILE (entry # FREE) & (cnt <= maxClusters+1) DO
  310. INC(c); IF (c = maxClusters+2) THEN c := 2 END;
  311. INC(cnt);
  312. entry := ReadFATEntryX(c)
  313. END;
  314. IF (entry = FREE) THEN
  315. IF (link >= 2) THEN WriteFATEntryX(link, c, res)
  316. ELSE res := Ok
  317. END;
  318. IF (res = Ok) THEN
  319. WriteFATEntryX(c, EOC, res);
  320. IF (freeCount > 0) THEN DEC(freeCount) END;
  321. freeCluster := c + 1;
  322. IF (freeCluster = maxClusters+2) THEN freeCluster := 2 END;
  323. IF (res = Ok) THEN RETURN c END
  324. END;
  325. ioError := TRUE;
  326. res := ErrIOError
  327. ELSE res := ErrDiskFull
  328. END;
  329. RETURN BAD
  330. END AllocCluster;
  331. PROCEDURE FreeClusterChain*(cluster: Address; VAR res: LONGINT);
  332. VAR c, new: Address;
  333. BEGIN {EXCLUSIVE}
  334. c := cluster; res := Ok;
  335. WHILE (c >= 2) & (res = Ok) DO
  336. new := ReadFATEntryX(c);
  337. WriteFATEntryX(c, FREE, res);
  338. IF (freeCount > 0) THEN INC(freeCount) END;
  339. c := new
  340. END
  341. END FreeClusterChain;
  342. PROCEDURE QuickFormat*;
  343. VAR sec: ARRAY BS OF CHAR; i, res: LONGINT; entries: ARRAY 2 OF LONGINT;
  344. BEGIN {EXCLUSIVE}
  345. unsafe := TRUE; entries[0] := ReadFATEntryX(0);
  346. unsafe := TRUE; entries[1] := ReadFATEntryX(1);
  347. FOR i := 0 TO fatSize*numFATs-1 DO
  348. WriteSectorX(startFAT + i, sec, 0, res) (* ignore res *)
  349. END;
  350. unsafe := TRUE; WriteFATEntryX(0, entries[0], res);
  351. unsafe := TRUE; WriteFATEntryX(1, entries[1], res);
  352. freeCluster := 2; freeCount := maxClusters;
  353. InitRoot;
  354. END QuickFormat;
  355. PROCEDURE InitRoot;
  356. BEGIN HALT(301)
  357. END InitRoot;
  358. PROCEDURE Synchronize;
  359. BEGIN {EXCLUSIVE}
  360. IF (fatCache # NIL) THEN FlushCache(fatCache) END;
  361. IF (dataCache # NIL) THEN FlushCache(dataCache) END
  362. END Synchronize;
  363. PROCEDURE AwaitDeath*;
  364. BEGIN {EXCLUSIVE}
  365. AWAIT(dead)
  366. END AwaitDeath;
  367. BEGIN {ACTIVE, SAFE}
  368. dead := FALSE; NEW(timer);
  369. WHILE ~quit DO
  370. timer.Sleep(CacheUpdateTime);
  371. IF ~quit THEN Synchronize END
  372. END;
  373. dead := TRUE
  374. END Volume;
  375. FAT1216Volume* = OBJECT(Volume)
  376. VAR
  377. firstRootSector-: Address;
  378. numRootSectors-: LONGINT; (* first sector & number of sectors of root directory relative to start *)
  379. PROCEDURE Initialize(VAR bpb: BPB; MaxClusters: LONGINT): BOOLEAN;
  380. VAR i: LONGINT;
  381. BEGIN (* must be exclusive in overridden methods *)
  382. IF Initialize^(bpb, MaxClusters) THEN
  383. fatSize := GetUnsignedInteger(bpb, 22);
  384. firstRootSector := startFAT + (numFATs*fatSize);
  385. numRootSectors := (GetUnsignedInteger(bpb, 17)*32 + BS - 1) DIV BS;
  386. endFAT := start + firstRootSector + numRootSectors - 1;
  387. startData := start + firstRootSector + numRootSectors - 2*sectorsPC;
  388. IF (bpb[38] = 029X) THEN
  389. FOR i := 0 TO 10 DO label[i] := bpb[43+i] END; label[11] := 0X;
  390. Strings.TrimRight(label, " ")
  391. END;
  392. freeCluster := 2;
  393. freeCount := -1;
  394. RETURN TRUE
  395. ELSE RETURN FALSE
  396. END
  397. END Initialize;
  398. PROCEDURE InitRoot;
  399. VAR sec: ARRAY BS OF CHAR; i, res: LONGINT;
  400. BEGIN
  401. FOR i := 0 TO numRootSectors-1 DO
  402. WriteSectorX(firstRootSector + i, sec, 0, res) (* ignore res *)
  403. END;
  404. END InitRoot;
  405. END FAT1216Volume;
  406. CONST
  407. fat12EOC = 0FF8H; (* end of clusterchain; test for greater or equal *)
  408. fat12BAD = 0FF7H; (* bad cluster *)
  409. fat12FREE = 0; (* free cluster *)
  410. TYPE
  411. FAT12Volume* = OBJECT(FAT1216Volume)
  412. PROCEDURE Initialize(VAR bpb: BPB; MaxClusters: LONGINT): BOOLEAN;
  413. BEGIN {EXCLUSIVE}
  414. RETURN Initialize^(bpb, MaxClusters) (* see note in Volumes.Initialize *)
  415. END Initialize;
  416. PROCEDURE Finalize*;
  417. BEGIN {EXCLUSIVE}
  418. Finalize^ (* see note in Volumes.Finalize *)
  419. END Finalize;
  420. PROCEDURE ReadFATEntryX(adr: Address): Address;
  421. VAR offset, res: LONGINT;
  422. entry: Address;
  423. data: ARRAY 2*BS OF CHAR;
  424. BEGIN
  425. INC(NreadFAT);
  426. IF ~unsafe & ((adr < 2) OR (adr > maxClusters+1)) THEN HALT(ErrInvalidParams) END;
  427. offset := adr + adr DIV 2;
  428. ReadSectorX(startFAT + offset DIV BS, data, 0, res);
  429. IF (offset MOD BS = BS-1) & (res = Disks.Ok) THEN (* this entry spans a sector boundary *)
  430. ReadSectorX(startFAT + offset DIV BS + 1, data, BS, res)
  431. END;
  432. IF (res = Disks.Ok) THEN
  433. entry := GetUnsignedInteger(data, offset MOD BS);
  434. IF ODD(adr) THEN entry := LSH(entry, -4) (* get high 12 bits *)
  435. ELSE entry := AND(entry, 0FFFH) (* get low 12 bits *)
  436. END;
  437. IF ~unsafe THEN
  438. IF (entry >= fat12EOC) THEN entry := EOC
  439. ELSIF (entry = fat12BAD) THEN entry := BAD
  440. END
  441. ELSE
  442. unsafe := FALSE
  443. END
  444. ELSE
  445. entry := IOERROR; ioError := TRUE; unsafe := FALSE
  446. END;
  447. RETURN entry
  448. END ReadFATEntryX;
  449. PROCEDURE WriteFATEntryX(adr, link: Address; VAR res: LONGINT);
  450. VAR offset, entry: Address; i: LONGINT;
  451. data: ARRAY 2*BS OF CHAR;
  452. BEGIN
  453. INC(NwriteFAT);
  454. IF ~unsafe THEN
  455. IF ((adr < 2) OR (adr > maxClusters+1)) OR ((link # EOC) & (link # FREE) & ((link < 2) OR (link > maxClusters+1))) THEN
  456. HALT(ErrInvalidParams)
  457. END;
  458. IF (link = EOC) THEN link := fat12EOC
  459. ELSIF (link = BAD) THEN link := fat12BAD
  460. ELSIF (link = FREE) THEN link := fat12FREE
  461. END
  462. END;
  463. offset := adr + adr DIV 2;
  464. ReadSectorX(startFAT + offset DIV BS, data, 0, res);
  465. IF (offset MOD BS = BS-1) & (res = Disks.Ok) THEN (* this entry spans a sector boundary *)
  466. ReadSectorX(startFAT + offset DIV BS + 1, data, BS, res)
  467. END;
  468. IF (res = Disks.Ok) THEN
  469. entry := GetUnsignedInteger(data, offset MOD BS);
  470. IF ODD(adr) THEN (* set high 12 bits, preserve low 4 bits *)
  471. entry := LSH(link, 4) + AND(entry, 0FH)
  472. ELSE (* preserve high 4 bits, set low 12 bits *)
  473. entry := AND(entry, 0F000H) + link
  474. END;
  475. PutUnsignedInteger(data, offset MOD BS, entry);
  476. FOR i := 0 TO numFATs-1 DO
  477. WriteSectorX(startFAT + i*fatSize + offset DIV BS, data, 0, res);
  478. IF (offset MOD BS = BS-1) & (res = Disks.Ok) THEN (* this entry spans a sector boundary *)
  479. WriteSectorX(startFAT + i*fatSize + offset DIV BS + 1, data, BS, res)
  480. END;
  481. IF (res # Disks.Ok) THEN
  482. res := IOERROR; ioError := TRUE
  483. END
  484. END
  485. ELSE
  486. res := IOERROR; ioError := TRUE
  487. END
  488. END WriteFATEntryX;
  489. END FAT12Volume;
  490. CONST
  491. fat16EOC = 0FFF8H; (* end of clusterchain; test for greater or equal *)
  492. fat16BAD = 0FFF7H; (* bad cluster *)
  493. fat16FREE = 0H; (* free cluster *)
  494. fat16CleanShutdown = {15};
  495. fat16IOError = {14};
  496. fat16VCF = fat16CleanShutdown + fat16IOError; (* Volume Clean Flags *)
  497. TYPE
  498. FAT16Volume* = OBJECT(FAT1216Volume)
  499. PROCEDURE Initialize(VAR bpb: BPB; MaxClusters: LONGINT): BOOLEAN;
  500. VAR vcf: SET; res: LONGINT;
  501. BEGIN {EXCLUSIVE}
  502. IF Initialize^(bpb, MaxClusters) THEN (* see note in Volumes.Initialize *)
  503. unsafe := TRUE; vcf := SYSTEM.VAL(SET, ReadFATEntryX(1));
  504. IF (vcf * fat16VCF = fat16VCF) THEN (* volume is clean *)
  505. unsafe := TRUE;
  506. IF ~(Files.ReadOnly IN flags) THEN WriteFATEntryX(1, SYSTEM.VAL(LONGINT, vcf - fat16CleanShutdown), res) END
  507. ELSE
  508. KernelLog.Ln; KernelLog.Enter;
  509. IF (Override IN flags) THEN
  510. KernelLog.String(" WARNING: volume was not properly unmounted; volume might be damaged !!!")
  511. ELSE
  512. INCL(flags, Files.ReadOnly);
  513. KernelLog.String(" volume was not properly unmounted; mounting read-only. Run Scandisk under DOS/Windows")
  514. END;
  515. KernelLog.Exit
  516. END;
  517. RETURN TRUE
  518. ELSE RETURN FALSE
  519. END
  520. END Initialize;
  521. PROCEDURE Finalize*;
  522. VAR vcf: SET; res: LONGINT;
  523. BEGIN {EXCLUSIVE}
  524. IF ~(Files.ReadOnly IN flags) THEN
  525. unsafe := TRUE; vcf := SYSTEM.VAL(SET, ReadFATEntryX(1));
  526. vcf := vcf + fat16CleanShutdown;
  527. IF ioError THEN vcf := vcf - fat16IOError ELSE vcf := vcf + fat16IOError END;
  528. unsafe := TRUE; WriteFATEntryX(1, SYSTEM.VAL(LONGINT, vcf), res) (* ignore res *)
  529. END;
  530. Finalize^ (* see note in Volume.Finalize *)
  531. END Finalize;
  532. PROCEDURE ReadFATEntryX(adr: Address): Address;
  533. VAR offset, entry: Address;
  534. res: LONGINT;
  535. data: ARRAY BS OF CHAR;
  536. BEGIN
  537. INC(NreadFAT);
  538. IF ~unsafe & ((adr < 2) OR (adr > maxClusters+1)) THEN HALT(ErrInvalidParams) END;
  539. offset := adr*2;
  540. ReadSectorX(startFAT + offset DIV BS, data, 0, res);
  541. IF (res = Disks.Ok) THEN
  542. entry := GetUnsignedInteger(data, offset MOD BS);
  543. IF ~unsafe THEN
  544. IF (entry >= fat16EOC) THEN entry := EOC
  545. ELSIF (entry = fat16BAD) THEN entry := BAD
  546. END
  547. ELSE
  548. unsafe := FALSE
  549. END
  550. ELSE
  551. entry := IOERROR; ioError := TRUE; unsafe := FALSE
  552. END;
  553. RETURN entry
  554. END ReadFATEntryX;
  555. PROCEDURE WriteFATEntryX(adr, link: Address; VAR res: LONGINT);
  556. VAR offset: Address; i: LONGINT;
  557. data: ARRAY BS OF CHAR;
  558. BEGIN
  559. INC(NwriteFAT);
  560. IF ~unsafe THEN
  561. IF ((adr < 2) OR (adr > maxClusters+1)) OR ((link # EOC) & (link # FREE) & ((link < 2) OR (link > maxClusters+1))) THEN
  562. HALT(ErrInvalidParams)
  563. END;
  564. IF (link = EOC) THEN link := fat16EOC
  565. ELSIF (link = BAD) THEN link := fat16BAD
  566. ELSIF (link = FREE) THEN link := fat16FREE
  567. END
  568. ELSE
  569. unsafe := FALSE
  570. END;
  571. offset := adr*2;
  572. ReadSectorX(startFAT + offset DIV BS, data, 0, res);
  573. IF (res = Disks.Ok) THEN
  574. PutUnsignedInteger(data, offset MOD BS, link);
  575. FOR i := 0 TO numFATs-1 DO
  576. WriteSectorX(startFAT + i*fatSize + offset DIV BS, data, 0, res);
  577. IF (res # Disks.Ok) THEN
  578. res := IOERROR; ioError := TRUE
  579. END
  580. END
  581. ELSE
  582. res := IOERROR; ioError := TRUE
  583. END;
  584. END WriteFATEntryX;
  585. END FAT16Volume;
  586. CONST
  587. fat32EOC = 0FFFFFF8H; (* end of clusterchain; test for greater or equal *)
  588. fat32BAD = 0FFFFFF7H; (* bad cluster *)
  589. fat32FREE = 0H; (* free cluster *)
  590. fat32CleanShutdown = {27};
  591. fat32IOError = {26};
  592. fat32VCF = fat32CleanShutdown + fat32IOError; (* Volume Clean Flags *)
  593. TYPE
  594. FAT32Volume* = OBJECT(Volume)
  595. VAR
  596. rootCluster-, (* first cluster of root directory *)
  597. fsInfo-: Address; (* sector number of FSInfo sector (relative to 'start') *)
  598. PROCEDURE Initialize(VAR bpb: BPB; MaxClusters: LONGINT): BOOLEAN;
  599. VAR i, res: LONGINT; result: BOOLEAN; info: ARRAY BS OF CHAR; vcf: SET;
  600. BEGIN {EXCLUSIVE}
  601. result := FALSE;
  602. IF Initialize^(bpb, MaxClusters) THEN (* see note in Volumes.Initialize *)
  603. IF (bpb[42] = 0X) & (bpb[43] = 0X) THEN (* version 0.0 supported *)
  604. fatSize := GetLongint(bpb, 36);
  605. endFAT := start + startFAT + (numFATs*fatSize) - 1;
  606. startData := start + startFAT + (numFATs*fatSize) - 2*sectorsPC;
  607. rootCluster := GetLongint(bpb, 44);
  608. IF (bpb[66] = 029X) THEN
  609. FOR i := 0 TO 10 DO label[i] := bpb[71+i] END; label[11] := 0X;
  610. Strings.TrimRight(label, " ")
  611. END;
  612. fsInfo := GetUnsignedInteger(bpb, 48);
  613. dev.Transfer(Disks.Read, start + fsInfo, 1, info, 0, res);
  614. IF (res = Ok) &
  615. (GetLongint(info, 0) = 041615252H) & (GetLongint(info, 508) = 0AA550000H) & (* lead & trail signature *)
  616. (GetLongint(info, 484) = 061417272H) (* struc signature *)
  617. THEN
  618. freeCount := GetLongint(info, 488);
  619. freeCluster := GetLongint(info, 492);
  620. IF (freeCluster < 2) OR (freeCluster > maxClusters+1) OR (freeCount < 0) OR (freeCount > maxClusters) THEN
  621. KernelLog.Enter; KernelLog.String("WARNING: free cluster, free count info in BPB invalid (");
  622. KernelLog.Int(freeCluster, 0); KernelLog.String(", "); KernelLog.Int(freeCount, 0); KernelLog.Char(")");
  623. KernelLog.Exit;
  624. freeCount := -1;
  625. freeCluster := 2
  626. END
  627. ELSE
  628. freeCount := -1;
  629. freeCluster := 2
  630. END;
  631. result := TRUE;
  632. unsafe := TRUE; vcf := SYSTEM.VAL(SET, ReadFATEntryX(1));
  633. IF (vcf * fat32VCF = fat32VCF) THEN (* volume is clean *)
  634. unsafe := TRUE;
  635. IF ~(Files.ReadOnly IN flags) THEN WriteFATEntryX(1, SYSTEM.VAL(LONGINT, vcf - fat32CleanShutdown), res) END
  636. ELSE
  637. KernelLog.Ln; KernelLog.Enter;
  638. IF (Override IN flags) THEN
  639. KernelLog.String(" WARNING: volume was not properly unmounted; volume might be damaged !!!")
  640. ELSE
  641. INCL(flags, Files.ReadOnly);
  642. KernelLog.String(" volume was not properly unmounted; mounting read-only. Run Scandisk under DOS/Windows")
  643. END;
  644. KernelLog.Exit
  645. END
  646. ELSE
  647. KernelLog.String(" unsupported FAT32 version")
  648. END
  649. END;
  650. RETURN result
  651. END Initialize;
  652. PROCEDURE Finalize*;
  653. VAR vcf: SET; res: LONGINT; info: ARRAY BS OF CHAR;
  654. BEGIN {EXCLUSIVE}
  655. IF ~(Files.ReadOnly IN flags) THEN
  656. dev.Transfer(Disks.Read, start + fsInfo, 1, info, 0, res);
  657. IF (res = Ok) &
  658. (GetLongint(info, 0) = 041615252H) & (GetLongint(info, 508) = 0AA550000H) & (* lead & trail signature *)
  659. (GetLongint(info, 484) = 061417272H) (* struc signature *)
  660. THEN
  661. PutLongint(info, 488, freeCount);
  662. PutLongint(info, 492, freeCluster);
  663. dev.Transfer(Disks.Write, start + fsInfo, 1, info, 0, res);
  664. ioError := ioError OR (res # Ok)
  665. END;
  666. unsafe := TRUE; vcf := SYSTEM.VAL(SET, ReadFATEntryX(1));
  667. vcf := vcf + fat32CleanShutdown;
  668. IF ioError THEN vcf := vcf - fat32IOError ELSE vcf := vcf + fat32IOError END;
  669. unsafe := TRUE; WriteFATEntryX(1, SYSTEM.VAL(LONGINT, vcf), res) (* ignore res *)
  670. END;
  671. Finalize^ (* see note in Volumes.Finalize *)
  672. END Finalize;
  673. PROCEDURE InitRoot;
  674. VAR cluster: POINTER TO ARRAY OF CHAR; res: LONGINT;
  675. BEGIN
  676. NEW(cluster,clusterSize);
  677. WriteFATEntryX(rootCluster, EOC, res); (* ignore res *)
  678. WriteClusterX(rootCluster, cluster^, res) (* ignore res *)
  679. END InitRoot;
  680. PROCEDURE ReadFATEntryX(adr: Address): Address;
  681. VAR offset, entry: Address; res: LONGINT; data: ARRAY BS OF CHAR;
  682. BEGIN
  683. INC(NreadFAT);
  684. IF ~unsafe & ((adr < 2) OR (adr > maxClusters+1)) THEN HALT(ErrInvalidParams) END;
  685. offset := adr*4;
  686. ReadSectorX(startFAT + offset DIV BS, data, 0, res);
  687. IF (res = Disks.Ok) THEN
  688. entry := AND(GetLongint(data, offset MOD BS), 0FFFFFFFH);
  689. IF ~unsafe THEN
  690. IF (entry >= fat32EOC) THEN entry := EOC
  691. ELSIF (entry = fat32BAD) THEN entry := BAD
  692. END
  693. ELSE
  694. unsafe := FALSE
  695. END
  696. ELSE
  697. entry := IOERROR; ioError := TRUE; unsafe := FALSE
  698. END;
  699. RETURN entry
  700. END ReadFATEntryX;
  701. PROCEDURE WriteFATEntryX(adr, link: Address; VAR res: LONGINT);
  702. VAR offset: Address; i: LONGINT; data: ARRAY BS OF CHAR;
  703. BEGIN
  704. INC(NwriteFAT);
  705. IF ~unsafe THEN
  706. IF ((adr < 2) OR (adr > maxClusters+1)) OR ((link # EOC) & (link # FREE) & ((link < 2) OR (link > maxClusters+1))) THEN
  707. HALT(ErrInvalidParams)
  708. END;
  709. IF (link = EOC) THEN link := fat32EOC
  710. ELSIF (link = BAD) THEN link := fat32BAD
  711. ELSIF (link = FREE) THEN link := fat32FREE
  712. END;
  713. ELSE
  714. unsafe := FALSE
  715. END;
  716. offset := adr*4;
  717. ReadSectorX(startFAT + offset DIV BS, data, 0, res);
  718. IF (res = Disks.Ok) THEN
  719. PutLongint(data, offset MOD BS, AND(GetLongint(data, offset MOD BS), LONGINT(0F0000000H)) + link);
  720. FOR i := 0 TO numFATs-1 DO
  721. WriteSectorX(startFAT + i*fatSize + offset DIV BS, data, 0, res);
  722. IF (res # Disks.Ok) THEN
  723. res := IOERROR; ioError := TRUE
  724. END
  725. END
  726. ELSE
  727. res := IOERROR; ioError := TRUE
  728. END
  729. END WriteFATEntryX;
  730. END FAT32Volume;
  731. PROCEDURE New*(context : Files.Parameters);
  732. VAR
  733. part, res, type, cacheSize: LONGINT; flags: SET; stop: BOOLEAN;
  734. name: Plugins.Name; plugin: Plugins.Plugin; dev: Disks.Device;
  735. ptable: Disks.PartitionTable; ch : CHAR;
  736. BEGIN {EXCLUSIVE}
  737. context.vol := NIL;
  738. cacheSize := DfltDataCacheSize;
  739. Files.GetDevPart(context.arg, name, part);
  740. (* read parameters: [",R"] [",X"] [",C:"<cache size>] *)
  741. flags := {}; stop := FALSE;
  742. context.arg.SkipWhitespace;
  743. ch := context.arg.Peek();
  744. WHILE (ch = ",") & (context.arg.res = Streams.Ok) & (~stop) DO
  745. context.arg.Char(ch); (* consume "," *)
  746. context.arg.Char(ch);
  747. CASE ch OF
  748. |"R": INCL(flags, Files.ReadOnly);
  749. |"X": INCL(flags, Override);
  750. |"C":
  751. context.arg.Char(ch); (* consume ":" *)
  752. context.arg.Int(cacheSize, FALSE);
  753. ELSE
  754. stop := TRUE;
  755. END;
  756. context.arg.SkipWhitespace;
  757. ch := context.arg.Peek();
  758. END;
  759. context.out.String("FATVolumes: Device "); context.out.String(name);
  760. plugin := Disks.registry.Get(name);
  761. IF (plugin # NIL) THEN
  762. dev := plugin(Disks.Device);
  763. dev.Open(res);
  764. IF res = Disks.Ok THEN
  765. ptable := dev.table;
  766. context.out.Char("#"); context.out.Int(part, 1);
  767. IF ((LEN(ptable) = 1) & (part = 0)) OR ((part > 0) & (part < LEN(ptable))) THEN
  768. type := ptable[part].type;
  769. IF (type = 1) OR (* FAT12 *)
  770. (type = 4) OR (* FAT16, up to 32MB *)
  771. (type = 6) OR (type = 0EH) OR (* FAT16, over 32MB / FAT16 LBA *)
  772. (type = 0BH) OR (type = 0CH) OR (* FAT32, up to 2047GB / FAT32 LBA *)
  773. ~IsPartitioned(dev)
  774. THEN
  775. IF ~(Disks.Mounted IN ptable[part].flags) THEN
  776. InitVol(dev, part, type, context.vol, flags, cacheSize);
  777. IF (context.vol # NIL) THEN
  778. IF (Files.ReadOnly IN flags) THEN INCL(ptable[part].flags, Disks.ReadOnly) END;
  779. INCL(ptable[part].flags, Disks.Mounted)
  780. END
  781. ELSE context.error.String(" already mounted"); context.error.Ln;
  782. END
  783. ELSE context.error.String(" wrong partition type"); context.error.Ln;
  784. END
  785. ELSE context.error.String(" invalid partition"); context.error.Ln;
  786. END;
  787. IF context.vol = NIL THEN
  788. dev.Close(res); (* close again - ignore res *)
  789. END
  790. ELSE context.error.String(" error "); context.error.Int(res, 1); context.error.Ln;
  791. END
  792. ELSE context.error.String(" not found"); context.error.Ln;
  793. END;
  794. END New;
  795. PROCEDURE WritePartitionType(type: LONGINT);
  796. BEGIN
  797. IF (type = 1) THEN KernelLog.String("FAT12")
  798. ELSIF (type = 4) OR (type = 6) OR (type = 0EH) THEN KernelLog.String("FAT16")
  799. ELSIF (type = 0BH) OR (type = 0CH) THEN KernelLog.String("FAT32")
  800. END
  801. END WritePartitionType;
  802. PROCEDURE InitVol(dev: Disks.Device; partIdx, type: LONGINT; VAR vol: Files.Volume; flags: SET; cacheSize: LONGINT);
  803. VAR
  804. bpb: BPB; vol12: FAT12Volume; vol16: FAT16Volume; vol32: FAT32Volume; fatString : ARRAY 12 OF CHAR;
  805. fatSize, numSectors, numClusters, reserved, numFATs, rootEntryCount, sectPC, res, fat : LONGINT;
  806. BEGIN
  807. dev.Transfer(Disks.Read, dev.table[partIdx].start, 1, bpb, 0, res);
  808. IF (res = Ok) THEN
  809. IF (bpb[510]=055X) & (bpb[511]=0AAX) THEN (* boot sector signature ok *)
  810. (* determine FAT type *)
  811. fatSize := GetUnsignedInteger(bpb, 22);
  812. IF (fatSize = 0) THEN fatSize := GetLongint(bpb, 36) END;
  813. numSectors := GetUnsignedInteger(bpb, 19);
  814. IF (numSectors = 0) THEN numSectors := GetLongint(bpb, 32) END;
  815. reserved := GetUnsignedInteger(bpb, 14);
  816. numFATs := ORD(bpb[16]);
  817. rootEntryCount := GetUnsignedInteger(bpb, 17);
  818. sectPC := ORD(bpb[13]);
  819. (* FAT type determination *)
  820. numClusters := (numSectors - (reserved + (numFATs*fatSize) + (rootEntryCount*32 + BS - 1) DIV BS)) DIV sectPC;
  821. IF (numClusters < 4085) THEN NEW(vol12); vol := vol12; fat := 12; fatString := "FAT12";
  822. ELSIF (numClusters < 65525) THEN NEW(vol16); vol := vol16; fat := 16; fatString := "FAT16";
  823. ELSE NEW(vol32); vol := vol32; fat := 32; fatString := "FAT32";
  824. END;
  825. IF ~IsPartitioned(dev) THEN
  826. KernelLog.String(" ("); KernelLog.String(fatString); KernelLog.String(" volume)"); KernelLog.Ln;
  827. ELSE
  828. (* check if partition type matches with determined FS type *)
  829. IF ((type = 1) & (fat # 12)) OR
  830. (((type = 4) OR (type = 6) OR (type = 0EH)) & (fat # 16)) OR
  831. (((type = 0BH) OR (type = 0CH)) & (fat # 32))
  832. THEN
  833. KernelLog.String(" failed: BPB (bios parameter block) or partition table corrupt"); KernelLog.Ln;
  834. KernelLog.String(" file system type according to partition table: "); WritePartitionType(type); KernelLog.Ln;
  835. KernelLog.String(" determined file system type: FAT"); KernelLog.Int(fat, 0);
  836. vol := NIL;
  837. RETURN
  838. END;
  839. END;
  840. WITH vol: Volume DO
  841. vol.name := "<no name>";
  842. vol.size := dev.table[partIdx].size;
  843. vol.blockSize := BS; vol.flags := flags;
  844. vol.dev := dev; vol.start := dev.table[partIdx].start;
  845. IF (Disks.ReadOnly IN dev.flags) THEN INCL(flags, Files.ReadOnly) END;
  846. IF ~vol.Initialize(bpb, numClusters) THEN
  847. EXCL(dev.table[partIdx].flags, Disks.Mounted);
  848. vol := NIL
  849. ELSE
  850. IF FATCacheEnabled & (FATCacheSize > 0) THEN vol.SetCache(FAT, FATCacheSize, FATWriteBehind) END;
  851. IF (cacheSize # 0) THEN vol.SetCache(Data, ABS(cacheSize), cacheSize < 0)
  852. ELSE vol.SetCache(Data, 0, FALSE)
  853. END
  854. END
  855. END
  856. ELSE KernelLog.String(" BPB signature wrong")
  857. END;
  858. ELSE KernelLog.String(" cannot read BPB (bios parameter block")
  859. END
  860. END InitVol;
  861. (** ---------- helper functions --------------*)
  862. (** AND - bitwise AND *)
  863. PROCEDURE AND*(a,b: LONGINT): LONGINT;
  864. BEGIN RETURN SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, a) * SYSTEM.VAL(SET, b))
  865. END AND;
  866. (** Or - bitwise OR *)
  867. PROCEDURE Or*(a,b: LONGINT): LONGINT;
  868. BEGIN RETURN SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, a) + SYSTEM.VAL(SET, b))
  869. END Or;
  870. (** GetUnsignedInteger - returns an unsigned integer at offset 'ofs' in 'b' which is assumed to be little endian *)
  871. PROCEDURE GetUnsignedInteger*(VAR b: ARRAY OF CHAR; ofs: LONGINT): LONGINT;
  872. BEGIN
  873. RETURN 100H*LONG(ORD(b[ofs+1])) + LONG(ORD(b[ofs]))
  874. END GetUnsignedInteger;
  875. (** PutUnsignedInteger - writes an unsigned integer at offset 'ofs' in 'b' which is assumed to be little endian *)
  876. PROCEDURE PutUnsignedInteger*(VAR b: ARRAY OF CHAR; ofs, value: LONGINT);
  877. BEGIN
  878. b[ofs] := CHR(value);
  879. b[ofs+1] := CHR(value DIV 100H)
  880. END PutUnsignedInteger;
  881. (** GetLongint - returns a long integer at offset 'ofs' in 'b' which is assumed to be little endian *)
  882. PROCEDURE GetLongint*(VAR b: ARRAY OF CHAR; ofs: LONGINT): LONGINT;
  883. BEGIN
  884. RETURN 1000000H*LONG(ORD(b[ofs+3])) + 10000H*LONG(ORD(b[ofs+2])) +
  885. 100H*LONG(ORD(b[ofs+1])) + LONG(ORD(b[ofs]))
  886. END GetLongint;
  887. (** PutLongint - writes a long integer at offset 'ofs' in 'b' which is assumed to be little endian *)
  888. PROCEDURE PutLongint*(VAR b: ARRAY OF CHAR; ofs, value: LONGINT);
  889. VAR i : INTEGER;
  890. BEGIN
  891. FOR i := 0 TO 3 DO b[ofs+i] := CHR(value); value := value DIV 100H END
  892. END PutLongint;
  893. PROCEDURE IsPartitioned(dev : Disks.Device) : BOOLEAN;
  894. BEGIN
  895. ASSERT((dev # NIL) & (dev.table # NIL));
  896. RETURN dev.table[0].flags * {Disks.Valid} # {};
  897. END IsPartitioned;
  898. END FATVolumes.
  899. History:
  900. 20.02.2006 Allow unpartitioned devices to be mounted (staubesv)
  901. System.Free FATVolumes ~