1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537 |
- (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
- MODULE DiskFS; (** AUTHOR "pjm"; PURPOSE "Aos disk file system"; *)
- IMPORT SYSTEM, Machine, KernelLog, Modules, Clock, Files, Kernel;
- CONST
- SkipIndexFlag = 31; (* DiskFS filesystem flag. Do not write index map back to disk when unmounting *)
- MinVolSize = 4;
- SectorFactor = 29;
- (* WARNING: When the maximum length of filenames is changed, volumes must be re-formatted!!! *)
- FileNameLength = 128; (* includes 0X *)
- SectorTableSize = 128;
- SectorSize = 4096;
- IndexSize = SectorSize DIV 4;
- DiskAdrSize = 4; (* bytes *)
- HeaderSize = 4 (* mark *) + FileNameLength + 4*4 (* aleng, bleng, time, date *) + (SectorTableSize+1)*DiskAdrSize;
- DirEntrySize = FileNameLength + 2*DiskAdrSize (* adr, p *);
- DirPgHeaderSize = 2*4 (* mark, m *) + DiskAdrSize (* p0 *) + 4 (* min. FillerSize *);
- DirPgSize = (SectorSize - DirPgHeaderSize) DIV DirEntrySize;
- FillerSize = (SectorSize - DirPgHeaderSize) MOD DirEntrySize + 4 (* min. FillerSize *);
- DirRootAdr = 1*SectorFactor;
- N = DirPgSize DIV 2;
- DirMark = LONGINT(9B1EA38DH);
- HeaderMark = LONGINT(9BA71D86H);
- MapIndexSize = (SectorSize-4) DIV 4;
- MapSize = SectorSize DIV SIZEOF (SET); (* {MapSize MOD SIZEOF (SET) = 0} *)
- MapMark = LONGINT(9C2F977FH);
- MaxBufs = 1024;
- InitHint = 200*SectorFactor;
- Closed = 0X; Opening = 1X; Opened = 2X; Closing = 3X;
- SetSize = MAX (SET) + 1;
- TYPE
- DiskSector = RECORD END; (* Oberon Sector, size SectorSize *)
- DiskSectorArr = ARRAY SectorSize OF CHAR;
- DiskAdr = LONGINT;
- FileName = ARRAY FileNameLength OF CHAR;
- SectorTable = ARRAY SectorTableSize OF DiskAdr;
- FileHeader = RECORD (DiskSector) (* allocated in the first page of each file on disk *)
- mark: LONGINT;
- name: FileName;
- aleng, bleng: LONGINT;
- date, time: LONGINT;
- sec: SectorTable;
- ext: DiskAdr;
- data: ARRAY SectorSize-HeaderSize OF CHAR
- END;
- IndexSector = RECORD (DiskSector)
- x: ARRAY IndexSize OF DiskAdr
- END;
- DataSector = RECORD (DiskSector)
- B: ARRAY SectorSize OF CHAR
- END;
- DirEntry = RECORD (*B-tree node*)
- name: FileName;
- adr: DiskAdr; (*sec no of file header*)
- p: DiskAdr (*sec no of descendant in directory*)
- END;
- DirPage = RECORD (DiskSector)
- mark: LONGINT;
- m: LONGINT;
- p0: DiskAdr; (*sec no of left descendant in directory*)
- fill: ARRAY FillerSize OF CHAR;
- e: ARRAY DirPgSize OF DirEntry
- END;
- MapIndex = RECORD (DiskSector)
- mark: LONGINT;
- index: ARRAY MapIndexSize OF DiskAdr
- END;
- MapSector = RECORD (DiskSector)
- map: ARRAY MapSize OF SET
- END;
- Buffer = POINTER TO RECORD (Files.Hint)
- apos, lim: LONGINT;
- mod: BOOLEAN;
- next: Buffer;
- data: DataSector
- END;
- SuperIndex = POINTER TO RECORD
- adr: DiskAdr;
- mod: BOOLEAN;
- sub: ARRAY IndexSize OF SubIndex
- END;
- SubIndex = POINTER TO RECORD
- adr: DiskAdr;
- mod: BOOLEAN;
- sec: IndexSector
- END;
- TYPE
- Directory = OBJECT
- VAR
- vol: Files.Volume;
- state: CHAR;
- lastSectorReserved, noCleanup: BOOLEAN;
- (* "exported" methods: Search, Insert, Delete *)
- PROCEDURE Search(VAR name: FileName; VAR A: DiskAdr);
- VAR i, L, R: LONGINT; dadr: DiskAdr; a: DirPage;
- BEGIN {EXCLUSIVE}
- ASSERT(state = Opened);
- dadr := DirRootAdr;
- LOOP
- GetSector(vol, dadr, a);
- ASSERT(a.mark = DirMark);
- L := 0; R := a.m; (*binary search*)
- WHILE L < R DO
- i := (L+R) DIV 2;
- IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
- END ;
- IF (R < a.m) & (name = a.e[R].name) THEN
- A := a.e[R].adr; EXIT (*found*)
- END ;
- IF R = 0 THEN dadr := a.p0 ELSE dadr := a.e[R-1].p END ;
- IF dadr = 0 THEN A := 0; EXIT (*not found*) END
- END
- END Search;
- PROCEDURE insert(VAR name: FileName; dpg0: DiskAdr; VAR h: BOOLEAN; VAR v: DirEntry; fad: DiskAdr);
- (*h = "tree has become higher and v is ascending element"*)
- VAR ch: CHAR; i, j, L, R: LONGINT; dpg1: DiskAdr; u: DirEntry; a: DirPage;
- BEGIN (*~h*)
- ASSERT(state = Opened);
- GetSector(vol, dpg0, a);
- L := 0; R := a.m; (*binary search*)
- WHILE L < R DO
- i := (L+R) DIV 2;
- IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
- END ;
- IF (R < a.m) & (name = a.e[R].name) THEN
- a.e[R].adr := fad; PutSector(vol, dpg0, a) (*replace*)
- ELSE (*not on this page*)
- IF R = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[R-1].p END ;
- IF dpg1 = 0 THEN (*not in tree, insert*)
- u.adr := fad; u.p := 0; h := TRUE; j := 0;
- REPEAT ch := name[j]; u.name[j] := ch; INC(j)
- UNTIL ch = 0X;
- WHILE j < FileNameLength DO u.name[j] := 0X; INC(j) END
- ELSE
- insert(name, dpg1, h, u, fad)
- END ;
- IF h THEN (*insert u to the left of e[R]*)
- IF a.m < DirPgSize THEN
- h := FALSE; i := a.m;
- WHILE i > R DO DEC(i); a.e[i+1] := a.e[i] END ;
- a.e[R] := u; INC(a.m)
- ELSE (*split page and assign the middle element to v*)
- a.m := N; a.mark := DirMark;
- IF R < N THEN (*insert in left half*)
- v := a.e[N-1]; i := N-1;
- WHILE i > R DO DEC(i); a.e[i+1] := a.e[i] END ;
- a.e[R] := u; PutSector(vol, dpg0, a);
- AllocSector(vol, dpg0, dpg0); i := 0;
- WHILE i < N DO a.e[i] := a.e[i+N]; INC(i) END
- ELSE (*insert in right half*)
- PutSector(vol, dpg0, a);
- AllocSector(vol, dpg0, dpg0); DEC(R, N); i := 0;
- IF R = 0 THEN v := u
- ELSE v := a.e[N];
- WHILE i < R-1 DO a.e[i] := a.e[N+1+i]; INC(i) END ;
- a.e[i] := u; INC(i)
- END ;
- WHILE i < N DO a.e[i] := a.e[N+i]; INC(i) END
- END ;
- a.p0 := v.p; v.p := dpg0
- END ;
- PutSector(vol, dpg0, a)
- END
- END
- END insert;
- PROCEDURE Insert(VAR name: FileName; fad: DiskAdr);
- VAR oldroot: DiskAdr; h: BOOLEAN; U: DirEntry; a: DirPage;
- BEGIN {EXCLUSIVE}
- h := FALSE;
- insert(name, DirRootAdr, h, U, fad);
- IF h THEN (*root overflow*)
- GetSector(vol, DirRootAdr, a);
- AllocSector(vol, DirRootAdr, oldroot); PutSector(vol, oldroot, a);
- a.mark := DirMark; a.m := 1; a.p0 := oldroot; a.e[0] := U;
- PutSector(vol, DirRootAdr, a)
- END
- END Insert;
- PROCEDURE underflow(VAR c: DirPage; (*ancestor page*) dpg0: DiskAdr; s: LONGINT; (*insertion point in c*)
- VAR h: BOOLEAN); (*c undersize*)
- VAR i, k: LONGINT; dpg1: DiskAdr; a, b: DirPage; (*a := underflowing page, b := neighbouring page*)
- BEGIN
- GetSector(vol, dpg0, a);
- (*h & a.m = N-1 & dpg0 = c.e[s-1].p*)
- IF s < c.m THEN (*b := page to the right of a*)
- dpg1 := c.e[s].p; GetSector(vol, dpg1, b);
- k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
- a.e[N-1] := c.e[s]; a.e[N-1].p := b.p0;
- IF k > 0 THEN
- (*move k-1 items from b to a, one to c*) i := 0;
- WHILE i < k-1 DO a.e[i+N] := b.e[i]; INC(i) END ;
- c.e[s] := b.e[i]; b.p0 := c.e[s].p;
- c.e[s].p := dpg1; DEC(b.m, k); i := 0;
- WHILE i < b.m DO b.e[i] := b.e[i+k]; INC(i) END ;
- PutSector(vol, dpg1, b); a.m := N-1+k; h := FALSE
- ELSE (*merge pages a and b, discard b*) i := 0;
- WHILE i < N DO a.e[i+N] := b.e[i]; INC(i) END ;
- i := s; DEC(c.m);
- WHILE i < c.m DO c.e[i] := c.e[i+1]; INC(i) END ;
- a.m := 2*N; h := c.m < N;
- FreeSector(vol, dpg1) (* free b *)
- END ;
- PutSector(vol, dpg0, a)
- ELSE (*b := page to the left of a*) DEC(s);
- IF s = 0 THEN dpg1 := c.p0 ELSE dpg1 := c.e[s-1].p END ;
- GetSector(vol, dpg1, b);
- k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
- IF k > 0 THEN
- i := N-1;
- WHILE i > 0 DO DEC(i); a.e[i+k] := a.e[i] END ;
- i := k-1; a.e[i] := c.e[s]; a.e[i].p := a.p0;
- (*move k-1 items from b to a, one to c*) DEC(b.m, k);
- WHILE i > 0 DO DEC(i); a.e[i] := b.e[i+b.m+1] END ;
- c.e[s] := b.e[b.m]; a.p0 := c.e[s].p;
- c.e[s].p := dpg0; a.m := N-1+k; h := FALSE;
- PutSector(vol, dpg0, a)
- ELSE (*merge pages a and b, discard a*)
- c.e[s].p := a.p0; b.e[N] := c.e[s]; i := 0;
- WHILE i < N-1 DO b.e[i+N+1] := a.e[i]; INC(i) END ;
- b.m := 2*N; DEC(c.m); h := c.m < N;
- FreeSector(vol, dpg0) (* free a *)
- END ;
- PutSector(vol, dpg1, b)
- END
- END underflow;
- PROCEDURE delete(VAR name: FileName; dpg0: DiskAdr; VAR h: BOOLEAN; VAR fad: DiskAdr);
- (*search and delete entry with key name; if a page underflow arises,
- balance with adjacent page or merge; h := "page dpg0 is undersize"*)
- VAR i, L, R: LONGINT; dpg1: DiskAdr; a: DirPage;
- PROCEDURE del(dpg1: DiskAdr; VAR h: BOOLEAN);
- VAR dpg2: DiskAdr; (*global: a, R*) b: DirPage;
- BEGIN
- GetSector(vol, dpg1, b); dpg2 := b.e[b.m-1].p;
- IF dpg2 # 0 THEN del(dpg2, h);
- IF h THEN underflow(b, dpg2, b.m, h); PutSector(vol, dpg1, b) END
- ELSE
- b.e[b.m-1].p := a.e[R].p; a.e[R] := b.e[b.m-1];
- DEC(b.m); h := b.m < N; PutSector(vol, dpg1, b)
- END
- END del;
- BEGIN (*~h*)
- ASSERT(state = Opened);
- GetSector(vol, dpg0, a);
- L := 0; R := a.m; (*binary search*)
- WHILE L < R DO
- i := (L+R) DIV 2;
- IF name <= a.e[i].name THEN R := i ELSE L := i+1 END
- END ;
- IF R = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[R-1].p END ;
- IF (R < a.m) & (name = a.e[R].name) THEN
- (*found, now delete*) fad := a.e[R].adr;
- IF dpg1 = 0 THEN (*a is a leaf page*)
- DEC(a.m); h := a.m < N; i := R;
- WHILE i < a.m DO a.e[i] := a.e[i+1]; INC(i) END
- ELSE del(dpg1, h);
- IF h THEN underflow(a, dpg1, R, h) END
- END ;
- PutSector(vol, dpg0, a)
- ELSIF dpg1 # 0 THEN
- delete(name, dpg1, h, fad);
- IF h THEN underflow(a, dpg1, R, h); PutSector(vol, dpg0, a) END
- ELSE (*not in tree*) fad := 0
- END
- END delete;
- PROCEDURE Delete(VAR name: FileName; VAR fad: DiskAdr);
- VAR h: BOOLEAN; newroot: DiskAdr; a: DirPage;
- BEGIN {EXCLUSIVE}
- h := FALSE;
- delete(name, DirRootAdr, h, fad);
- IF h THEN (*root underflow*)
- GetSector(vol, DirRootAdr, a);
- IF (a.m = 0) & (a.p0 # 0) THEN
- newroot := a.p0; GetSector(vol, newroot, a);
- PutSector(vol, DirRootAdr, a); (*discard newroot*)
- FreeSector(vol, newroot)
- END
- END
- END Delete;
- PROCEDURE Startup;
- VAR
- j, sec, size, q, free, thres: LONGINT; mi: MapIndex; ms: MapSector;
- s: ARRAY 10 OF CHAR; found: BOOLEAN;
- BEGIN (* only called from Init *)
- size := vol.size; found := FALSE;
- IF (vol.Available() = size) & (size # 0) THEN (* all sectors available *)
- GetSector(vol, size*SectorFactor, mi);
- IF mi.mark = MapMark THEN
- j := 0; (* check consistency of index *)
- WHILE (j # MapIndexSize) & (mi.index[j] >= 0) & (mi.index[j] MOD SectorFactor = 0) DO
- INC(j)
- END;
- IF j = MapIndexSize THEN
- found := TRUE;
- mi.mark := 0; PutSector(vol, size*SectorFactor, mi); (* invalidate index *)
- j := 0; sec := 1; q := 0;
- LOOP
- IF (j = MapIndexSize) OR (mi.index[j] = 0) THEN EXIT END;
- GetSector(vol, mi.index[j], ms);
- REPEAT
- IF (sec MOD SetSize) IN ms.map[sec DIV SetSize MOD MapSize] THEN
- MarkSector(vol, sec*SectorFactor);
- INC(q)
- END;
- IF sec = size THEN EXIT END;
- INC(sec)
- UNTIL sec MOD (MapSize*SetSize) = 0;
- INC(j)
- END;
- Machine.GetConfig("DiskGC", s);
- thres := 0; j := 0;
- WHILE s[j] # 0X DO thres := thres*10+(ORD(s[j])-48); INC(j) END;
- IF thres < 10 THEN thres := 10
- ELSIF thres > 100 THEN thres := 100
- END;
- ASSERT(q = size-vol.Available());
- free := vol.Available()*100 DIV size;
- IF (free > thres) & (vol.Available() > 100000H DIV SectorSize) THEN
- state := Opened
- ELSE (* undo *)
- FOR j := SectorFactor TO size*SectorFactor BY SectorFactor DO
- IF Marked(vol, j) THEN FreeSector(vol, j) END
- END;
- ASSERT(vol.Available() = size);
- KernelLog.String("DiskFS: "); KernelLog.Int(free, 1);
- KernelLog.String("% free, forcing disk GC on ");
- KernelLog.String(vol.name); KernelLog.Ln
- END
- END
- END;
- IF ~found THEN
- KernelLog.String("DiskFS: Index not found on ");
- KernelLog.String(vol.name); KernelLog.Ln
- END
- END
- END Startup;
- PROCEDURE &Init*(vol: Files.Volume);
- VAR k: LONGINT; A: ARRAY 2000 OF DiskAdr; files: LONGINT; bad: BOOLEAN;
- PROCEDURE MarkSectors;
- VAR L, R, i, j, n: LONGINT; x: DiskAdr; hd: FileHeader; sup, sub: IndexSector; mark: ARRAY 512 OF DiskAdr; markPosition: LONGINT;
- PROCEDURE StartMarking;
- BEGIN
- markPosition := 0;
- END StartMarking;
- PROCEDURE FinishMarking;
- BEGIN
- vol.MarkBlocks(mark,0,markPosition);
- markPosition := 0;
- END FinishMarking;
- PROCEDURE MarkSector(vol: Files.Volume (* ignored *); sec: LONGINT);
- BEGIN
- mark[markPosition] := sec DIV SectorFactor;
- INC(markPosition);
- IF markPosition = LEN(mark) THEN
- FinishMarking;
- END;
- END MarkSector;
- PROCEDURE sift(L, R: LONGINT);
- VAR i, j: LONGINT; x: DiskAdr;
- BEGIN j := L; x := A[j];
- LOOP i := j; j := 2*j + 1;
- IF (j+1 < R) & (A[j] < A[j+1]) THEN INC(j) END ;
- IF (j >= R) OR (x > A[j]) THEN EXIT END ;
- A[i] := A[j]
- END ;
- A[i] := x
- END sift;
- BEGIN
- StartMarking;
- KernelLog.String(" marking");
- L := k DIV 2; R := k; (*heapsort*)
- WHILE L > 0 DO DEC(L); sift(L, R) END ;
- WHILE R > 0 DO
- DEC(R); x := A[0]; A[0] := A[R]; A[R] := x; sift(L, R)
- END;
- WHILE L < k DO
- bad := FALSE; INC(files);
- IF files MOD 128 = 0 THEN KernelLog.Char(".") END;
- GetSector(vol, A[L], hd);
- IF hd.aleng < SectorTableSize THEN
- j := hd.aleng + 1;
- REPEAT
- DEC(j);
- IF hd.sec[j] # 0 THEN MarkSector(vol, hd.sec[j]) ELSE hd.aleng := j-1; bad := TRUE END
- UNTIL j = 0
- ELSE
- j := SectorTableSize;
- REPEAT
- DEC(j);
- IF hd.sec[j] # 0 THEN MarkSector(vol, hd.sec[j]) ELSE hd.aleng := j-1; bad := TRUE END
- UNTIL j = 0;
- IF hd.ext = 0 THEN hd.aleng := SectorTableSize-1; bad := TRUE END;
- IF ~bad THEN
- MarkSector(vol, hd.ext); GetSector(vol, hd.ext, sup);
- n := (hd.aleng - SectorTableSize) DIV IndexSize; i := 0;
- WHILE (i <= n) & ~bad DO
- IF sup.x[i] # 0 THEN
- MarkSector(vol, sup.x[i]); GetSector(vol, sup.x[i], sub);
- IF i < n THEN j := IndexSize
- ELSE j := (hd.aleng - SectorTableSize) MOD IndexSize + 1
- END;
- REPEAT
- DEC(j);
- IF (sub.x[j] MOD SectorFactor = 0) & (sub.x[j] > 0) THEN
- MarkSector(vol, sub.x[j])
- ELSE
- bad := TRUE
- END
- UNTIL j = 0;
- INC(i)
- ELSE bad := TRUE
- END;
- IF bad THEN
- IF i = 0 THEN hd.aleng := SectorTableSize-1
- ELSE hd.aleng := SectorTableSize + (i-1) * IndexSize
- END
- END
- END
- END
- END;
- IF bad THEN
- KernelLog.Ln; KernelLog.String(hd.name); KernelLog.String(" truncated");
- hd.bleng := SectorSize; IF hd.aleng < 0 THEN hd.aleng := 0 (* really bad *) END;
- PutSector(vol, A[L], hd)
- END;
- INC(L)
- END;
- FinishMarking;
- END MarkSectors;
- PROCEDURE TraverseDir(dpg: DiskAdr);
- VAR i: LONGINT; a: DirPage;
- BEGIN
- GetSector(vol, dpg, a); MarkSector(vol, dpg); i := 0;
- WHILE i < a.m DO
- A[k] := a.e[i].adr;
- (*
- IF A[k] = 0DEADDEADH THEN
- KernelLog.Enter; KernelLog.Int(dpg DIV SectorFactor, 1); KernelLog.Char(" "); KernelLog.Int(k, 1); KernelLog.Exit
- END;
- *)
- INC(k); INC(i);
- IF k = 2000 THEN MarkSectors; k := 0 END
- END ;
- IF a.p0 # 0 THEN
- TraverseDir(a.p0); i := 0;
- WHILE i < a.m DO
- TraverseDir(a.e[i].p); INC(i)
- END
- END
- END TraverseDir;
- BEGIN
- SELF.vol := vol; lastSectorReserved := FALSE;
- IF ~(Files.ReadOnly IN vol.flags) THEN
- state := Opening; k := 0;
- Startup;
- IF state # Opened THEN
- files := 0; KernelLog.String("DiskFS: Scanning ");
- KernelLog.String(vol.name); KernelLog.String("...");
- TraverseDir(DirRootAdr);
- MarkSectors;
- KernelLog.Int(files, 6); KernelLog.String(" files"); KernelLog.Ln;
- state := Opened
- END;
- IF ~Marked(vol, vol.size*SectorFactor) THEN (* last sector still free *)
- MarkSector(vol, vol.size*SectorFactor); lastSectorReserved := TRUE (* allocate it *)
- END;
- KernelLog.String("DiskFS: "); KernelLog.Int(vol.Available() * (SectorSize DIV 1024), 1);
- KernelLog.String("K of "); KernelLog.Int(vol.size * (SectorSize DIV 1024), 1);
- KernelLog.String("K available on "); KernelLog.String(vol.name);
- KernelLog.Ln
- ELSE
- state := Opened
- END
- END Init;
- PROCEDURE Cleanup;
- VAR i, j, p, q, sec, size: LONGINT; mi: MapIndex; ms: MapSector;
- BEGIN {EXCLUSIVE}
- (*KernelLog.String("DiskFS: Cleanup "); KernelLog.String(vol.name); KernelLog.Ln;*)
- state := Closing;
- size := vol.size; i := size*SectorFactor;
- IF ~(Files.ReadOnly IN vol.flags) & ~noCleanup THEN
- IF lastSectorReserved THEN FreeSector(vol, i); lastSectorReserved := FALSE END;
- IF ~Marked(vol, i) THEN (* last sector is available for us *)
- j := 0; sec := 1; q := 0;
- LOOP
- REPEAT DEC(i, SectorFactor) UNTIL (i = 0) OR ~Marked(vol, i); (* find a free sector *)
- IF i = 0 THEN RETURN END; (* no more space, don't commit *)
- mi.index[j] := i; INC(j);
- FOR p := 0 TO MapSize-1 DO ms.map[p] := {} END;
- REPEAT
- IF Marked(vol, sec*SectorFactor) THEN
- INCL(ms.map[sec DIV SetSize MOD MapSize], sec MOD SetSize);
- INC(q)
- END;
- IF sec = size THEN
- PutSector(vol, i, ms);
- EXIT
- END;
- INC(sec)
- UNTIL sec MOD (MapSize*SetSize) = 0;
- PutSector(vol, i, ms)
- END;
- WHILE j # MapIndexSize DO mi.index[j] := 0; INC(j) END;
- mi.mark := MapMark;
- PutSector(vol, size*SectorFactor, mi); (* commit *)
- KernelLog.String("DiskFS: Map saved on ");
- KernelLog.String(vol.name); KernelLog.Ln
- (*ELSE
- KernelLog.String("DiskFS: sector in use "); KernelLog.Int(size, 1); KernelLog.Ln*)
- END
- (*ELSE
- KernelLog.String("DiskFS: Read-only"); KernelLog.Ln*)
- END;
- state := Closed; vol := NIL
- END Cleanup;
- END Directory;
- TYPE
- FileSystem = OBJECT (Files.FileSystem) (* our file system type *)
- VAR
- dir: Directory;
- finalizeFiles: Kernel.FinalizedCollection;
- openFiles: DiskAdrList;
- (* all files that are registered, must be stored separately of finalizeFiles because of race
- between Delete0/Rename0 and deferred execution of file close finalizer *)
- tempRegFileSec: DiskAdrTable; (* temporary used for PurgeOpenedFile *)
- PROCEDURE &Init*;
- BEGIN NEW(finalizeFiles); NEW(openFiles); NEW(tempRegFileSec)
- END Init;
- PROCEDURE New0*(name: ARRAY OF CHAR): Files.File;
- VAR i: LONGINT; res: WORD; f: File; buf: Buffer; head {UNTRACED}: POINTER {UNSAFE} TO FileHeader; namebuf: FileName;
- BEGIN {EXCLUSIVE}
- f := NIL; Check(name, namebuf, res);
- IF res <= 0 THEN
- NEW(buf); buf.apos := 0; buf.mod := TRUE; buf.lim := HeaderSize; buf.next := buf;
- head := ADDRESSOF(buf.data);
- head.mark := HeaderMark;
- head.aleng := 0; head.bleng := HeaderSize; head.name := namebuf;
- Clock.Get(head.time, head.date);
- NEW(f); f.fs := SELF; f.key := 0; f.aleng := 0; f.bleng := HeaderSize; f.modH := TRUE;
- f.time := head.time; f.date := head.date;
- f.firstbuf := buf; f.nofbufs := 1; f.name := namebuf; f.sechint := InitHint;
- f.registered := (f.name[0] = 0X);
- f.ext := NIL; i := 0;
- REPEAT f.sec[i] := 0; head.sec[i] := 0; INC(i) UNTIL i = SectorTableSize;
- finalizeFiles.Add(f, Collect);
- ELSE
- KernelLog.String("DiskFS: "); KernelLog.String(name); KernelLog.String(", res: "); KernelLog.Int(res, 0); KernelLog.Ln;
- END;
- RETURN f
- END New0;
- PROCEDURE Old0*(name: ARRAY OF CHAR): Files.File;
- VAR
- i, k: LONGINT; res: WORD; f: File; header: DiskAdr; buf: Buffer; head {UNTRACED}: POINTER {UNSAFE} TO FileHeader;
- namebuf: FileName; super: SuperIndex; sub: SubIndex; sec: IndexSector;
- BEGIN {EXCLUSIVE}
- f := NIL; Check(name, namebuf, res);
- IF res = 0 THEN
- dir.Search(namebuf, header);
- IF header # 0 THEN
- NEW(buf); buf.apos := 0; buf.next := buf; buf.mod := FALSE;
- GetSector(vol, header, buf.data);
- head := ADDRESSOF(buf.data);
- NEW(f); f.fs := SELF; f.key := header;
- f.aleng := head.aleng; f.bleng := head.bleng;
- f.time := head.time; f.date := head.date;
- IF f.aleng = 0 THEN buf.lim := f.bleng ELSE buf.lim := SectorSize END;
- f.firstbuf := buf; f.nofbufs := 1;
- f.name := namebuf; f.registered := TRUE;
- f.sec := head.sec;
- k := (f.aleng + (IndexSize-SectorTableSize)) DIV IndexSize;
- IF k # 0 THEN
- NEW(super); super.adr := head.ext; super.mod := FALSE; f.ext := super;
- GetSector(vol, super.adr, sec); i := 0;
- WHILE i # k DO
- NEW(sub); sub.adr := sec.x[i]; sub.mod := FALSE; super.sub[i] := sub;
- GetSector(vol, sub.adr, sub.sec); INC(i)
- END;
- WHILE i # IndexSize DO super.sub[i] := NIL; INC(i) END
- ELSE
- f.ext := NIL
- END;
- f.sechint := header; f.modH := FALSE;
- finalizeFiles.Add(f, Collect); openFiles.Add(f.key)
- END
- END;
- RETURN f
- END Old0;
- PROCEDURE Delete0*(name: ARRAY OF CHAR; VAR key: LONGINT; VAR res: WORD);
- VAR adr: DiskAdr; namebuf: FileName; head: FileHeader;
- BEGIN {EXCLUSIVE}
- Check(name, namebuf, res);
- IF res = 0 THEN
- dir.Delete(namebuf, adr);
- key := adr;
- IF adr # 0 THEN
- IF ~openFiles.Contains(adr) THEN
- PurgeByAdr(adr)
- ELSE
- GetSector(vol, adr, head);
- head.mark := HeaderMark+1; (* invalidate mark *)
- PutSector(vol, adr, head)
- END
- ELSE
- res := 2
- END
- ELSE
- key := 0
- END
- END Delete0;
- PROCEDURE Rename0*(old, new: ARRAY OF CHAR; f: Files.File; VAR res: WORD);
- VAR adr, newAdr: DiskAdr; oldbuf, newbuf: FileName; head: FileHeader;
- BEGIN {EXCLUSIVE}
- Check(old, oldbuf, res);
- IF res = 0 THEN
- Check(new, newbuf, res);
- IF res = 0 THEN
- dir.Delete(oldbuf, adr);
- IF adr # 0 THEN
- dir.Search(newbuf, newAdr); ASSERT(adr # newAdr);
- IF (newAdr # 0) & ~openFiles.Contains(newAdr) THEN
- PurgeByAdr(newAdr)
- END;
- IF f # NIL THEN (* file is open *)
- ASSERT(f.key = adr); (* it's key must match *)
- f(File).name := newbuf
- END;
- dir.Insert(newbuf, adr);
- GetSector(vol, adr, head);
- head.name := newbuf;
- PutSector(vol, adr, head)
- ELSE res := 2
- END
- END
- END
- END Rename0;
- PROCEDURE Enumerate0*(mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator);
- VAR b: BOOLEAN; fh: FileHeader; fn: ARRAY Files.PrefixLength+FileNameLength OF CHAR;
- BEGIN {EXCLUSIVE}
- b := TRUE; enumerate(SELF, mask, DirRootAdr, flags, enum, b, fh, fn)
- END Enumerate0;
- PROCEDURE FileKey*(name: ARRAY OF CHAR): LONGINT;
- VAR res: WORD; namebuf: FileName; header: DiskAdr;
- BEGIN {EXCLUSIVE}
- header := 0;
- Check(name, namebuf, res);
- IF res = 0 THEN
- dir.Search(namebuf, header)
- END;
- RETURN header
- END FileKey;
- (* exlcusive lock must be acquired, result in tempRegFileSec *)
- PROCEDURE CollectRegisteredFileSectors(adr: DiskAdr);
- VAR hd: FileHeader; i, p, m, n: LONGINT; super, sub: IndexSector;
- BEGIN
- tempRegFileSec.Clear;
- GetSector(vol, adr, hd);
- tempRegFileSec.Add(adr);
- ASSERT(hd.sec[0] = adr);
- IF hd.aleng < SectorTableSize THEN m := hd.aleng + 1 ELSE m := SectorTableSize END; p := 1;
- WHILE p < m DO
- IF hd.sec[p] # 0 THEN tempRegFileSec.Add(hd.sec[p]) END;
- INC(p)
- END;
- IF (hd.aleng >= SectorTableSize) & (hd.ext # 0) THEN
- GetSector(vol, hd.ext, super); tempRegFileSec.Add(hd.ext);
- n := (hd.aleng - SectorTableSize) DIV IndexSize; i := 0;
- WHILE i <= n DO
- IF super.x[i] # 0 THEN
- GetSector(vol, super.x[i], sub); tempRegFileSec.Add(super.x[i]);
- IF i < n THEN m := IndexSize
- ELSE m := (hd.aleng - SectorTableSize) MOD IndexSize + 1
- END;
- p := 0;
- WHILE p < m DO
- IF sub.x[p] # 0 THEN tempRegFileSec.Add(sub.x[p]) END;
- INC(p)
- END
- END;
- INC(i)
- END
- END
- END CollectRegisteredFileSectors;
- (* exlcusive lock must be acquired! *)
- PROCEDURE PurgeByAdr(adr: DiskAdr);
- VAR hd: FileHeader; i, p, m, n: LONGINT; super, sub: IndexSector;
- BEGIN
- GetSector(vol, adr, hd);
- FreeSector(vol, adr);
- ASSERT(hd.sec[0] = adr);
- IF hd.aleng < SectorTableSize THEN m := hd.aleng + 1 ELSE m := SectorTableSize END; p := 1;
- WHILE p < m DO
- IF hd.sec[p] # 0 THEN FreeSector(vol, hd.sec[p]) END;
- INC(p)
- END;
- IF (hd.aleng >= SectorTableSize) & (hd.ext # 0) THEN
- GetSector(vol, hd.ext, super); FreeSector(vol, hd.ext);
- n := (hd.aleng - SectorTableSize) DIV IndexSize; i := 0;
- WHILE i <= n DO
- IF super.x[i] # 0 THEN
- GetSector(vol, super.x[i], sub); FreeSector(vol, super.x[i]);
- IF i < n THEN m := IndexSize
- ELSE m := (hd.aleng - SectorTableSize) MOD IndexSize + 1
- END;
- p := 0;
- WHILE p < m DO
- IF sub.x[p] # 0 THEN FreeSector(vol, sub.x[p]) END;
- INC(p)
- END
- END;
- INC(i)
- END
- END
- END PurgeByAdr;
- (* purge all sectors of f except the sectors in 'except', except may be NIL *)
- PROCEDURE PurgeOpenedFile(f: File; except: DiskAdrTable);
- VAR i, p, m, n: LONGINT; super, sub: IndexSector; free: ARRAY 512 OF DiskAdr; freePosition: Files.TSize;
- PROCEDURE StartFreeing;
- BEGIN
- freePosition := 0;
- END StartFreeing;
- PROCEDURE FinishFreeing;
- BEGIN
- vol.FreeBlocks(free,0,freePosition);
- freePosition := 0;
- END FinishFreeing;
- PROCEDURE FreeSector(vol: Files.Volume (* ignored *); sec: LONGINT);
- BEGIN
- free[freePosition] := sec DIV SectorFactor;
- INC(freePosition);
- IF freePosition = LEN(free) THEN
- FinishFreeing;
- END;
- END FreeSector;
- PROCEDURE FreeExcept(sec: DiskAdr);
- BEGIN
- IF (except = NIL) OR ~except.Contains(sec) THEN FreeSector(vol, sec) END
- END FreeExcept;
- BEGIN
- StartFreeing;
- IF f.aleng < SectorTableSize THEN m := f.aleng + 1 ELSE m := SectorTableSize END; p := 0; (* include sec[0] *)
- WHILE p < m DO
- IF f.sec[p] # 0 THEN FreeExcept(f.sec[p]) END;
- INC(p)
- END;
- IF (f.aleng >= SectorTableSize) & (f.ext # NIL) & (f.ext.adr # 0) THEN
- GetSector(vol, f.ext.adr, super); FreeExcept(f.ext.adr);
- n := (f.aleng - SectorTableSize) DIV IndexSize; i := 0;
- WHILE i <= n DO
- IF super.x[i] # 0 THEN
- GetSector(vol, super.x[i], sub); FreeExcept(super.x[i]);
- IF i < n THEN m := IndexSize
- ELSE m := (f.aleng - SectorTableSize) MOD IndexSize + 1
- END;
- p := 0;
- WHILE p < m DO
- IF sub.x[p] # 0 THEN FreeExcept(sub.x[p]) END;
- INC(p)
- END
- END;
- INC(i)
- END
- END;
- FinishFreeing;
- END PurgeOpenedFile;
- PROCEDURE Close(f: File);
- VAR adr: DiskAdr;
- BEGIN {EXCLUSIVE}
- IF f.key # 0 THEN
- ASSERT(openFiles.Contains(f.key));
- openFiles.Remove(f.key);
- dir.Search(f.name, adr);
- IF (adr = 0) OR (adr # f.key) THEN (* deleted or overwritten *)
- PurgeOpenedFile(f, NIL)
- ELSE
- CollectRegisteredFileSectors(adr);
- PurgeOpenedFile(f, tempRegFileSec);
- tempRegFileSec.Clear
- END
- ELSE
- PurgeOpenedFile(f, NIL)
- END
- END Close;
- PROCEDURE Finalize*;
- BEGIN {EXCLUSIVE}
- dir.Cleanup();
- vol.Finalize;
- Finalize^ (* see note in Files *)
- END Finalize;
- END FileSystem;
- DiskAdrArray = POINTER TO ARRAY OF DiskAdr;
- (* analogous to TFClasses.List *)
- DiskAdrList = OBJECT
- VAR
- list : DiskAdrArray;
- count : LONGINT;
- PROCEDURE &New*;
- BEGIN NEW(list, 8); count := 0
- END New;
- PROCEDURE Grow;
- VAR old: DiskAdrArray; i : LONGINT;
- BEGIN
- old := list;
- NEW(list, LEN(list)*2);
- FOR i := 0 TO count-1 DO list[i] := old[i] END
- END Grow;
- PROCEDURE Add(x: DiskAdr);
- BEGIN {EXCLUSIVE}
- ASSERT(x # 0);
- IF count = LEN(list) THEN Grow END;
- list[count] := x;
- INC(count)
- END Add;
- PROCEDURE Remove(x: DiskAdr);
- VAR i : LONGINT;
- BEGIN {EXCLUSIVE}
- ASSERT(x # 0);
- i := 0; WHILE (i < count) & (list[i] # x) DO INC(i) END;
- IF i < count THEN
- WHILE (i < count-1) DO list[i] := list[i+1]; INC(i) END;
- DEC(count);
- list[count] := 0
- END
- END Remove;
- PROCEDURE Contains(x: DiskAdr) : BOOLEAN;
- VAR i: LONGINT;
- BEGIN {EXCLUSIVE}
- i := 0 ; WHILE i < count DO IF list[i] = x THEN RETURN TRUE END; INC(i) END;
- RETURN FALSE
- END Contains;
- END DiskAdrList;
- DiskAdrTable = OBJECT
- VAR
- table : DiskAdrArray;
- count :SIZE;
- size: SIZE; (* cache: invariant size = LEN(table) *)
- CONST
- threshold = 4; (* 1/4 filled -> grow*)
- PROCEDURE &New*;
- BEGIN NEW(table, 8); size := LEN(table)-1; count := 0;
- END New;
- PROCEDURE Clear;
- VAR i: SIZE;
- BEGIN{EXCLUSIVE}
- FOR i := 0 TO LEN(table)-1 DO
- table[i] := 0;
- END;
- count := 0;
- END Clear;
- PROCEDURE Grow;
- VAR old: DiskAdrArray; i,x: LONGINT; index: SIZE;
- BEGIN
- old := table;
- NEW(table, LEN(old)*2); (* filled with zeroes -- ok *)
- size := LEN(table)-1;
- count := 0;
- FOR i := 0 TO LEN(old)-1 DO
- x := old[i];
- IF (x # 0) THEN
- index := HashValue(x);
- IF (table[index] = 0) THEN
- table[index] := x;
- INC(count);
- ELSE
- HALT(100); (* double entry *)
- END;
- END;
- END;
- END Grow;
- PROCEDURE HashValue(key: DiskAdr):SIZE;
- VAR index, h, i := 0 : SIZE;
- BEGIN
- h := key MOD size;
- REPEAT
- index := (h + i) MOD size;
- INC(i);
- UNTIL((table[index] = 0) OR (table[index] = key) OR (i > size));
- ASSERT((table[index] = 0) OR (table[index] = key));
- RETURN index;
- END HashValue;
- PROCEDURE Add(x: DiskAdr);
- VAR index: SIZE;
- BEGIN {EXCLUSIVE}
- ASSERT(x # 0);
- IF count > size DIV threshold THEN Grow END;
- index := HashValue(x);
- IF table[index] = 0 THEN
- table[index] := x;
- INC(count);
- ELSE
- ASSERT(table[index] = x);
- END;
- END Add;
- PROCEDURE Contains(x: DiskAdr) : BOOLEAN;
- BEGIN {EXCLUSIVE}
- RETURN table[HashValue(x)] = x;
- END Contains;
- END DiskAdrTable;
- TYPE
- File = OBJECT (Files.File)
- VAR
- aleng, bleng: LONGINT;
- nofbufs: LONGINT;
- modH, registered: BOOLEAN;
- firstbuf: Buffer;
- sechint: DiskAdr;
- name: FileName;
- time, date: LONGINT;
- ext: SuperIndex;
- sec: SectorTable;
- PROCEDURE Set*(VAR r: Files.Rider; pos: LONGINT);
- VAR a, b: LONGINT;
- BEGIN {EXCLUSIVE}
- r.eof := FALSE; r.res := 0; r.file := SELF; r.fs := fs;
- IF pos < 0 THEN
- a := 0; b := HeaderSize
- ELSIF pos < aleng*SectorSize + bleng - HeaderSize THEN
- a := (pos + HeaderSize) DIV SectorSize; b := (pos + HeaderSize) MOD SectorSize
- ELSE
- a := aleng; b := bleng
- END;
- r.apos := a; r.bpos := b; r.hint := firstbuf
- END Set;
- PROCEDURE Pos*(VAR r: Files.Rider): LONGINT;
- BEGIN
- RETURN r.apos*SectorSize + r.bpos - HeaderSize
- END Pos;
- PROCEDURE Read*(VAR r: Files.Rider; VAR x: CHAR);
- VAR buf: Buffer;
- BEGIN {EXCLUSIVE}
- buf := r.hint(Buffer);
- IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
- IF r.bpos < buf.lim THEN
- x := buf.data.B[r.bpos]; INC(r.bpos)
- ELSIF r.apos < aleng THEN
- INC(r.apos);
- buf := SearchBuf(SELF, r.apos);
- IF buf = NIL THEN
- buf := r.hint(Buffer);
- IF buf.mod THEN WriteBuf(SELF, buf) END ;
- ReadBuf(SELF, buf, r.apos)
- ELSE
- r.hint := buf
- END;
- ASSERT(buf.lim > 0);
- x := buf.data.B[0]; r.bpos := 1
- ELSE
- x := 0X; r.eof := TRUE
- END
- END Read;
- PROCEDURE ReadBytes*(VAR r: Files.Rider; VAR x: ARRAY OF CHAR; ofs, len: LONGINT);
- VAR src: ADDRESS; m: LONGINT; buf: Buffer;
- BEGIN {EXCLUSIVE}
- IF LEN(x)-ofs < len THEN SYSTEM.HALT(19) END;
- IF len > 0 THEN
- buf := r.hint(Buffer);
- IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
- LOOP
- IF len <= 0 THEN EXIT END ;
- src := ADDRESSOF(buf.data.B[0]) + r.bpos; m := r.bpos + len;
- IF m <= buf.lim THEN
- SYSTEM.MOVE(src, ADDRESSOF(x[ofs]), len); r.bpos := m; r.res := 0; EXIT
- ELSIF buf.lim = SectorSize THEN
- m := buf.lim - r.bpos;
- IF m > 0 THEN SYSTEM.MOVE(src, ADDRESSOF(x[ofs]), m); INC(ofs, m); DEC(len, m) END ;
- IF r.apos < aleng THEN
- INC(r.apos); r.bpos := 0; buf := SearchBuf(SELF, r.apos);
- IF buf = NIL THEN
- buf := r.hint(Buffer);
- IF buf.mod THEN WriteBuf(SELF, buf) END ;
- ReadBuf(SELF, buf, r.apos)
- ELSE
- r.hint := buf
- END
- ELSE
- r.bpos := buf.lim; r.res := len; r.eof := TRUE; EXIT
- END
- ELSE
- m := buf.lim - r.bpos;
- IF m > 0 THEN SYSTEM.MOVE(src, ADDRESSOF(x[ofs]), m); r.bpos := buf.lim END ;
- r.res := len - m; r.eof := TRUE; EXIT
- END
- END;
- ELSE
- r.res := 0
- END
- END ReadBytes;
- PROCEDURE Write*(VAR r: Files.Rider; x: CHAR);
- VAR buf: Buffer;
- BEGIN {EXCLUSIVE}
- buf := r.hint(Buffer);
- IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
- IF r.bpos >= buf.lim THEN
- IF r.bpos < SectorSize THEN
- INC(buf.lim); INC(bleng); modH := TRUE
- ELSE
- WriteBuf(SELF, buf); INC(r.apos); buf := SearchBuf(SELF, r.apos);
- IF buf = NIL THEN
- buf := r.hint(Buffer);
- IF r.apos <= aleng THEN
- ReadBuf(SELF, buf, r.apos)
- ELSE
- buf.apos := r.apos; buf.lim := 1; INC(aleng); bleng := 1; modH := TRUE;
- IF (aleng - SectorTableSize) MOD IndexSize = 0 THEN NewSub(SELF) END
- END
- ELSE
- r.hint := buf
- END;
- r.bpos := 0
- END
- END;
- buf.data.B[r.bpos] := x; INC(r.bpos); buf.mod := TRUE
- END Write;
- PROCEDURE WriteBytes*(VAR r: Files.Rider; CONST x: ARRAY OF CHAR; ofs, len: LONGINT);
- VAR dst: ADDRESS; m: LONGINT; buf: Buffer;
- BEGIN {EXCLUSIVE}
- IF LEN(x)-ofs < len THEN SYSTEM.HALT(19) END;
- IF len > 0 THEN
- buf := r.hint(Buffer);
- IF r.apos # buf.apos THEN buf := GetBuf(SELF, r.apos); r.hint := buf END;
- LOOP
- IF len <= 0 THEN EXIT END;
- buf.mod := TRUE; dst := ADDRESSOF(buf.data.B[0]) + r.bpos; m := r.bpos + len;
- IF m <= buf.lim THEN
- SYSTEM.MOVE(ADDRESSOF(x[ofs]), dst, len); r.bpos := m; EXIT
- ELSIF m <= SectorSize THEN
- SYSTEM.MOVE(ADDRESSOF(x[ofs]), dst, len); r.bpos := m;
- bleng := m; buf.lim := m; modH := TRUE; EXIT
- ELSE
- m := SectorSize - r.bpos;
- IF m > 0 THEN SYSTEM.MOVE(ADDRESSOF(x[ofs]), dst, m); INC(ofs, m); DEC(len, m) END;
- WriteBuf(SELF, buf); INC(r.apos); r.bpos := 0; buf := SearchBuf(SELF, r.apos);
- IF buf = NIL THEN
- buf := r.hint(Buffer);
- IF r.apos <= aleng THEN ReadBuf(SELF, buf, r.apos)
- ELSE
- buf.apos := r.apos; buf.lim := 0; INC(aleng); bleng := 0; modH := TRUE;
- IF (aleng - SectorTableSize) MOD IndexSize = 0 THEN NewSub(SELF) END
- END
- ELSE
- r.hint := buf
- END
- END
- END
- END
- END WriteBytes;
- PROCEDURE Length*(): LONGINT;
- BEGIN {EXCLUSIVE}
- RETURN aleng*SectorSize + bleng - HeaderSize
- END Length;
- PROCEDURE GetDate*(VAR t, d: LONGINT);
- BEGIN {EXCLUSIVE}
- t := time; d := date
- END GetDate;
- PROCEDURE SetDate*(t, d: LONGINT);
- BEGIN {EXCLUSIVE}
- modH := TRUE; time := t; date := d
- END SetDate;
- PROCEDURE GetName*(VAR name: ARRAY OF CHAR);
- BEGIN {EXCLUSIVE}
- Files.JoinName(fs.prefix, SELF.name, name)
- END GetName;
- PROCEDURE Register0*(VAR res: WORD);
- VAR oldAdr: DiskAdr; fs0: FileSystem;
- BEGIN {EXCLUSIVE}
- Unbuffer(SELF);
- IF ~registered & (name # "") THEN
- fs0 := fs(FileSystem);
- fs0.dir.Search(name, oldAdr);
- fs0.dir.Insert(name, sec[0]);
- registered := TRUE; key := sec[0];
- fs0.openFiles.Add(key);
- IF (oldAdr # 0) & ~fs0.openFiles.Contains(oldAdr) THEN (* overwrite not opened file *)
- ASSERT(oldAdr # key);
- fs0.PurgeByAdr(oldAdr)
- END;
- res := 0
- ELSE
- res := 1
- END
- END Register0;
- PROCEDURE Update*;
- BEGIN {EXCLUSIVE}
- Unbuffer(SELF)
- END Update;
- END File;
- PROCEDURE Collect(f: ANY);
- VAR file: File; fs: FileSystem;
- BEGIN
- file := f(File);
- IF file.fs # NIL THEN
- fs := file.fs(FileSystem);
- IF (fs.vol # NIL) & ~(Files.ReadOnly IN fs.vol.flags) THEN fs.Close(file) END
- END
- END Collect;
- PROCEDURE GetSector(vol: Files.Volume; src: DiskAdr; VAR dest: DiskSector);
- BEGIN
- IF src MOD SectorFactor # 0 THEN SYSTEM.HALT(15) END;
- vol.GetBlock(src DIV SectorFactor, SYSTEM.VAL(DiskSectorArr, dest))
- END GetSector;
- PROCEDURE PutSector(vol: Files.Volume; dest: DiskAdr; VAR src: DiskSector);
- BEGIN
- ASSERT(~(Files.ReadOnly IN vol.flags));
- IF dest MOD SectorFactor # 0 THEN SYSTEM.HALT(15) END;
- vol.PutBlock(dest DIV SectorFactor, SYSTEM.VAL(DiskSectorArr, src))
- END PutSector;
- PROCEDURE AllocSector(vol: Files.Volume; hint: DiskAdr; VAR sec: DiskAdr);
- BEGIN
- ASSERT(~(Files.ReadOnly IN vol.flags));
- vol.AllocBlock(hint DIV SectorFactor, sec);
- sec := sec * SectorFactor
- END AllocSector;
- PROCEDURE MarkSector(vol: Files.Volume; sec: LONGINT);
- BEGIN
- ASSERT(~(Files.ReadOnly IN vol.flags));
- vol.MarkBlock(sec DIV SectorFactor)
- END MarkSector;
- PROCEDURE FreeSector(vol: Files.Volume; sec: LONGINT);
- BEGIN
- ASSERT(~(Files.ReadOnly IN vol.flags));
- ASSERT(Marked(vol, sec));
- vol.FreeBlock(sec DIV SectorFactor)
- END FreeSector;
- PROCEDURE Marked(vol: Files.Volume; sec: LONGINT): BOOLEAN;
- BEGIN
- ASSERT(~(Files.ReadOnly IN vol.flags));
- RETURN vol.Marked(sec DIV SectorFactor)
- END Marked;
- PROCEDURE Match*(mask, name: ARRAY OF CHAR): BOOLEAN;
- VAR m,n, om, on: LONGINT; f: BOOLEAN;
- BEGIN
- m := 0; n := 0; om := -1;
- f := TRUE;
- LOOP
- IF (mask[m] = "*") THEN
- om := m; INC(m);
- WHILE (name[n] # 0X) & (name[n] # mask[m]) DO INC(n) END;
- on := n
- ELSIF (mask[m] = "?") THEN
- IF (name[n] = 0X) THEN f := FALSE; EXIT END;
- INC(m); INC(n)
- ELSE
- IF (mask[m] # name[n]) THEN
- IF (om = -1) THEN f := FALSE; EXIT
- ELSIF (name[n] # 0X) THEN (* try the next position *)
- m := om; n := on + 1;
- IF (name[n] = 0X) THEN f := FALSE; EXIT END
- ELSE
- f := FALSE; EXIT
- END
- ELSE INC(m); INC(n)
- END
- END;
- IF (mask[m] = 0X) & ((name[n] = 0X) OR (om=-1)) THEN EXIT END
- END;
- RETURN f & (name[n] = 0X)
- END Match;
- 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);
- VAR i, diff: LONGINT; dpg1: DiskAdr; a: DirPage; time, date, size: LONGINT;
- BEGIN
- GetSector(fs.vol, dpg, a); i := 0;
- WHILE (i < a.m) & continue DO
- (* MatchPrefix(mask, a.e[i].name, pos, diff); *)
- IF i = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[i-1].p END;
- IF diff >= 0 THEN (* matching prefix *)
- IF dpg1 # 0 THEN enumerate(fs, mask, dpg1, flags, enum, continue, fh, fn) END;
- IF diff = 0 THEN
- IF continue & ((mask = "") OR Match(mask, a.e[i].name)) THEN
- time := 0; date := 0; size := 0;
- IF flags * {Files.EnumTime, Files.EnumSize} # {} THEN
- GetSector(fs.vol, a.e[i].adr, fh);
- IF Files.EnumTime IN flags THEN
- time := fh.time; date := fh.date
- END;
- IF Files.EnumSize IN flags THEN
- size := fh.aleng*SectorSize + fh.bleng - HeaderSize
- END
- END;
- Files.JoinName(fs.prefix, a.e[i].name, fn);
- enum.PutEntry(fn, {}, time, date, size)
- END
- ELSE continue := FALSE
- END
- END;
- INC(i)
- END;
- IF continue & (i > 0) & (a.e[i-1].p # 0) THEN
- enumerate(fs, mask, a.e[i-1].p, flags, enum, continue, fh, fn)
- END
- END enumerate;
- (* Check a file name. *)
- PROCEDURE Check(VAR s: ARRAY OF CHAR; VAR name: FileName; VAR res: WORD);
- VAR i, k: LONGINT; ch: CHAR;
- BEGIN
- ch := s[0]; i := 0; k := 0;
- IF (ch = 0X) THEN name[0] := 0X; res := -1
- ELSE
- IF (ch = Files.PathDelimiter) THEN k := 1; ch := s[k] END; (* skip first path delimiter *)
- LOOP
- IF (ch < " ") OR (ch = ":") OR (ch = Files.PathDelimiter) THEN res := 3; EXIT END;
- name[i] := ch; INC(i); INC(k); ch := s[k];
- IF (ch = 0X) THEN
- WHILE (i < FileNameLength) DO name[i] := 0X; INC(i) END;
- res := 0; EXIT
- END;
- IF (i = FileNameLength-1) THEN res := 4; EXIT END
- END
- END
- END Check;
- PROCEDURE UpdateHeader(f: File; VAR h: FileHeader);
- BEGIN
- h.aleng := f.aleng; h.bleng := f.bleng;
- h.sec := f.sec;
- IF f.ext # NIL THEN h.ext := f.ext.adr ELSE h.ext := 0 END;
- h.date := f.date; h.time := f.time
- END UpdateHeader;
- PROCEDURE ReadBuf(f: File; buf: Buffer; pos: LONGINT);
- VAR sec: DiskAdr; xpos: LONGINT;
- BEGIN
- IF pos < SectorTableSize THEN
- sec := f.sec[pos]
- ELSE
- xpos := pos-SectorTableSize;
- sec := f.ext.sub[xpos DIV IndexSize].sec.x[xpos MOD IndexSize]
- END;
- GetSector(f.fs.vol, sec, buf.data);
- IF pos < f.aleng THEN buf.lim := SectorSize ELSE buf.lim := f.bleng END;
- buf.apos := pos; buf.mod := FALSE
- END ReadBuf;
- PROCEDURE NewSuper(f: File);
- VAR i: LONGINT; super: SuperIndex;
- BEGIN
- NEW(super); super.adr := 0; super.mod := TRUE; f.modH := TRUE; f.ext := super;
- FOR i := 0 TO IndexSize-1 DO super.sub[i] := NIL END
- END NewSuper;
- PROCEDURE WriteBuf(f: File; buf: Buffer);
- VAR i, k, xpos: LONGINT; secadr: DiskAdr; super: SuperIndex; sub: SubIndex; vol: Files.Volume;
- BEGIN
- vol := f.fs.vol;
- Clock.Get(f.time, f.date); f.modH := TRUE;
- IF buf.apos < SectorTableSize THEN
- secadr := f.sec[buf.apos];
- IF secadr = 0 THEN
- AllocSector(vol, f.sechint, secadr);
- f.modH := TRUE; f.sec[buf.apos] := secadr; f.sechint := secadr
- END;
- IF buf.apos = 0 THEN
- UpdateHeader(f, SYSTEM.VAL(FileHeader, buf.data)); f.modH := FALSE
- END
- ELSE
- super := f.ext;
- IF super = NIL THEN NewSuper(f); super := f.ext END;
- xpos := buf.apos-SectorTableSize;
- i := xpos DIV IndexSize; sub := super.sub[i];
- IF sub = NIL THEN
- NEW(sub); sub.adr := 0; sub.sec.x[0] := 0; super.sub[i] := sub; super.mod := TRUE
- END;
- k := xpos MOD IndexSize; secadr := sub.sec.x[k];
- IF secadr = 0 THEN
- AllocSector(vol, f.sechint, secadr); f.sechint := secadr;
- sub.mod := TRUE; sub.sec.x[k] := secadr
- END
- END;
- PutSector(vol, secadr, buf.data); buf.mod := FALSE
- END WriteBuf;
- PROCEDURE SearchBuf(f: File; pos: LONGINT): Buffer;
- VAR buf: Buffer;
- BEGIN
- buf := f.firstbuf;
- LOOP
- IF buf.apos = pos THEN EXIT END;
- buf := buf.next;
- IF buf = f.firstbuf THEN buf := NIL; EXIT END
- END;
- RETURN buf
- END SearchBuf;
- PROCEDURE GetBuf(f: File; pos: LONGINT): Buffer;
- VAR buf: Buffer;
- BEGIN
- buf := f.firstbuf;
- LOOP
- IF buf.apos = pos THEN EXIT END;
- IF buf.next = f.firstbuf THEN
- IF f.nofbufs < MaxBufs THEN (* allocate new buffer *)
- NEW(buf); buf.next := f.firstbuf.next; f.firstbuf.next := buf;
- INC(f.nofbufs)
- ELSE (* take one of the buffers *)
- f.firstbuf := buf;
- IF buf.mod THEN WriteBuf(f, buf) END
- END;
- buf.apos := pos;
- IF pos <= f.aleng THEN ReadBuf(f, buf, pos) END;
- EXIT
- END;
- buf := buf.next
- END;
- RETURN buf
- END GetBuf;
- PROCEDURE Unbuffer(f: File);
- VAR
- i, k: LONGINT; buf: Buffer; super: SuperIndex; sub: SubIndex; head: FileHeader;
- sec: IndexSector; vol: Files.Volume;
- BEGIN
- vol := f.fs.vol;
- buf := f.firstbuf;
- REPEAT
- IF buf.mod THEN WriteBuf(f, buf) END;
- buf := buf.next
- UNTIL buf = f.firstbuf;
- super := f.ext;
- IF super # NIL THEN
- k := (f.aleng + (IndexSize-SectorTableSize)) DIV IndexSize; i := 0;
- WHILE i # k DO
- sub := super.sub[i]; INC(i);
- IF sub.mod THEN
- IF sub.adr = 0 THEN
- AllocSector(vol, f.sechint, sub.adr); f.sechint := sub.adr;
- super.mod := TRUE
- END;
- PutSector(vol, sub.adr, sub.sec); sub.mod := FALSE
- END
- END;
- IF super.mod THEN
- IF super.adr = 0 THEN
- AllocSector(vol, f.sechint, super.adr); f.sechint := super.adr;
- f.modH := TRUE
- END;
- i := 0;
- WHILE i # k DO sec.x[i] := super.sub[i].adr; INC(i) END;
- WHILE i # IndexSize DO sec.x[i] := 0; INC(i) END;
- PutSector(vol, super.adr, sec); super.mod := FALSE
- END
- END;
- IF f.modH THEN
- GetSector(vol, f.sec[0], head); UpdateHeader(f, head);
- PutSector(vol, f.sec[0], head); f.modH := FALSE
- END
- END Unbuffer;
- PROCEDURE NewSub(f: File);
- VAR i, k: LONGINT; sub: SubIndex;
- BEGIN
- k := (f.aleng - SectorTableSize) DIV IndexSize;
- IF k = IndexSize THEN SYSTEM.HALT(18) END;
- NEW(sub); sub.adr := 0; sub.mod := TRUE;
- FOR i := 0 TO IndexSize-1 DO sub.sec.x[i] := 0 END;
- IF f.ext = NIL THEN NewSuper(f) END;
- f.ext.sub[k] := sub
- END NewSub;
- (** Generate a new file system object. Files.NewVol has volume parameter, Files.Par has mount prefix. *)
- PROCEDURE NewFS*(context : Files.Parameters);
- VAR fs: FileSystem; fh: FileHeader; skipIndexMapWriteback: BOOLEAN; options: ARRAY 8 OF CHAR;
- BEGIN
- (* Get options *)
- context.arg.SkipWhitespace;
- REPEAT UNTIL ~context.arg.GetString(options) OR (options = '|');
- IF context.arg.GetString(options) THEN
- skipIndexMapWriteback := options = 'N'
- END;
- IF Files.This(context.prefix) = NIL THEN
- IF (context.vol.blockSize = SectorSize) & (context.vol.size >= MinVolSize) THEN
- GetSector(context.vol, DirRootAdr, fh);
- IF fh.mark = DirMark THEN (* assume it is an Aos filesystem *)
- NEW(fs); fs.vol := context.vol;
- ASSERT(context.vol.size < MAX(LONGINT) DIV SectorFactor);
- fs.desc := "AosFS";
- NEW(fs.dir, context.vol); (* initialize directory and volume *)
- ASSERT(fs.dir.state = Opened); (* will have to undo changes to vol before continuing *)
- Files.Add(fs, context.prefix);
- IF skipIndexMapWriteback THEN
- INCL(fs.flags, SkipIndexFlag);
- fs.dir.noCleanup := TRUE
- END
- ELSE
- context.error.String("DiskFS: File system not found on ");
- context.error.String(context.vol.name); context.error.Ln
- END
- ELSE
- context.error.String("DiskFS: Bad volume size"); context.error.Ln
- END
- ELSE
- context.error.String("DiskFS: "); context.error.String(context.prefix);
- context.error.String(" already in use"); context.error.Ln
- END;
- END NewFS;
- (* Clean up when module unloaded. *)
- PROCEDURE Cleanup;
- VAR ft: Files.FileSystemTable; i: LONGINT;
- BEGIN
- IF Modules.shutdown = Modules.None THEN
- Files.GetList(ft);
- IF ft # NIL THEN
- FOR i := 0 TO LEN(ft^)-1 DO
- IF ft[i] IS FileSystem THEN Files.Remove(ft[i]) END
- END
- END
- END
- END Cleanup;
- BEGIN
- ASSERT((SIZEOF(FileHeader) = SectorSize) & (SIZEOF(IndexSector) = SectorSize) & (SIZEOF(DataSector) = SectorSize) &
- (SIZEOF(DirPage) = SectorSize) & (SIZEOF(MapIndex) = SectorSize) & (SIZEOF(MapSector) = SectorSize) &
- (DirPgSize MOD 2 = 0));
- Modules.InstallTermHandler(Cleanup);
- END DiskFS.
- (*
- aleng * SectorSize + bleng = length (including header)
- apos * SectorSize + bpos = current position
- 0 <= bpos <= lim <= SectorSize
- 0 <= apos <= aleng < SectorTableSize + IndexSize*IndexSize
- (apos < aleng) & (lim = SectorSize) OR (apos = aleng)
- Methods with {} notation are explicitly unprotected. They must be called only from a protected context.
- *)
- (*
- 04.02.2004 lb Prevent disk space leaks during system run (disk GC)
- 03.01.2006 staubesv Avoid longint overflow that caused disk gc even if not necessary
- *)
|