OberonFS.Mod 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE OberonFS; (** AUTHOR "pjm"; PURPOSE "Native Oberon file system"; *)
  3. IMPORT SYSTEM, Machine, KernelLog, Modules, Clock, Files;
  4. CONST
  5. MinVolSize = 4;
  6. SF = 29; (* SectorFactor *)
  7. FnLength = 32; (* includes 0X *)
  8. STS = 64; (* SecTabSize *)
  9. ETS = 12; (* ExTabSize *)
  10. SS = 2048; (* SectorSize *)
  11. XS = SS DIV 4; (* IndexSize *)
  12. HS = 352; (* HeaderSize *)
  13. DirRootAdr = 1*SF;
  14. DirPgSize = 50;
  15. N = DirPgSize DIV 2;
  16. DirMark = LONGINT(9B1EA38DH);
  17. HeaderMark = LONGINT(9BA71D86H);
  18. FillerSize = 36;
  19. MapIndexSize = (SS-4) DIV 4;
  20. MapSize = SS DIV SIZEOF (SET); (* {MapSize MOD 8*SIZEOF(SET) = 0} *)
  21. MapMark = LONGINT(9C2F977FH);
  22. MaxBufs = 4;
  23. InitHint = 200*SF;
  24. Closed = 0X; Opening = 1X; Opened = 2X; Closing = 3X;
  25. SetSize = MAX (SET) + 1;
  26. TYPE
  27. DiskSector = RECORD END; (* Oberon Sector, size SS *)
  28. DiskSectorArr = ARRAY SS OF CHAR;
  29. DiskAdr = LONGINT;
  30. FileName = ARRAY FnLength OF CHAR;
  31. SectorTable = ARRAY STS OF DiskAdr;
  32. ExtensionTable = ARRAY ETS OF DiskAdr;
  33. FileHeader = RECORD (DiskSector) (* allocated in the first page of each file on disk *)
  34. mark: LONGINT;
  35. name: FileName;
  36. aleng, bleng: INTEGER;
  37. date, time: LONGINT;
  38. ext: ExtensionTable;
  39. sec: SectorTable;
  40. data: ARRAY SS-HS OF CHAR
  41. END;
  42. IndexSector = RECORD (DiskSector)
  43. x: ARRAY XS OF DiskAdr
  44. END;
  45. DataSector = RECORD (DiskSector)
  46. B: ARRAY SS OF CHAR
  47. END;
  48. DirEntry = RECORD (*B-tree node*)
  49. name: FileName;
  50. adr: DiskAdr; (*sec no of file header*)
  51. p: DiskAdr (*sec no of descendant in directory*)
  52. END;
  53. DirPage = RECORD (DiskSector)
  54. mark: LONGINT;
  55. m: INTEGER;
  56. p0: DiskAdr; (*sec no of left descendant in directory*)
  57. fill: ARRAY FillerSize OF CHAR;
  58. e: ARRAY DirPgSize OF DirEntry
  59. END;
  60. MapIndex = RECORD (DiskSector)
  61. mark: LONGINT;
  62. index: ARRAY MapIndexSize OF DiskAdr
  63. END;
  64. MapSector = RECORD (DiskSector)
  65. map: ARRAY MapSize OF SET
  66. END;
  67. FileHd = POINTER TO FileHeader;
  68. Buffer = POINTER TO RECORD (Files.Hint)
  69. apos, lim: LONGINT;
  70. mod: BOOLEAN;
  71. next: Buffer;
  72. data: DataSector
  73. END;
  74. Index = POINTER TO RECORD
  75. adr: DiskAdr;
  76. mod: BOOLEAN;
  77. sec: IndexSector
  78. END;
  79. TYPE
  80. Directory = OBJECT
  81. VAR
  82. vol: Files.Volume;
  83. state: CHAR;
  84. lastSectorReserved: BOOLEAN;
  85. (* "exported" methods: Search, Insert, Delete *)
  86. PROCEDURE Search(VAR name: FileName; VAR A: DiskAdr);
  87. VAR i, L, R: LONGINT; dadr: DiskAdr; a: DirPage;
  88. BEGIN {EXCLUSIVE}
  89. ASSERT(state = Opened);
  90. dadr := DirRootAdr;
  91. LOOP
  92. GetSector(vol, dadr, a);
  93. ASSERT(a.mark = DirMark);
  94. L := 0; R := a.m; (*binary search*)
  95. WHILE L < R DO
  96. i := (L+R) DIV 2;
  97. IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
  98. END ;
  99. IF (R < a.m) & (name = a.e[R].name) THEN
  100. A := a.e[R].adr; EXIT (*found*)
  101. END ;
  102. IF R = 0 THEN dadr := a.p0 ELSE dadr := a.e[R-1].p END ;
  103. IF dadr = 0 THEN A := 0; EXIT (*not found*) END
  104. END
  105. END Search;
  106. PROCEDURE insert(VAR name: FileName; dpg0: DiskAdr; VAR h: BOOLEAN; VAR v: DirEntry; fad: DiskAdr);
  107. (*h = "tree has become higher and v is ascending element"*)
  108. VAR ch: CHAR; i, j, L, R: LONGINT; dpg1: DiskAdr; u: DirEntry; a: DirPage;
  109. BEGIN (*~h*)
  110. ASSERT(state = Opened);
  111. GetSector(vol, dpg0, a);
  112. L := 0; R := a.m; (*binary search*)
  113. WHILE L < R DO
  114. i := (L+R) DIV 2;
  115. IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
  116. END ;
  117. IF (R < a.m) & (name = a.e[R].name) THEN
  118. a.e[R].adr := fad; PutSector(vol, dpg0, a) (*replace*)
  119. ELSE (*not on this page*)
  120. IF R = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[R-1].p END ;
  121. IF dpg1 = 0 THEN (*not in tree, insert*)
  122. u.adr := fad; u.p := 0; h := TRUE; j := 0;
  123. REPEAT ch := name[j]; u.name[j] := ch; INC(j)
  124. UNTIL ch = 0X;
  125. WHILE j < FnLength DO u.name[j] := 0X; INC(j) END
  126. ELSE
  127. insert(name, dpg1, h, u, fad)
  128. END ;
  129. IF h THEN (*insert u to the left of e[R]*)
  130. IF a.m < DirPgSize THEN
  131. h := FALSE; i := a.m;
  132. WHILE i > R DO DEC(i); a.e[i+1] := a.e[i] END ;
  133. a.e[R] := u; INC(a.m)
  134. ELSE (*split page and assign the middle element to v*)
  135. a.m := N; a.mark := DirMark;
  136. IF R < N THEN (*insert in left half*)
  137. v := a.e[N-1]; i := N-1;
  138. WHILE i > R DO DEC(i); a.e[i+1] := a.e[i] END ;
  139. a.e[R] := u; PutSector(vol, dpg0, a);
  140. AllocSector(vol, dpg0, dpg0); i := 0;
  141. WHILE i < N DO a.e[i] := a.e[i+N]; INC(i) END
  142. ELSE (*insert in right half*)
  143. PutSector(vol, dpg0, a);
  144. AllocSector(vol, dpg0, dpg0); DEC(R, N); i := 0;
  145. IF R = 0 THEN v := u
  146. ELSE v := a.e[N];
  147. WHILE i < R-1 DO a.e[i] := a.e[N+1+i]; INC(i) END ;
  148. a.e[i] := u; INC(i)
  149. END ;
  150. WHILE i < N DO a.e[i] := a.e[N+i]; INC(i) END
  151. END ;
  152. a.p0 := v.p; v.p := dpg0
  153. END ;
  154. PutSector(vol, dpg0, a)
  155. END
  156. END
  157. END insert;
  158. PROCEDURE Insert(VAR name: FileName; fad: DiskAdr);
  159. VAR oldroot: DiskAdr; h: BOOLEAN; U: DirEntry; a: DirPage;
  160. BEGIN {EXCLUSIVE}
  161. h := FALSE;
  162. insert(name, DirRootAdr, h, U, fad);
  163. IF h THEN (*root overflow*)
  164. GetSector(vol, DirRootAdr, a);
  165. AllocSector(vol, DirRootAdr, oldroot); PutSector(vol, oldroot, a);
  166. a.mark := DirMark; a.m := 1; a.p0 := oldroot; a.e[0] := U;
  167. PutSector(vol, DirRootAdr, a)
  168. END
  169. END Insert;
  170. PROCEDURE underflow(VAR c: DirPage; (*ancestor page*) dpg0: DiskAdr; s: LONGINT; (*insertion point in c*)
  171. VAR h: BOOLEAN); (*c undersize*)
  172. VAR i, k: LONGINT; dpg1: DiskAdr; a, b: DirPage; (*a := underflowing page, b := neighbouring page*)
  173. BEGIN
  174. GetSector(vol, dpg0, a);
  175. (*h & a.m = N-1 & dpg0 = c.e[s-1].p*)
  176. IF s < c.m THEN (*b := page to the right of a*)
  177. dpg1 := c.e[s].p; GetSector(vol, dpg1, b);
  178. k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
  179. a.e[N-1] := c.e[s]; a.e[N-1].p := b.p0;
  180. IF k > 0 THEN
  181. (*move k-1 items from b to a, one to c*) i := 0;
  182. WHILE i < k-1 DO a.e[i+N] := b.e[i]; INC(i) END ;
  183. c.e[s] := b.e[i]; b.p0 := c.e[s].p;
  184. c.e[s].p := dpg1; DEC(b.m, SHORT(k)); i := 0;
  185. WHILE i < b.m DO b.e[i] := b.e[i+k]; INC(i) END ;
  186. PutSector(vol, dpg1, b); a.m := SHORT(N-1+k); h := FALSE
  187. ELSE (*merge pages a and b, discard b*) i := 0;
  188. WHILE i < N DO a.e[i+N] := b.e[i]; INC(i) END ;
  189. i := s; DEC(c.m);
  190. WHILE i < c.m DO c.e[i] := c.e[i+1]; INC(i) END ;
  191. a.m := 2*N; h := c.m < N
  192. END ;
  193. PutSector(vol, dpg0, a)
  194. ELSE (*b := page to the left of a*) DEC(s);
  195. IF s = 0 THEN dpg1 := c.p0 ELSE dpg1 := c.e[s-1].p END ;
  196. GetSector(vol, dpg1, b);
  197. k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
  198. IF k > 0 THEN
  199. i := N-1;
  200. WHILE i > 0 DO DEC(i); a.e[i+k] := a.e[i] END ;
  201. i := k-1; a.e[i] := c.e[s]; a.e[i].p := a.p0;
  202. (*move k-1 items from b to a, one to c*) DEC(b.m, SHORT(k));
  203. WHILE i > 0 DO DEC(i); a.e[i] := b.e[i+b.m+1] END ;
  204. c.e[s] := b.e[b.m]; a.p0 := c.e[s].p;
  205. c.e[s].p := dpg0; a.m := SHORT(N-1+k); h := FALSE;
  206. PutSector(vol, dpg0, a)
  207. ELSE (*merge pages a and b, discard a*)
  208. c.e[s].p := a.p0; b.e[N] := c.e[s]; i := 0;
  209. WHILE i < N-1 DO b.e[i+N+1] := a.e[i]; INC(i) END ;
  210. b.m := 2*N; DEC(c.m); h := c.m < N
  211. END ;
  212. PutSector(vol, dpg1, b)
  213. END
  214. END underflow;
  215. PROCEDURE delete(VAR name: FileName; dpg0: DiskAdr; VAR h: BOOLEAN; VAR fad: DiskAdr);
  216. (*search and delete entry with key name; if a page underflow arises,
  217. balance with adjacent page or merge; h := "page dpg0 is undersize"*)
  218. VAR i, L, R: LONGINT; dpg1: DiskAdr; a: DirPage;
  219. PROCEDURE del(dpg1: DiskAdr; VAR h: BOOLEAN);
  220. VAR dpg2: DiskAdr; (*global: a, R*) b: DirPage;
  221. BEGIN
  222. GetSector(vol, dpg1, b); dpg2 := b.e[b.m-1].p;
  223. IF dpg2 # 0 THEN del(dpg2, h);
  224. IF h THEN underflow(b, dpg2, b.m, h); PutSector(vol, dpg1, b) END
  225. ELSE
  226. b.e[b.m-1].p := a.e[R].p; a.e[R] := b.e[b.m-1];
  227. DEC(b.m); h := b.m < N; PutSector(vol, dpg1, b)
  228. END
  229. END del;
  230. BEGIN (*~h*)
  231. ASSERT(state = Opened);
  232. GetSector(vol, dpg0, a);
  233. L := 0; R := a.m; (*binary search*)
  234. WHILE L < R DO
  235. i := (L+R) DIV 2;
  236. IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
  237. END ;
  238. IF R = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[R-1].p END ;
  239. IF (R < a.m) & (name = a.e[R].name) THEN
  240. (*found, now delete*) fad := a.e[R].adr;
  241. IF dpg1 = 0 THEN (*a is a leaf page*)
  242. DEC(a.m); h := a.m < N; i := R;
  243. WHILE i < a.m DO a.e[i] := a.e[i+1]; INC(i) END
  244. ELSE del(dpg1, h);
  245. IF h THEN underflow(a, dpg1, R, h) END
  246. END ;
  247. PutSector(vol, dpg0, a)
  248. ELSIF dpg1 # 0 THEN
  249. delete(name, dpg1, h, fad);
  250. IF h THEN underflow(a, dpg1, R, h); PutSector(vol, dpg0, a) END
  251. ELSE (*not in tree*) fad := 0
  252. END
  253. END delete;
  254. PROCEDURE Delete(VAR name: FileName; VAR fad: DiskAdr);
  255. VAR h: BOOLEAN; newroot: DiskAdr; a: DirPage;
  256. BEGIN {EXCLUSIVE}
  257. h := FALSE;
  258. delete(name, DirRootAdr, h, fad);
  259. IF h THEN (*root underflow*)
  260. GetSector(vol, DirRootAdr, a);
  261. IF (a.m = 0) & (a.p0 # 0) THEN
  262. newroot := a.p0; GetSector(vol, newroot, a);
  263. PutSector(vol, DirRootAdr, a) (*discard newroot*)
  264. END
  265. END
  266. END Delete;
  267. PROCEDURE Startup;
  268. VAR
  269. j, sec, size, q, free, thres: LONGINT; mi: MapIndex; ms: MapSector;
  270. s: ARRAY 10 OF CHAR; found: BOOLEAN;
  271. BEGIN (* only called from Init *)
  272. size := vol.size; found := FALSE;
  273. IF (vol.Available() = size) & (size # 0) THEN (* all sectors available *)
  274. GetSector(vol, size*SF, mi);
  275. IF mi.mark = MapMark THEN
  276. j := 0; (* check consistency of index *)
  277. WHILE (j # MapIndexSize) & (mi.index[j] >= 0) & (mi.index[j] MOD SF = 0) DO
  278. INC(j)
  279. END;
  280. IF j = MapIndexSize THEN
  281. found := TRUE;
  282. mi.mark := 0; PutSector(vol, size*SF, mi); (* invalidate index *)
  283. j := 0; sec := 1; q := 0;
  284. LOOP
  285. IF (j = MapIndexSize) OR (mi.index[j] = 0) THEN EXIT END;
  286. GetSector(vol, mi.index[j], ms);
  287. REPEAT
  288. IF (sec MOD SetSize) IN ms.map[sec DIV SetSize MOD MapSize] THEN
  289. MarkSector(vol, sec*SF);
  290. INC(q)
  291. END;
  292. IF sec = size THEN EXIT END;
  293. INC(sec)
  294. UNTIL sec MOD (MapSize*SetSize) = 0;
  295. INC(j)
  296. END;
  297. Machine.GetConfig("DiskGC", s);
  298. thres := 0; j := 0;
  299. WHILE s[j] # 0X DO thres := thres*10+(ORD(s[j])-48); INC(j) END;
  300. IF thres < 10 THEN thres := 10
  301. ELSIF thres > 100 THEN thres := 100
  302. END;
  303. ASSERT(q = size-vol.Available());
  304. free := vol.Available()*100 DIV size;
  305. IF (free > thres) & (vol.Available()*SS > 100000H) THEN
  306. state := Opened
  307. ELSE (* undo *)
  308. FOR j := SF TO size*SF BY SF DO
  309. IF Marked(vol, j) THEN FreeSector(vol, j) END
  310. END;
  311. ASSERT(vol.Available() = size);
  312. KernelLog.String("OberonFS: "); KernelLog.Int(free, 1);
  313. KernelLog.String("% free, forcing disk GC on ");
  314. KernelLog.String(vol.name); KernelLog.Ln
  315. END
  316. END
  317. END;
  318. IF ~found THEN
  319. KernelLog.String("OberonFS: Index not found on ");
  320. KernelLog.String(vol.name); KernelLog.Ln
  321. END
  322. END
  323. END Startup;
  324. PROCEDURE &Init*(vol: Files.Volume);
  325. VAR k: LONGINT; A: ARRAY 2000 OF DiskAdr; files: LONGINT; bad: BOOLEAN;
  326. PROCEDURE MarkSectors;
  327. VAR L, R, i, j, n: LONGINT; x: DiskAdr; hd: FileHeader; B: IndexSector;
  328. PROCEDURE sift(L, R: LONGINT);
  329. VAR i, j: LONGINT; x: DiskAdr;
  330. BEGIN j := L; x := A[j];
  331. LOOP i := j; j := 2*j + 1;
  332. IF (j+1 < R) & (A[j] < A[j+1]) THEN INC(j) END ;
  333. IF (j >= R) OR (x > A[j]) THEN EXIT END ;
  334. A[i] := A[j]
  335. END ;
  336. A[i] := x
  337. END sift;
  338. BEGIN
  339. KernelLog.String(" marking");
  340. L := k DIV 2; R := k; (*heapsort*)
  341. WHILE L > 0 DO DEC(L); sift(L, R) END ;
  342. WHILE R > 0 DO
  343. DEC(R); x := A[0]; A[0] := A[R]; A[R] := x; sift(L, R)
  344. END;
  345. WHILE L < k DO
  346. bad := FALSE; INC(files);
  347. IF files MOD 128 = 0 THEN KernelLog.Char(".") END;
  348. GetSector(vol, A[L], hd);
  349. IF hd.aleng < STS THEN
  350. j := hd.aleng + 1;
  351. REPEAT
  352. DEC(j);
  353. IF hd.sec[j] # 0 THEN MarkSector(vol, hd.sec[j])
  354. ELSE hd.aleng := SHORT(j-1); bad := TRUE
  355. END
  356. UNTIL j = 0
  357. ELSE
  358. j := STS;
  359. REPEAT
  360. DEC(j);
  361. IF hd.sec[j] # 0 THEN MarkSector(vol, hd.sec[j])
  362. ELSE hd.aleng := SHORT(j-1); bad := TRUE
  363. END
  364. UNTIL j = 0;
  365. n := (hd.aleng - STS) DIV XS; i := 0;
  366. WHILE (i <= n) & ~bad DO
  367. IF hd.ext[i] # 0 THEN
  368. MarkSector(vol, hd.ext[i]); GetSector(vol, hd.ext[i], B);
  369. IF i < n THEN j := XS
  370. ELSE j := (hd.aleng - STS) MOD XS + 1
  371. END;
  372. REPEAT
  373. DEC(j);
  374. IF (B.x[j] MOD SF = 0) & (B.x[j] > 0) THEN
  375. MarkSector(vol, B.x[j])
  376. ELSE
  377. bad := TRUE
  378. END
  379. UNTIL j = 0;
  380. INC(i)
  381. ELSE bad := TRUE
  382. END;
  383. IF bad THEN
  384. IF i = 0 THEN hd.aleng := STS-1
  385. ELSE hd.aleng := SHORT(STS + (i-1) * XS)
  386. END
  387. END
  388. END
  389. END;
  390. IF bad THEN
  391. KernelLog.Ln; KernelLog.String(hd.name); KernelLog.String(" truncated");
  392. hd.bleng := SS; IF hd.aleng < 0 THEN hd.aleng := 0 (* really bad *) END;
  393. PutSector(vol, A[L], hd)
  394. END;
  395. INC(L)
  396. END
  397. END MarkSectors;
  398. PROCEDURE TraverseDir(dpg: DiskAdr);
  399. VAR i: LONGINT; a: DirPage;
  400. BEGIN
  401. GetSector(vol, dpg, a); MarkSector(vol, dpg); i := 0;
  402. WHILE i < a.m DO
  403. A[k] := a.e[i].adr; INC(k); INC(i);
  404. IF k = 2000 THEN MarkSectors; k := 0 END
  405. END ;
  406. IF a.p0 # 0 THEN
  407. TraverseDir(a.p0); i := 0;
  408. WHILE i < a.m DO
  409. TraverseDir(a.e[i].p); INC(i)
  410. END
  411. END
  412. END TraverseDir;
  413. BEGIN
  414. SELF.vol := vol; lastSectorReserved := FALSE;
  415. IF ~(Files.ReadOnly IN vol.flags) THEN
  416. state := Opening; k := 0;
  417. Startup;
  418. IF state # Opened THEN
  419. files := 0; KernelLog.String("OberonFS: Scanning ");
  420. KernelLog.String(vol.name); KernelLog.String("...");
  421. TraverseDir(DirRootAdr); MarkSectors;
  422. KernelLog.Int(files, 6); KernelLog.String(" files"); KernelLog.Ln;
  423. state := Opened
  424. END;
  425. IF ~Marked(vol, vol.size*SF) THEN (* last sector still free *)
  426. MarkSector(vol, vol.size*SF); lastSectorReserved := TRUE (* allocate it *)
  427. END;
  428. KernelLog.String("OberonFS: "); KernelLog.Int(vol.Available() * (SS DIV 1024), 1);
  429. KernelLog.String("K of "); KernelLog.Int(vol.size * (SS DIV 1024), 1);
  430. KernelLog.String("K available on "); KernelLog.String(vol.name);
  431. KernelLog.Ln
  432. ELSE
  433. state := Opened
  434. END
  435. END Init;
  436. PROCEDURE Cleanup;
  437. VAR i, j, p, q, sec, size: LONGINT; mi: MapIndex; ms: MapSector;
  438. BEGIN {EXCLUSIVE}
  439. state := Closing;
  440. size := vol.size; i := size*SF;
  441. IF ~(Files.ReadOnly IN vol.flags) THEN
  442. IF lastSectorReserved THEN FreeSector(vol, i); lastSectorReserved := FALSE END;
  443. IF ~Marked(vol, i) THEN (* last sector is available for us *)
  444. j := 0; sec := 1; q := 0;
  445. LOOP
  446. REPEAT DEC(i, SF) UNTIL (i = 0) OR ~Marked(vol, i); (* find a free sector *)
  447. IF i = 0 THEN RETURN END; (* no more space, don't commit *)
  448. mi.index[j] := i; INC(j);
  449. FOR p := 0 TO MapSize-1 DO ms.map[p] := {} END;
  450. REPEAT
  451. IF Marked(vol, sec*SF) THEN
  452. INCL(ms.map[sec DIV SetSize MOD MapSize], sec MOD SetSize);
  453. INC(q)
  454. END;
  455. IF sec = size THEN
  456. PutSector(vol, i, ms);
  457. EXIT
  458. END;
  459. INC(sec)
  460. UNTIL sec MOD (MapSize*SetSize) = 0;
  461. PutSector(vol, i, ms)
  462. END;
  463. WHILE j # MapIndexSize DO mi.index[j] := 0; INC(j) END;
  464. mi.mark := MapMark;
  465. PutSector(vol, size*SF, mi); (* commit *)
  466. KernelLog.String("OberonFS: Map saved on ");
  467. KernelLog.String(vol.name); KernelLog.Ln
  468. END
  469. END;
  470. state := Closed; vol := NIL
  471. END Cleanup;
  472. END Directory;
  473. TYPE
  474. FileSystem = OBJECT (Files.FileSystem) (* our file system type *)
  475. VAR dir: Directory;
  476. PROCEDURE New0*(name: ARRAY OF CHAR): Files.File;
  477. VAR i: LONGINT; res: WORD; f: File; buf: Buffer; head: FileHd; namebuf: FileName;
  478. BEGIN {EXCLUSIVE}
  479. f := NIL; Check(name, namebuf, res);
  480. IF res <= 0 THEN
  481. NEW(buf); buf.apos := 0; buf.mod := TRUE; buf.lim := HS; buf.next := buf;
  482. head := SYSTEM.VAL(FileHd, ADDRESSOF(buf.data));
  483. head.mark := HeaderMark;
  484. head.aleng := 0; head.bleng := HS; head.name := namebuf;
  485. Clock.Get(head.time, head.date);
  486. NEW(f); f.fs := SELF; f.key := 0; f.aleng := 0; f.bleng := HS; f.modH := TRUE;
  487. f.time := head.time; f.date := head.date;
  488. f.firstbuf := buf; f.nofbufs := 1; f.name := namebuf; f.sechint := InitHint;
  489. f.registered := (f.name[0] = 0X);
  490. i := 0; REPEAT f.ext[i] := NIL; head.ext[i] := 0; INC(i) UNTIL i = ETS;
  491. i := 0; REPEAT f.sec[i] := 0; head.sec[i] := 0; INC(i) UNTIL i = STS
  492. END;
  493. RETURN f
  494. END New0;
  495. PROCEDURE Old0*(name: ARRAY OF CHAR): Files.File;
  496. VAR
  497. i, k: LONGINT; res: WORD; f: File; header: DiskAdr; buf: Buffer; head: FileHd;
  498. namebuf: FileName; inxpg: Index;
  499. BEGIN {EXCLUSIVE}
  500. f := NIL; Check(name, namebuf, res);
  501. IF res = 0 THEN
  502. dir.Search(namebuf, header);
  503. IF header # 0 THEN
  504. NEW(buf); buf.apos := 0; buf.next := buf; buf.mod := FALSE;
  505. GetSector(vol, header, buf.data);
  506. head := SYSTEM.VAL(FileHd, ADDRESSOF(buf.data));
  507. NEW(f); f.fs := SELF; f.key := header;
  508. f.aleng := head.aleng; f.bleng := head.bleng;
  509. f.time := head.time; f.date := head.date;
  510. IF f.aleng = 0 THEN buf.lim := f.bleng ELSE buf.lim := SS END;
  511. f.firstbuf := buf; f.nofbufs := 1;
  512. f.name := namebuf; f.registered := TRUE;
  513. f.sec := head.sec;
  514. k := (f.aleng + (XS-STS)) DIV XS; i := 0;
  515. WHILE i < k DO
  516. NEW(inxpg); inxpg.adr := head.ext[i]; inxpg.mod := FALSE;
  517. GetSector(vol, inxpg.adr, inxpg.sec); f.ext[i] := inxpg; INC(i)
  518. END;
  519. WHILE i < ETS DO f.ext[i] := NIL; INC(i) END;
  520. f.sechint := header; f.modH := FALSE
  521. END
  522. END;
  523. RETURN f
  524. END Old0;
  525. PROCEDURE Delete0*(name: ARRAY OF CHAR; VAR key: LONGINT; VAR res: WORD);
  526. VAR adr: DiskAdr; namebuf: FileName; head: FileHeader;
  527. BEGIN {EXCLUSIVE}
  528. Check(name, namebuf, res);
  529. IF res = 0 THEN
  530. dir.Delete(namebuf, adr);
  531. key := adr;
  532. IF adr # 0 THEN
  533. GetSector(vol, adr, head);
  534. head.mark := HeaderMark+1; (* invalidate mark *)
  535. PutSector(vol, adr, head)
  536. ELSE
  537. res := 2
  538. END
  539. ELSE
  540. key := 0
  541. END
  542. END Delete0;
  543. PROCEDURE Rename0*(old, new: ARRAY OF CHAR; f: Files.File; VAR res: WORD);
  544. VAR adr: DiskAdr; oldbuf, newbuf: FileName; head: FileHeader;
  545. BEGIN {EXCLUSIVE}
  546. Check(old, oldbuf, res);
  547. IF res = 0 THEN
  548. Check(new, newbuf, res);
  549. IF res = 0 THEN
  550. dir.Delete(oldbuf, adr);
  551. IF adr # 0 THEN
  552. IF f # NIL THEN (* file is open *)
  553. ASSERT(f.key = adr); (* it's key must match *)
  554. f(File).name := newbuf
  555. END;
  556. dir.Insert(newbuf, adr);
  557. GetSector(vol, adr, head);
  558. head.name := newbuf;
  559. PutSector(vol, adr, head)
  560. ELSE res := 2
  561. END
  562. END
  563. END
  564. END Rename0;
  565. PROCEDURE Enumerate0*(mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator);
  566. VAR b: BOOLEAN; fh: FileHeader; fn: ARRAY Files.PrefixLength+FnLength OF CHAR;
  567. BEGIN {EXCLUSIVE}
  568. b := TRUE; enumerate(SELF, mask, DirRootAdr, flags, enum, b, fh, fn)
  569. END Enumerate0;
  570. PROCEDURE FileKey*(name: ARRAY OF CHAR): LONGINT;
  571. VAR res: WORD; namebuf: FileName; header: DiskAdr;
  572. BEGIN {EXCLUSIVE}
  573. header := 0;
  574. Check(name, namebuf, res);
  575. IF res = 0 THEN
  576. dir.Search(namebuf, header)
  577. END;
  578. RETURN header
  579. END FileKey;
  580. PROCEDURE Finalize*;
  581. BEGIN {EXCLUSIVE}
  582. dir.Cleanup();
  583. vol.Finalize;
  584. Finalize^ (* see note in Files *)
  585. END Finalize;
  586. END FileSystem;
  587. TYPE
  588. File = OBJECT (Files.File)
  589. VAR
  590. aleng, bleng: LONGINT;
  591. nofbufs: LONGINT;
  592. modH, registered: BOOLEAN;
  593. firstbuf: Buffer;
  594. sechint: DiskAdr;
  595. name: FileName;
  596. time, date: LONGINT;
  597. ext: ARRAY ETS OF Index;
  598. sec: SectorTable;
  599. PROCEDURE Set*(VAR r: Files.Rider; pos: LONGINT);
  600. VAR a, b: LONGINT;
  601. BEGIN {EXCLUSIVE}
  602. r.eof := FALSE; r.res := 0; r.file := SELF; r.fs := fs;
  603. IF pos < 0 THEN
  604. a := 0; b := HS
  605. ELSIF pos < aleng*SS + bleng - HS THEN
  606. a := (pos + HS) DIV SS; b := (pos + HS) MOD SS
  607. ELSE
  608. a := aleng; b := bleng
  609. END;
  610. r.apos := a; r.bpos := b; r.hint := firstbuf
  611. END Set;
  612. PROCEDURE Pos*(VAR r: Files.Rider): LONGINT;
  613. BEGIN
  614. RETURN r.apos*SS + r.bpos - HS
  615. END Pos;
  616. PROCEDURE Read*(VAR r: Files.Rider; VAR x: CHAR);
  617. VAR buf: Buffer;
  618. BEGIN {EXCLUSIVE}
  619. ASSERT(r.file = SELF);
  620. buf := r.hint(Buffer);
  621. IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
  622. IF r.bpos < buf.lim THEN
  623. x := buf.data.B[r.bpos]; INC(r.bpos)
  624. ELSIF r.apos < aleng THEN
  625. INC(r.apos);
  626. buf := SearchBuf(SELF, r.apos);
  627. IF buf = NIL THEN
  628. buf := r.hint(Buffer);
  629. IF buf.mod THEN WriteBuf(SELF, buf) END ;
  630. ReadBuf(SELF, buf, r.apos)
  631. ELSE
  632. r.hint := buf
  633. END;
  634. ASSERT(buf.lim > 0);
  635. x := buf.data.B[0]; r.bpos := 1
  636. ELSE
  637. x := 0X; r.eof := TRUE
  638. END
  639. END Read;
  640. PROCEDURE ReadBytes*(VAR r: Files.Rider; VAR x: ARRAY OF CHAR; ofs, len: LONGINT);
  641. VAR src: ADDRESS; m: LONGINT; buf: Buffer;
  642. BEGIN {EXCLUSIVE}
  643. ASSERT(r.file = SELF);
  644. IF LEN(x)-ofs < len THEN SYSTEM.HALT(19) END ;
  645. buf := r.hint(Buffer);
  646. IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
  647. LOOP
  648. IF len <= 0 THEN EXIT END ;
  649. src := ADDRESSOF(buf.data.B[0]) + r.bpos; m := r.bpos + len;
  650. IF m <= buf.lim THEN
  651. SYSTEM.MOVE(src, ADDRESSOF(x[ofs]), len); r.bpos := m; r.res := 0; EXIT
  652. ELSIF buf.lim = SS THEN
  653. m := buf.lim - r.bpos;
  654. IF m > 0 THEN SYSTEM.MOVE(src, ADDRESSOF(x[ofs]), m); INC(ofs, m); DEC(len, m) END ;
  655. IF r.apos < aleng THEN
  656. INC(r.apos); r.bpos := 0; buf := SearchBuf(SELF, r.apos);
  657. IF buf = NIL THEN
  658. buf := r.hint(Buffer);
  659. IF buf.mod THEN WriteBuf(SELF, buf) END ;
  660. ReadBuf(SELF, buf, r.apos)
  661. ELSE
  662. r.hint := buf
  663. END
  664. ELSE
  665. r.bpos := buf.lim; r.res := len; r.eof := TRUE; EXIT
  666. END
  667. ELSE
  668. m := buf.lim - r.bpos;
  669. IF m > 0 THEN SYSTEM.MOVE(src, ADDRESSOF(x[ofs]), m); r.bpos := buf.lim END ;
  670. r.res := len - m; r.eof := TRUE; EXIT
  671. END
  672. END;
  673. END ReadBytes;
  674. PROCEDURE Write*(VAR r: Files.Rider; x: CHAR);
  675. VAR buf: Buffer;
  676. BEGIN {EXCLUSIVE}
  677. ASSERT(r.file = SELF);
  678. buf := r.hint(Buffer);
  679. IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
  680. IF r.bpos >= buf.lim THEN
  681. IF r.bpos < SS THEN
  682. INC(buf.lim); INC(bleng); modH := TRUE
  683. ELSE
  684. WriteBuf(SELF, buf); INC(r.apos); buf := SearchBuf(SELF, r.apos);
  685. IF buf = NIL THEN
  686. buf := r.hint(Buffer);
  687. IF r.apos <= aleng THEN
  688. ReadBuf(SELF, buf, r.apos)
  689. ELSE
  690. buf.apos := r.apos; buf.lim := 1; INC(aleng); bleng := 1; modH := TRUE;
  691. IF (aleng - STS) MOD XS = 0 THEN NewExt(SELF) END
  692. END
  693. ELSE
  694. r.hint := buf
  695. END;
  696. r.bpos := 0
  697. END
  698. END;
  699. buf.data.B[r.bpos] := x; INC(r.bpos); buf.mod := TRUE
  700. END Write;
  701. PROCEDURE WriteBytes*(VAR r: Files.Rider; CONST x: ARRAY OF CHAR; ofs, len: LONGINT);
  702. VAR dst: ADDRESS; m: LONGINT; buf: Buffer;
  703. BEGIN {EXCLUSIVE}
  704. ASSERT(r.file = SELF);
  705. IF LEN(x)-ofs < len THEN SYSTEM.HALT(19) END;
  706. buf := r.hint(Buffer);
  707. IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
  708. LOOP
  709. IF len <= 0 THEN EXIT END;
  710. buf.mod := TRUE; dst := ADDRESSOF(buf.data.B[0]) + r.bpos; m := r.bpos + len;
  711. IF m <= buf.lim THEN
  712. SYSTEM.MOVE(ADDRESSOF(x[ofs]), dst, len); r.bpos := m; EXIT
  713. ELSIF m <= SS THEN
  714. SYSTEM.MOVE(ADDRESSOF(x[ofs]), dst, len); r.bpos := m;
  715. bleng := m; buf.lim := m; modH := TRUE; EXIT
  716. ELSE
  717. m := SS - r.bpos;
  718. IF m > 0 THEN SYSTEM.MOVE(ADDRESSOF(x[ofs]), dst, m); INC(ofs, m); DEC(len, m) END;
  719. WriteBuf(SELF, buf); INC(r.apos); r.bpos := 0; buf := SearchBuf(SELF, r.apos);
  720. IF buf = NIL THEN
  721. buf := r.hint(Buffer);
  722. IF r.apos <= aleng THEN ReadBuf(SELF, buf, r.apos)
  723. ELSE
  724. buf.apos := r.apos; buf.lim := 0; INC(aleng); bleng := 0; modH := TRUE;
  725. IF (aleng - STS) MOD XS = 0 THEN NewExt(SELF) END
  726. END
  727. ELSE
  728. r.hint := buf
  729. END
  730. END
  731. END
  732. END WriteBytes;
  733. PROCEDURE Length*(): LONGINT;
  734. BEGIN {EXCLUSIVE}
  735. RETURN aleng*SS + bleng - HS
  736. END Length;
  737. PROCEDURE GetDate*(VAR t, d: LONGINT);
  738. BEGIN {EXCLUSIVE}
  739. t := time; d := date
  740. END GetDate;
  741. PROCEDURE SetDate*(t, d: LONGINT);
  742. BEGIN {EXCLUSIVE}
  743. modH := TRUE; time := t; date := d
  744. END SetDate;
  745. PROCEDURE GetName*(VAR name: ARRAY OF CHAR);
  746. BEGIN {EXCLUSIVE}
  747. Files.JoinName(fs.prefix, SELF.name, name)
  748. END GetName;
  749. PROCEDURE Register0*(VAR res: WORD);
  750. BEGIN {EXCLUSIVE}
  751. Unbuffer(SELF);
  752. IF ~registered & (name # "") THEN
  753. fs(FileSystem).dir.Insert(name, sec[0]);
  754. registered := TRUE; key := sec[0];
  755. res := 0
  756. ELSE
  757. res := 1
  758. END
  759. END Register0;
  760. PROCEDURE Update*;
  761. BEGIN {EXCLUSIVE}
  762. Unbuffer(SELF)
  763. END Update;
  764. END File;
  765. PROCEDURE GetSector(vol: Files.Volume; src: DiskAdr; VAR dest: DiskSector);
  766. BEGIN
  767. IF src MOD SF # 0 THEN SYSTEM.HALT(15) END;
  768. vol.GetBlock(src DIV SF, SYSTEM.VAL(DiskSectorArr, dest))
  769. END GetSector;
  770. PROCEDURE PutSector(vol: Files.Volume; dest: DiskAdr; VAR src: DiskSector);
  771. BEGIN
  772. ASSERT(~(Files.ReadOnly IN vol.flags));
  773. IF dest MOD SF # 0 THEN SYSTEM.HALT(15) END;
  774. vol.PutBlock(dest DIV SF, SYSTEM.VAL(DiskSectorArr, src))
  775. END PutSector;
  776. PROCEDURE AllocSector(vol: Files.Volume; hint: DiskAdr; VAR sec: DiskAdr);
  777. BEGIN
  778. ASSERT(~(Files.ReadOnly IN vol.flags));
  779. vol.AllocBlock(hint DIV SF, sec);
  780. sec := sec * SF
  781. END AllocSector;
  782. PROCEDURE MarkSector(vol: Files.Volume; sec: LONGINT);
  783. BEGIN
  784. ASSERT(~(Files.ReadOnly IN vol.flags));
  785. vol.MarkBlock(sec DIV SF)
  786. END MarkSector;
  787. PROCEDURE FreeSector(vol: Files.Volume; sec: LONGINT);
  788. BEGIN
  789. ASSERT(~(Files.ReadOnly IN vol.flags));
  790. vol.FreeBlock(sec DIV SF)
  791. END FreeSector;
  792. PROCEDURE Marked(vol: Files.Volume; sec: LONGINT): BOOLEAN;
  793. BEGIN
  794. ASSERT(~(Files.ReadOnly IN vol.flags));
  795. RETURN vol.Marked(sec DIV SF)
  796. END Marked;
  797. PROCEDURE MatchPrefix(VAR mask, name: ARRAY OF CHAR; VAR pos, diff: LONGINT);
  798. BEGIN
  799. pos := 0;
  800. LOOP
  801. IF mask[pos] = 0X THEN
  802. pos := -1; diff := 0; EXIT (* no "*" found, match all files with this prefix *)
  803. ELSIF mask[pos] = "*" THEN
  804. IF mask[pos+1] = 0X THEN pos := -1 END; (* "*" found at end, match all files with this prefix *)
  805. diff := 0; EXIT (* "*" found, do Match *)
  806. END;
  807. diff := ORD(name[pos]) - ORD(mask[pos]);
  808. IF diff # 0 THEN EXIT END;
  809. INC(pos)
  810. END
  811. END MatchPrefix;
  812. PROCEDURE Match(pos: LONGINT; VAR pat, name: ARRAY OF CHAR): BOOLEAN;
  813. VAR i0, i1, j0, j1: LONGINT; f: BOOLEAN;
  814. BEGIN
  815. f := TRUE;
  816. IF pos # -1 THEN
  817. i0 := pos; j0 := pos;
  818. LOOP
  819. IF pat[i0] = "*" THEN
  820. INC(i0);
  821. IF pat[i0] = 0X THEN EXIT END
  822. ELSE
  823. IF name[j0] # 0X THEN f := FALSE END;
  824. EXIT
  825. END;
  826. f := FALSE;
  827. LOOP
  828. IF name[j0] = 0X THEN EXIT END;
  829. i1 := i0; j1 := j0;
  830. LOOP
  831. IF (pat[i1] = 0X) OR (pat[i1] = "*") THEN f := TRUE; EXIT END;
  832. IF pat[i1] # name[j1] THEN EXIT END;
  833. INC(i1); INC(j1)
  834. END;
  835. IF f THEN j0 := j1; i0 := i1; EXIT END;
  836. INC(j0)
  837. END;
  838. IF ~f THEN EXIT END
  839. END
  840. END;
  841. RETURN f & (name[0] # 0X)
  842. END Match;
  843. 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);
  844. VAR i, pos, diff: LONGINT; dpg1: DiskAdr; a: DirPage; time, date, size: LONGINT;
  845. BEGIN
  846. GetSector(fs.vol, dpg, a); i := 0;
  847. WHILE (i < a.m) & continue DO
  848. MatchPrefix(mask, a.e[i].name, pos, diff);
  849. IF i = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[i-1].p END;
  850. IF diff >= 0 THEN (* matching prefix *)
  851. IF dpg1 # 0 THEN enumerate(fs, mask, dpg1, flags, enum, continue, fh, fn) END;
  852. IF diff = 0 THEN
  853. IF continue & Match(pos, mask, a.e[i].name) THEN
  854. time := 0; date := 0; size := 0;
  855. IF flags * {Files.EnumTime, Files.EnumSize} # {} THEN
  856. GetSector(fs.vol, a.e[i].adr, fh);
  857. IF Files.EnumTime IN flags THEN
  858. time := fh.time; date := fh.date
  859. END;
  860. IF Files.EnumSize IN flags THEN
  861. size := LONG(fh.aleng)*SS + fh.bleng - HS
  862. END
  863. END;
  864. Files.JoinName(fs.prefix, a.e[i].name, fn);
  865. enum.PutEntry(fn, {}, time, date, size)
  866. END
  867. ELSE continue := FALSE
  868. END
  869. END;
  870. INC(i)
  871. END;
  872. IF continue & (i > 0) & (a.e[i-1].p # 0) THEN
  873. enumerate(fs, mask, a.e[i-1].p, flags, enum, continue, fh, fn)
  874. END
  875. END enumerate;
  876. (* Check a file name. *)
  877. PROCEDURE Check(VAR s: ARRAY OF CHAR; VAR name: FileName; VAR res: WORD);
  878. VAR i: LONGINT; ch: CHAR;
  879. BEGIN
  880. ch := s[0]; i := 0;
  881. IF ("A" <= CAP(ch)) & (CAP(ch) <= "Z") THEN
  882. LOOP name[i] := ch; INC(i); ch := s[i];
  883. IF ch = 0X THEN
  884. WHILE i < FnLength DO name[i] := 0X; INC(i) END ;
  885. res := 0; EXIT
  886. END ;
  887. IF ~(("A" <= CAP(ch)) & (CAP(ch) <= "Z")
  888. OR ("0" <= ch) & (ch <= "9") OR (ch = ".")) THEN res := 3; EXIT
  889. END ;
  890. IF i = FnLength-1 THEN res := 4; EXIT END
  891. END
  892. ELSIF ch = 0X THEN name[0] := 0X; res := -1
  893. ELSE res := 3
  894. END
  895. END Check;
  896. PROCEDURE UpdateHeader(f: File; VAR h: FileHeader);
  897. VAR k: LONGINT;
  898. BEGIN
  899. h.aleng := SHORT(f.aleng); h.bleng := SHORT(f.bleng);
  900. h.sec := f.sec; k := (f.aleng + (XS-STS)) DIV XS;
  901. WHILE k > 0 DO DEC(k); h.ext[k] := f.ext[k].adr END;
  902. h.date := f.date; h.time := f.time
  903. END UpdateHeader;
  904. PROCEDURE ReadBuf(f: File; buf: Buffer; pos: LONGINT);
  905. VAR sec: DiskAdr;
  906. BEGIN
  907. IF pos < STS THEN
  908. sec := f.sec[pos]
  909. ELSE
  910. sec := f.ext[(pos-STS) DIV XS].sec.x[(pos-STS) MOD XS]
  911. END;
  912. GetSector(f.fs.vol, sec, buf.data);
  913. IF pos < f.aleng THEN buf.lim := SS ELSE buf.lim := f.bleng END;
  914. buf.apos := pos; buf.mod := FALSE
  915. END ReadBuf;
  916. PROCEDURE WriteBuf(f: File; buf: Buffer);
  917. VAR i, k: LONGINT; secadr: DiskAdr; inx: Index; vol: Files.Volume;
  918. BEGIN
  919. vol := f.fs.vol;
  920. Clock.Get(f.time, f.date); f.modH := TRUE;
  921. IF buf.apos < STS THEN
  922. secadr := f.sec[buf.apos];
  923. IF secadr = 0 THEN
  924. AllocSector(vol, f.sechint, secadr);
  925. f.modH := TRUE; f.sec[buf.apos] := secadr; f.sechint := secadr
  926. END;
  927. IF buf.apos = 0 THEN
  928. UpdateHeader(f, SYSTEM.VAL(FileHeader, buf.data)); f.modH := FALSE
  929. END
  930. ELSE
  931. i := (buf.apos - STS) DIV XS; inx := f.ext[i];
  932. IF inx = NIL THEN
  933. NEW(inx); inx.adr := 0; inx.sec.x[0] := 0; f.ext[i] := inx; f.modH := TRUE
  934. END;
  935. k := (buf.apos - STS) MOD XS; secadr := inx.sec.x[k];
  936. IF secadr = 0 THEN
  937. AllocSector(vol, f.sechint, secadr);
  938. f.modH := TRUE; inx.mod := TRUE; inx.sec.x[k] := secadr; f.sechint := secadr
  939. END
  940. END;
  941. PutSector(vol, secadr, buf.data); buf.mod := FALSE
  942. END WriteBuf;
  943. PROCEDURE SearchBuf(f: File; pos: LONGINT): Buffer;
  944. VAR buf: Buffer;
  945. BEGIN
  946. buf := f.firstbuf;
  947. LOOP
  948. IF buf.apos = pos THEN EXIT END;
  949. buf := buf.next;
  950. IF buf = f.firstbuf THEN buf := NIL; EXIT END
  951. END;
  952. RETURN buf
  953. END SearchBuf;
  954. PROCEDURE GetBuf(f: File; pos: LONGINT): Buffer;
  955. VAR buf: Buffer;
  956. BEGIN
  957. buf := f.firstbuf;
  958. LOOP
  959. IF buf.apos = pos THEN EXIT END;
  960. IF buf.next = f.firstbuf THEN
  961. IF f.nofbufs < MaxBufs THEN (* allocate new buffer *)
  962. NEW(buf); buf.next := f.firstbuf.next; f.firstbuf.next := buf;
  963. INC(f.nofbufs)
  964. ELSE (* take one of the buffers *)
  965. f.firstbuf := buf;
  966. IF buf.mod THEN WriteBuf(f, buf) END
  967. END;
  968. buf.apos := pos;
  969. IF pos <= f.aleng THEN ReadBuf(f, buf, pos) END;
  970. EXIT
  971. END;
  972. buf := buf.next
  973. END;
  974. RETURN buf
  975. END GetBuf;
  976. PROCEDURE Unbuffer(f: File);
  977. VAR i, k: LONGINT; buf: Buffer; inx: Index; head: FileHeader; vol: Files.Volume;
  978. BEGIN
  979. vol := f.fs.vol;
  980. buf := f.firstbuf;
  981. REPEAT
  982. IF buf.mod THEN WriteBuf(f, buf) END;
  983. buf := buf.next
  984. UNTIL buf = f.firstbuf;
  985. k := (f.aleng + (XS-STS)) DIV XS; i := 0;
  986. WHILE i < k DO
  987. inx := f.ext[i]; INC(i);
  988. IF inx.mod THEN
  989. IF inx.adr = 0 THEN
  990. AllocSector(vol, f.sechint, inx.adr); f.sechint := inx.adr; f.modH := TRUE
  991. END;
  992. PutSector(vol, inx.adr, inx.sec); inx.mod := FALSE
  993. END
  994. END;
  995. IF f.modH THEN
  996. GetSector(vol, f.sec[0], head); UpdateHeader(f, head);
  997. PutSector(vol, f.sec[0], head); f.modH := FALSE
  998. END
  999. END Unbuffer;
  1000. PROCEDURE NewExt(f: File);
  1001. VAR i, k: LONGINT; ext: Index;
  1002. BEGIN
  1003. k := (f.aleng - STS) DIV XS;
  1004. IF k = ETS THEN SYSTEM.HALT(18) END;
  1005. NEW(ext); ext.adr := 0; ext.mod := TRUE;
  1006. f.ext[k] := ext; i := XS;
  1007. REPEAT DEC(i); ext.sec.x[i] := 0 UNTIL i = 0
  1008. END NewExt;
  1009. (** Generate a new file system object. Files.NewVol has volume parameter, Files.Par has mount prefix. *)
  1010. PROCEDURE NewFS*(context : Files.Parameters);
  1011. VAR fs: FileSystem; fh: FileHeader;
  1012. BEGIN
  1013. IF Files.This(context.prefix) = NIL THEN
  1014. IF (context.vol.blockSize = SS) & (context.vol.size >= MinVolSize) THEN
  1015. GetSector(context.vol, DirRootAdr, fh);
  1016. IF fh.mark = DirMark THEN (* assume it is an old-format Native filesystem *)
  1017. NEW(fs); fs.vol := context.vol;
  1018. ASSERT(context.vol.size < MAX(LONGINT) DIV SF);
  1019. fs.desc := "NatFS";
  1020. NEW(fs.dir, context.vol); (* initialize directory and volume *)
  1021. ASSERT(fs.dir.state = Opened); (* will have to undo changes to vol before continuing *)
  1022. Files.Add(fs, context.prefix)
  1023. ELSE
  1024. context.error.String("OberonFS: File system not found on ");
  1025. context.error.String(context.vol.name); context.error.Ln
  1026. END
  1027. ELSE
  1028. context.error.String("OberonFS: Bad volume size"); context.error.Ln
  1029. END
  1030. ELSE
  1031. context.error.String("OberonFS: "); context.error.String(context.prefix);
  1032. context.error.String(" already in use"); context.error.Ln
  1033. END;
  1034. END NewFS;
  1035. (* Clean up when module freed. *)
  1036. PROCEDURE Cleanup;
  1037. VAR ft: Files.FileSystemTable; i: LONGINT;
  1038. BEGIN
  1039. IF Modules.shutdown = Modules.None THEN
  1040. Files.GetList(ft);
  1041. IF ft # NIL THEN
  1042. FOR i := 0 TO LEN(ft^)-1 DO
  1043. IF ft[i] IS FileSystem THEN Files.Remove(ft[i]) END
  1044. END
  1045. END
  1046. END
  1047. END Cleanup;
  1048. BEGIN
  1049. ASSERT((SIZEOF(FileHeader) = SS) & (SIZEOF(IndexSector) = SS) & (SIZEOF(DataSector) = SS) &
  1050. (SIZEOF(DirPage) = SS) & (SIZEOF(MapIndex) = SS) & (SIZEOF(MapSector) = SS));
  1051. Modules.InstallTermHandler(Cleanup)
  1052. END OberonFS.
  1053. (*
  1054. aleng * SS + bleng = length (including header)
  1055. apos * SS + bpos = current position
  1056. 0 <= bpos <= lim <= SS
  1057. 0 <= apos <= aleng < STS
  1058. (apos < aleng) & (lim = SS) OR (apos = aleng)
  1059. *)