FATScavenger.Mod 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086
  1. MODULE FATScavenger; (** AUTHOR "staubesv"; PURPOSE "Scavenger and format for FAT file systems"; *)
  2. (*
  3. * FAT Scavenger
  4. *
  5. * checks implemented: (d)etect, (f)ix
  6. *
  7. * - long entries:
  8. * - corresponding shortEntry existent (d)
  9. * - checksum matches shortEntry checksum (d)
  10. * - order correct and terminated with 0x40 mask (d)
  11. * - longname contains only legal characters (d)
  12. * - terminated longnames are padded (d)
  13. *
  14. * - short entries
  15. * - name contains only legal characters (d)
  16. * - fileSize > number of cluster in chain - 1 (d)
  17. * - dot and dot dot point to current rsp. parent folder (d)
  18. *
  19. * - FAT
  20. * - all FATs are equal (d)
  21. * - crosslink (d)
  22. * - lost clusters (d+f)
  23. * - lost cluster is crosslinked to valid cluster (d+f)
  24. *
  25. * - volume:
  26. * - bad clusters (d+f)
  27. *
  28. * Reference:
  29. * [1] Microsoft Extensible Firmware Initiative: FAT32 File System Specification, Veriosn 1.03, December 6, 2000
  30. *
  31. * History:
  32. *
  33. * 05.08.2005 Cleanup (staubesv)
  34. * 12.12.2005 Don't open/close Disks.Device here, it's now done in PartitionsLib.Operation (staubesv)
  35. * 19.12.2005 Enable write access for scavenger, scavenger cleanup (staubesv)
  36. *)
  37. IMPORT
  38. SYSTEM, KernelLog, Streams, Files, FATVolumes, Disks, FATFiles, UTF8Strings,
  39. Strings, PartitionsLib, Clock;
  40. CONST
  41. Trace = FALSE;
  42. Details = FALSE;
  43. OK = Disks.Ok;
  44. LongName = 15;
  45. (* directory entry marks *)
  46. EntryFree = 0E5X;
  47. EntryFreeLast = 0X;
  48. (* FAT entry marks *)
  49. FREE = FATVolumes.FREE;
  50. EOC = FATVolumes.EOC;
  51. BAD = FATVolumes.BAD;
  52. (* constants for FStype *)
  53. FAT12* = 0; FAT16* = 1; FAT32* = 2;
  54. (* volume dirty flags *)
  55. fat32CleanShutdown = {27};
  56. fat32IOError = {26};
  57. fat16CleanShutdown = {15};
  58. fat16IOError = {14};
  59. SectorSize = 512;
  60. BufferSize = 512; (* in nbr of sectors per FAT; used in CompareFATs; default:=512KByte per FAT *)
  61. BitmapSize = 65536; (* nbr of 32bit entries in one block of the bitmap *)
  62. BS = PartitionsLib.BS;
  63. (* BIOS Parameter Block offsets (sector 0 of FAT Volumes) (ALL FAT FS) *)
  64. (* signature : BootSec[510] = 055X; BootSec[511] = 0AAX; *)
  65. BsJmpBoot = 0;
  66. BsOEMName = 3;
  67. BpbBytsPerSec = 11;
  68. BpbSecPerClus = 13;
  69. BpbRsvdSecCnt = 14;
  70. BpbNumFATs = 16;
  71. BpbRootEntCnt = 17;
  72. BpbTotSec16 = 19;
  73. BpbMedia = 21;
  74. BpbFATSz16 = 22;
  75. BpbSecPerTrk = 24;
  76. BpbNumHeads = 26;
  77. BpbHiddSec = 28;
  78. BpbTotSec32 = 32;
  79. (* Beginning from offset 36 the different FAT types differ *)
  80. (* FAT12/FAT16 *)
  81. BsDrvNum = 36;
  82. BsReserved1 = 37;
  83. BsBootSig = 38;
  84. BsVolID = 39;
  85. BsVolLab = 43;
  86. BsFilSysType = 54;
  87. (* FAT32 *)
  88. BpbFATSz32 = 36;
  89. BpbExtFlags = 40;
  90. BpbFSVer = 42;
  91. BpbRootClus = 44;
  92. BpbFSInfo = 48;
  93. BpbBkBootSec = 50;
  94. BpbReserved = 52;
  95. Bs32DrvNum = 64;
  96. Bs32Reserved1 = 65;
  97. Bs32BootSig = 66;
  98. Bs32VolID = 67;
  99. Bs32VolLab = 71;
  100. Bs32FilSysType = 82;
  101. (* FAT32 only: FSInfo sector structure *)
  102. FsiLeadSig = 0;
  103. FsiReserved1 = 4;
  104. FsiStrucSig = 484;
  105. FsiFreeCount = 488;
  106. FsiNxtFree = 492;
  107. FsiReserved2 = 496;
  108. FsiTrailSig = 508;
  109. TYPE
  110. Block = PartitionsLib.Block;
  111. String = PartitionsLib.String;
  112. (* data structure to track progress *)
  113. Node = POINTER TO RECORD
  114. cluster, offset : LONGINT;
  115. parent, first : LONGINT; (* parent: first cluster of parent folder; first: first cluster of clusterchain containing this cluster *)
  116. next : Node;
  117. END;
  118. (* Stack for Node elements*)
  119. STACK = OBJECT
  120. VAR
  121. head : Node;
  122. PROCEDURE PushCluster(cluster : Cluster);
  123. VAR
  124. temp : Node;
  125. BEGIN
  126. ASSERT(cluster#NIL);
  127. NEW(temp);
  128. temp.cluster:=cluster.cluster; temp.offset:=cluster.GetPos();
  129. temp.first:=cluster.first; temp.parent:=cluster.parent;
  130. temp.next:=head.next; head.next:=temp;
  131. (* the fields temp.first and temp.parent are initializes in ProcessHead if necessary *)
  132. END PushCluster;
  133. PROCEDURE Push(node : Node);
  134. BEGIN
  135. ASSERT(node#NIL);
  136. node.next:=head.next; head.next:=node;
  137. END Push;
  138. PROCEDURE ReplaceTop(cluster : Cluster);
  139. BEGIN
  140. ASSERT((cluster#NIL) & (~Empty()));
  141. head.next.cluster:=cluster.cluster;
  142. head.next.offset:=cluster.GetPos();
  143. END ReplaceTop;
  144. PROCEDURE RemoveTop; BEGIN ASSERT(~Empty()); head.next:=head.next.next; END RemoveTop;
  145. PROCEDURE GetTop():Node; BEGIN ASSERT(~Empty()); RETURN head.next; END GetTop;
  146. PROCEDURE Empty():BOOLEAN; BEGIN RETURN (head.next=NIL); END Empty;
  147. PROCEDURE &Init*;
  148. BEGIN
  149. NEW(head); head.next:=NIL;
  150. END Init;
  151. END STACK;
  152. TYPE
  153. LongEntryList = OBJECT
  154. VAR
  155. head, current : LongEntry;
  156. PROCEDURE Insert(entry: LongEntry);
  157. BEGIN
  158. entry.next:=head.next; head.next:=entry;
  159. END Insert;
  160. PROCEDURE GetNext():LongEntry;
  161. VAR
  162. result : LongEntry;
  163. BEGIN
  164. ASSERT((head.next#NIL) & (current#NIL));
  165. result:=current; current:=current.next;
  166. RETURN result;
  167. END GetNext;
  168. PROCEDURE SetCurrent; BEGIN current:=head.next; END SetCurrent;
  169. PROCEDURE HasNext():BOOLEAN; BEGIN RETURN (current#NIL); END HasNext;
  170. PROCEDURE Clear; BEGIN head.next:=NIL; current:=NIL; END Clear;
  171. PROCEDURE &Init*;
  172. BEGIN
  173. NEW(head); head.next:=NIL; current:=NIL;
  174. END Init;
  175. END LongEntryList;
  176. TYPE
  177. (* abstract data type for FAT directory entry *)
  178. Entry = OBJECT
  179. VAR
  180. (* if errors are found when checking this short entry, the checker fills
  181. the data field <correctedEntry> with the corrected version of the orginal data (rawEntry) *)
  182. rawEntry, correctedEntry : ARRAY 32 OF CHAR;
  183. (* This entry is located at cluster number <cluster> at <offset>*32Bytes *)
  184. cluster : Files.Address;
  185. offset : LONGINT;
  186. PROCEDURE ParseRawEntry;
  187. BEGIN
  188. HALT(99); (* abstract *)
  189. END ParseRawEntry;
  190. (* Debug: prints the information contained in rawEntry in human readable form to the KernelLog *)
  191. PROCEDURE Print;
  192. BEGIN
  193. HALT(99); (* abstract *)
  194. END Print;
  195. END Entry;
  196. ShortEntry = OBJECT(Entry)
  197. VAR
  198. (* FAT FS directory entry fields *)
  199. shortName: ARRAY 12 OF CHAR; (* 11 byte from directory entry + 1 byte 0X for string termination*)
  200. attr : SET;
  201. (* NTRes : CHAR; ignored *)
  202. (* CrtTimeTenth : CHAR; ignored *)
  203. crtTime, crtDate : LONGINT;
  204. lstAccDate : LONGINT;
  205. firstCluster : LONGINT;
  206. wrtTime, wrtDate : LONGINT;
  207. fileSize : LONGINT;
  208. directory : BOOLEAN; (* is this ShortEntry a folder? *)
  209. PROCEDURE ParseRawEntry;
  210. VAR i : LONGINT;
  211. BEGIN
  212. (* get shortname *)
  213. IF rawEntry[0]=05X THEN (* special case: if the first character is 0x05 then the character 0xE5 (marks entry as free ) is meant *)
  214. shortName[0]:=0E5X
  215. ELSE
  216. shortName[0]:=rawEntry[0];
  217. END;
  218. FOR i:=1 TO 10 DO shortName[i]:=rawEntry[i]; END;
  219. shortName[11]:=0X;
  220. (* get attributes *)
  221. attr:=SYSTEM.VAL(SET, LONG(ORD(rawEntry[11])));
  222. IF (FATFiles.faDirectory IN attr) THEN directory:=TRUE; ELSE directory:=FALSE; END;
  223. (* get creation time and date *)
  224. crtTime:=FATFiles.TimeFAT2Oberon(FATVolumes.GetUnsignedInteger(rawEntry,14),ORD(rawEntry[13]));
  225. crtDate:=FATFiles.DateFAT2Oberon(FATVolumes.GetUnsignedInteger(rawEntry,16));
  226. (* get last access date *)
  227. lstAccDate:=FATFiles.DateFAT2Oberon(FATVolumes.GetUnsignedInteger(rawEntry, 18));
  228. (* get first cluster *)
  229. firstCluster:=FATVolumes.GetUnsignedInteger(rawEntry, 26);
  230. IF fsType2=FAT32 THEN firstCluster:=firstCluster+10000H*FATVolumes.GetUnsignedInteger(rawEntry,20); END;
  231. (* get time and date of last write access *)
  232. wrtTime:=FATFiles.TimeFAT2Oberon(FATVolumes.GetUnsignedInteger(rawEntry,22),0);
  233. wrtDate:=FATFiles.DateFAT2Oberon(FATVolumes.GetUnsignedInteger(rawEntry,24));
  234. (* get filesize *)
  235. fileSize:=FATVolumes.GetLongint(rawEntry,28);
  236. (* update correctedRawEntry *)
  237. COPY(rawEntry, correctedEntry);
  238. END ParseRawEntry;
  239. (* calculates a checksum for the shortName field which must match the checksum-field of long directory entries *)
  240. PROCEDURE GetChecksum(): LONGINT;
  241. VAR
  242. checksum, i : LONGINT;
  243. BEGIN
  244. checksum:=0;
  245. FOR i:=0 TO 10 DO
  246. IF ODD(checksum) THEN checksum := 80H + checksum DIV 2 ELSE checksum := checksum DIV 2 END;
  247. checksum := (checksum + ORD(shortName[i])) MOD 100H
  248. END;
  249. RETURN checksum;
  250. END GetChecksum;
  251. (* debug: display content of this short entry to KernelLog *)
  252. PROCEDURE Print;
  253. BEGIN
  254. KernelLog.String(shortName);
  255. IF FATFiles.faReadOnly IN attr THEN KernelLog.String(" R"); ELSE KernelLog.String(" r"); END;
  256. IF FATFiles.faHidden IN attr THEN KernelLog.String("H"); ELSE KernelLog.String("h"); END;
  257. IF FATFiles.faSystem IN attr THEN KernelLog.String("S"); ELSE KernelLog.String("s"); END;
  258. IF FATFiles.faArchive IN attr THEN KernelLog.String("A"); ELSE KernelLog.String("a"); END;
  259. IF FATFiles.faDirectory IN attr THEN KernelLog.String("D"); ELSE KernelLog.String("d"); END;
  260. IF FATFiles.faVolumeID IN attr THEN KernelLog.String("V "); ELSE KernelLog.String("v "); END;
  261. KernelLog.String("1st: "); KernelLog.Int(firstCluster,10);
  262. KernelLog.String(" size: "); KernelLog.Int(fileSize,10);
  263. KernelLog.Ln;
  264. END Print;
  265. END ShortEntry;
  266. LongEntry = OBJECT(Entry)
  267. VAR
  268. (* FAT Long Directory Entry Structure *)
  269. order : INTEGER; (* in cluster chain *)
  270. name: ARRAY 13 OF LONGINT; (* unicode *)
  271. type : INTEGER;
  272. chksum : LONGINT;
  273. FstClusLO : LONGINT;
  274. next : LongEntry; (* link to next longentry; used by LongEntryList *)
  275. last : BOOLEAN; (* last cluster in longname cluster chain *)
  276. PROCEDURE ParseRawEntry;
  277. VAR
  278. i,j : INTEGER;
  279. BEGIN
  280. (* last cluster in the chain ? *)
  281. IF FATVolumes.AND(40H, ORD(rawEntry[0])) = 40H THEN
  282. last:=TRUE;
  283. order:=ORD(rawEntry[0]) MOD 40H;
  284. ELSE
  285. order:=ORD(rawEntry[0]);
  286. END;
  287. chksum:=ORD(rawEntry[13]);
  288. (* read in long name component *)
  289. j:=0;
  290. FOR i:=0 TO 4 DO name[j]:=FATVolumes.GetUnsignedInteger(rawEntry,1+2*i); INC(j); END;
  291. FOR i:=0 TO 5 DO name[j]:=FATVolumes.GetUnsignedInteger(rawEntry,14+2*i); INC(j); END;
  292. FOR i:=0 TO 1 DO name[j]:=FATVolumes.GetUnsignedInteger(rawEntry,28+2*i); INC(j); END;
  293. type:=ORD(rawEntry[12]);
  294. FstClusLO:=FATVolumes.GetUnsignedInteger(rawEntry,26);
  295. (* initialize correctedEntry field *)
  296. COPY(rawEntry, correctedEntry);
  297. END ParseRawEntry;
  298. PROCEDURE Print;
  299. VAR longname : ARRAY 256 OF CHAR;
  300. BEGIN
  301. KernelLog.String("Long: "); KernelLog.String("order: "); KernelLog.Int(order,3); KernelLog.String(" ");
  302. UTF8Strings.UnicodetoUTF8(name, longname); KernelLog.String(longname);
  303. KernelLog.Ln;
  304. END Print;
  305. PROCEDURE &Init*;
  306. BEGIN
  307. last:=FALSE; next:=NIL;
  308. END Init;
  309. END LongEntry;
  310. TYPE
  311. Bitmap = POINTER TO ARRAY OF SET;
  312. ClusterBitmap = OBJECT
  313. VAR
  314. maxClusters : LONGINT;
  315. bitmaps : POINTER TO ARRAY OF Bitmap;
  316. bitmapsPos, bmPos, bmOffset: LONGINT;
  317. PROCEDURE &Init*(MaxClusters : LONGINT);
  318. VAR
  319. size : LONGINT;
  320. BEGIN
  321. maxClusters:=MaxClusters;
  322. size:=maxClusters DIV BitmapSize*MAX(SET);
  323. IF maxClusters MOD BitmapSize*MAX(SET)>0 THEN INC(size); END;
  324. NEW(bitmaps, size);
  325. ASSERT(bitmaps#NIL);
  326. END Init;
  327. PROCEDURE CalcAddress(pos: LONGINT);
  328. VAR
  329. bitmapSize : LONGINT;
  330. BEGIN
  331. ASSERT((pos<=maxClusters+1) & (pos>1));
  332. bitmapSize:=BitmapSize*(MAX(SET)+1);
  333. bitmapsPos:=pos DIV bitmapSize;
  334. bmPos := ( pos MOD bitmapSize ) DIV (MAX(SET)+1);
  335. bmOffset := ( pos MOD bitmapSize ) MOD (MAX(SET)+1);
  336. ASSERT((bmOffset <= MAX(SET)) & (bmPos<BitmapSize));
  337. END CalcAddress;
  338. PROCEDURE SetBit(pos : LONGINT; VAR collision: BOOLEAN);
  339. VAR
  340. bitmap : Bitmap;
  341. BEGIN
  342. ASSERT((pos<=maxClusters+1) & (pos>1));
  343. CalcAddress(pos);
  344. IF bitmaps[bitmapsPos]=NIL THEN (* create Bitmap if not yet done *)
  345. NEW(bitmap, BitmapSize);
  346. ASSERT(bitmap#NIL);
  347. bitmaps[bitmapsPos]:=bitmap;
  348. END;
  349. bitmap:=bitmaps[bitmapsPos];
  350. IF bmOffset IN bitmap[bmPos] THEN (* ERROR: report collision, FAT entry already used *)
  351. collision:=TRUE;
  352. ELSE
  353. INCL(bitmap[bmPos], bmOffset); collision:=FALSE;
  354. END;
  355. END SetBit;
  356. (* returns true if the bit at position <pos> is set, false otherwise *)
  357. PROCEDURE IsSet(pos : LONGINT):BOOLEAN;
  358. BEGIN
  359. CalcAddress(pos);
  360. RETURN bmOffset IN bitmaps[bitmapsPos][bmPos];
  361. END IsSet;
  362. END ClusterBitmap;
  363. Cluster = OBJECT
  364. VAR
  365. cluster : LONGINT; (* FAT32: address of the cluster; FAT1216: if rootDir t-...... *)
  366. parent, first : LONGINT; (* parent: first cluster of parent folder; first: first cluster of this foldes *)
  367. clusterSize : LONGINT;
  368. currentEntry, maxEntries: LONGINT;
  369. data : POINTER TO ARRAY OF CHAR;
  370. next : LONGINT; (* cluster number of next cluster of this foldes, 0: none*)
  371. PROCEDURE &Init*(csize :LONGINT);
  372. BEGIN
  373. clusterSize:=csize; maxEntries:=clusterSize DIV 32;
  374. NEW(data, clusterSize);
  375. next:=0;
  376. END Init;
  377. PROCEDURE SetPos(pos: LONGINT);
  378. BEGIN
  379. ASSERT((pos<=maxEntries) & (pos >= 0)); (* position can be one greater than allowed. will be checked *)
  380. currentEntry:=pos;
  381. END SetPos;
  382. PROCEDURE GetPos(): LONGINT;
  383. BEGIN
  384. ASSERT(currentEntry<=maxEntries); (* can be bigger than maxEntries-1 *)
  385. RETURN currentEntry;
  386. END GetPos;
  387. PROCEDURE GetNext():Entry;
  388. VAR
  389. result : Entry;
  390. shortEntry : ShortEntry;
  391. longEntry : LongEntry;
  392. i ,j : LONGINT;
  393. type: LONGINT;
  394. BEGIN
  395. ASSERT(currentEntry<=maxEntries-1);
  396. type:=FATVolumes.AND(3FH, ORD(data[currentEntry*32+11])); (*loads lower 6 bits from attr field *)
  397. IF (data[currentEntry*32]#EntryFree) & (data[currentEntry*32]#EntryFreeLast) THEN (* entry not free *)
  398. IF (type=LongName) THEN (* long directory entry or invalid entry *)
  399. NEW(longEntry); result:=longEntry;
  400. ELSE (* short directory entry, volumeID entry or invalid entry *)
  401. NEW(shortEntry); result:=shortEntry;
  402. END;
  403. j:=0;
  404. FOR i:=currentEntry*32 TO currentEntry*32+31 DO result.rawEntry[j]:=data[i];INC(j); END;
  405. result.offset:=currentEntry;
  406. result.ParseRawEntry; (* evaluate the result.rawEntry[] data *)
  407. ELSE (* free entry *)
  408. result:=NIL;
  409. END;
  410. currentEntry:=currentEntry+1;
  411. RETURN result;
  412. END GetNext;
  413. PROCEDURE HasNext():BOOLEAN;
  414. BEGIN
  415. RETURN (currentEntry <= maxEntries - 1);
  416. END HasNext;
  417. END Cluster;
  418. TYPE
  419. PathName = POINTER TO RECORD
  420. name : POINTER TO ARRAY OF CHAR;
  421. next : PathName;
  422. END;
  423. Path = OBJECT
  424. VAR
  425. head : PathName;
  426. prefix : Files.Prefix;
  427. PROCEDURE &Init*(CONST prefix : Files.Prefix);
  428. BEGIN
  429. NEW(head); head.next:=NIL;
  430. SELF.prefix := prefix; (* prefix of the mounted volume *)
  431. END Init;
  432. PROCEDURE Append(CONST dirname : ARRAY OF CHAR);
  433. VAR temp, new : PathName; i : INTEGER;
  434. BEGIN
  435. NEW(new); NEW(new.name,LEN(dirname));
  436. i:=0;
  437. WHILE i<LEN(dirname) DO
  438. IF dirname[i]#" " THEN new.name[i] :=dirname[i];END;
  439. INC(i);
  440. END;
  441. temp:=head; WHILE temp.next#NIL DO temp:=temp.next; END;
  442. temp.next:=new;
  443. END Append;
  444. PROCEDURE RemoveLast;
  445. VAR temp : PathName;
  446. BEGIN
  447. ASSERT(head.next#NIL);
  448. temp:=head; WHILE(temp.next.next#NIL) DO temp:=temp.next; END;
  449. temp.next:=NIL; (* last node removed *)
  450. END RemoveLast;
  451. (* get the current path as string *)
  452. PROCEDURE Get(VAR result: ARRAY OF CHAR);
  453. VAR
  454. temp : PathName;
  455. writer : Streams.StringWriter;
  456. BEGIN
  457. NEW(writer, 2048);
  458. temp := head;
  459. writer.String(prefix);
  460. writer.String(":");
  461. WHILE (temp.next#NIL) DO
  462. temp := temp.next;
  463. writer.String(temp.name^);
  464. writer.String(Files.PathDelimiter);
  465. END;
  466. writer.Get(result);
  467. END Get;
  468. PROCEDURE Print;
  469. VAR temp : PathName;
  470. BEGIN
  471. ASSERT(head.next#NIL);
  472. KernelLog.String(prefix); KernelLog.String(":");
  473. temp:=head;
  474. WHILE(temp.next#NIL) DO
  475. temp:=temp.next;
  476. KernelLog.String(temp.name^); KernelLog.String(Files.PathDelimiter);
  477. END;
  478. KernelLog.Ln;
  479. END Print;
  480. END Path;
  481. LostCluster = POINTER TO RECORD
  482. cluster, link : LONGINT;
  483. next : LostCluster;
  484. chain : LostCluster;
  485. terminated, crosslink : BOOLEAN;
  486. END;
  487. ClusterList = OBJECT
  488. VAR
  489. head, tail, previous, current : LostCluster;
  490. size : LONGINT;
  491. currentDeleted : BOOLEAN;
  492. (* can be used to reset list *)
  493. PROCEDURE &Init*;
  494. BEGIN
  495. IF(head=NIL) THEN NEW(head); END;
  496. head.next:=NIL; tail:=NIL;
  497. previous:=head; current:=head;
  498. size:=0; currentDeleted:=FALSE;
  499. END Init;
  500. PROCEDURE Insert(lost : LostCluster);
  501. BEGIN
  502. IF head.next=NIL THEN
  503. lost.next:=NIL;
  504. head.next:=lost; tail:=lost;
  505. ELSE
  506. lost.next:=head.next;
  507. head.next:=lost;
  508. END;
  509. INC(size);
  510. END Insert;
  511. PROCEDURE Append(lost : LostCluster);
  512. BEGIN
  513. lost.next:=NIL;
  514. IF head.next=NIL THEN
  515. head.next:=lost; tail:=lost;
  516. ELSE
  517. tail.next:=lost; tail:=lost;
  518. END;
  519. INC(size);
  520. END Append;
  521. PROCEDURE SetCurrent;
  522. BEGIN
  523. current:=head; previous:=head;
  524. END SetCurrent;
  525. PROCEDURE GetNext(): LostCluster;
  526. BEGIN
  527. ASSERT((head.next#NIL) & (current#NIL));
  528. IF currentDeleted THEN
  529. currentDeleted:=FALSE;
  530. current:=previous.next;
  531. ELSE
  532. previous:=current;
  533. current:=current.next;
  534. END;
  535. RETURN current;
  536. END GetNext;
  537. PROCEDURE HasNext():BOOLEAN;
  538. BEGIN
  539. RETURN current.next#NIL;
  540. END HasNext;
  541. PROCEDURE RemoveCurrent; (* continuing iteration via GetNext() and HasNext() is allowed *)
  542. BEGIN
  543. ASSERT((current#NIL) & (previous#NIL) & ~Empty() & (current#head));
  544. IF current=tail THEN tail:=previous; END;
  545. previous.next:=current.next;
  546. currentDeleted:=TRUE;
  547. DEC(size);
  548. END RemoveCurrent;
  549. PROCEDURE Empty():BOOLEAN;
  550. BEGIN
  551. RETURN head.next=NIL;
  552. END Empty;
  553. PROCEDURE Print;
  554. VAR
  555. temp : LostCluster;
  556. BEGIN
  557. temp:=head;
  558. KernelLog.String("*** lost cluster list ***"); KernelLog.Ln;
  559. WHILE temp.next#NIL DO
  560. temp:=temp.next;
  561. KernelLog.Int(temp.cluster,10); KernelLog.Int(temp.link,10); KernelLog.Ln;
  562. END;
  563. END Print;
  564. END ClusterList;
  565. TYPE
  566. FATScavenger*= OBJECT(PartitionsLib.Operation);
  567. VAR
  568. (* parameters: operations to be done *)
  569. doSurfaceScan, doCompareFATs, doLostClusters, doWrite : BOOLEAN;
  570. (* scavenger information *)
  571. filesScanned-, directoriesScanned- : LONGINT;
  572. longEntriesScanned-, shortEntriesScanned-, emptyEntriesScanned- : LONGINT;
  573. freeClusters-, badClusters-, lostClusters-, lostClusterChains- : LONGINT;
  574. (* fragmentation infos *)
  575. nbrFreeFragments : LONGINT;
  576. errorsFound: LONGINT;
  577. ioError : BOOLEAN;
  578. curOp, maxOp : LONGINT;
  579. vol : FATVolumes.Volume; dev : Disks.Device;
  580. path : Path;
  581. cluster, baseCluster : Cluster;
  582. processStack : STACK;
  583. longList : LongEntryList;
  584. lostList, lostErrorList, fileList, xlinkedList : ClusterList;
  585. fsType : LONGINT;
  586. FAT1216rootDir : Cluster;
  587. clusterBitmap : ClusterBitmap;
  588. (* fields of fsinfo block *)
  589. fsinfo : ARRAY FATVolumes.BS OF CHAR;
  590. fsinfoAddress : LONGINT;
  591. fsInfoLoaded : BOOLEAN;
  592. (* external updated fields *)
  593. deleted : LONGINT;
  594. PROCEDURE SetParameters*(doSurfaceScan, doCompareFATs, doLostClusters, doWrite : BOOLEAN);
  595. BEGIN
  596. SELF.doSurfaceScan := doSurfaceScan; SELF.doCompareFATs := doCompareFATs; SELF.doLostClusters := doLostClusters;
  597. SELF.doWrite := doWrite;
  598. IF doWrite THEN locktype := PartitionsLib.WriterLock ELSE locktype := PartitionsLib.ReaderLock; END;
  599. curOp := 0; maxOp := 1;
  600. IF doSurfaceScan THEN INC(maxOp); END; IF doCompareFATs THEN INC(maxOp); END;
  601. IF doLostClusters THEN INC(maxOp); END;
  602. END SetParameters;
  603. PROCEDURE ValidParameters*() : BOOLEAN;
  604. BEGIN
  605. dev := disk.device;
  606. IF dev = NIL THEN ReportError("Could not access device"); RETURN FALSE; END;
  607. IF ~PartitionsLib.IsFatType(disk.table[partition].type) & ~disk.isDiskette THEN
  608. ReportError("Scavenger only supports FAT formatted partitions"); RETURN FALSE;
  609. END;
  610. RETURN TRUE;
  611. END ValidParameters;
  612. PROCEDURE DoOperation*;
  613. VAR
  614. string, str : String;
  615. collision : BOOLEAN;
  616. vdf, clnShutdown, ioError : SET; (* Volume dirty flags (FAT16/32) *)
  617. bpb : Block;
  618. res : WORD;
  619. BEGIN
  620. IF Trace THEN KernelLog.String("FATScavenger started on "); END;
  621. SetStatus(state.status, "Scavenger starting...", 0, 0, 0, FALSE);
  622. (* Get FAT volume *)
  623. dev.Transfer(Disks.Read, disk.table[partition].start, 1, bpb, 0, res);
  624. IF res # Disks.Ok THEN
  625. PartitionsLib.GetErrorMsg("Could not load boot sectors", res, string); ReportError(string);
  626. RETURN;
  627. END;
  628. vol := GetVolume(dev, partition, bpb);
  629. IF vol = NIL THEN
  630. ReportError("Could not get FAT volume");
  631. RETURN;
  632. ELSE
  633. info.String("FAT volume type: ");
  634. IF vol IS FATVolumes.FAT12Volume THEN
  635. fsType := FAT12; info.String("FAT12");
  636. ELSIF vol IS FATVolumes.FAT16Volume THEN
  637. fsType := FAT16; info.String("FAT16");
  638. ELSIF vol IS FATVolumes.FAT32Volume THEN
  639. fsType := FAT32; info.String("FAT32");
  640. ELSE
  641. ReportError("Only FAT12, FAT16 and FAT32 volumes are supported");
  642. info.String("No FAT (Error)");
  643. RETURN;
  644. END;
  645. info.Ln;
  646. END;
  647. fsType2 := fsType;
  648. NEW(clusterBitmap, vol.maxClusters); NEW(cluster, vol.clusterSize);
  649. NEW(path, "");
  650. (* evaluate volume dirty flags on FAT16/FAT32 volumes *)
  651. IF (fsType = FAT16) OR (fsType = FAT32) THEN
  652. vol.unsafe := TRUE;
  653. vdf := SYSTEM.VAL(SET, vol.ReadFATEntry(1));
  654. vol.unsafe := FALSE;
  655. IF fsType = FAT16 THEN
  656. clnShutdown := fat16CleanShutdown; ioError := fat16IOError;
  657. ELSE
  658. clnShutdown := fat32CleanShutdown; ioError := fat32IOError;
  659. END;
  660. IF vdf * ioError # ioError THEN (* I/O error *)
  661. info.String("Device reports I/O error"); info.Ln;
  662. IF ~doSurfaceScan THEN
  663. ReportError("Device reports I/O errors. A surface scan is recommended.");
  664. END;
  665. END;
  666. IF vdf * clnShutdown # clnShutdown THEN (* volume has not been unmounted properly *)
  667. info.String("The volume has not been properly unmounted last time"); info.Ln;
  668. END;
  669. (* write back the volume dirty flags *)
  670. IF doWrite THEN
  671. IF vdf * (clnShutdown + ioError) # (clnShutdown + ioError) THEN
  672. vdf := vdf + clnShutdown + ioError;
  673. vol.unsafe := TRUE;
  674. vol.WriteFATEntry(1, SYSTEM.VAL(LONGINT, vdf), res);
  675. vol.unsafe := FALSE;
  676. IF res # Disks.Ok THEN
  677. PartitionsLib.GetErrorMsg("Could not write back volume dirty flags ", res, string); ReportError (string);
  678. ELSE
  679. info.String("Corrected: Cleared volume dirty flag"); info.Ln;
  680. END;
  681. END;
  682. END;
  683. END;
  684. (* scan the disk surface for BAD clusters *)
  685. IF alive & doSurfaceScan THEN SurfaceScan; END;
  686. (* compare all FATs *)
  687. IF alive & doCompareFATs THEN CompareFATs; END;
  688. IF alive THEN
  689. (* setup data structures for ProcessHead *)
  690. (* load root cluster *)
  691. CASE fsType OF
  692. FAT12..FAT16: BEGIN
  693. (* load root directory into memory. Requires a maximum of 2MByte *)
  694. NEW(FAT1216rootDir,vol(FATVolumes.FAT1216Volume).numRootSectors*FATVolumes.BS);
  695. ASSERT(FAT1216rootDir#NIL);
  696. dev.Transfer(Disks.Read, vol.start + vol(FATVolumes.FAT1216Volume).firstRootSector,
  697. vol(FATVolumes.FAT1216Volume).numRootSectors, FAT1216rootDir.data^,0,res);
  698. IF res # Disks.Ok THEN
  699. ReportTransferError("Critical: Couldn't load FAT1216 root directoy",
  700. Disks.Read, vol.start + vol(FATVolumes.FAT1216Volume).firstRootSector, res);
  701. alive := FALSE;
  702. END;
  703. baseCluster := cluster; (* I don't want the cluster to be collected *)
  704. cluster := FAT1216rootDir;
  705. cluster.next := EOC;
  706. cluster.first := 1; cluster.parent := 1; cluster.cluster := 1; (* used to identify FAT1216rootDir *)
  707. END;
  708. | FAT32: BEGIN
  709. cluster.cluster := vol(FATVolumes.FAT32Volume).rootCluster;
  710. vol(FATVolumes.FAT32Volume).ReadCluster(cluster.cluster, cluster.data^, res);
  711. IF res # Disks.Ok THEN
  712. PartitionsLib.GetErrorMsg("Critical: Couldn't load FAT32 root directory, res:", res, string); ReportError(string);
  713. alive := FALSE;
  714. END;
  715. cluster.first := cluster.cluster;
  716. clusterBitmap.SetBit(cluster.cluster, collision); (* collision is not possible, ignore *)
  717. END;
  718. END;
  719. cluster.SetPos(0); processStack.PushCluster(cluster);
  720. path.Append(Files.PathDelimiter); (* root directory path string*)
  721. (* traverse the FAT directory structure *)
  722. IF alive THEN ProcessHead; END;
  723. END;
  724. (* check clusterbitmap for lost clusters *)
  725. IF alive & doLostClusters THEN
  726. NEW(lostList); NEW(lostErrorList);
  727. TraverseFAT;
  728. IF ~lostList.Empty() & alive THEN (* lost clusters were found... analyze them !! *)
  729. CheckLostClusters;
  730. string := "Found "; Strings.IntToStr(lostClusters, str); Strings.Append(string, str);
  731. Strings.Append(string, " lost cluster in "); Strings.IntToStr(lostClusterChains, str);
  732. Strings.Append(string, str); Strings.Append(string, " chains");
  733. IF doWrite & alive THEN
  734. DeleteLostClusters; (* WARNING: WRITE ACCESS *)
  735. Strings.Append(string, "(corrected)");
  736. ReportError(string);
  737. ELSE
  738. Strings.Append(string, "(not correctly since writes not allowed by user)");
  739. ReportError(string);
  740. END;
  741. END;
  742. END;
  743. IF alive THEN
  744. result.String("Scavenger on "); result.String(diskpartString); result.String(" finished ");
  745. IF PartitionsLib.StatusError IN state.status THEN
  746. result.String("with "); result.Int(state.errorCount, 0); result.String(" errors");
  747. ELSE
  748. result.String("without errors");
  749. END;
  750. BuildInfo;
  751. END;
  752. IF Trace THEN IF ~alive THEN KernelLog.String("Scanner aborted."); ELSE KernelLog.String("Scanner finished."); KernelLog.Ln; END; END;
  753. END DoOperation;
  754. PROCEDURE &Init*(disk : PartitionsLib.Disk; partition : LONGINT; out : Streams.Writer);
  755. BEGIN
  756. Init^(disk, partition, out);
  757. name := "FATScavenger"; desc := "Check FAT file system on partition"; locktype := PartitionsLib.WriterLock;
  758. NEW(longList); NEW(processStack); NEW(xlinkedList);
  759. END Init;
  760. PROCEDURE BuildInfo;
  761. BEGIN
  762. info.String("Volume Information: "); info.String(diskpartString); info.Ln;
  763. info.String("Clusters: "); info.Int(vol.maxClusters, 3); info.String(" ClusterSize: "); info.Int(vol.clusterSize, 3); info.String("B");
  764. info.Ln;
  765. info.String("Files: "); info.Int(filesScanned, 3); info.String(" Directories: "); info.Int(directoriesScanned, 3);
  766. info.Ln;
  767. info.String("Short Entries: "); info.Int(shortEntriesScanned, 3);
  768. info.String(" Long Entries: "); info.Int(longEntriesScanned, 3);
  769. info.String(" Empty Entries: "); info.Int(emptyEntriesScanned, 3);
  770. info.Ln;
  771. IF errorsFound > 0 THEN
  772. info.Int(lostClusters, 4); info.String(" lost clusters found in "); info.Int(lostClusterChains, 3); info.String(" chains."); info.Ln;
  773. END;
  774. END BuildInfo;
  775. PROCEDURE GetFSInfo(VAR freeCount, nextFree : LONGINT) : BOOLEAN;
  776. VAR
  777. bootsector : ARRAY FATVolumes.BS OF CHAR;
  778. res : WORD;
  779. BEGIN
  780. (* load the boot sector to get address of FSinfo block *)
  781. dev.Transfer(Disks.Read, vol.start, 1, bootsector, 0, res);
  782. IF res # OK THEN
  783. ReportTransferError("Could not load FSinfo block (", Disks.Read, vol.start + vol.startFAT, res);
  784. RETURN FALSE;
  785. END;
  786. (* Get address & FAT32 file system version check; version exspected to be 0:0; *)
  787. IF (bootsector[42] # 0X) OR (bootsector[43] # 0X) THEN
  788. ReportError("Couldn't not load FSInfo block (Wrong FAT32 FS verion)");
  789. RETURN FALSE;
  790. END;
  791. fsinfoAddress := FATVolumes.GetUnsignedInteger(bootsector, 48);
  792. (* load the file system info block *)
  793. dev.Transfer(Disks.Read, vol.start + fsinfoAddress, 1, fsinfo, 0, res);
  794. IF res # OK THEN
  795. ReportTransferError("Couldn't load FSinfo block (", Disks.Read, vol.start + fsinfoAddress, res);
  796. RETURN FALSE;
  797. END;
  798. (* get freeCount & nextFree information from FSinfo block *)
  799. IF (FATVolumes.GetLongint(fsinfo, 0) = 041615252H) & (* lead signature *)
  800. (FATVolumes.GetLongint(fsinfo, 508) = 0AA550000H) & (* structure signature *)
  801. (FATVolumes.GetLongint(fsinfo, 484) = 061417272H) (* trail signature *)
  802. THEN
  803. (* it's the FSinfo block *)
  804. fsInfoLoaded := TRUE;
  805. freeCount := FATVolumes.GetLongint(fsinfo, 488);
  806. nextFree := FATVolumes.GetLongint(fsinfo, 492);
  807. ELSE
  808. ReportError("Signature of FSinfo block if wrong");
  809. RETURN FALSE;
  810. END;
  811. RETURN TRUE;
  812. END GetFSInfo;
  813. PROCEDURE SetFSInfo(freeCount, nextFree : LONGINT);
  814. VAR res : WORD;
  815. BEGIN
  816. IF fsInfoLoaded THEN
  817. FATVolumes.PutLongint(fsinfo, 488, freeCount); (* - deleted + created *)
  818. FATVolumes.PutLongint(fsinfo, 492, nextFree);
  819. IF doWrite THEN
  820. dev.Transfer(Disks.Write, vol.start + fsinfoAddress, 1, fsinfo, 0, res);
  821. IF res # OK THEN
  822. ReportTransferError("Could not store FSinfo block (", Disks.Write, vol.start+ fsinfoAddress, res);
  823. END;
  824. END;
  825. ELSE ReportError("FSinfo block is not loaded");
  826. END;
  827. END SetFSInfo;
  828. (* Compares the n FATs on the volume *)
  829. PROCEDURE CompareFATs;
  830. CONST UpdateRate = 3;
  831. VAR
  832. buffer : POINTER TO ARRAY OF CHAR;
  833. buffersize, reads : LONGINT;
  834. operation : String;
  835. i, j, k : LONGINT; res : WORD;
  836. BEGIN
  837. IF Trace THEN KernelLog.String("Comparing FATs... "); END;
  838. INC(curOp); operation := GetString(curOp, maxOp, "", "Comparing FAT structures");
  839. SetStatus(state.status, operation, 0, 0, vol.fatSize, TRUE);
  840. (* allocate read buffer *)
  841. buffersize := BufferSize * FATVolumes.BS; (* buffersize: in bytes; BufferSize: number of sectors *)
  842. NEW(buffer, buffersize * vol.numFATs);
  843. ASSERT(buffer # NIL);
  844. (* reads*BufferSize*FATVolumes.BS + (vol.fatSize MOD BufferSize) bytes will be read from each FAT and then compared to FAT1 *)
  845. reads := vol.fatSize DIV BufferSize;
  846. k := 0;
  847. WHILE k < reads DO
  848. INC(k);
  849. (* special case: IF (fatSize DIV BufferSize) buffers are read, load & compare (fatSize MOD BufferSize) bytes *)
  850. IF (k = reads) & ((vol.fatSize MOD BufferSize) # 0) THEN
  851. buffersize := (vol.fatSize MOD BufferSize) * FATVolumes.BS;
  852. END;
  853. (* read BufferSize sectors from each FAT *)
  854. FOR i := 0 TO vol.numFATs-1 DO
  855. dev.Transfer(Disks.Read, vol.start + vol.startFAT+ i*vol.fatSize, BufferSize, buffer^, i*buffersize, res);
  856. IF res # OK THEN
  857. ReportTransferError("CompareFATs: IO error (", Disks.Read, vol.start + vol.startFAT+ i*vol.fatSize, res);
  858. (* continue *)
  859. END;
  860. END;
  861. (* compare entries of numFATs FATs *)
  862. FOR i := 0 TO buffersize-1 DO
  863. FOR j := 1 TO vol.numFATs-1 DO
  864. IF buffer[i + j*buffersize] # buffer[i] THEN
  865. ReportError("CompareFATs: SERIOUS ERROR: File allocation tables are not equal");
  866. alive := FALSE;
  867. END;
  868. END;
  869. END;
  870. IF (k MOD UpdateRate = 0) OR (k >= reads) THEN
  871. SetCurrentProgress(k * BufferSize);
  872. END;
  873. IF ~alive THEN IF Trace THEN KernelLog.String("aborted."); KernelLog.Ln; END; RETURN; END;
  874. END;
  875. IF alive THEN info.String("Comparing FATs succeeded."); info.Ln; END;
  876. IF Trace & alive THEN KernelLog.String("done."); KernelLog.Ln;END;
  877. END CompareFATs;
  878. PROCEDURE TraverseFAT;
  879. CONST
  880. UpdateRate = 10000;
  881. VAR
  882. collision, lastFree : BOOLEAN;
  883. lost : LostCluster;
  884. cluster, link, firstFree : LONGINT;
  885. operation, string : String;
  886. BEGIN
  887. IF Trace THEN KernelLog.String("Building up cluster bitmap... "); END;
  888. ASSERT((clusterBitmap # NIL) & (lostList # NIL));
  889. INC(curOp); operation := GetString(curOp, maxOp, "", "Analyzing FAT: ");
  890. string := operation; Strings.Append(string, GetString(0, vol.maxClusters+1, "Cluster", ""));
  891. SetStatus(state.status, string, 0, 0, vol.maxClusters, TRUE);
  892. lastFree := FALSE; firstFree := -1;
  893. FOR cluster:=2 TO vol.maxClusters+1 DO
  894. link := vol.ReadFATEntry(cluster);
  895. CASE link OF
  896. |FREE: BEGIN
  897. IF firstFree=-1 THEN firstFree:=cluster; END;
  898. INC(freeClusters);
  899. IF ~lastFree THEN INC(nbrFreeFragments); END;
  900. lastFree:=TRUE;
  901. END;
  902. |BAD: BEGIN
  903. lastFree:=FALSE;
  904. INC(badClusters);
  905. clusterBitmap.SetBit(cluster, collision);
  906. IF collision THEN (* ERROR: file contains bad cluster *)
  907. INC(errorsFound);
  908. collision := FALSE;
  909. info.String("Cannot fix: File contains bad cluster."); info.Ln;
  910. KernelLog.String("error: bad cluster in file"); KernelLog.Ln; (* TODO: fix *)
  911. END;
  912. END;
  913. ELSE
  914. lastFree := FALSE;
  915. IF ~clusterBitmap.IsSet(cluster) THEN (* ERROR: lost cluster found *)
  916. INC(errorsFound);
  917. NEW(lost); lost.cluster:=cluster; lost.link:=link;
  918. IF (link # EOC) & clusterBitmap.IsSet(link) THEN (* ERROR: lost cluster crosslinked to a valid cluster*)
  919. lostErrorList.Append(lost);
  920. ELSE
  921. lostList.Append(lost);
  922. END;
  923. INC(lostClusters);
  924. END;
  925. END;
  926. IF (cluster MOD UpdateRate = 0) OR (cluster = vol.maxClusters+1) THEN
  927. string := operation; Strings.Append(string, GetString(cluster, vol.maxClusters+1, "Cluster", ""));
  928. SetStatus(state.status, string, 0, cluster - 1, state.max, TRUE);
  929. END;
  930. IF ~alive THEN IF Trace THEN KernelLog.String("aborted."); END; RETURN; END;
  931. END;
  932. (* check the free cluster count of the fsinfo field and the pointer to the first free cluster (FAT32 only) *)
  933. (* TODO: does not yet work correctly
  934. IF (vol IS FATVolumes.FAT32Volume) THEN
  935. ASSERT(fsinfo#NIL);
  936. IF fsinfo.freeCount#info.freeClusters THEN (* ERROR: wrong free cluster count in fsinfo block *)
  937. KernelLog.String("ERROR: FSINFO wrong freecount"); KernelLog.Ln;
  938. fsinfo.freeCount:=info.freeClusters; fsinfo.modified:=TRUE;
  939. END;
  940. IF fsinfo.nextFree#firstFree THEN (* ERROR: pointer to first free cluster of volume is wrong *)
  941. KernelLog.String("ERROR: FSINFO wrong nextfree"); fsinfo.modified:=TRUE; KernelLog.Ln;
  942. fsinfo.nextFree:=firstFree;
  943. END;
  944. IF doWrite THEN fsinfo.Store; END; (* WARNING!! *****************);
  945. END;
  946. *)
  947. IF Trace THEN KernelLog.String("done."); KernelLog.Ln; END;
  948. END TraverseFAT;
  949. PROCEDURE SurfaceScan;
  950. CONST
  951. UpdateRate = 99;
  952. VAR
  953. data : POINTER TO ARRAY OF CHAR;
  954. newBadClusters, cluster, link : LONGINT;
  955. operation, string, temp : String;
  956. address : Files.Address;
  957. res : WORD;
  958. BEGIN
  959. NEW(data,vol.clusterSize);
  960. IF Trace THEN KernelLog.String("Surface scan started..."); END;
  961. INC(curOp); operation := GetString(curOp, maxOp, "", "Surface scan: ");
  962. string := operation; Strings.Append(string, GetString(0, vol.maxClusters, "Cluster", ""));
  963. SetStatus(state.status, string, 0, 0, vol.maxClusters, TRUE);
  964. newBadClusters := 0; (* number of known bad clusters already in info.badClusters *)
  965. FOR cluster := 2 TO vol.maxClusters+1 DO
  966. link := vol.ReadFATEntry(cluster);
  967. address := vol.startData + (cluster * vol.sectorsPC);
  968. dev.Transfer(Disks.Read, address, vol.sectorsPC, data^, 0, res);
  969. IF res#OK THEN
  970. string := "Cluster "; Strings.IntToStr(cluster, temp); Strings.Append(string, temp); Strings.Append(string, " is bad");
  971. PartitionsLib.GetErrorMsg(", res: ", res, temp); Strings.Append(string, temp);
  972. ReportError(string);
  973. IF link#BAD THEN (* mark cluster as bad cluster *)
  974. IF link=FREE THEN INC(deleted); END;
  975. INC(newBadClusters);
  976. IF doWrite THEN vol.WriteFATEntry(cluster, BAD, res);
  977. IF res # OK THEN
  978. string := "Failed to mark cluster "; Strings.Append(string, temp); Strings.Append(string, " as bad");
  979. PartitionsLib.GetErrorMsg(", res: ", res, temp); Strings.Append(string, temp);
  980. ReportError(string);
  981. ELSE
  982. string := "Cluster"; Strings.Append(string, temp); Strings.Append(string, " marked as bad");
  983. info.String(string); info.Ln;
  984. END;
  985. END;
  986. END;
  987. (* continue *)
  988. END;
  989. IF (cluster MOD UpdateRate = 0) OR (cluster = vol.maxClusters+1) THEN
  990. string := operation; Strings.Append(string, GetString(cluster, vol.maxClusters+1, "Cluster", ""));
  991. SetStatus(state.status, string, 0, cluster, vol.maxClusters, TRUE);
  992. END;
  993. IF ~alive THEN IF Trace THEN KernelLog.String("aborted."); KernelLog.Ln; END; RETURN; END;
  994. END;
  995. info.String("Surface scan: "); info.Int(newBadClusters, 0); info.String(" bad sectors found."); info.Ln;
  996. IF Trace THEN KernelLog.Int(newBadClusters,6); KernelLog.String(" new bad sectors found...");KernelLog.String("done."); KernelLog.Ln; END;
  997. END SurfaceScan;
  998. PROCEDURE DeleteLostClusters;
  999. VAR
  1000. temp, temp2 : LostCluster;
  1001. string, error : String;
  1002. counter : LONGINT; res : WORD;
  1003. BEGIN
  1004. IF Trace THEN KernelLog.String("Deleting lost clusters... "); END;
  1005. ASSERT((lostErrorList#NIL) & (fileList#NIL));
  1006. counter := 0;
  1007. (* delete lost clusters which are crosslinked to valid clusters *)
  1008. lostErrorList.SetCurrent;
  1009. WHILE lostErrorList.HasNext() DO
  1010. temp := lostErrorList.GetNext();
  1011. IF doWrite THEN vol.WriteFATEntry(temp.cluster, FREE, res); INC(counter); END;
  1012. IF res # OK THEN
  1013. string := ""; PartitionsLib.GetErrorMsg("Critical: Could not delete lost clusters (", res, error); Strings.Append(string, error); Strings.Append(string, ")");
  1014. ReportError(string);
  1015. RETURN;
  1016. END;
  1017. END;
  1018. (* delete lost cluster chains *)
  1019. fileList.SetCurrent;
  1020. WHILE fileList.HasNext() DO
  1021. temp := fileList.GetNext();
  1022. IF doWrite THEN vol.WriteFATEntry(temp.cluster, FREE, res); INC(counter); END;
  1023. IF res # OK THEN
  1024. string := ""; PartitionsLib.GetErrorMsg("Critical2: Could not delete lost clusters (", res, error); Strings.Append(string, error); Strings.Append(string, ")");
  1025. ReportError(string);
  1026. RETURN;
  1027. END;
  1028. temp2 := temp.chain;
  1029. WHILE temp2 # NIL DO
  1030. IF doWrite THEN vol.WriteFATEntry(temp2.cluster, FREE, res); INC(counter); END;
  1031. IF res # OK THEN
  1032. string := ""; PartitionsLib.GetErrorMsg("Critical3: Could not delete lost clusters (", res, error); Strings.Append(string, error); Strings.Append(string, ")");
  1033. ReportError(string);
  1034. RETURN;
  1035. END;
  1036. temp2 := temp2.next;
  1037. END;
  1038. END;
  1039. (* update values in fsinfo block (FAT32only) *)
  1040. IF vol IS FATVolumes.FAT32Volume THEN INC(deleted); END;
  1041. info.Int(counter, 0); info.String(" lost clusters deleted"); info.Ln;
  1042. IF Trace THEN KernelLog.String("Deleted "); KernelLog.Int(counter, 0); KernelLog.String("clusters, done."); KernelLog.Ln; END;
  1043. END DeleteLostClusters;
  1044. (* tries to find cluster chains in the lost cluster list *)
  1045. PROCEDURE CheckLostClusters;
  1046. VAR
  1047. tempList : ClusterList;
  1048. tempCluster, tempLink : LONGINT; (* address of the first cluster in list; link of the last cluster in list; *)
  1049. lost, temp : LostCluster;
  1050. found : BOOLEAN;
  1051. xlink, collision, terminated: BOOLEAN;
  1052. BEGIN
  1053. IF Trace THEN KernelLog.String("Processing lost cluster list... "); END;
  1054. ASSERT(lostList#NIL);
  1055. NEW(fileList); (* list of cluster chains *)
  1056. NEW(tempList);
  1057. (* find cluster chains in lost cluster list *)
  1058. WHILE ~lostList.Empty() DO
  1059. found:=TRUE;
  1060. WHILE found=TRUE DO
  1061. lostList.SetCurrent;
  1062. found:=FALSE;
  1063. WHILE(lostList.HasNext()) DO
  1064. lost:=lostList.GetNext();
  1065. IF tempList.Empty() THEN
  1066. lostList.RemoveCurrent; lost.next:=NIL;
  1067. tempList.Insert(lost);
  1068. tempCluster:=lost.cluster; tempLink:=lost.link;
  1069. found:=TRUE;
  1070. END;
  1071. IF lost.cluster=tempLink THEN (* last cluster of tempList linked to this one -> append cluster to cluster chain *)
  1072. lostList.RemoveCurrent; lost.next:=NIL;
  1073. tempList.Append(lost);
  1074. tempLink:=lost.link;
  1075. found:=TRUE;
  1076. ELSIF lost.link=tempCluster THEN (* linked to first cluster of tempList -> insert cluster into cluster chain *)
  1077. lostList.RemoveCurrent; lost.next:=NIL;
  1078. tempList.Insert(lost);
  1079. tempCluster:=lost.cluster;
  1080. found:=TRUE;
  1081. END;
  1082. END;
  1083. END;
  1084. IF found=FALSE THEN (* no more clusters of lostList belong to currently processed cluster chain *)
  1085. (* store found cluster chain in file list *)
  1086. tempList.SetCurrent;
  1087. ASSERT(~tempList.Empty());
  1088. lost:=tempList.GetNext(); (* first element of tempList *)
  1089. lost.chain:=lost.next; lost.next:=NIL;
  1090. fileList.Append(lost);
  1091. fileList.SetCurrent;
  1092. lost:=fileList.GetNext();
  1093. tempList.Init; (* clear list *)
  1094. END;
  1095. END;
  1096. (* check lost cluster chains againt crosslinks to other lost cluster chains. Crosslinks to valid cluster were already
  1097. checked in TraverseFAT.
  1098. check also wether the lost cluster chains are terminated with EOC *)
  1099. fileList.SetCurrent;
  1100. WHILE fileList.HasNext() DO (* for each lost cluster chain... *)
  1101. lost:=fileList.GetNext();
  1102. clusterBitmap.SetBit(lost.cluster,collision); xlink:=collision; terminated:=(lost.chain=NIL) & (lost.link=EOC);
  1103. temp:=lost.chain;
  1104. WHILE(temp#NIL) DO (* ... and each lost cluster of that chains *)
  1105. clusterBitmap.SetBit(temp.cluster, collision);
  1106. IF collision THEN xlink:=TRUE; END;
  1107. IF (temp.next=NIL) & (temp.link=EOC) THEN terminated:=TRUE; END;
  1108. temp:=temp.next;
  1109. END;
  1110. lost.terminated:=terminated;
  1111. lost.crosslink:=xlink;
  1112. END;
  1113. lostClusterChains := fileList.size;
  1114. IF Trace THEN KernelLog.String(" done."); KernelLog.Ln; END;
  1115. END CheckLostClusters;
  1116. (* check the long entries which are associated with this shortentry; FIX: delete incorrect entries *)
  1117. PROCEDURE CheckLongEntries(shortEntry : ShortEntry);
  1118. VAR
  1119. chksum : LONGINT;
  1120. temp : LongEntry;
  1121. order : INTEGER;
  1122. longName : ARRAY 256 OF CHAR; (* maximum length of a long file name: 255 characters + NUL *)
  1123. unicode : ARRAY 256 OF LONGINT;
  1124. lastFound : BOOLEAN;
  1125. padding : BOOLEAN; firstPadding : INTEGER;
  1126. i : INTEGER;
  1127. BEGIN
  1128. IF Details THEN KernelLog.String("CheckLongEntries of "); KernelLog.String(shortEntry.shortName); KernelLog.String("..."); END;
  1129. chksum := shortEntry.GetChecksum(); order := 0; lastFound := FALSE;
  1130. longList.SetCurrent();
  1131. WHILE (longList.HasNext()) & (order<20) & (~lastFound) DO (* no more than 20 long entires *)
  1132. INC(order);
  1133. temp:=longList.GetNext();
  1134. IF temp.order#order THEN (* ERROR: wrong sequence number *)
  1135. KernelLog.String("Cannot fix: Long entry order mismatch"); KernelLog.Ln; (* TODO: fix *)
  1136. info.String("Cannot fix: Long entry order mismatch."); info.Ln;
  1137. END;
  1138. IF temp.chksum#chksum THEN (* ERROR: checksum doesn't match *)
  1139. KernelLog.String("Cannot fix: Long entry chksum mismatch"); KernelLog.Ln; (* TODO: fix *)
  1140. info.String("Cannot fix: Long enty chksum mismatch."); info.Ln;
  1141. END;
  1142. FOR i:=0 TO 12 DO unicode[(order-1)*13+i]:=temp.name[i]; END;
  1143. IF temp.last THEN lastFound:=TRUE; unicode[order*13]:=0; END;
  1144. END;
  1145. IF (order#0) & (~lastFound) THEN (* ERROR: wrong long entries *)
  1146. KernelLog.String("Cannot fix: Last entry of long entry sequence not found."); KernelLog.Ln; (* TODO: fix *)
  1147. info.String("Cannot fix: Last entry of long entry sequence not found."); info.Ln;
  1148. ELSE (* okay: got long name. Is it valid ? *)
  1149. UTF8Strings.UnicodetoUTF8(unicode,longName);
  1150. (* check padding *)
  1151. padding:=FALSE; firstPadding:=0;
  1152. FOR i:=0 TO order*13-1 DO
  1153. IF (padding=TRUE) & (unicode[i]#0FFFFH) THEN (* ERROR: padding with 0FFFFH not correct *)
  1154. KernelLog.String("Cannot fix: Incorrect padding in long entry."); KernelLog.Ln; (* TODO: fix *)
  1155. info.String("Cannot fix: Incorrect padding in long entry."); info.Ln;
  1156. END;
  1157. IF (unicode[i]=0) & (padding=FALSE) THEN padding:=TRUE; firstPadding:=i;END;
  1158. END;
  1159. (* check characters*)
  1160. IF firstPadding=0 THEN firstPadding:=order*13-1; END;
  1161. FOR i:=0 TO firstPadding-1 DO
  1162. IF ~ValidLongChar(longName[i]) THEN (* invalid char in long name *)
  1163. KernelLog.String("Cannot fix: Invalid char in long name"); KernelLog.Ln; (* TODO: fix *)
  1164. info.String("Cannot fix: Invalid char in long name."); info.Ln;
  1165. END;
  1166. END;
  1167. END;
  1168. IF longList.HasNext() THEN (* error: the remaining long entries have no correspondig short entry *)
  1169. KernelLog.String("Cannot fix: Remaing long entries are orphans"); KernelLog.Ln; (* TODO: fix *)
  1170. info.String("Cannot fix: Remaining long entries are orphans."); info.Ln;
  1171. END;
  1172. longList.Clear(); (* remove long entries from list *)
  1173. IF Details THEN KernelLog.String(" done."); KernelLog.Ln; END;
  1174. END CheckLongEntries;
  1175. PROCEDURE CheckDotDot(shortEntry : ShortEntry);
  1176. VAR
  1177. rootDir : LONGINT;
  1178. BEGIN
  1179. (* possible input shortEntrys are all which satisfy shortEntry.shortName[0]="." *)
  1180. ASSERT(shortEntry.shortName[0]=".");
  1181. IF shortEntry.shortName=". " THEN
  1182. IF shortEntry.firstCluster#cluster.first THEN (* ERROR: dot entry does not point to this directory *)
  1183. KernelLog.String("Cannot fix: dot entry points wrong."); KernelLog.Ln; (* TODO: fix *)
  1184. info.String("Cannot fix: Dot entry points wrong."); info.Ln;
  1185. END;
  1186. ELSIF shortEntry.shortName=".. " THEN
  1187. IF shortEntry.firstCluster=0 THEN (* parent is rootDirectory *)
  1188. CASE fsType OF
  1189. FAT12..FAT16 : rootDir:=1;
  1190. |FAT32 : rootDir:=vol(FATVolumes.FAT32Volume).rootCluster;
  1191. END;
  1192. IF cluster.parent#rootDir THEN (* ERROR: dot dot points to rootDir but shouldn't *)
  1193. KernelLog.String("Warning: dot dot points to root but should not"); (* TODO: fix *)
  1194. info.String("Cannot fix: Dot dot point to root but should not."); info.Ln;
  1195. END;
  1196. ELSIF shortEntry.firstCluster#cluster.parent THEN (* ERROR: dot dot entry doesn't point to parent folder *)
  1197. KernelLog.String("ERROR: dot dot entry points wrong:"); KernelLog.Int(shortEntry.firstCluster,10);
  1198. KernelLog.String(" parent is :"); KernelLog.Int(cluster.parent,10); KernelLog.Ln;
  1199. info.String("Cannot fix: Dot dot entry wrong."); info.Ln;
  1200. END;
  1201. ELSE
  1202. (* ERROR: this is no valid dot entry; Invalid shortEntry or invalid short name *)
  1203. KernelLog.String("Cannot fix: Invalid shortEntry (starts with .)"); KernelLog.Ln; (* TODO: fix *)
  1204. info.String("Cannot fix: Invalid short entry (name starts with . )"); info.Ln;
  1205. END;
  1206. END CheckDotDot;
  1207. PROCEDURE ProcessShortEntry(shortEntry: ShortEntry);
  1208. VAR
  1209. counter : SHORTINT;
  1210. clusterCount : LONGINT;
  1211. link, oldlink : LONGINT;
  1212. fragments : LONGINT;
  1213. collision : BOOLEAN;
  1214. xlinked : LostCluster;
  1215. BEGIN
  1216. (* the input shortEntry is either a file, a VolumeID or a invalid shortEntry *)
  1217. IF Details THEN shortEntry.Print; END;
  1218. (* check whether the short name is valid *)
  1219. FOR counter := 0 TO 10 DO
  1220. IF ~ValidShortChar(shortEntry.shortName[counter]) THEN (* ERROR: invalid short name *)
  1221. KernelLog.String("Invalid short name: "); KernelLog.String(shortEntry.shortName); KernelLog.Ln;
  1222. info.String("Invalid short name: "); info.String(shortEntry.shortName); info.Ln;
  1223. END;
  1224. END;
  1225. IF FATFiles.faVolumeID IN shortEntry.attr THEN (* it's a VolumeID *)
  1226. DEC(filesScanned); (* already counted in ProcessHead *)
  1227. ELSE (* it's a file *)
  1228. (* check the corresponding long entries *)
  1229. CheckLongEntries(shortEntry);
  1230. (* check cluster chain against fileSize *)
  1231. fragments:=1; collision:=FALSE;
  1232. clusterCount:=1; link:=shortEntry.firstCluster;
  1233. WHILE (link>1) & ((clusterCount-1)*vol.clusterSize<=shortEntry.fileSize) DO
  1234. oldlink:=link;
  1235. link:=vol.ReadFATEntry(link);
  1236. INC(clusterCount);
  1237. IF link#oldlink+1 THEN INC(fragments); END;
  1238. clusterBitmap.SetBit(oldlink, collision); ASSERT(collision=FALSE);
  1239. IF collision THEN (* ERROR : crosslinked files *)
  1240. collision:=FALSE; KernelLog.String("Warning: PSE:crosslink detected!"); KernelLog.Ln;
  1241. NEW(xlinked); xlinked.cluster:=oldlink; xlinked.link:=link;
  1242. xlinkedList.Append(xlinked);
  1243. END;
  1244. END;
  1245. (*inffo.FileFrag(fragments, clusterCount); *)
  1246. IF (shortEntry.fileSize>0) & (link#EOC) THEN (* ERROR: file size wrong *)
  1247. KernelLog.String("Cannot fix: Wrong file size"); (* TODO: fix *)
  1248. info.String("Cannot fix: Wrong file size: "); info.String(shortEntry.shortName); info.Ln;
  1249. END;
  1250. END;
  1251. END ProcessShortEntry;
  1252. PROCEDURE ProcessHead*;
  1253. VAR
  1254. temp: Node;
  1255. operation, string, tempStr : String;
  1256. entry : Entry; shortEntry : ShortEntry;
  1257. dirFragments, dirClusters : LONGINT;
  1258. collision : BOOLEAN;
  1259. res : WORD;
  1260. BEGIN
  1261. IF Trace THEN KernelLog.String("Scanning FAT directory structure... "); END;
  1262. INC(curOp); operation := GetString(curOp, maxOp, "", "Scanning FAT directory: ");
  1263. string := operation; path.Get(tempStr); Strings.Append(string, tempStr);
  1264. SetStatus(state.status, string, 0, 0, 0, FALSE);
  1265. dirFragments:=1; dirClusters:=1;
  1266. WHILE(~processStack.Empty()) DO
  1267. IF ~alive THEN IF Trace THEN KernelLog.String("aborted."); KernelLog.Ln; END; RETURN; END;
  1268. (* get next Node to process *)
  1269. temp:=processStack.GetTop();
  1270. (* special case: get FAT1216rootDir object if it's the next cluster to be processed *)
  1271. IF (fsType<=FAT16) & (temp.cluster=1) & (temp.parent=1) & (temp.first=1) THEN
  1272. cluster.cluster:=1; (* invalid address invalidates the old cluster object (forces reload) *)
  1273. cluster:=FAT1216rootDir;
  1274. END;
  1275. (* reload cluster if necessary *)
  1276. IF cluster.cluster # temp.cluster THEN
  1277. IF Details THEN
  1278. KernelLog.String("(re)load cluster: "); KernelLog.Int(temp.cluster,8); KernelLog.String(" offset: "); KernelLog.Int(temp.offset,4); KernelLog.Ln;
  1279. END;
  1280. ASSERT(temp.cluster>1);
  1281. vol.ReadCluster(temp.cluster, cluster.data^, res);
  1282. IF res#OK THEN
  1283. string := "ProcessHead: Could not read cluster "; Strings.IntToStr(temp.cluster, tempStr); Strings.Append(string, tempStr);
  1284. PartitionsLib.GetErrorMsg(" (res: ", res, tempStr); Strings.Append(string, tempStr);
  1285. ReportError(string);
  1286. KernelLog.String(" load failed!!"); (* to do: fix *)
  1287. END;
  1288. cluster.cluster:=temp.cluster;
  1289. cluster.first:=temp.first;
  1290. cluster.parent:=temp.parent;
  1291. cluster.SetPos(temp.offset);
  1292. END;
  1293. IF (cluster.HasNext()) THEN
  1294. entry:=cluster.GetNext();
  1295. IF entry#NIL THEN (* entry not free *)
  1296. (* entry.Print; *)
  1297. IF (entry IS LongEntry) THEN (* long directory entry, push it on stack *)
  1298. INC(longEntriesScanned);
  1299. longList.Insert(entry(LongEntry));
  1300. ELSE (* short entry *)
  1301. INC(shortEntriesScanned);
  1302. shortEntry:=entry(ShortEntry);
  1303. IF (shortEntry.directory=FALSE) THEN (* file, volumeID or invalid entry *)
  1304. ProcessShortEntry(shortEntry);
  1305. INC(filesScanned);
  1306. ELSE (* if it's a folder, it will be "opened" and processed as next *)
  1307. IF (shortEntry.shortName[0]=".") THEN (* dot or dotdot or invalid entry *)
  1308. CheckDotDot(shortEntry);
  1309. ELSE
  1310. path.Append(shortEntry.shortName);
  1311. string := operation; path.Get(tempStr); Strings.Append(string, tempStr);
  1312. SetStatus(state.status, string, 0, 0, 0, FALSE);
  1313. CheckLongEntries(shortEntry);
  1314. IF Details THEN
  1315. KernelLog.String("open directory "); KernelLog.String(entry(ShortEntry).shortName); KernelLog.Ln; path.Print;
  1316. END;
  1317. INC(dirClusters); INC(dirFragments); INC(directoriesScanned);
  1318. processStack.ReplaceTop(cluster); (* save state of currently processed cluster object, will continue later at stored offset *)
  1319. NEW(temp); temp.cluster:=entry(ShortEntry).firstCluster; temp.offset:=0;
  1320. temp.parent:=cluster.first; temp.first:=temp.cluster;
  1321. IF cluster=FAT1216rootDir THEN (* need normal cluster object for processing entry *)
  1322. cluster:=baseCluster;
  1323. cluster.cluster:=1; (* invalid address forces reloading ! *)
  1324. END;
  1325. clusterBitmap.SetBit(temp.cluster, collision);
  1326. processStack.Push(temp);
  1327. END;
  1328. END;
  1329. END;
  1330. ELSE (* entry is free *)
  1331. INC(emptyEntriesScanned);
  1332. (* processStack.ReplaceTop(cluster); (* updates the Node.offset fields which was changed by temp:=cluster.GetNext() *) *)
  1333. END;
  1334. ELSE (* all entries in this cluster were scanned. If this was not the last cluster in the cluster chain, load the next! *)
  1335. ASSERT(cluster.currentEntry=cluster.maxEntries);
  1336. processStack.RemoveTop; (* cluster completely scanned -> remove from process list*)
  1337. (* get address of next cluster; FAT1216rootDir.next is always set to EOC *)
  1338. IF cluster # FAT1216rootDir THEN cluster.next := vol.ReadFATEntry(cluster.cluster); END;
  1339. IF (cluster.next # EOC) & (cluster.next # FREE) & (cluster.next # BAD) THEN
  1340. (* next directory entry contained in next cluster *)
  1341. INC(dirClusters);
  1342. IF cluster.next # cluster.cluster+1 THEN INC(dirFragments);END;
  1343. NEW(temp);
  1344. temp.cluster:=cluster.next; temp.offset:=0;
  1345. temp.first:=cluster.first; temp.parent:=cluster.parent;
  1346. processStack.Push(temp); (* process this cluster as next *)
  1347. clusterBitmap.SetBit(temp.cluster,collision);
  1348. ELSE
  1349. path.RemoveLast;
  1350. END;
  1351. END;
  1352. END;
  1353. IF Trace THEN KernelLog.String(" done."); KernelLog.Ln; END;
  1354. END ProcessHead;
  1355. PROCEDURE WriteEntry(entry: Entry);
  1356. VAR
  1357. temp : Cluster;
  1358. temp2 : Entry;
  1359. test : BOOLEAN;
  1360. address : Files.Address;
  1361. offset : LONGINT; res : WORD;
  1362. BEGIN
  1363. ASSERT((entry # NIL) & (vol # NIL) & (dev # NIL));
  1364. test:=TRUE;
  1365. (* Reload cluster if necessary*)
  1366. IF cluster.cluster # entry.cluster THEN
  1367. IF (vol IS FATVolumes.FAT1216Volume) & (entry.cluster=1) THEN (* special case: FAT1216 root directory *)
  1368. ASSERT(FAT1216rootDir#NIL); cluster:=FAT1216rootDir;
  1369. ELSE
  1370. (* normal cluster, load with vol.ReadCluster() *)
  1371. vol.ReadCluster(entry.cluster, temp.data^, res); (* ignore res, handled in FATVolumes *)
  1372. END;
  1373. END;
  1374. (* optional: check correctness of cluster *) (* do it *)
  1375. temp.SetPos(entry.offset);
  1376. ASSERT(cluster.HasNext());
  1377. temp2:=cluster.GetNext();
  1378. ASSERT(Equals(temp2.rawEntry, entry.rawEntry));
  1379. (* cluster.ReplaceEntry(entry); *)
  1380. (* determine address of the sector which contains the entry *)
  1381. IF (fsType <= FAT16) & (entry.cluster = 1) THEN (* special case: FAT1216 root directory *)
  1382. address := vol.startData + vol(FATVolumes.FAT1216Volume).firstRootSector + (entry.offset DIV SectorSize);
  1383. offset := entry.offset MOD SectorSize;
  1384. ELSE
  1385. address := vol.startData+ (entry.cluster*vol.sectorsPC) + (entry.offset DIV SectorSize); (* sectorsize consistent with FATVoluemes!!!!!*)
  1386. offset := entry.offset MOD SectorSize;
  1387. END;
  1388. ASSERT( (address>vol.endFAT) & (address<(vol.maxClusters+1)*vol.sectorsPC));
  1389. (* write cluster back to disk *)
  1390. IF doWrite THEN vol.WriteCluster(entry.cluster, cluster.data^, res); END;
  1391. END WriteEntry;
  1392. (* helper procedures *)
  1393. (* string := [unit " "] cur "of" max [": " status] *)
  1394. PROCEDURE GetString(cur, max : LONGINT; CONST unit, status : ARRAY OF CHAR) : String;
  1395. VAR string : String; temp : ARRAY 16 OF CHAR;
  1396. BEGIN
  1397. string := "";
  1398. IF unit#"" THEN Strings.Append(string, unit); Strings.Append(string, " "); END;
  1399. Strings.IntToStr(cur, temp); Strings.Append(string, temp); Strings.Append(string, " of ");
  1400. Strings.IntToStr(max, temp); Strings.Append(string, temp);
  1401. IF status#"" THEN Strings.Append(string, ": "); Strings.Append(string, status); END;
  1402. RETURN string;
  1403. END GetString;
  1404. PROCEDURE ReportTransferError(name : ARRAY OF CHAR; op, adr: LONGINT; res: WORD);
  1405. VAR temp: ARRAY 256 OF CHAR;
  1406. BEGIN
  1407. Strings.Append(name, " (");
  1408. PartitionsLib.GetTransferError(dev, op, adr, res, temp); Strings.Append(name, temp);
  1409. Strings.Append(name, ")");
  1410. ioError := TRUE; ReportError(name);
  1411. END ReportTransferError;
  1412. PROCEDURE ValidShortChar(ch : CHAR): BOOLEAN;
  1413. BEGIN
  1414. RETURN ((ch>=020X) & (ch#022X) & (ch#02AX) & (ch#02BX) & (ch#02CX) & (ch#02EX) & (ch#02FX) & (ch#03AX) &
  1415. (ch#03BX) & (ch#03CX) & (ch#03DX) & (ch#03EX) & (ch#03FX) & (ch#05BX) & (ch#05CX) & (ch#05DX) & (ch#07CX));
  1416. END ValidShortChar;
  1417. PROCEDURE ValidLongChar(ch: CHAR): BOOLEAN;
  1418. BEGIN
  1419. RETURN (ch >= 20X) & (ch # "\") & (ch # "/") & (ch # ":") & (ch # "*") & (ch # "?") & (ch # '"') & (ch # "<") & (ch # ">") & (ch # "|");
  1420. END ValidLongChar;
  1421. (* compares the two arrays op1 and op2; returns TRUE if op1=op2 *)
  1422. PROCEDURE Equals(CONST op1, op2 : ARRAY OF CHAR): BOOLEAN;
  1423. VAR i : LONGINT;
  1424. BEGIN
  1425. ASSERT( (LEN(op1)#0) & (LEN(op2)#0) );
  1426. IF LEN(op1)#LEN(op2) THEN
  1427. RETURN FALSE;
  1428. ELSE
  1429. i := 0;
  1430. WHILE i < LEN(op1) DO
  1431. IF op1[i]#op2[i] THEN RETURN FALSE; END;
  1432. END;
  1433. RETURN TRUE;
  1434. END;
  1435. END Equals;
  1436. END FATScavenger;
  1437. TYPE
  1438. (* Format a partition with a FAT file system *)
  1439. FormatPartition* = OBJECT(PartitionsLib.Operation);
  1440. VAR
  1441. (* parameters: *)
  1442. quickFormat : BOOLEAN; volumeName : Strings.String;
  1443. fs : LONGINT;
  1444. (* internal values; Initialized to default values in Init *)
  1445. oemName : ARRAY 9 OF CHAR;
  1446. rsvdSecCnt1216, rsvdSecCnt32 : LONGINT; (* Reserved sector count *)
  1447. numFATs : LONGINT;
  1448. rootEntCnt : LONGINT; (* FAT1216: count of 32-byte directory entries in root directory *)
  1449. fatsize : LONGINT; (* 16bit count of sectors occupied by one FAT *)
  1450. volLab : ARRAY 12 OF CHAR; (* volume label *)
  1451. rootCluster32 : LONGINT; (* FAT32 only: Cluster number of first cluster in root directory *)
  1452. fsinfo : LONGINT; (* FAT32 only: Sector number of FSINFO structure in the reserved area *)
  1453. backupBoot : LONGINT; (* FAT32 only: Sector number of copy of the boot record in reserved area *)
  1454. (* quickFormat: IF TRUE, only FATs & root directory will be cleared *)
  1455. PROCEDURE SetParameters*(volumeName : Strings.String; quickFormat : BOOLEAN);
  1456. BEGIN
  1457. SELF.volumeName := volumeName; SELF.quickFormat := quickFormat;
  1458. END SetParameters;
  1459. PROCEDURE ValidParameters*() : BOOLEAN;
  1460. BEGIN
  1461. IF disk.device.blockSize # BS THEN ReportError("Blocksize not supported"); RETURN FALSE END;
  1462. IF ~PartitionsLib.IsFatType(disk.table[partition].type) & ~disk.isDiskette THEN
  1463. ReportError("Partition type is not FAT"); RETURN FALSE;
  1464. END;
  1465. RETURN TRUE;
  1466. END ValidParameters;
  1467. PROCEDURE DoOperation*;
  1468. VAR
  1469. vol : FATVolumes.Volume;
  1470. block : Block;
  1471. null : POINTER TO ARRAY OF CHAR;
  1472. rootDirSectors, firstDataSector : LONGINT;
  1473. freeCount, firstFree, media: LONGINT;
  1474. spc, type : LONGINT; (* sectors per cluster *)
  1475. i : LONGINT; res : WORD;
  1476. temp: ARRAY 256 OF CHAR;
  1477. BEGIN
  1478. type := disk.table[partition].type;
  1479. IF (type = 1) OR disk.isDiskette THEN fs := FAT12; info.String("Formating FAT12 volume");
  1480. ELSIF (type = 4) OR (type = 6) OR (type = 0EH) THEN fs := FAT16; info.String("Formating FAT16 volume");
  1481. ELSE fs := FAT32; info.String("Formating FAT32 volume");
  1482. END;
  1483. (* first calculate the sectors per cluster value *)
  1484. SetStatus(state.status, "Formating...", 0, 0, 0, FALSE);
  1485. spc := GetSectorPerCluster(disk.table[partition].size);
  1486. info.String(" (clusterSize: "); info.Int(spc * disk.device.blockSize, 0); info.String("B)"); info.Ln;
  1487. IF spc # -1 THEN
  1488. (* calculate the FAT size *)
  1489. fatsize := GetFatSize(spc);
  1490. (* build BIOS parameter block *)
  1491. block := BuildBPB(spc);
  1492. ASSERT((disk.table[partition].start#0) OR (disk.isDiskette)); (* protect MBR *)
  1493. disk.device.Transfer(Disks.Write, disk.table[partition].start, 1, block, 0, res);
  1494. IF res # Disks.Ok THEN
  1495. PartitionsLib.GetErrorMsg("Format failed: Could not write boot sector", res, temp); ReportError(temp);
  1496. ELSE
  1497. IF fs = FAT32 THEN
  1498. rootDirSectors := 0;
  1499. firstDataSector := rsvdSecCnt32 + (numFATs * fatsize) + rootDirSectors;
  1500. ELSE (* FAT12 or FAT16 *)
  1501. rootDirSectors := ((rootEntCnt * 32) + (disk.device.blockSize -1)) DIV disk.device.blockSize;
  1502. firstDataSector := rsvdSecCnt1216 + (numFATs * fatsize) + rootDirSectors;
  1503. END;
  1504. vol := GetVolume(disk.device, partition, block);
  1505. IF vol#NIL THEN
  1506. IF quickFormat THEN (* clear root directory and FATS only *)
  1507. ClearSectors(2, firstDataSector -1);
  1508. IF fs = FAT32 THEN (* also clear first cluster of root directory *)
  1509. NEW(null, spc*disk.device.blockSize);
  1510. FOR i := 0 TO LEN(null)-1 DO null[i] := 0X; END;
  1511. vol.WriteCluster(rootCluster32, null^, res);
  1512. IF res # Disks.Ok THEN
  1513. PartitionsLib.GetErrorMsg("Could not clear root cluster", res, temp); ReportError(temp);
  1514. END;
  1515. END;
  1516. ELSE
  1517. ClearSectors(2, disk.table[partition].size - 1);
  1518. END;
  1519. vol.unsafe := TRUE; (* access to FAT[0]&FAT[1] needed *)
  1520. (* set FAT[0] media byte (removable: 0F0H, else : 0F8H, all other bits are set to 1) *)
  1521. IF Disks.Removable IN disk.device.flags THEN media := 0FFFFFF0H; ELSE media := 0FFFFFF8H; END;
  1522. vol.WriteFATEntry(0, media, res);
  1523. IF res # Disks.Ok THEN PartitionsLib.GetErrorMsg("Could not set media byte", res, temp); ReportError(temp); END;
  1524. (* set FAT[1] EOC mark *)
  1525. vol.unsafe := TRUE;
  1526. vol.WriteFATEntry(1, LONGINT(0FFFFFFFFH), res);
  1527. IF res # Disks.Ok THEN PartitionsLib.GetErrorMsg("Could not set EOC mark", res, temp); ReportError(temp); END;
  1528. vol.unsafe := FALSE;
  1529. IF fs = FAT32 THEN
  1530. (* set the FAT entry of the first cluster of the root directory to EOC *)
  1531. vol.unsafe := TRUE;
  1532. vol.WriteFATEntry(rootCluster32, 0FFFFFFFH, res);
  1533. vol.unsafe := FALSE;
  1534. IF res # Disks.Ok THEN PartitionsLib.GetErrorMsg("Could not set FAT entry of root cluster", res, temp); ReportError(temp); END;
  1535. (* make sure that the cluster number 0x0FFFFFF7 is not used by the file system driver. This is because the
  1536. value 0x0FFFFFF7 is used as BAD CLUSTER mark, so if a FAT entry contains this value, the cluster is
  1537. considered to be BAD. Only relevant for FAT32 volumes *)
  1538. IF freeCount + 3 >= 0FFFFFF7H THEN
  1539. vol.WriteFATEntry(0FFFFFF7H, 0FFFFFF7H, res);
  1540. IF res # Disks.Ok THEN PartitionsLib.GetErrorMsg("Could not set EOC mark", res, temp); ReportError(temp); END;
  1541. END;
  1542. END;
  1543. ELSE ReportError("Could not get volume object");
  1544. END;
  1545. (* TODO: create volume label entry *)
  1546. IF fs = FAT12 THEN
  1547. ELSIF fs = FAT16 THEN
  1548. ELSIF fs = FAT32 THEN
  1549. END;
  1550. IF fs = FAT32 THEN (* write backup boot sector & FSInfo block *)
  1551. disk.device.Transfer(Disks.Write, disk.table[partition].start + backupBoot, 1, block, 0, res);
  1552. IF res = Disks.Ok THEN
  1553. freeCount := ((disk.table[partition].size - (rsvdSecCnt32 + (numFATs*fatsize))) DIV spc) + 1;
  1554. (* the first two clusters are unusable because FAT[0]&FAT[1] are for reserved use only, 1 cluster is used for the
  1555. root directory on FAT32, so we subtract 3 clusters *)
  1556. freeCount := freeCount - 3;
  1557. IF rootCluster32 = 2 THEN firstFree := 3; ELSE firstFree := 2; END;
  1558. block := BuildFSInfo(freeCount, firstFree);
  1559. disk.device.Transfer(Disks.Write, disk.table[partition].start + fsinfo, 1, block, 0, res);
  1560. IF res # Disks.Ok THEN
  1561. PartitionsLib.GetErrorMsg("Could not write FSInfo sector", res, temp); ReportError(temp);
  1562. END;
  1563. ELSE PartitionsLib.GetErrorMsg("Could not write backup boot sector", res, temp); ReportError(temp);
  1564. END;
  1565. END;
  1566. result.String("Formatted "); result.String(diskpartString); result.String(" as ");
  1567. IF fs = FAT12 THEN result.String("FAT12 ");
  1568. ELSIF fs = FAT16 THEN result.String("FAT16 ");
  1569. ELSIF fs = FAT32 THEN result.String("FAT32 ");
  1570. END;
  1571. IF PartitionsLib.StatusError IN state.status THEN
  1572. result.String("with "); result.Int(state.errorCount, 0); result.String(" errors");
  1573. ELSE
  1574. result.String("without errors");
  1575. END;
  1576. END;
  1577. END;
  1578. END DoOperation;
  1579. (* write zeros to area *)
  1580. (* start, end : sector addresses relative to first sector of partition *)
  1581. PROCEDURE ClearSectors(from, to : LONGINT);
  1582. CONST BufSize = 1024;
  1583. VAR
  1584. buf : POINTER TO ARRAY OF CHAR;
  1585. ofs, num : LONGINT;
  1586. res : WORD;
  1587. temp: ARRAY 256 OF CHAR;
  1588. BEGIN
  1589. ASSERT(from < to);
  1590. ASSERT((disk.table[partition].start#0) OR (disk.isDiskette)); (* protect MBR *)
  1591. NEW(buf, BufSize*BS);
  1592. num := (to - from + 1) DIV BufSize;
  1593. ofs := from;
  1594. SetStatus(state.status, "Formating...", from, from, to - from + 1, TRUE);
  1595. WHILE (num > 0) DO
  1596. ASSERT(ofs + BufSize - 1<= disk.table[partition].size );
  1597. disk.device.Transfer(Disks.Write, disk.table[partition].start + ofs, BufSize, buf^, 0, res);
  1598. IF res # Disks.Ok THEN
  1599. PartitionsLib.GetTransferError(disk.device, Disks.Write, disk.table[partition].start + ofs, res, temp); ReportError(temp);
  1600. END;
  1601. SetCurrentProgress(from + ofs);
  1602. DEC(num); INC(ofs, BufSize);
  1603. END;
  1604. num := (to - from) MOD BufSize;
  1605. WHILE (num > 0) DO
  1606. ASSERT(ofs <= disk.table[partition].size);
  1607. disk.device.Transfer(Disks.Write, disk.table[partition].start + ofs, 1, buf^, 0, res);
  1608. IF res # Disks.Ok THEN
  1609. PartitionsLib.GetTransferError(disk.device, Disks.Write, disk.table[partition].start + ofs, res, temp); ReportError(temp);
  1610. END;
  1611. SetCurrentProgress(from + ofs);
  1612. DEC(num); INC(ofs);
  1613. END;
  1614. END ClearSectors;
  1615. PROCEDURE BuildBPB(secPerClus : LONGINT) : Block;
  1616. VAR
  1617. b : Block;
  1618. temp : ARRAY 9 OF CHAR;
  1619. i, t, d: LONGINT;
  1620. BEGIN
  1621. ASSERT(disk.device.blockSize = BS);
  1622. (* Jump instruction to boot code *)
  1623. b[BsJmpBoot] := 0E9X; b[BsJmpBoot+1] := 0X; b[BsJmpBoot+2] := 0X;
  1624. (* OEM name (MSWIN4.1 for compatibility reasons) *)
  1625. FOR i := 0 TO 7 DO b[BsOEMName + i] := oemName[i]; END;
  1626. (* Bytes per sector *)
  1627. PartitionsLib.Put2(b, BpbBytsPerSec, disk.device.blockSize);
  1628. (* Sectors per cluster *)
  1629. b[BpbSecPerClus] := CHR(secPerClus);
  1630. (* Number of reserved sectors in the Reserved region *)
  1631. IF (fs = FAT12) OR (fs = FAT16) THEN (* 1 for maximum compatibility *) b[BpbRsvdSecCnt] := CHR(rsvdSecCnt1216);
  1632. ELSE (* FAT32: typically 32 *) b[BpbRsvdSecCnt] := CHR(rsvdSecCnt32);
  1633. END;
  1634. (* Number of FAT data structures (should be 2 for maximum compability) *)
  1635. b[BpbNumFATs] := CHR(numFATs);
  1636. (* Number of 32-byte directory entries in root directory (FAT12/16) *)
  1637. IF (fs = FAT12) OR (fs = FAT16) THEN PartitionsLib.Put2(b, BpbRootEntCnt, rootEntCnt);
  1638. ELSE (* FAT32: must be 0 *) PartitionsLib.Put2(b, BpbRootEntCnt, 0);
  1639. END;
  1640. (* Total sector count 16bit *)
  1641. IF ((fs = FAT12) OR ((fs = FAT16) & (disk.table[partition].size < 10000H))) THEN PartitionsLib.Put2(b, BpbTotSec16, disk.table[partition].size);
  1642. ELSIF (fs = FAT32) THEN (* FAT32: must be 0, FAT1216: of totsec does not fit *) PartitionsLib.Put2(b, BpbTotSec16, 0);
  1643. END;
  1644. (* Media field: fixed media = 0xF8, removable media = 0xF0; must be same as low byte of FAT[0] *)
  1645. IF Disks.Removable IN disk.device.flags THEN b[BpbMedia] := CHR(0F0H); ELSE b[BpbMedia] := CHR(0F8H); END;
  1646. (* FAT size: 16bit count of sectors occupied by one FAT *)
  1647. IF (fs = FAT12) OR (fs = FAT16) THEN PartitionsLib.Put2(b, BpbFATSz16, fatsize);
  1648. ELSE (* FAT32: must be 0 *) PartitionsLib.Put2(b, BpbFATSz16, 0);
  1649. END;
  1650. PartitionsLib.Put2(b, BpbSecPerTrk, disk.geo.spt); (* sectors per track *)
  1651. PartitionsLib.Put2(b, BpbNumHeads, disk.geo.hds); (* number of heads *)
  1652. (* Hidden sectors; should be zero for non-partitioned media, 63 else *)
  1653. IF ((disk.device.table=NIL) OR (LEN(disk.device.table)=1)) THEN (* non-partitioned *) PartitionsLib.Put4(b, BpbHiddSec, 0); ELSE PartitionsLib.Put4(b, BpbHiddSec, 63) END;
  1654. (* Total sector count 32bit *)
  1655. IF ((fs = FAT12) OR ((fs = FAT16) & (disk.table[partition].size < 10000H))) THEN PartitionsLib.Put4(b, BpbTotSec32, 0); (* value in BpbTotSec16 *)
  1656. ELSE (* FAT32: must be 0, FAT1216: of totsec does not fit *) PartitionsLib.Put4(b, BpbTotSec32, disk.table[partition].size);
  1657. END;
  1658. IF (fs = FAT12) OR (fs = FAT16) THEN
  1659. b[BsDrvNum] := PartitionsLib.GetDriveNum(disk.device); (* Int13 driver number *)
  1660. b[BsReserved1] := 0X; (* Reserved *)
  1661. b[BsBootSig] := CHR(29H); (* Extended boot signature; 0x29 indicates the presence of the following three fields *)
  1662. Clock.Get(t,d); PartitionsLib.Put4(b, BsVolID, i); (* volume serial number *)
  1663. FOR i := 0 TO 10 DO b[BsVolLab + i] := volLab[i]; END; (* volume label *)
  1664. IF fs = FAT12 THEN temp := "FAT12 "; ELSIF fs = FAT16 THEN temp := "FAT16 "; END;
  1665. FOR i := 0 TO 7 DO b[BsFilSysType + i] := temp[i]; END;
  1666. ELSE (* fs = FAT32 *)
  1667. PartitionsLib.Put4(b, BpbFATSz32, fatsize); (* 32bit count of sectors occupied by one FAT *)
  1668. PartitionsLib.Put2(b, BpbExtFlags, 0); (* FAT is mirrored at runtime into all FATs *)
  1669. PartitionsLib.Put2(b, BpbFSVer, 0); (* FAT FS version: 0:0 *)
  1670. PartitionsLib.Put4(b, BpbRootClus, rootCluster32); (* cluster number of the first cluster of the root directory *)
  1671. PartitionsLib.Put2(b, BpbFSInfo, fsinfo); (* sector number of FSInfo structure in reserved region *)
  1672. PartitionsLib.Put2(b, BpbBkBootSec, backupBoot); (* sector number of copy of the boot record in reserved region *)
  1673. FOR i := 0 TO 11 DO b[BpbReserved] := 0X; END; (* reserved *)
  1674. b[Bs32DrvNum] := PartitionsLib.GetDriveNum(disk.device); (* Int13 driver number *)
  1675. b[Bs32Reserved1] := 0X; (* Reserved *)
  1676. b[Bs32BootSig] := CHR(29H); (* Extended boot signature; 0x29 indicates the presence of the following three fields *)
  1677. Clock.Get(t,d); PartitionsLib.Put4(b, Bs32VolID, i); (* volume serial number *)
  1678. FOR i := 0 TO 10 DO b[Bs32VolLab + i] := volLab[i]; END; (* volume label *)
  1679. IF fs = FAT32 THEN temp := "FAT32 "; END; FOR i := 0 TO 7 DO b[BsFilSysType + i] := temp[i]; END;
  1680. END;
  1681. b[510] := 055X; b[511] := 0AAX; (* boot sector signature *)
  1682. RETURN b;
  1683. END BuildBPB;
  1684. PROCEDURE BuildFSInfo(freecount, nextfree : LONGINT) : Block;
  1685. VAR b : Block; i : LONGINT;
  1686. BEGIN
  1687. PartitionsLib.Put4(b, FsiLeadSig, 41615252H); (* Lead signature *)
  1688. FOR i := 0 TO 479 DO b[FsiReserved1] := 0X; END; (* Reserved *)
  1689. PartitionsLib.Put4(b, FsiStrucSig, 61417272H); (* Structure signature *)
  1690. PartitionsLib.Put4(b, FsiFreeCount, freecount); (* last known free cluster count on volume *)
  1691. PartitionsLib.Put4(b, FsiNxtFree, nextfree); (* Hint: "next" free cluster known *)
  1692. FOR i := 0 TO 11 DO b[FsiReserved2] := 0X; END; (* Reserved *)
  1693. PartitionsLib.Put4(b, FsiTrailSig, LONGINT(0AA550000H)); (* Trail signature *)
  1694. RETURN b;
  1695. END BuildFSInfo;
  1696. (* Calculates the size of one FAT See [1], p. 21 *)
  1697. PROCEDURE GetFatSize(sectorPerCluster : LONGINT) : LONGINT;
  1698. VAR
  1699. rootDirSectors, rootEntCnt, bytsPerSec, rsvdSecCnt : LONGINT;
  1700. tmpVal1, tmpVal2 : LONGINT;
  1701. BEGIN
  1702. IF disk.isDiskette THEN
  1703. RETURN 9;
  1704. ELSE
  1705. IF fs = FAT32 THEN rootEntCnt := 0; rsvdSecCnt := rsvdSecCnt32; ELSE rootEntCnt := SELF.rootEntCnt; rsvdSecCnt := rsvdSecCnt1216; END;
  1706. bytsPerSec := disk.device.blockSize;
  1707. rootDirSectors := ((rootEntCnt * 32) + (bytsPerSec -1)) DIV bytsPerSec;
  1708. tmpVal1 := disk.table[partition].size - (rsvdSecCnt + rootDirSectors);
  1709. tmpVal2 := (256 * sectorPerCluster) + numFATs;
  1710. IF fs = FAT32 THEN tmpVal2 := tmpVal2 DIV 2; END;
  1711. RETURN (tmpVal1 + (tmpVal2 - 1)) DIV tmpVal2;
  1712. END;
  1713. END GetFatSize;
  1714. (* Uses table from [1]; Return -1 in error case *)
  1715. PROCEDURE GetSectorPerCluster(disksize : LONGINT) : LONGINT;
  1716. VAR spc : LONGINT;
  1717. BEGIN
  1718. ASSERT((disk.device.blockSize = 512) & (rsvdSecCnt1216 = 1) & (numFATs = 2));
  1719. ASSERT((fs = FAT12) OR (rootEntCnt = 512)); (* so that table works *)
  1720. IF fs = FAT12 THEN
  1721. spc := 1;
  1722. ELSIF fs = FAT16 THEN
  1723. IF disksize <= 8400 THEN spc := -1; ReportError("FAT16 volumes must be bigger than 4,1MB");
  1724. ELSIF disksize <= 32680 THEN spc := 2; (* 1K cluster *)
  1725. ELSIF disksize <= 262144 THEN spc := 4; (* 2K cluster *)
  1726. ELSIF disksize <= 524288 THEN spc := 8; (* 4K cluster *)
  1727. ELSIF disksize <= 1048576 THEN spc := 16; (* 8K cluster *)
  1728. (* to following entries are only used when FAT16 is forced *)
  1729. ELSIF disksize <= 2097152 THEN spc := 32; (* 16K cluster *)
  1730. ELSIF disksize <= 4194304 THEN spc := 64; (* 32K cluster *)
  1731. ELSE spc := -1; ReportError("FAT16 volumes can't be bigger than 2GB");
  1732. END;
  1733. ELSIF fs = FAT32 THEN
  1734. IF disksize <= 66600 THEN spc := -1; ReportError("FAT32 volumes must be bigger than 32,5MB");
  1735. ELSIF disksize <= 532480 THEN spc := 1; (* 0.5K cluster *)
  1736. ELSIF disksize <= 16777216 THEN spc := 8; (* 4K cluster *)
  1737. ELSIF disksize <= 33554432 THEN spc := 16; (* 8K cluster *)
  1738. ELSIF disksize <= 67108864 THEN spc := 32;(* 16K cluster *)
  1739. ELSE spc := 64; (* 32K cluster *)
  1740. END;
  1741. ELSE
  1742. HALT(301);
  1743. END;
  1744. RETURN spc;
  1745. END GetSectorPerCluster;
  1746. PROCEDURE ValidClusterSize(clusterSize : LONGINT):BOOLEAN;
  1747. BEGIN
  1748. RETURN ((clusterSize=512) OR (clusterSize=1024) OR (clusterSize=2048) OR (clusterSize=4096) OR
  1749. (clusterSize=8192) OR (clusterSize=16384) OR (clusterSize=32768));
  1750. END ValidClusterSize;
  1751. PROCEDURE &Init*(disk : PartitionsLib.Disk; partition : LONGINT; out : Streams.Writer);
  1752. BEGIN
  1753. Init^(disk, partition, out);
  1754. name := "FormatFAT"; desc := "Format partition"; locktype := PartitionsLib.WriterLock;
  1755. oemName := "MSWIN4.1"; (* 8 bytes; default for max compatibility *)
  1756. rsvdSecCnt1216 := 1; (* 2 bytes; default = 1 for max compatibility *)
  1757. rsvdSecCnt32 := 32; (* 2 bytes *)
  1758. IF disk.isDiskette THEN rootEntCnt := 224; ELSE rootEntCnt := 512; END;
  1759. numFATs := 2; (* 1 byte; default = 2 for max compability *)
  1760. volLab := "NO NAME "; (* 11 bytes *)
  1761. rootCluster32 := 2; (* 4 byte value; default = 2 for max compatibility *)
  1762. fsinfo := 1; (* 2 byte *)
  1763. backupBoot := 6; (* 2 byte; default = 6 is recommended *)
  1764. END Init;
  1765. END FormatPartition;
  1766. VAR
  1767. fsType2 : LONGINT;
  1768. PROCEDURE GetVolume(dev : Disks.Device; partIdx : LONGINT; bpb : Block) : FATVolumes.Volume;
  1769. CONST CacheSize = 65563; (* in sectors *)
  1770. VAR
  1771. vol : FATVolumes.Volume; vol12: FATVolumes.FAT12Volume; vol16: FATVolumes.FAT16Volume; vol32: FATVolumes.FAT32Volume;
  1772. fatSize, numSectors, numClusters, reserved, numFATs, rootEntryCount, sectPC, fat : LONGINT;
  1773. BEGIN
  1774. IF (LEN(bpb) = 512) & (bpb[510] = 055X) & (bpb[511] = 0AAX) THEN (* boot sector signature ok *)
  1775. (* determine FAT type *)
  1776. fatSize := FATVolumes.GetUnsignedInteger(bpb, BpbFATSz16);
  1777. IF (fatSize = 0) THEN fatSize := FATVolumes.GetLongint(bpb, BpbFATSz32) END;
  1778. numSectors := FATVolumes.GetUnsignedInteger(bpb, BpbTotSec16);
  1779. IF (numSectors = 0) THEN numSectors := FATVolumes.GetLongint(bpb, BpbTotSec32) END;
  1780. reserved := FATVolumes.GetUnsignedInteger(bpb, BpbRsvdSecCnt);
  1781. numFATs := ORD(bpb[BpbNumFATs]);
  1782. rootEntryCount := FATVolumes.GetUnsignedInteger(bpb, BpbRootEntCnt);
  1783. sectPC := ORD(bpb[BpbSecPerClus]);
  1784. numClusters := (numSectors - (reserved + (numFATs * fatSize) + (rootEntryCount * 32 + BS - 1) DIV BS)) DIV sectPC;
  1785. IF (numClusters < 4085) THEN NEW(vol12); vol := vol12; fat := 12
  1786. ELSIF (numClusters < 65525) THEN NEW(vol16); vol := vol16; fat := 16
  1787. ELSE NEW(vol32); vol := vol32; fat := 32
  1788. END;
  1789. IF ~vol.InitLowLevel(bpb, numClusters, dev, dev.table[partIdx].start, dev.table[partIdx].size, BS) THEN
  1790. vol := NIL;
  1791. ELSE
  1792. vol.SetCache(FATVolumes.Data, CacheSize, FALSE);
  1793. EXCL(vol.flags, Files.ReadOnly);
  1794. END;
  1795. END;
  1796. RETURN vol;
  1797. END GetVolume;
  1798. END FATScavenger.