DiskFS.Mod 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE DiskFS; (** AUTHOR "pjm"; PURPOSE "Aos disk file system"; *)
  3. IMPORT SYSTEM, Machine, KernelLog, Modules, Clock, Files, Kernel;
  4. CONST
  5. SkipIndexFlag = 31; (* DiskFS filesystem flag. Do not write index map back to disk when unmounting *)
  6. MinVolSize = 4;
  7. SectorFactor = 29;
  8. (* WARNING: When the maximum length of filenames is changed, volumes must be re-formatted!!! *)
  9. FileNameLength = 128; (* includes 0X *)
  10. SectorTableSize = 128;
  11. SectorSize = 4096;
  12. IndexSize = SectorSize DIV 4;
  13. DiskAdrSize = 4; (* bytes *)
  14. HeaderSize = 4 (* mark *) + FileNameLength + 4*4 (* aleng, bleng, time, date *) + (SectorTableSize+1)*DiskAdrSize;
  15. DirEntrySize = FileNameLength + 2*DiskAdrSize (* adr, p *);
  16. DirPgHeaderSize = 2*4 (* mark, m *) + DiskAdrSize (* p0 *) + 4 (* min. FillerSize *);
  17. DirPgSize = (SectorSize - DirPgHeaderSize) DIV DirEntrySize;
  18. FillerSize = (SectorSize - DirPgHeaderSize) MOD DirEntrySize + 4 (* min. FillerSize *);
  19. DirRootAdr = 1*SectorFactor;
  20. N = DirPgSize DIV 2;
  21. DirMark = LONGINT(9B1EA38DH);
  22. HeaderMark = LONGINT(9BA71D86H);
  23. MapIndexSize = (SectorSize-4) DIV 4;
  24. MapSize = SectorSize DIV SIZEOF (SET); (* {MapSize MOD SIZEOF (SET) = 0} *)
  25. MapMark = LONGINT(9C2F977FH);
  26. MaxBufs = 1024;
  27. InitHint = 200*SectorFactor;
  28. Closed = 0X; Opening = 1X; Opened = 2X; Closing = 3X;
  29. SetSize = MAX (SET) + 1;
  30. TYPE
  31. DiskSector = RECORD END; (* Oberon Sector, size SectorSize *)
  32. DiskSectorArr = ARRAY SectorSize OF CHAR;
  33. DiskAdr = LONGINT;
  34. FileName = ARRAY FileNameLength OF CHAR;
  35. SectorTable = ARRAY SectorTableSize OF DiskAdr;
  36. FileHeader = RECORD (DiskSector) (* allocated in the first page of each file on disk *)
  37. mark: LONGINT;
  38. name: FileName;
  39. aleng, bleng: LONGINT;
  40. date, time: LONGINT;
  41. sec: SectorTable;
  42. ext: DiskAdr;
  43. data: ARRAY SectorSize-HeaderSize OF CHAR
  44. END;
  45. IndexSector = RECORD (DiskSector)
  46. x: ARRAY IndexSize OF DiskAdr
  47. END;
  48. DataSector = RECORD (DiskSector)
  49. B: ARRAY SectorSize OF CHAR
  50. END;
  51. DirEntry = RECORD (*B-tree node*)
  52. name: FileName;
  53. adr: DiskAdr; (*sec no of file header*)
  54. p: DiskAdr (*sec no of descendant in directory*)
  55. END;
  56. DirPage = RECORD (DiskSector)
  57. mark: LONGINT;
  58. m: LONGINT;
  59. p0: DiskAdr; (*sec no of left descendant in directory*)
  60. fill: ARRAY FillerSize OF CHAR;
  61. e: ARRAY DirPgSize OF DirEntry
  62. END;
  63. MapIndex = RECORD (DiskSector)
  64. mark: LONGINT;
  65. index: ARRAY MapIndexSize OF DiskAdr
  66. END;
  67. MapSector = RECORD (DiskSector)
  68. map: ARRAY MapSize OF SET
  69. END;
  70. Buffer = POINTER TO RECORD (Files.Hint)
  71. apos, lim: LONGINT;
  72. mod: BOOLEAN;
  73. next: Buffer;
  74. data: DataSector
  75. END;
  76. SuperIndex = POINTER TO RECORD
  77. adr: DiskAdr;
  78. mod: BOOLEAN;
  79. sub: ARRAY IndexSize OF SubIndex
  80. END;
  81. SubIndex = POINTER TO RECORD
  82. adr: DiskAdr;
  83. mod: BOOLEAN;
  84. sec: IndexSector
  85. END;
  86. TYPE
  87. Directory = OBJECT
  88. VAR
  89. vol: Files.Volume;
  90. state: CHAR;
  91. lastSectorReserved, noCleanup: BOOLEAN;
  92. (* "exported" methods: Search, Insert, Delete *)
  93. PROCEDURE Search(VAR name: FileName; VAR A: DiskAdr);
  94. VAR i, L, R: LONGINT; dadr: DiskAdr; a: DirPage;
  95. BEGIN {EXCLUSIVE}
  96. ASSERT(state = Opened);
  97. dadr := DirRootAdr;
  98. LOOP
  99. GetSector(vol, dadr, a);
  100. ASSERT(a.mark = DirMark);
  101. L := 0; R := a.m; (*binary search*)
  102. WHILE L < R DO
  103. i := (L+R) DIV 2;
  104. IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
  105. END ;
  106. IF (R < a.m) & (name = a.e[R].name) THEN
  107. A := a.e[R].adr; EXIT (*found*)
  108. END ;
  109. IF R = 0 THEN dadr := a.p0 ELSE dadr := a.e[R-1].p END ;
  110. IF dadr = 0 THEN A := 0; EXIT (*not found*) END
  111. END
  112. END Search;
  113. PROCEDURE insert(VAR name: FileName; dpg0: DiskAdr; VAR h: BOOLEAN; VAR v: DirEntry; fad: DiskAdr);
  114. (*h = "tree has become higher and v is ascending element"*)
  115. VAR ch: CHAR; i, j, L, R: LONGINT; dpg1: DiskAdr; u: DirEntry; a: DirPage;
  116. BEGIN (*~h*)
  117. ASSERT(state = Opened);
  118. GetSector(vol, dpg0, a);
  119. L := 0; R := a.m; (*binary search*)
  120. WHILE L < R DO
  121. i := (L+R) DIV 2;
  122. IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
  123. END ;
  124. IF (R < a.m) & (name = a.e[R].name) THEN
  125. a.e[R].adr := fad; PutSector(vol, dpg0, a) (*replace*)
  126. ELSE (*not on this page*)
  127. IF R = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[R-1].p END ;
  128. IF dpg1 = 0 THEN (*not in tree, insert*)
  129. u.adr := fad; u.p := 0; h := TRUE; j := 0;
  130. REPEAT ch := name[j]; u.name[j] := ch; INC(j)
  131. UNTIL ch = 0X;
  132. WHILE j < FileNameLength DO u.name[j] := 0X; INC(j) END
  133. ELSE
  134. insert(name, dpg1, h, u, fad)
  135. END ;
  136. IF h THEN (*insert u to the left of e[R]*)
  137. IF a.m < DirPgSize THEN
  138. h := FALSE; i := a.m;
  139. WHILE i > R DO DEC(i); a.e[i+1] := a.e[i] END ;
  140. a.e[R] := u; INC(a.m)
  141. ELSE (*split page and assign the middle element to v*)
  142. a.m := N; a.mark := DirMark;
  143. IF R < N THEN (*insert in left half*)
  144. v := a.e[N-1]; i := N-1;
  145. WHILE i > R DO DEC(i); a.e[i+1] := a.e[i] END ;
  146. a.e[R] := u; PutSector(vol, dpg0, a);
  147. AllocSector(vol, dpg0, dpg0); i := 0;
  148. WHILE i < N DO a.e[i] := a.e[i+N]; INC(i) END
  149. ELSE (*insert in right half*)
  150. PutSector(vol, dpg0, a);
  151. AllocSector(vol, dpg0, dpg0); DEC(R, N); i := 0;
  152. IF R = 0 THEN v := u
  153. ELSE v := a.e[N];
  154. WHILE i < R-1 DO a.e[i] := a.e[N+1+i]; INC(i) END ;
  155. a.e[i] := u; INC(i)
  156. END ;
  157. WHILE i < N DO a.e[i] := a.e[N+i]; INC(i) END
  158. END ;
  159. a.p0 := v.p; v.p := dpg0
  160. END ;
  161. PutSector(vol, dpg0, a)
  162. END
  163. END
  164. END insert;
  165. PROCEDURE Insert(VAR name: FileName; fad: DiskAdr);
  166. VAR oldroot: DiskAdr; h: BOOLEAN; U: DirEntry; a: DirPage;
  167. BEGIN {EXCLUSIVE}
  168. h := FALSE;
  169. insert(name, DirRootAdr, h, U, fad);
  170. IF h THEN (*root overflow*)
  171. GetSector(vol, DirRootAdr, a);
  172. AllocSector(vol, DirRootAdr, oldroot); PutSector(vol, oldroot, a);
  173. a.mark := DirMark; a.m := 1; a.p0 := oldroot; a.e[0] := U;
  174. PutSector(vol, DirRootAdr, a)
  175. END
  176. END Insert;
  177. PROCEDURE underflow(VAR c: DirPage; (*ancestor page*) dpg0: DiskAdr; s: LONGINT; (*insertion point in c*)
  178. VAR h: BOOLEAN); (*c undersize*)
  179. VAR i, k: LONGINT; dpg1: DiskAdr; a, b: DirPage; (*a := underflowing page, b := neighbouring page*)
  180. BEGIN
  181. GetSector(vol, dpg0, a);
  182. (*h & a.m = N-1 & dpg0 = c.e[s-1].p*)
  183. IF s < c.m THEN (*b := page to the right of a*)
  184. dpg1 := c.e[s].p; GetSector(vol, dpg1, b);
  185. k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
  186. a.e[N-1] := c.e[s]; a.e[N-1].p := b.p0;
  187. IF k > 0 THEN
  188. (*move k-1 items from b to a, one to c*) i := 0;
  189. WHILE i < k-1 DO a.e[i+N] := b.e[i]; INC(i) END ;
  190. c.e[s] := b.e[i]; b.p0 := c.e[s].p;
  191. c.e[s].p := dpg1; DEC(b.m, k); i := 0;
  192. WHILE i < b.m DO b.e[i] := b.e[i+k]; INC(i) END ;
  193. PutSector(vol, dpg1, b); a.m := N-1+k; h := FALSE
  194. ELSE (*merge pages a and b, discard b*) i := 0;
  195. WHILE i < N DO a.e[i+N] := b.e[i]; INC(i) END ;
  196. i := s; DEC(c.m);
  197. WHILE i < c.m DO c.e[i] := c.e[i+1]; INC(i) END ;
  198. a.m := 2*N; h := c.m < N;
  199. FreeSector(vol, dpg1) (* free b *)
  200. END ;
  201. PutSector(vol, dpg0, a)
  202. ELSE (*b := page to the left of a*) DEC(s);
  203. IF s = 0 THEN dpg1 := c.p0 ELSE dpg1 := c.e[s-1].p END ;
  204. GetSector(vol, dpg1, b);
  205. k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
  206. IF k > 0 THEN
  207. i := N-1;
  208. WHILE i > 0 DO DEC(i); a.e[i+k] := a.e[i] END ;
  209. i := k-1; a.e[i] := c.e[s]; a.e[i].p := a.p0;
  210. (*move k-1 items from b to a, one to c*) DEC(b.m, k);
  211. WHILE i > 0 DO DEC(i); a.e[i] := b.e[i+b.m+1] END ;
  212. c.e[s] := b.e[b.m]; a.p0 := c.e[s].p;
  213. c.e[s].p := dpg0; a.m := N-1+k; h := FALSE;
  214. PutSector(vol, dpg0, a)
  215. ELSE (*merge pages a and b, discard a*)
  216. c.e[s].p := a.p0; b.e[N] := c.e[s]; i := 0;
  217. WHILE i < N-1 DO b.e[i+N+1] := a.e[i]; INC(i) END ;
  218. b.m := 2*N; DEC(c.m); h := c.m < N;
  219. FreeSector(vol, dpg0) (* free a *)
  220. END ;
  221. PutSector(vol, dpg1, b)
  222. END
  223. END underflow;
  224. PROCEDURE delete(VAR name: FileName; dpg0: DiskAdr; VAR h: BOOLEAN; VAR fad: DiskAdr);
  225. (*search and delete entry with key name; if a page underflow arises,
  226. balance with adjacent page or merge; h := "page dpg0 is undersize"*)
  227. VAR i, L, R: LONGINT; dpg1: DiskAdr; a: DirPage;
  228. PROCEDURE del(dpg1: DiskAdr; VAR h: BOOLEAN);
  229. VAR dpg2: DiskAdr; (*global: a, R*) b: DirPage;
  230. BEGIN
  231. GetSector(vol, dpg1, b); dpg2 := b.e[b.m-1].p;
  232. IF dpg2 # 0 THEN del(dpg2, h);
  233. IF h THEN underflow(b, dpg2, b.m, h); PutSector(vol, dpg1, b) END
  234. ELSE
  235. b.e[b.m-1].p := a.e[R].p; a.e[R] := b.e[b.m-1];
  236. DEC(b.m); h := b.m < N; PutSector(vol, dpg1, b)
  237. END
  238. END del;
  239. BEGIN (*~h*)
  240. ASSERT(state = Opened);
  241. GetSector(vol, dpg0, a);
  242. L := 0; R := a.m; (*binary search*)
  243. WHILE L < R DO
  244. i := (L+R) DIV 2;
  245. IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
  246. END ;
  247. IF R = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[R-1].p END ;
  248. IF (R < a.m) & (name = a.e[R].name) THEN
  249. (*found, now delete*) fad := a.e[R].adr;
  250. IF dpg1 = 0 THEN (*a is a leaf page*)
  251. DEC(a.m); h := a.m < N; i := R;
  252. WHILE i < a.m DO a.e[i] := a.e[i+1]; INC(i) END
  253. ELSE del(dpg1, h);
  254. IF h THEN underflow(a, dpg1, R, h) END
  255. END ;
  256. PutSector(vol, dpg0, a)
  257. ELSIF dpg1 # 0 THEN
  258. delete(name, dpg1, h, fad);
  259. IF h THEN underflow(a, dpg1, R, h); PutSector(vol, dpg0, a) END
  260. ELSE (*not in tree*) fad := 0
  261. END
  262. END delete;
  263. PROCEDURE Delete(VAR name: FileName; VAR fad: DiskAdr);
  264. VAR h: BOOLEAN; newroot: DiskAdr; a: DirPage;
  265. BEGIN {EXCLUSIVE}
  266. h := FALSE;
  267. delete(name, DirRootAdr, h, fad);
  268. IF h THEN (*root underflow*)
  269. GetSector(vol, DirRootAdr, a);
  270. IF (a.m = 0) & (a.p0 # 0) THEN
  271. newroot := a.p0; GetSector(vol, newroot, a);
  272. PutSector(vol, DirRootAdr, a); (*discard newroot*)
  273. FreeSector(vol, newroot)
  274. END
  275. END
  276. END Delete;
  277. PROCEDURE Startup;
  278. VAR
  279. j, sec, size, q, free, thres: LONGINT; mi: MapIndex; ms: MapSector;
  280. s: ARRAY 10 OF CHAR; found: BOOLEAN;
  281. BEGIN (* only called from Init *)
  282. size := vol.size; found := FALSE;
  283. IF (vol.Available() = size) & (size # 0) THEN (* all sectors available *)
  284. GetSector(vol, size*SectorFactor, mi);
  285. IF mi.mark = MapMark THEN
  286. j := 0; (* check consistency of index *)
  287. WHILE (j # MapIndexSize) & (mi.index[j] >= 0) & (mi.index[j] MOD SectorFactor = 0) DO
  288. INC(j)
  289. END;
  290. IF j = MapIndexSize THEN
  291. found := TRUE;
  292. mi.mark := 0; PutSector(vol, size*SectorFactor, mi); (* invalidate index *)
  293. j := 0; sec := 1; q := 0;
  294. LOOP
  295. IF (j = MapIndexSize) OR (mi.index[j] = 0) THEN EXIT END;
  296. GetSector(vol, mi.index[j], ms);
  297. REPEAT
  298. IF (sec MOD SetSize) IN ms.map[sec DIV SetSize MOD MapSize] THEN
  299. MarkSector(vol, sec*SectorFactor);
  300. INC(q)
  301. END;
  302. IF sec = size THEN EXIT END;
  303. INC(sec)
  304. UNTIL sec MOD (MapSize*SetSize) = 0;
  305. INC(j)
  306. END;
  307. Machine.GetConfig("DiskGC", s);
  308. thres := 0; j := 0;
  309. WHILE s[j] # 0X DO thres := thres*10+(ORD(s[j])-48); INC(j) END;
  310. IF thres < 10 THEN thres := 10
  311. ELSIF thres > 100 THEN thres := 100
  312. END;
  313. ASSERT(q = size-vol.Available());
  314. free := vol.Available()*100 DIV size;
  315. IF (free > thres) & (vol.Available() > 100000H DIV SectorSize) THEN
  316. state := Opened
  317. ELSE (* undo *)
  318. FOR j := SectorFactor TO size*SectorFactor BY SectorFactor DO
  319. IF Marked(vol, j) THEN FreeSector(vol, j) END
  320. END;
  321. ASSERT(vol.Available() = size);
  322. KernelLog.String("DiskFS: "); KernelLog.Int(free, 1);
  323. KernelLog.String("% free, forcing disk GC on ");
  324. KernelLog.String(vol.name); KernelLog.Ln
  325. END
  326. END
  327. END;
  328. IF ~found THEN
  329. KernelLog.String("DiskFS: Index not found on ");
  330. KernelLog.String(vol.name); KernelLog.Ln
  331. END
  332. END
  333. END Startup;
  334. PROCEDURE &Init*(vol: Files.Volume);
  335. VAR k: LONGINT; A: ARRAY 2000 OF DiskAdr; files: LONGINT; bad: BOOLEAN;
  336. PROCEDURE MarkSectors;
  337. VAR L, R, i, j, n: LONGINT; x: DiskAdr; hd: FileHeader; sup, sub: IndexSector; mark: ARRAY 512 OF DiskAdr; markPosition: LONGINT;
  338. PROCEDURE StartMarking;
  339. BEGIN
  340. markPosition := 0;
  341. END StartMarking;
  342. PROCEDURE FinishMarking;
  343. BEGIN
  344. vol.MarkBlocks(mark,0,markPosition);
  345. markPosition := 0;
  346. END FinishMarking;
  347. PROCEDURE MarkSector(vol: Files.Volume (* ignored *); sec: LONGINT);
  348. BEGIN
  349. mark[markPosition] := sec DIV SectorFactor;
  350. INC(markPosition);
  351. IF markPosition = LEN(mark) THEN
  352. FinishMarking;
  353. END;
  354. END MarkSector;
  355. PROCEDURE sift(L, R: LONGINT);
  356. VAR i, j: LONGINT; x: DiskAdr;
  357. BEGIN j := L; x := A[j];
  358. LOOP i := j; j := 2*j + 1;
  359. IF (j+1 < R) & (A[j] < A[j+1]) THEN INC(j) END ;
  360. IF (j >= R) OR (x > A[j]) THEN EXIT END ;
  361. A[i] := A[j]
  362. END ;
  363. A[i] := x
  364. END sift;
  365. BEGIN
  366. StartMarking;
  367. KernelLog.String(" marking");
  368. L := k DIV 2; R := k; (*heapsort*)
  369. WHILE L > 0 DO DEC(L); sift(L, R) END ;
  370. WHILE R > 0 DO
  371. DEC(R); x := A[0]; A[0] := A[R]; A[R] := x; sift(L, R)
  372. END;
  373. WHILE L < k DO
  374. bad := FALSE; INC(files);
  375. IF files MOD 128 = 0 THEN KernelLog.Char(".") END;
  376. GetSector(vol, A[L], hd);
  377. IF hd.aleng < SectorTableSize THEN
  378. j := hd.aleng + 1;
  379. REPEAT
  380. DEC(j);
  381. IF hd.sec[j] # 0 THEN MarkSector(vol, hd.sec[j]) ELSE hd.aleng := j-1; bad := TRUE END
  382. UNTIL j = 0
  383. ELSE
  384. j := SectorTableSize;
  385. REPEAT
  386. DEC(j);
  387. IF hd.sec[j] # 0 THEN MarkSector(vol, hd.sec[j]) ELSE hd.aleng := j-1; bad := TRUE END
  388. UNTIL j = 0;
  389. IF hd.ext = 0 THEN hd.aleng := SectorTableSize-1; bad := TRUE END;
  390. IF ~bad THEN
  391. MarkSector(vol, hd.ext); GetSector(vol, hd.ext, sup);
  392. n := (hd.aleng - SectorTableSize) DIV IndexSize; i := 0;
  393. WHILE (i <= n) & ~bad DO
  394. IF sup.x[i] # 0 THEN
  395. MarkSector(vol, sup.x[i]); GetSector(vol, sup.x[i], sub);
  396. IF i < n THEN j := IndexSize
  397. ELSE j := (hd.aleng - SectorTableSize) MOD IndexSize + 1
  398. END;
  399. REPEAT
  400. DEC(j);
  401. IF (sub.x[j] MOD SectorFactor = 0) & (sub.x[j] > 0) THEN
  402. MarkSector(vol, sub.x[j])
  403. ELSE
  404. bad := TRUE
  405. END
  406. UNTIL j = 0;
  407. INC(i)
  408. ELSE bad := TRUE
  409. END;
  410. IF bad THEN
  411. IF i = 0 THEN hd.aleng := SectorTableSize-1
  412. ELSE hd.aleng := SectorTableSize + (i-1) * IndexSize
  413. END
  414. END
  415. END
  416. END
  417. END;
  418. IF bad THEN
  419. KernelLog.Ln; KernelLog.String(hd.name); KernelLog.String(" truncated");
  420. hd.bleng := SectorSize; IF hd.aleng < 0 THEN hd.aleng := 0 (* really bad *) END;
  421. PutSector(vol, A[L], hd)
  422. END;
  423. INC(L)
  424. END;
  425. FinishMarking;
  426. END MarkSectors;
  427. PROCEDURE TraverseDir(dpg: DiskAdr);
  428. VAR i: LONGINT; a: DirPage;
  429. BEGIN
  430. GetSector(vol, dpg, a); MarkSector(vol, dpg); i := 0;
  431. WHILE i < a.m DO
  432. A[k] := a.e[i].adr;
  433. (*
  434. IF A[k] = 0DEADDEADH THEN
  435. KernelLog.Enter; KernelLog.Int(dpg DIV SectorFactor, 1); KernelLog.Char(" "); KernelLog.Int(k, 1); KernelLog.Exit
  436. END;
  437. *)
  438. INC(k); INC(i);
  439. IF k = 2000 THEN MarkSectors; k := 0 END
  440. END ;
  441. IF a.p0 # 0 THEN
  442. TraverseDir(a.p0); i := 0;
  443. WHILE i < a.m DO
  444. TraverseDir(a.e[i].p); INC(i)
  445. END
  446. END
  447. END TraverseDir;
  448. BEGIN
  449. SELF.vol := vol; lastSectorReserved := FALSE;
  450. IF ~(Files.ReadOnly IN vol.flags) THEN
  451. state := Opening; k := 0;
  452. Startup;
  453. IF state # Opened THEN
  454. files := 0; KernelLog.String("DiskFS: Scanning ");
  455. KernelLog.String(vol.name); KernelLog.String("...");
  456. TraverseDir(DirRootAdr);
  457. MarkSectors;
  458. KernelLog.Int(files, 6); KernelLog.String(" files"); KernelLog.Ln;
  459. state := Opened
  460. END;
  461. IF ~Marked(vol, vol.size*SectorFactor) THEN (* last sector still free *)
  462. MarkSector(vol, vol.size*SectorFactor); lastSectorReserved := TRUE (* allocate it *)
  463. END;
  464. KernelLog.String("DiskFS: "); KernelLog.Int(vol.Available() * (SectorSize DIV 1024), 1);
  465. KernelLog.String("K of "); KernelLog.Int(vol.size * (SectorSize DIV 1024), 1);
  466. KernelLog.String("K available on "); KernelLog.String(vol.name);
  467. KernelLog.Ln
  468. ELSE
  469. state := Opened
  470. END
  471. END Init;
  472. PROCEDURE Cleanup;
  473. VAR i, j, p, q, sec, size: LONGINT; mi: MapIndex; ms: MapSector;
  474. BEGIN {EXCLUSIVE}
  475. (*KernelLog.String("DiskFS: Cleanup "); KernelLog.String(vol.name); KernelLog.Ln;*)
  476. state := Closing;
  477. size := vol.size; i := size*SectorFactor;
  478. IF ~(Files.ReadOnly IN vol.flags) & ~noCleanup THEN
  479. IF lastSectorReserved THEN FreeSector(vol, i); lastSectorReserved := FALSE END;
  480. IF ~Marked(vol, i) THEN (* last sector is available for us *)
  481. j := 0; sec := 1; q := 0;
  482. LOOP
  483. REPEAT DEC(i, SectorFactor) UNTIL (i = 0) OR ~Marked(vol, i); (* find a free sector *)
  484. IF i = 0 THEN RETURN END; (* no more space, don't commit *)
  485. mi.index[j] := i; INC(j);
  486. FOR p := 0 TO MapSize-1 DO ms.map[p] := {} END;
  487. REPEAT
  488. IF Marked(vol, sec*SectorFactor) THEN
  489. INCL(ms.map[sec DIV SetSize MOD MapSize], sec MOD SetSize);
  490. INC(q)
  491. END;
  492. IF sec = size THEN
  493. PutSector(vol, i, ms);
  494. EXIT
  495. END;
  496. INC(sec)
  497. UNTIL sec MOD (MapSize*SetSize) = 0;
  498. PutSector(vol, i, ms)
  499. END;
  500. WHILE j # MapIndexSize DO mi.index[j] := 0; INC(j) END;
  501. mi.mark := MapMark;
  502. PutSector(vol, size*SectorFactor, mi); (* commit *)
  503. KernelLog.String("DiskFS: Map saved on ");
  504. KernelLog.String(vol.name); KernelLog.Ln
  505. (*ELSE
  506. KernelLog.String("DiskFS: sector in use "); KernelLog.Int(size, 1); KernelLog.Ln*)
  507. END
  508. (*ELSE
  509. KernelLog.String("DiskFS: Read-only"); KernelLog.Ln*)
  510. END;
  511. state := Closed; vol := NIL
  512. END Cleanup;
  513. END Directory;
  514. TYPE
  515. FileSystem = OBJECT (Files.FileSystem) (* our file system type *)
  516. VAR
  517. dir: Directory;
  518. finalizeFiles: Kernel.FinalizedCollection;
  519. openFiles: DiskAdrList;
  520. (* all files that are registered, must be stored separately of finalizeFiles because of race
  521. between Delete0/Rename0 and deferred execution of file close finalizer *)
  522. tempRegFileSec: DiskAdrTable; (* temporary used for PurgeOpenedFile *)
  523. PROCEDURE &Init*;
  524. BEGIN NEW(finalizeFiles); NEW(openFiles); NEW(tempRegFileSec)
  525. END Init;
  526. PROCEDURE New0*(name: ARRAY OF CHAR): Files.File;
  527. VAR i: LONGINT; res: WORD; f: File; buf: Buffer; head {UNTRACED}: POINTER {UNSAFE} TO FileHeader; namebuf: FileName;
  528. BEGIN {EXCLUSIVE}
  529. f := NIL; Check(name, namebuf, res);
  530. IF res <= 0 THEN
  531. NEW(buf); buf.apos := 0; buf.mod := TRUE; buf.lim := HeaderSize; buf.next := buf;
  532. head := ADDRESSOF(buf.data);
  533. head.mark := HeaderMark;
  534. head.aleng := 0; head.bleng := HeaderSize; head.name := namebuf;
  535. Clock.Get(head.time, head.date);
  536. NEW(f); f.fs := SELF; f.key := 0; f.aleng := 0; f.bleng := HeaderSize; f.modH := TRUE;
  537. f.time := head.time; f.date := head.date;
  538. f.firstbuf := buf; f.nofbufs := 1; f.name := namebuf; f.sechint := InitHint;
  539. f.registered := (f.name[0] = 0X);
  540. f.ext := NIL; i := 0;
  541. REPEAT f.sec[i] := 0; head.sec[i] := 0; INC(i) UNTIL i = SectorTableSize;
  542. finalizeFiles.Add(f, Collect);
  543. ELSE
  544. KernelLog.String("DiskFS: "); KernelLog.String(name); KernelLog.String(", res: "); KernelLog.Int(res, 0); KernelLog.Ln;
  545. END;
  546. RETURN f
  547. END New0;
  548. PROCEDURE Old0*(name: ARRAY OF CHAR): Files.File;
  549. VAR
  550. i, k: LONGINT; res: WORD; f: File; header: DiskAdr; buf: Buffer; head {UNTRACED}: POINTER {UNSAFE} TO FileHeader;
  551. namebuf: FileName; super: SuperIndex; sub: SubIndex; sec: IndexSector;
  552. BEGIN {EXCLUSIVE}
  553. f := NIL; Check(name, namebuf, res);
  554. IF res = 0 THEN
  555. dir.Search(namebuf, header);
  556. IF header # 0 THEN
  557. NEW(buf); buf.apos := 0; buf.next := buf; buf.mod := FALSE;
  558. GetSector(vol, header, buf.data);
  559. head := ADDRESSOF(buf.data);
  560. NEW(f); f.fs := SELF; f.key := header;
  561. f.aleng := head.aleng; f.bleng := head.bleng;
  562. f.time := head.time; f.date := head.date;
  563. IF f.aleng = 0 THEN buf.lim := f.bleng ELSE buf.lim := SectorSize END;
  564. f.firstbuf := buf; f.nofbufs := 1;
  565. f.name := namebuf; f.registered := TRUE;
  566. f.sec := head.sec;
  567. k := (f.aleng + (IndexSize-SectorTableSize)) DIV IndexSize;
  568. IF k # 0 THEN
  569. NEW(super); super.adr := head.ext; super.mod := FALSE; f.ext := super;
  570. GetSector(vol, super.adr, sec); i := 0;
  571. WHILE i # k DO
  572. NEW(sub); sub.adr := sec.x[i]; sub.mod := FALSE; super.sub[i] := sub;
  573. GetSector(vol, sub.adr, sub.sec); INC(i)
  574. END;
  575. WHILE i # IndexSize DO super.sub[i] := NIL; INC(i) END
  576. ELSE
  577. f.ext := NIL
  578. END;
  579. f.sechint := header; f.modH := FALSE;
  580. finalizeFiles.Add(f, Collect); openFiles.Add(f.key)
  581. END
  582. END;
  583. RETURN f
  584. END Old0;
  585. PROCEDURE Delete0*(name: ARRAY OF CHAR; VAR key: LONGINT; VAR res: WORD);
  586. VAR adr: DiskAdr; namebuf: FileName; head: FileHeader;
  587. BEGIN {EXCLUSIVE}
  588. Check(name, namebuf, res);
  589. IF res = 0 THEN
  590. dir.Delete(namebuf, adr);
  591. key := adr;
  592. IF adr # 0 THEN
  593. IF ~openFiles.Contains(adr) THEN
  594. PurgeByAdr(adr)
  595. ELSE
  596. GetSector(vol, adr, head);
  597. head.mark := HeaderMark+1; (* invalidate mark *)
  598. PutSector(vol, adr, head)
  599. END
  600. ELSE
  601. res := 2
  602. END
  603. ELSE
  604. key := 0
  605. END
  606. END Delete0;
  607. PROCEDURE Rename0*(old, new: ARRAY OF CHAR; f: Files.File; VAR res: WORD);
  608. VAR adr, newAdr: DiskAdr; oldbuf, newbuf: FileName; head: FileHeader;
  609. BEGIN {EXCLUSIVE}
  610. Check(old, oldbuf, res);
  611. IF res = 0 THEN
  612. Check(new, newbuf, res);
  613. IF res = 0 THEN
  614. dir.Delete(oldbuf, adr);
  615. IF adr # 0 THEN
  616. dir.Search(newbuf, newAdr); ASSERT(adr # newAdr);
  617. IF (newAdr # 0) & ~openFiles.Contains(newAdr) THEN
  618. PurgeByAdr(newAdr)
  619. END;
  620. IF f # NIL THEN (* file is open *)
  621. ASSERT(f.key = adr); (* it's key must match *)
  622. f(File).name := newbuf
  623. END;
  624. dir.Insert(newbuf, adr);
  625. GetSector(vol, adr, head);
  626. head.name := newbuf;
  627. PutSector(vol, adr, head)
  628. ELSE res := 2
  629. END
  630. END
  631. END
  632. END Rename0;
  633. PROCEDURE Enumerate0*(mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator);
  634. VAR b: BOOLEAN; fh: FileHeader; fn: ARRAY Files.PrefixLength+FileNameLength OF CHAR;
  635. BEGIN {EXCLUSIVE}
  636. b := TRUE; enumerate(SELF, mask, DirRootAdr, flags, enum, b, fh, fn)
  637. END Enumerate0;
  638. PROCEDURE FileKey*(name: ARRAY OF CHAR): LONGINT;
  639. VAR res: WORD; namebuf: FileName; header: DiskAdr;
  640. BEGIN {EXCLUSIVE}
  641. header := 0;
  642. Check(name, namebuf, res);
  643. IF res = 0 THEN
  644. dir.Search(namebuf, header)
  645. END;
  646. RETURN header
  647. END FileKey;
  648. (* exlcusive lock must be acquired, result in tempRegFileSec *)
  649. PROCEDURE CollectRegisteredFileSectors(adr: DiskAdr);
  650. VAR hd: FileHeader; i, p, m, n: LONGINT; super, sub: IndexSector;
  651. BEGIN
  652. tempRegFileSec.Clear;
  653. GetSector(vol, adr, hd);
  654. tempRegFileSec.Add(adr);
  655. ASSERT(hd.sec[0] = adr);
  656. IF hd.aleng < SectorTableSize THEN m := hd.aleng + 1 ELSE m := SectorTableSize END; p := 1;
  657. WHILE p < m DO
  658. IF hd.sec[p] # 0 THEN tempRegFileSec.Add(hd.sec[p]) END;
  659. INC(p)
  660. END;
  661. IF (hd.aleng >= SectorTableSize) & (hd.ext # 0) THEN
  662. GetSector(vol, hd.ext, super); tempRegFileSec.Add(hd.ext);
  663. n := (hd.aleng - SectorTableSize) DIV IndexSize; i := 0;
  664. WHILE i <= n DO
  665. IF super.x[i] # 0 THEN
  666. GetSector(vol, super.x[i], sub); tempRegFileSec.Add(super.x[i]);
  667. IF i < n THEN m := IndexSize
  668. ELSE m := (hd.aleng - SectorTableSize) MOD IndexSize + 1
  669. END;
  670. p := 0;
  671. WHILE p < m DO
  672. IF sub.x[p] # 0 THEN tempRegFileSec.Add(sub.x[p]) END;
  673. INC(p)
  674. END
  675. END;
  676. INC(i)
  677. END
  678. END
  679. END CollectRegisteredFileSectors;
  680. (* exlcusive lock must be acquired! *)
  681. PROCEDURE PurgeByAdr(adr: DiskAdr);
  682. VAR hd: FileHeader; i, p, m, n: LONGINT; super, sub: IndexSector;
  683. BEGIN
  684. GetSector(vol, adr, hd);
  685. FreeSector(vol, adr);
  686. ASSERT(hd.sec[0] = adr);
  687. IF hd.aleng < SectorTableSize THEN m := hd.aleng + 1 ELSE m := SectorTableSize END; p := 1;
  688. WHILE p < m DO
  689. IF hd.sec[p] # 0 THEN FreeSector(vol, hd.sec[p]) END;
  690. INC(p)
  691. END;
  692. IF (hd.aleng >= SectorTableSize) & (hd.ext # 0) THEN
  693. GetSector(vol, hd.ext, super); FreeSector(vol, hd.ext);
  694. n := (hd.aleng - SectorTableSize) DIV IndexSize; i := 0;
  695. WHILE i <= n DO
  696. IF super.x[i] # 0 THEN
  697. GetSector(vol, super.x[i], sub); FreeSector(vol, super.x[i]);
  698. IF i < n THEN m := IndexSize
  699. ELSE m := (hd.aleng - SectorTableSize) MOD IndexSize + 1
  700. END;
  701. p := 0;
  702. WHILE p < m DO
  703. IF sub.x[p] # 0 THEN FreeSector(vol, sub.x[p]) END;
  704. INC(p)
  705. END
  706. END;
  707. INC(i)
  708. END
  709. END
  710. END PurgeByAdr;
  711. (* purge all sectors of f except the sectors in 'except', except may be NIL *)
  712. PROCEDURE PurgeOpenedFile(f: File; except: DiskAdrTable);
  713. VAR i, p, m, n: LONGINT; super, sub: IndexSector; free: ARRAY 512 OF DiskAdr; freePosition: Files.TSize;
  714. PROCEDURE StartFreeing;
  715. BEGIN
  716. freePosition := 0;
  717. END StartFreeing;
  718. PROCEDURE FinishFreeing;
  719. BEGIN
  720. vol.FreeBlocks(free,0,freePosition);
  721. freePosition := 0;
  722. END FinishFreeing;
  723. PROCEDURE FreeSector(vol: Files.Volume (* ignored *); sec: LONGINT);
  724. BEGIN
  725. free[freePosition] := sec DIV SectorFactor;
  726. INC(freePosition);
  727. IF freePosition = LEN(free) THEN
  728. FinishFreeing;
  729. END;
  730. END FreeSector;
  731. PROCEDURE FreeExcept(sec: DiskAdr);
  732. BEGIN
  733. IF (except = NIL) OR ~except.Contains(sec) THEN FreeSector(vol, sec) END
  734. END FreeExcept;
  735. BEGIN
  736. StartFreeing;
  737. IF f.aleng < SectorTableSize THEN m := f.aleng + 1 ELSE m := SectorTableSize END; p := 0; (* include sec[0] *)
  738. WHILE p < m DO
  739. IF f.sec[p] # 0 THEN FreeExcept(f.sec[p]) END;
  740. INC(p)
  741. END;
  742. IF (f.aleng >= SectorTableSize) & (f.ext # NIL) & (f.ext.adr # 0) THEN
  743. GetSector(vol, f.ext.adr, super); FreeExcept(f.ext.adr);
  744. n := (f.aleng - SectorTableSize) DIV IndexSize; i := 0;
  745. WHILE i <= n DO
  746. IF super.x[i] # 0 THEN
  747. GetSector(vol, super.x[i], sub); FreeExcept(super.x[i]);
  748. IF i < n THEN m := IndexSize
  749. ELSE m := (f.aleng - SectorTableSize) MOD IndexSize + 1
  750. END;
  751. p := 0;
  752. WHILE p < m DO
  753. IF sub.x[p] # 0 THEN FreeExcept(sub.x[p]) END;
  754. INC(p)
  755. END
  756. END;
  757. INC(i)
  758. END
  759. END;
  760. FinishFreeing;
  761. END PurgeOpenedFile;
  762. PROCEDURE Close(f: File);
  763. VAR adr: DiskAdr;
  764. BEGIN {EXCLUSIVE}
  765. IF f.key # 0 THEN
  766. ASSERT(openFiles.Contains(f.key));
  767. openFiles.Remove(f.key);
  768. dir.Search(f.name, adr);
  769. IF (adr = 0) OR (adr # f.key) THEN (* deleted or overwritten *)
  770. PurgeOpenedFile(f, NIL)
  771. ELSE
  772. CollectRegisteredFileSectors(adr);
  773. PurgeOpenedFile(f, tempRegFileSec);
  774. tempRegFileSec.Clear
  775. END
  776. ELSE
  777. PurgeOpenedFile(f, NIL)
  778. END
  779. END Close;
  780. PROCEDURE Finalize*;
  781. BEGIN {EXCLUSIVE}
  782. dir.Cleanup();
  783. vol.Finalize;
  784. Finalize^ (* see note in Files *)
  785. END Finalize;
  786. END FileSystem;
  787. DiskAdrArray = POINTER TO ARRAY OF DiskAdr;
  788. (* analogous to TFClasses.List *)
  789. DiskAdrList = OBJECT
  790. VAR
  791. list : DiskAdrArray;
  792. count : LONGINT;
  793. PROCEDURE &New*;
  794. BEGIN NEW(list, 8); count := 0
  795. END New;
  796. PROCEDURE Grow;
  797. VAR old: DiskAdrArray; i : LONGINT;
  798. BEGIN
  799. old := list;
  800. NEW(list, LEN(list)*2);
  801. FOR i := 0 TO count-1 DO list[i] := old[i] END
  802. END Grow;
  803. PROCEDURE Add(x: DiskAdr);
  804. BEGIN {EXCLUSIVE}
  805. ASSERT(x # 0);
  806. IF count = LEN(list) THEN Grow END;
  807. list[count] := x;
  808. INC(count)
  809. END Add;
  810. PROCEDURE Remove(x: DiskAdr);
  811. VAR i : LONGINT;
  812. BEGIN {EXCLUSIVE}
  813. ASSERT(x # 0);
  814. i := 0; WHILE (i < count) & (list[i] # x) DO INC(i) END;
  815. IF i < count THEN
  816. WHILE (i < count-1) DO list[i] := list[i+1]; INC(i) END;
  817. DEC(count);
  818. list[count] := 0
  819. END
  820. END Remove;
  821. PROCEDURE Contains(x: DiskAdr) : BOOLEAN;
  822. VAR i: LONGINT;
  823. BEGIN {EXCLUSIVE}
  824. i := 0 ; WHILE i < count DO IF list[i] = x THEN RETURN TRUE END; INC(i) END;
  825. RETURN FALSE
  826. END Contains;
  827. END DiskAdrList;
  828. DiskAdrTable = OBJECT
  829. VAR
  830. table : DiskAdrArray;
  831. count :SIZE;
  832. size: SIZE; (* cache: invariant size = LEN(table) *)
  833. CONST
  834. threshold = 4; (* 1/4 filled -> grow*)
  835. PROCEDURE &New*;
  836. BEGIN NEW(table, 8); size := LEN(table)-1; count := 0;
  837. END New;
  838. PROCEDURE Clear;
  839. VAR i: SIZE;
  840. BEGIN{EXCLUSIVE}
  841. FOR i := 0 TO LEN(table)-1 DO
  842. table[i] := 0;
  843. END;
  844. count := 0;
  845. END Clear;
  846. PROCEDURE Grow;
  847. VAR old: DiskAdrArray; i,x: LONGINT; index: SIZE;
  848. BEGIN
  849. old := table;
  850. NEW(table, LEN(old)*2); (* filled with zeroes -- ok *)
  851. size := LEN(table)-1;
  852. count := 0;
  853. FOR i := 0 TO LEN(old)-1 DO
  854. x := old[i];
  855. IF (x # 0) THEN
  856. index := HashValue(x);
  857. IF (table[index] = 0) THEN
  858. table[index] := x;
  859. INC(count);
  860. ELSE
  861. HALT(100); (* double entry *)
  862. END;
  863. END;
  864. END;
  865. END Grow;
  866. PROCEDURE HashValue(key: DiskAdr):SIZE;
  867. VAR index, h, i := 0 : SIZE;
  868. BEGIN
  869. h := key MOD size;
  870. REPEAT
  871. index := (h + i) MOD size;
  872. INC(i);
  873. UNTIL((table[index] = 0) OR (table[index] = key) OR (i > size));
  874. ASSERT((table[index] = 0) OR (table[index] = key));
  875. RETURN index;
  876. END HashValue;
  877. PROCEDURE Add(x: DiskAdr);
  878. VAR index: SIZE;
  879. BEGIN {EXCLUSIVE}
  880. ASSERT(x # 0);
  881. IF count > size DIV threshold THEN Grow END;
  882. index := HashValue(x);
  883. IF table[index] = 0 THEN
  884. table[index] := x;
  885. INC(count);
  886. ELSE
  887. ASSERT(table[index] = x);
  888. END;
  889. END Add;
  890. PROCEDURE Contains(x: DiskAdr) : BOOLEAN;
  891. BEGIN {EXCLUSIVE}
  892. RETURN table[HashValue(x)] = x;
  893. END Contains;
  894. END DiskAdrTable;
  895. TYPE
  896. File = OBJECT (Files.File)
  897. VAR
  898. aleng, bleng: LONGINT;
  899. nofbufs: LONGINT;
  900. modH, registered: BOOLEAN;
  901. firstbuf: Buffer;
  902. sechint: DiskAdr;
  903. name: FileName;
  904. time, date: LONGINT;
  905. ext: SuperIndex;
  906. sec: SectorTable;
  907. PROCEDURE Set*(VAR r: Files.Rider; pos: LONGINT);
  908. VAR a, b: LONGINT;
  909. BEGIN {EXCLUSIVE}
  910. r.eof := FALSE; r.res := 0; r.file := SELF; r.fs := fs;
  911. IF pos < 0 THEN
  912. a := 0; b := HeaderSize
  913. ELSIF pos < aleng*SectorSize + bleng - HeaderSize THEN
  914. a := (pos + HeaderSize) DIV SectorSize; b := (pos + HeaderSize) MOD SectorSize
  915. ELSE
  916. a := aleng; b := bleng
  917. END;
  918. r.apos := a; r.bpos := b; r.hint := firstbuf
  919. END Set;
  920. PROCEDURE Pos*(VAR r: Files.Rider): LONGINT;
  921. BEGIN
  922. RETURN r.apos*SectorSize + r.bpos - HeaderSize
  923. END Pos;
  924. PROCEDURE Read*(VAR r: Files.Rider; VAR x: CHAR);
  925. VAR buf: Buffer;
  926. BEGIN {EXCLUSIVE}
  927. buf := r.hint(Buffer);
  928. IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
  929. IF r.bpos < buf.lim THEN
  930. x := buf.data.B[r.bpos]; INC(r.bpos)
  931. ELSIF r.apos < aleng THEN
  932. INC(r.apos);
  933. buf := SearchBuf(SELF, r.apos);
  934. IF buf = NIL THEN
  935. buf := r.hint(Buffer);
  936. IF buf.mod THEN WriteBuf(SELF, buf) END ;
  937. ReadBuf(SELF, buf, r.apos)
  938. ELSE
  939. r.hint := buf
  940. END;
  941. ASSERT(buf.lim > 0);
  942. x := buf.data.B[0]; r.bpos := 1
  943. ELSE
  944. x := 0X; r.eof := TRUE
  945. END
  946. END Read;
  947. PROCEDURE ReadBytes*(VAR r: Files.Rider; VAR x: ARRAY OF CHAR; ofs, len: LONGINT);
  948. VAR src: ADDRESS; m: LONGINT; buf: Buffer;
  949. BEGIN {EXCLUSIVE}
  950. IF LEN(x)-ofs < len THEN SYSTEM.HALT(19) END;
  951. IF len > 0 THEN
  952. buf := r.hint(Buffer);
  953. IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
  954. LOOP
  955. IF len <= 0 THEN EXIT END ;
  956. src := ADDRESSOF(buf.data.B[0]) + r.bpos; m := r.bpos + len;
  957. IF m <= buf.lim THEN
  958. SYSTEM.MOVE(src, ADDRESSOF(x[ofs]), len); r.bpos := m; r.res := 0; EXIT
  959. ELSIF buf.lim = SectorSize THEN
  960. m := buf.lim - r.bpos;
  961. IF m > 0 THEN SYSTEM.MOVE(src, ADDRESSOF(x[ofs]), m); INC(ofs, m); DEC(len, m) END ;
  962. IF r.apos < aleng THEN
  963. INC(r.apos); r.bpos := 0; buf := SearchBuf(SELF, r.apos);
  964. IF buf = NIL THEN
  965. buf := r.hint(Buffer);
  966. IF buf.mod THEN WriteBuf(SELF, buf) END ;
  967. ReadBuf(SELF, buf, r.apos)
  968. ELSE
  969. r.hint := buf
  970. END
  971. ELSE
  972. r.bpos := buf.lim; r.res := len; r.eof := TRUE; EXIT
  973. END
  974. ELSE
  975. m := buf.lim - r.bpos;
  976. IF m > 0 THEN SYSTEM.MOVE(src, ADDRESSOF(x[ofs]), m); r.bpos := buf.lim END ;
  977. r.res := len - m; r.eof := TRUE; EXIT
  978. END
  979. END;
  980. ELSE
  981. r.res := 0
  982. END
  983. END ReadBytes;
  984. PROCEDURE Write*(VAR r: Files.Rider; x: CHAR);
  985. VAR buf: Buffer;
  986. BEGIN {EXCLUSIVE}
  987. buf := r.hint(Buffer);
  988. IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
  989. IF r.bpos >= buf.lim THEN
  990. IF r.bpos < SectorSize THEN
  991. INC(buf.lim); INC(bleng); modH := TRUE
  992. ELSE
  993. WriteBuf(SELF, buf); INC(r.apos); buf := SearchBuf(SELF, r.apos);
  994. IF buf = NIL THEN
  995. buf := r.hint(Buffer);
  996. IF r.apos <= aleng THEN
  997. ReadBuf(SELF, buf, r.apos)
  998. ELSE
  999. buf.apos := r.apos; buf.lim := 1; INC(aleng); bleng := 1; modH := TRUE;
  1000. IF (aleng - SectorTableSize) MOD IndexSize = 0 THEN NewSub(SELF) END
  1001. END
  1002. ELSE
  1003. r.hint := buf
  1004. END;
  1005. r.bpos := 0
  1006. END
  1007. END;
  1008. buf.data.B[r.bpos] := x; INC(r.bpos); buf.mod := TRUE
  1009. END Write;
  1010. PROCEDURE WriteBytes*(VAR r: Files.Rider; CONST x: ARRAY OF CHAR; ofs, len: LONGINT);
  1011. VAR dst: ADDRESS; m: LONGINT; buf: Buffer;
  1012. BEGIN {EXCLUSIVE}
  1013. IF LEN(x)-ofs < len THEN SYSTEM.HALT(19) END;
  1014. IF len > 0 THEN
  1015. buf := r.hint(Buffer);
  1016. IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
  1017. LOOP
  1018. IF len <= 0 THEN EXIT END;
  1019. buf.mod := TRUE; dst := ADDRESSOF(buf.data.B[0]) + r.bpos; m := r.bpos + len;
  1020. IF m <= buf.lim THEN
  1021. SYSTEM.MOVE(ADDRESSOF(x[ofs]), dst, len); r.bpos := m; EXIT
  1022. ELSIF m <= SectorSize THEN
  1023. SYSTEM.MOVE(ADDRESSOF(x[ofs]), dst, len); r.bpos := m;
  1024. bleng := m; buf.lim := m; modH := TRUE; EXIT
  1025. ELSE
  1026. m := SectorSize - r.bpos;
  1027. IF m > 0 THEN SYSTEM.MOVE(ADDRESSOF(x[ofs]), dst, m); INC(ofs, m); DEC(len, m) END;
  1028. WriteBuf(SELF, buf); INC(r.apos); r.bpos := 0; buf := SearchBuf(SELF, r.apos);
  1029. IF buf = NIL THEN
  1030. buf := r.hint(Buffer);
  1031. IF r.apos <= aleng THEN ReadBuf(SELF, buf, r.apos)
  1032. ELSE
  1033. buf.apos := r.apos; buf.lim := 0; INC(aleng); bleng := 0; modH := TRUE;
  1034. IF (aleng - SectorTableSize) MOD IndexSize = 0 THEN NewSub(SELF) END
  1035. END
  1036. ELSE
  1037. r.hint := buf
  1038. END
  1039. END
  1040. END
  1041. END
  1042. END WriteBytes;
  1043. PROCEDURE Length*(): LONGINT;
  1044. BEGIN {EXCLUSIVE}
  1045. RETURN aleng*SectorSize + bleng - HeaderSize
  1046. END Length;
  1047. PROCEDURE GetDate*(VAR t, d: LONGINT);
  1048. BEGIN {EXCLUSIVE}
  1049. t := time; d := date
  1050. END GetDate;
  1051. PROCEDURE SetDate*(t, d: LONGINT);
  1052. BEGIN {EXCLUSIVE}
  1053. modH := TRUE; time := t; date := d
  1054. END SetDate;
  1055. PROCEDURE GetName*(VAR name: ARRAY OF CHAR);
  1056. BEGIN {EXCLUSIVE}
  1057. Files.JoinName(fs.prefix, SELF.name, name)
  1058. END GetName;
  1059. PROCEDURE Register0*(VAR res: WORD);
  1060. VAR oldAdr: DiskAdr; fs0: FileSystem;
  1061. BEGIN {EXCLUSIVE}
  1062. Unbuffer(SELF);
  1063. IF ~registered & (name # "") THEN
  1064. fs0 := fs(FileSystem);
  1065. fs0.dir.Search(name, oldAdr);
  1066. fs0.dir.Insert(name, sec[0]);
  1067. registered := TRUE; key := sec[0];
  1068. fs0.openFiles.Add(key);
  1069. IF (oldAdr # 0) & ~fs0.openFiles.Contains(oldAdr) THEN (* overwrite not opened file *)
  1070. ASSERT(oldAdr # key);
  1071. fs0.PurgeByAdr(oldAdr)
  1072. END;
  1073. res := 0
  1074. ELSE
  1075. res := 1
  1076. END
  1077. END Register0;
  1078. PROCEDURE Update*;
  1079. BEGIN {EXCLUSIVE}
  1080. Unbuffer(SELF)
  1081. END Update;
  1082. END File;
  1083. PROCEDURE Collect(f: ANY);
  1084. VAR file: File; fs: FileSystem;
  1085. BEGIN
  1086. file := f(File);
  1087. IF file.fs # NIL THEN
  1088. fs := file.fs(FileSystem);
  1089. IF (fs.vol # NIL) & ~(Files.ReadOnly IN fs.vol.flags) THEN fs.Close(file) END
  1090. END
  1091. END Collect;
  1092. PROCEDURE GetSector(vol: Files.Volume; src: DiskAdr; VAR dest: DiskSector);
  1093. BEGIN
  1094. IF src MOD SectorFactor # 0 THEN SYSTEM.HALT(15) END;
  1095. vol.GetBlock(src DIV SectorFactor, SYSTEM.VAL(DiskSectorArr, dest))
  1096. END GetSector;
  1097. PROCEDURE PutSector(vol: Files.Volume; dest: DiskAdr; VAR src: DiskSector);
  1098. BEGIN
  1099. ASSERT(~(Files.ReadOnly IN vol.flags));
  1100. IF dest MOD SectorFactor # 0 THEN SYSTEM.HALT(15) END;
  1101. vol.PutBlock(dest DIV SectorFactor, SYSTEM.VAL(DiskSectorArr, src))
  1102. END PutSector;
  1103. PROCEDURE AllocSector(vol: Files.Volume; hint: DiskAdr; VAR sec: DiskAdr);
  1104. BEGIN
  1105. ASSERT(~(Files.ReadOnly IN vol.flags));
  1106. vol.AllocBlock(hint DIV SectorFactor, sec);
  1107. sec := sec * SectorFactor
  1108. END AllocSector;
  1109. PROCEDURE MarkSector(vol: Files.Volume; sec: LONGINT);
  1110. BEGIN
  1111. ASSERT(~(Files.ReadOnly IN vol.flags));
  1112. vol.MarkBlock(sec DIV SectorFactor)
  1113. END MarkSector;
  1114. PROCEDURE FreeSector(vol: Files.Volume; sec: LONGINT);
  1115. BEGIN
  1116. ASSERT(~(Files.ReadOnly IN vol.flags));
  1117. ASSERT(Marked(vol, sec));
  1118. vol.FreeBlock(sec DIV SectorFactor)
  1119. END FreeSector;
  1120. PROCEDURE Marked(vol: Files.Volume; sec: LONGINT): BOOLEAN;
  1121. BEGIN
  1122. ASSERT(~(Files.ReadOnly IN vol.flags));
  1123. RETURN vol.Marked(sec DIV SectorFactor)
  1124. END Marked;
  1125. PROCEDURE Match*(mask, name: ARRAY OF CHAR): BOOLEAN;
  1126. VAR m,n, om, on: LONGINT; f: BOOLEAN;
  1127. BEGIN
  1128. m := 0; n := 0; om := -1;
  1129. f := TRUE;
  1130. LOOP
  1131. IF (mask[m] = "*") THEN
  1132. om := m; INC(m);
  1133. WHILE (name[n] # 0X) & (name[n] # mask[m]) DO INC(n) END;
  1134. on := n
  1135. ELSIF (mask[m] = "?") THEN
  1136. IF (name[n] = 0X) THEN f := FALSE; EXIT END;
  1137. INC(m); INC(n)
  1138. ELSE
  1139. IF (mask[m] # name[n]) THEN
  1140. IF (om = -1) THEN f := FALSE; EXIT
  1141. ELSIF (name[n] # 0X) THEN (* try the next position *)
  1142. m := om; n := on + 1;
  1143. IF (name[n] = 0X) THEN f := FALSE; EXIT END
  1144. ELSE
  1145. f := FALSE; EXIT
  1146. END
  1147. ELSE INC(m); INC(n)
  1148. END
  1149. END;
  1150. IF (mask[m] = 0X) & ((name[n] = 0X) OR (om=-1)) THEN EXIT END
  1151. END;
  1152. RETURN f & (name[n] = 0X)
  1153. END Match;
  1154. PROCEDURE enumerate(fs: Files.FileSystem; VAR mask: ARRAY OF CHAR; dpg: DiskAdr; flags: SET; enum: Files.Enumerator; VAR continue: BOOLEAN; VAR fh: FileHeader; VAR fn: ARRAY OF CHAR);
  1155. VAR i, diff: LONGINT; dpg1: DiskAdr; a: DirPage; time, date, size: LONGINT;
  1156. BEGIN
  1157. GetSector(fs.vol, dpg, a); i := 0;
  1158. WHILE (i < a.m) & continue DO
  1159. (* MatchPrefix(mask, a.e[i].name, pos, diff); *)
  1160. IF i = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[i-1].p END;
  1161. IF diff >= 0 THEN (* matching prefix *)
  1162. IF dpg1 # 0 THEN enumerate(fs, mask, dpg1, flags, enum, continue, fh, fn) END;
  1163. IF diff = 0 THEN
  1164. IF continue & ((mask = "") OR Match(mask, a.e[i].name)) THEN
  1165. time := 0; date := 0; size := 0;
  1166. IF flags * {Files.EnumTime, Files.EnumSize} # {} THEN
  1167. GetSector(fs.vol, a.e[i].adr, fh);
  1168. IF Files.EnumTime IN flags THEN
  1169. time := fh.time; date := fh.date
  1170. END;
  1171. IF Files.EnumSize IN flags THEN
  1172. size := fh.aleng*SectorSize + fh.bleng - HeaderSize
  1173. END
  1174. END;
  1175. Files.JoinName(fs.prefix, a.e[i].name, fn);
  1176. enum.PutEntry(fn, {}, time, date, size)
  1177. END
  1178. ELSE continue := FALSE
  1179. END
  1180. END;
  1181. INC(i)
  1182. END;
  1183. IF continue & (i > 0) & (a.e[i-1].p # 0) THEN
  1184. enumerate(fs, mask, a.e[i-1].p, flags, enum, continue, fh, fn)
  1185. END
  1186. END enumerate;
  1187. (* Check a file name. *)
  1188. PROCEDURE Check(VAR s: ARRAY OF CHAR; VAR name: FileName; VAR res: WORD);
  1189. VAR i, k: LONGINT; ch: CHAR;
  1190. BEGIN
  1191. ch := s[0]; i := 0; k := 0;
  1192. IF (ch = 0X) THEN name[0] := 0X; res := -1
  1193. ELSE
  1194. IF (ch = Files.PathDelimiter) THEN k := 1; ch := s[k] END; (* skip first path delimiter *)
  1195. LOOP
  1196. IF (ch < " ") OR (ch = ":") OR (ch = Files.PathDelimiter) THEN res := 3; EXIT END;
  1197. name[i] := ch; INC(i); INC(k); ch := s[k];
  1198. IF (ch = 0X) THEN
  1199. WHILE (i < FileNameLength) DO name[i] := 0X; INC(i) END;
  1200. res := 0; EXIT
  1201. END;
  1202. IF (i = FileNameLength-1) THEN res := 4; EXIT END
  1203. END
  1204. END
  1205. END Check;
  1206. PROCEDURE UpdateHeader(f: File; VAR h: FileHeader);
  1207. BEGIN
  1208. h.aleng := f.aleng; h.bleng := f.bleng;
  1209. h.sec := f.sec;
  1210. IF f.ext # NIL THEN h.ext := f.ext.adr ELSE h.ext := 0 END;
  1211. h.date := f.date; h.time := f.time
  1212. END UpdateHeader;
  1213. PROCEDURE ReadBuf(f: File; buf: Buffer; pos: LONGINT);
  1214. VAR sec: DiskAdr; xpos: LONGINT;
  1215. BEGIN
  1216. IF pos < SectorTableSize THEN
  1217. sec := f.sec[pos]
  1218. ELSE
  1219. xpos := pos-SectorTableSize;
  1220. sec := f.ext.sub[xpos DIV IndexSize].sec.x[xpos MOD IndexSize]
  1221. END;
  1222. GetSector(f.fs.vol, sec, buf.data);
  1223. IF pos < f.aleng THEN buf.lim := SectorSize ELSE buf.lim := f.bleng END;
  1224. buf.apos := pos; buf.mod := FALSE
  1225. END ReadBuf;
  1226. PROCEDURE NewSuper(f: File);
  1227. VAR i: LONGINT; super: SuperIndex;
  1228. BEGIN
  1229. NEW(super); super.adr := 0; super.mod := TRUE; f.modH := TRUE; f.ext := super;
  1230. FOR i := 0 TO IndexSize-1 DO super.sub[i] := NIL END
  1231. END NewSuper;
  1232. PROCEDURE WriteBuf(f: File; buf: Buffer);
  1233. VAR i, k, xpos: LONGINT; secadr: DiskAdr; super: SuperIndex; sub: SubIndex; vol: Files.Volume;
  1234. BEGIN
  1235. vol := f.fs.vol;
  1236. Clock.Get(f.time, f.date); f.modH := TRUE;
  1237. IF buf.apos < SectorTableSize THEN
  1238. secadr := f.sec[buf.apos];
  1239. IF secadr = 0 THEN
  1240. AllocSector(vol, f.sechint, secadr);
  1241. f.modH := TRUE; f.sec[buf.apos] := secadr; f.sechint := secadr
  1242. END;
  1243. IF buf.apos = 0 THEN
  1244. UpdateHeader(f, SYSTEM.VAL(FileHeader, buf.data)); f.modH := FALSE
  1245. END
  1246. ELSE
  1247. super := f.ext;
  1248. IF super = NIL THEN NewSuper(f); super := f.ext END;
  1249. xpos := buf.apos-SectorTableSize;
  1250. i := xpos DIV IndexSize; sub := super.sub[i];
  1251. IF sub = NIL THEN
  1252. NEW(sub); sub.adr := 0; sub.sec.x[0] := 0; super.sub[i] := sub; super.mod := TRUE
  1253. END;
  1254. k := xpos MOD IndexSize; secadr := sub.sec.x[k];
  1255. IF secadr = 0 THEN
  1256. AllocSector(vol, f.sechint, secadr); f.sechint := secadr;
  1257. sub.mod := TRUE; sub.sec.x[k] := secadr
  1258. END
  1259. END;
  1260. PutSector(vol, secadr, buf.data); buf.mod := FALSE
  1261. END WriteBuf;
  1262. PROCEDURE SearchBuf(f: File; pos: LONGINT): Buffer;
  1263. VAR buf: Buffer;
  1264. BEGIN
  1265. buf := f.firstbuf;
  1266. LOOP
  1267. IF buf.apos = pos THEN EXIT END;
  1268. buf := buf.next;
  1269. IF buf = f.firstbuf THEN buf := NIL; EXIT END
  1270. END;
  1271. RETURN buf
  1272. END SearchBuf;
  1273. PROCEDURE GetBuf(f: File; pos: LONGINT): Buffer;
  1274. VAR buf: Buffer;
  1275. BEGIN
  1276. buf := f.firstbuf;
  1277. LOOP
  1278. IF buf.apos = pos THEN EXIT END;
  1279. IF buf.next = f.firstbuf THEN
  1280. IF f.nofbufs < MaxBufs THEN (* allocate new buffer *)
  1281. NEW(buf); buf.next := f.firstbuf.next; f.firstbuf.next := buf;
  1282. INC(f.nofbufs)
  1283. ELSE (* take one of the buffers *)
  1284. f.firstbuf := buf;
  1285. IF buf.mod THEN WriteBuf(f, buf) END
  1286. END;
  1287. buf.apos := pos;
  1288. IF pos <= f.aleng THEN ReadBuf(f, buf, pos) END;
  1289. EXIT
  1290. END;
  1291. buf := buf.next
  1292. END;
  1293. RETURN buf
  1294. END GetBuf;
  1295. PROCEDURE Unbuffer(f: File);
  1296. VAR
  1297. i, k: LONGINT; buf: Buffer; super: SuperIndex; sub: SubIndex; head: FileHeader;
  1298. sec: IndexSector; vol: Files.Volume;
  1299. BEGIN
  1300. vol := f.fs.vol;
  1301. buf := f.firstbuf;
  1302. REPEAT
  1303. IF buf.mod THEN WriteBuf(f, buf) END;
  1304. buf := buf.next
  1305. UNTIL buf = f.firstbuf;
  1306. super := f.ext;
  1307. IF super # NIL THEN
  1308. k := (f.aleng + (IndexSize-SectorTableSize)) DIV IndexSize; i := 0;
  1309. WHILE i # k DO
  1310. sub := super.sub[i]; INC(i);
  1311. IF sub.mod THEN
  1312. IF sub.adr = 0 THEN
  1313. AllocSector(vol, f.sechint, sub.adr); f.sechint := sub.adr;
  1314. super.mod := TRUE
  1315. END;
  1316. PutSector(vol, sub.adr, sub.sec); sub.mod := FALSE
  1317. END
  1318. END;
  1319. IF super.mod THEN
  1320. IF super.adr = 0 THEN
  1321. AllocSector(vol, f.sechint, super.adr); f.sechint := super.adr;
  1322. f.modH := TRUE
  1323. END;
  1324. i := 0;
  1325. WHILE i # k DO sec.x[i] := super.sub[i].adr; INC(i) END;
  1326. WHILE i # IndexSize DO sec.x[i] := 0; INC(i) END;
  1327. PutSector(vol, super.adr, sec); super.mod := FALSE
  1328. END
  1329. END;
  1330. IF f.modH THEN
  1331. GetSector(vol, f.sec[0], head); UpdateHeader(f, head);
  1332. PutSector(vol, f.sec[0], head); f.modH := FALSE
  1333. END
  1334. END Unbuffer;
  1335. PROCEDURE NewSub(f: File);
  1336. VAR i, k: LONGINT; sub: SubIndex;
  1337. BEGIN
  1338. k := (f.aleng - SectorTableSize) DIV IndexSize;
  1339. IF k = IndexSize THEN SYSTEM.HALT(18) END;
  1340. NEW(sub); sub.adr := 0; sub.mod := TRUE;
  1341. FOR i := 0 TO IndexSize-1 DO sub.sec.x[i] := 0 END;
  1342. IF f.ext = NIL THEN NewSuper(f) END;
  1343. f.ext.sub[k] := sub
  1344. END NewSub;
  1345. (** Generate a new file system object. Files.NewVol has volume parameter, Files.Par has mount prefix. *)
  1346. PROCEDURE NewFS*(context : Files.Parameters);
  1347. VAR fs: FileSystem; fh: FileHeader; skipIndexMapWriteback: BOOLEAN; options: ARRAY 8 OF CHAR;
  1348. BEGIN
  1349. (* Get options *)
  1350. context.arg.SkipWhitespace;
  1351. REPEAT UNTIL ~context.arg.GetString(options) OR (options = '|');
  1352. IF context.arg.GetString(options) THEN
  1353. skipIndexMapWriteback := options = 'N'
  1354. END;
  1355. IF Files.This(context.prefix) = NIL THEN
  1356. IF (context.vol.blockSize = SectorSize) & (context.vol.size >= MinVolSize) THEN
  1357. GetSector(context.vol, DirRootAdr, fh);
  1358. IF fh.mark = DirMark THEN (* assume it is an Aos filesystem *)
  1359. NEW(fs); fs.vol := context.vol;
  1360. ASSERT(context.vol.size < MAX(LONGINT) DIV SectorFactor);
  1361. fs.desc := "AosFS";
  1362. NEW(fs.dir, context.vol); (* initialize directory and volume *)
  1363. ASSERT(fs.dir.state = Opened); (* will have to undo changes to vol before continuing *)
  1364. Files.Add(fs, context.prefix);
  1365. IF skipIndexMapWriteback THEN
  1366. INCL(fs.flags, SkipIndexFlag);
  1367. fs.dir.noCleanup := TRUE
  1368. END
  1369. ELSE
  1370. context.error.String("DiskFS: File system not found on ");
  1371. context.error.String(context.vol.name); context.error.Ln
  1372. END
  1373. ELSE
  1374. context.error.String("DiskFS: Bad volume size"); context.error.Ln
  1375. END
  1376. ELSE
  1377. context.error.String("DiskFS: "); context.error.String(context.prefix);
  1378. context.error.String(" already in use"); context.error.Ln
  1379. END;
  1380. END NewFS;
  1381. (* Clean up when module unloaded. *)
  1382. PROCEDURE Cleanup;
  1383. VAR ft: Files.FileSystemTable; i: LONGINT;
  1384. BEGIN
  1385. IF Modules.shutdown = Modules.None THEN
  1386. Files.GetList(ft);
  1387. IF ft # NIL THEN
  1388. FOR i := 0 TO LEN(ft^)-1 DO
  1389. IF ft[i] IS FileSystem THEN Files.Remove(ft[i]) END
  1390. END
  1391. END
  1392. END
  1393. END Cleanup;
  1394. BEGIN
  1395. ASSERT((SIZEOF(FileHeader) = SectorSize) & (SIZEOF(IndexSector) = SectorSize) & (SIZEOF(DataSector) = SectorSize) &
  1396. (SIZEOF(DirPage) = SectorSize) & (SIZEOF(MapIndex) = SectorSize) & (SIZEOF(MapSector) = SectorSize) &
  1397. (DirPgSize MOD 2 = 0));
  1398. Modules.InstallTermHandler(Cleanup);
  1399. END DiskFS.
  1400. (*
  1401. aleng * SectorSize + bleng = length (including header)
  1402. apos * SectorSize + bpos = current position
  1403. 0 <= bpos <= lim <= SectorSize
  1404. 0 <= apos <= aleng < SectorTableSize + IndexSize*IndexSize
  1405. (apos < aleng) & (lim = SectorSize) OR (apos = aleng)
  1406. Methods with {} notation are explicitly unprotected. They must be called only from a protected context.
  1407. *)
  1408. (*
  1409. 04.02.2004 lb Prevent disk space leaks during system run (disk GC)
  1410. 03.01.2006 staubesv Avoid longint overflow that caused disk gc even if not necessary
  1411. *)