1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086 |
- (* ported version of Minos to work with the ARM backend of the Fox Compiler Suite *)
- (* ETH Oberon, Copyright 2006 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
- Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
- MODULE OFS; (** non-portable *) (* pjm/bsm/be *)
- (* Oberon file system base - Support for file system and volume implementations. *)
- IMPORT SYSTEM, Kernel, Log, Strings, Trace;
- CONST
- ReadOnly* = 0; Removable* = 1; (** Volume property flags *)
-
- (** Enumerate flags supported by *)
- EnumSize* = 0; (** enable size parameter of EntryHandler. *)
- EnumTime* = 1; (** enable time and data parameters of EntryHandler. *)
- EnumRecursive* = 2; (** enumerate recursively into subdirectories. *)
- EnumStop* = 15; (** stop enumeration (can be set in EntryHandler). *)
- (** return values for a FileSystem's handler procedure *)
- Ok* = 0;
- Unsupported* = -1;
- Error* = 2;
- (* Reserved = 32; (* Blocks reserved for system on Boot volumes *) *)
-
- InvalidAddress = -1;
-
- (* Length of filenames *)
- PreLength = 16; (* Length of filesystem identifier *)
- LocalLength = 128; (* Length of filename without filesystem identifier *)
- FileNameLength = PreLength + LocalLength;
-
- (* TODO: Do this better. At the moment the Bitmap is statically preallocated *)
- BitmapSize = 4096;
- DEBUG = FALSE;
- thisModuleName = "OFS"; (* used for kernel log messages *)
- (* min number of block a valume can have *)
- MinVolSize = 4;
- SF = 29; (* SectorFactor *)
- DirMark* = LONGINT(9B1EA38DH); (* The main directory *)
- HeaderMark = LONGINT(9BA71D86H); (* a data file *)
- MapMark = LONGINT(9C2F977FH); (* a bitmap block *)
-
- DirRootAdr = 1*SF;
- MaxFiles = 10;
- MaxBuffers = 2;
- InitHint = 200*SF;
- (* Change the following parameters to get a different sectorsize *)
- (* From here: 4096 byte blocks *)
- (*SS* = 4096; (* SectorSize *)
- STS = 128; (* SecTabSize *)
- XS = SS DIV 4; (* IndexSize *)
- HS = 568; (* HeaderSize *)
- DirPgSize = 102;
- N = DirPgSize DIV 2;
- FillerSize = 4;*)
- STS = 128;
- SS* = 4096;
- XS = SS DIV 4;
- DiskAdrSize = 4; (* bytes *)
- HS = 4 (* mark *) + LocalLength + 4*4 (* aleng, bleng, time, date *) + (STS+1)*DiskAdrSize;
- DirEntrySize = LocalLength + 2*DiskAdrSize (* adr, p *);
- DirPgHeaderSize = 2*4 (* mark, m *) + DiskAdrSize (* p0 *) + 4 (* min. FillerSize *);
- DirPgSize = (SS - DirPgHeaderSize) DIV DirEntrySize;
- FillerSize = (SS - DirPgHeaderSize) MOD DirEntrySize + 4 (* min. FillerSize *);
- N = DirPgSize DIV 2;
- (* generic bitmap size *)
- MapIndexSize = (SS-4) DIV 4;
- MapSize = SS DIV 4; (* {MapSize MOD 32 = 0} *)
- TYPE
- Array = ARRAY 2000 OF LONGINT;
- (* A callback function which is used in Enumerate *)
- EntryHandler* = PROCEDURE(CONST name: ARRAY OF CHAR; time, date, size: LONGINT; VAR flags: SET);
- (* The basic disc sector, all "real" used disksector types are derived from this *)
- DiskSector* = RECORD END;
- (* Volume is the base type of all volumes. It provides operations on an abstract array of file system
- data blocks of blockSize bytes, numbered from 1 to size. *)
- Volume* = POINTER TO VolumeDesc;
- VolumeDesc* = RECORD
- name*: ARRAY 32 OF CHAR; (* descriptive name - e.g. for matching with Partitions.Show *)
- blockSize*: LONGINT; (* block size in bytes *)
- size*: LONGINT; (* size in blocks *)
- flags*: SET; (* ReadOnly, Removable, Boot *)
- AllocBlock*: PROCEDURE (vol: Volume; hint: LONGINT; VAR adr: LONGINT; VAR res: LONGINT);
- FreeBlock*, MarkBlock*: PROCEDURE (vol: Volume; adr: LONGINT; VAR res: LONGINT);
- (* A the block with number "adr" as used *)
- Marked*: PROCEDURE (vol: Volume; adr: LONGINT; VAR res: LONGINT): BOOLEAN;
- (* Returns the number of available blocks *)
- Available*: PROCEDURE (vol: Volume): LONGINT;
- GetBlock*, PutBlock*: PROCEDURE (vol: Volume; adr: LONGINT; VAR blk: ARRAY OF SYSTEM.BYTE; ofs: LONGINT; VAR res: LONGINT);
- Finalize*: PROCEDURE (vol: Volume);
- Sync*: PROCEDURE(vol: Volume);
- (* used blocks *)
- used: LONGINT;
- (* This bitmap is used to mark in memory if a block is used or not. Currently static allocated... todo: change this *)
- map: POINTER TO ARRAY (*BitmapSize*) OF SET
- END;
-
- (* The name of a mounted filesystem which can be used to address it *)
- Prefix* = ARRAY PreLength OF CHAR;
- (* The physically stored filename without prefix *)
- LocalName* = ARRAY LocalLength OF CHAR;
- (* Filename with prefix and LocalName *)
- FileName* = ARRAY FileNameLength OF CHAR;
- FileSystem* = POINTER TO FileSystemDesc;
- File* = POINTER TO FileDesc;
- Buffer* = POINTER TO BufferRecord;
-
- Rider* = RECORD
- eof*: BOOLEAN; (** has end of file been passed *)
- res*: LONGINT; (** leftover byte count for ReadBytes/WriteBytes *)
- (** private fields for implementors *)
- apos*, bpos*: LONGINT; (* apos: The sectornumber, bpos: The offset in the sector *)
- hint*: Buffer; (* used as a hint for new block allocation. This is basically the current data block *)
- file*: File; (* The file this filesystem is working on *)
- fs*: FileSystem (* The mounted filesystem, this file is working on *)
- END;
-
- FileHd = POINTER TO FileHeader;
- (* Generic disk sector *)
- DataSector = RECORD (DiskSector)
- B: ARRAY SS OF CHAR
- END;
-
- (* A disk sector buffer, used in RAM *)
- BufferRecord* = RECORD
- apos*, lim*: LONGINT; (* apos: sectornumber, lin: current pos in buffer *)
- mod*: BOOLEAN;
- next*: Buffer;
- data*: DataSector
- END;
- (* A physical Index sector *)
- IndexSector = RECORD (DiskSector)
- x: ARRAY XS OF LONGINT
- END;
-
- SectorTable = ARRAY STS OF LONGINT;
-
- FileDesc* = RECORD
- key*: LONGINT; (* unique id for registered file, never 0, basically the blocknumber of the file header *)
- fs*: FileSystem; (* file system containing file *)
- next*: File;
- aleng, bleng: LONGINT; (* aleng: number of used sectors, blen: pos in last used sector *)
- nofbufs: LONGINT; (* number of data buffers *)
- modH, registered: BOOLEAN; (* modH: File header modified flag, registered: This file has already been registered *)
- sechint: LONGINT; (* the number of the last block allocated to this file *)
- name: FileName; (* The filename *)
- time: LONGINT; (* The last access time *)
- ext: LONGINT; (* number of sector representing the superIndex *)
- firstbuf*: Buffer; (* the one and only data buffer *)
- sec: SectorTable (* table that contains the addresses of the directly addressable sectors *)
- END;
- FileSystemDesc* = RECORD
- next: FileSystem; (* file system search path *)
- link: FileSystem; (* list of mounted file systems *)
- prefix*: Prefix; (** mount prefix *)
- desc*: ARRAY 32 OF CHAR; (** description of file system *)
- vol*: Volume (** underlying volume, if any (a boot FS must have a volume) *)
- END;
-
- FileHeader = RECORD (DiskSector) (* allocated in the first page of each file on disk *)
- mark: LONGINT; (* The mark distinguishes the type of the sector. In this case always a file *)
- name: LocalName;
- aleng, bleng: LONGINT;
- date, time: LONGINT;
- sec: SectorTable; (* direct addressable sectors *)
- ext: LONGINT; (* number of sector that contains the indirect addressable sectors *)
- data: ARRAY SS - HS OF CHAR (* File data to fill the FileHeader *)
- END;
- DirEntry = RECORD (*B-tree node*) (* a directory entry *)
- name: LocalName;
- adr: LONGINT; (*sec no of file header*)
- p: LONGINT (*sec no of left descendant (DirPage) in directory, used if the name is *)
- END;
- DirPage = RECORD (DiskSector) (* A disk sector that contains Directory entries *)
- mark: LONGINT; (* The mark distinguishes the type of the sector. In this case a directory *)
- m: LONGINT; (* number of stored elements *)
- p0: LONGINT; (* sec no of left descendant (DirPage) in directory. Contains the files with names
- "smaller" than e[0] *)
- fill: ARRAY FillerSize OF CHAR;
- e: ARRAY DirPgSize OF DirEntry
- END;
- (* Header of the bitmap which is stored to disk. *)
- (* The bitmap is always indirectly addressed *)
- MapIndex = RECORD (DiskSector)
- mark: LONGINT;
- index: ARRAY MapIndexSize OF LONGINT
- END;
-
- (* a sector which actually contains a part of the bitmap *)
- MapSector = RECORD (DiskSector)
- map: ARRAY MapSize OF SET
- END;
-
- VAR
- (* The one and only directory *)
- hp: FileHeader;
- fullname: FileName;
- fsroot: FileSystem; (* file system search path root *)
- fsmount: FileSystem; (* list of known file systems *)
- froot: File; (* list of known open files, with dummy head *)
-
- filePool: File; (* A list of files. The number of elements in this pool determine the maximum number of files that can be opened *)
- (* bufPool: Buffer; (* A list of buffers. Contains MaxFiles*MaxBuffers elements *) *)
- (** Predefined volume methods (need not be used). *)
- PROCEDURE First*(): FileSystem;
- BEGIN
- RETURN fsroot
- END First;
- (* Join prefix and name to fullname = ( prefix ":" name ) *)
- PROCEDURE JoinName*(CONST prefix, name: ARRAY OF CHAR; VAR fullname: ARRAY OF CHAR);
- VAR
- i, j: LONGINT;
- BEGIN
- i := 0; WHILE prefix[i] # 0X DO fullname[i] := prefix[i]; INC(i) END;
- fullname[i] := ':'; INC(i);
- j := 0; WHILE name[j] # 0X DO fullname[i] := name[j]; INC(i); INC(j) END;
- fullname[i] := 0X
- END JoinName;
- PROCEDURE FSGetSector(vol: Volume; src: LONGINT; VAR dest: DiskSector);
- VAR
- res: LONGINT;
- BEGIN
- (*
- IF src MOD SF # 0 THEN
- HALT(15)
- END;
- *)
- vol.GetBlock(vol, src DIV SF, dest, 0, res);
- END FSGetSector;
- PROCEDURE FSPutSector(vol: Volume; dest: LONGINT; VAR src: DiskSector);
- VAR
- res: LONGINT;
- BEGIN
- (*
- ASSERT(~(ReadOnly IN vol.flags));
- IF dest MOD SF # 0 THEN
- HALT(15)
- END;
- *)
- vol.PutBlock(vol, dest DIV SF, src, 0, res);
- END FSPutSector;
- PROCEDURE FSAllocSector(vol: Volume; hint: LONGINT; VAR sec: LONGINT);
- VAR
- res: LONGINT;
- BEGIN
- ASSERT(~(ReadOnly IN vol.flags));
- vol.AllocBlock(vol, hint DIV SF, sec, res);
- sec := sec * SF
- END FSAllocSector;
- PROCEDURE FSMarkSector(vol: Volume; sec: LONGINT);
- VAR
- res: LONGINT;
- BEGIN
- ASSERT(~(ReadOnly IN vol.flags));
- vol.MarkBlock(vol, sec DIV SF, res)
- END FSMarkSector;
- PROCEDURE FSFreeSector(vol: Volume; sec: LONGINT);
- VAR
- res: LONGINT;
- BEGIN
- ASSERT(~(ReadOnly IN vol.flags));
- vol.FreeBlock(vol, sec DIV SF, res)
- END FSFreeSector;
- PROCEDURE FSMarked(vol: Volume; sec: LONGINT): BOOLEAN;
- VAR
- res: LONGINT;
- BEGIN
- ASSERT(~(ReadOnly IN vol.flags));
- RETURN vol.Marked(vol, sec DIV SF, res)
- END FSMarked;
- PROCEDURE FSSize(vol: Volume): LONGINT;
- BEGIN
- ASSERT(vol.size >= MinVolSize);
- RETURN vol.size
- END FSSize;
- (* Search a file in a specific volume, A contains the address of the file. 0 if not found *)
- PROCEDURE Search(vol: Volume; CONST name: FileName; VAR A: LONGINT);
- VAR i, L, R: LONGINT; dadr: LONGINT; a: DirPage;
- BEGIN
- A := -1;
- dadr := DirRootAdr;
- REPEAT
- FSGetSector(vol, dadr, a);
- ASSERT(a.mark = DirMark);
- L := 0; R := a.m; (*binary search*)
- WHILE L < R DO
- i := (L+R) DIV 2;
- IF Strings.Compare(name, a.e[i].name) <= 0 THEN R := i ELSE L := i+1 END
- END ;
- IF (R < a.m) & (name = a.e[R].name) THEN
- A := a.e[R].adr;
- ELSE
- IF R = 0 THEN dadr := a.p0 ELSE dadr := a.e[R-1].p END ;
- IF dadr = 0 THEN A := 0; END;
- END;
- UNTIL A >= 0;
- END Search;
- PROCEDURE CopyDirEntry(CONST src: DirEntry; VAR dest: DirEntry );
- BEGIN
- Strings.Copy( src.name, dest.name);
- dest.adr := src.adr;
- dest.p := src.p;
- END CopyDirEntry;
- PROCEDURE insert(vol: Volume; CONST name: FileName; dpg0: LONGINT;
- VAR h: BOOLEAN; VAR v: DirEntry; fad: LONGINT; VAR replacedFad: LONGINT (*gc*));
- (*h = "tree has become higher and v is ascending element"*)
- VAR ch: CHAR; i, j, L, R: LONGINT; dpg1: LONGINT; u: DirEntry; a: DirPage;
- BEGIN (*~h*)
- FSGetSector(vol, dpg0, a);
- L := 0; R := a.m; (*binary search*)
- WHILE L < R DO
- i := (L+R) DIV 2;
- IF Strings.Compare(name, a.e[i].name) <= 0 THEN R := i ELSE L := i+1 END
- END ;
- replacedFad := 0;
- IF (R < a.m) & (name = a.e[R].name) THEN
- replacedFad := a.e[R].adr; (*gc*)
- a.e[R].adr := fad; FSPutSector(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 < LocalLength DO u.name[j] := 0X; INC(j) END
- ELSE
- insert(vol, name, dpg1, h, u, fad, replacedFad)
- 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); CopyDirEntry( a.e[i], a.e[i+1]) END;
- CopyDirEntry(u, a.e[R]); 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*)
- CopyDirEntry( a.e[N-1], v); i := N-1;
- WHILE i > R DO DEC(i); CopyDirEntry( a.e[i], a.e[i+1]) END ;
- CopyDirEntry(u, a.e[R]); FSPutSector(vol, dpg0, a);
- FSAllocSector(vol, dpg0, dpg0); i := 0;
- WHILE i < N DO CopyDirEntry(a.e[i+N], a.e[i]); INC(i) END
- ELSE (*insert in right half*)
- FSPutSector(vol, dpg0, a);
- FSAllocSector(vol, dpg0, dpg0); DEC(R, N); i := 0;
- IF R = 0 THEN CopyDirEntry(u, v);
- ELSE CopyDirEntry(a.e[N], v);
- WHILE i < R-1 DO CopyDirEntry(a.e[N+1+i], a.e[i]); INC(i) END ;
- CopyDirEntry(u, a.e[i]); INC(i)
- END ;
- WHILE i < N DO CopyDirEntry(a.e[N+i], a.e[i]); INC(i) END
- END ;
- a.p0 := v.p; v.p := dpg0
- END ;
- FSPutSector(vol, dpg0, a)
- END
- END
- END insert;
- PROCEDURE Insert(vol: Volume; CONST name: FileName; fad: LONGINT; VAR replacedFad: LONGINT);
- VAR oldroot: LONGINT; h: BOOLEAN; U: DirEntry; a: DirPage;
- BEGIN
- h := FALSE;
- insert(vol, name, DirRootAdr, h, U, fad, replacedFad); (*gc*)
- IF h THEN (*root overflow*)
- FSGetSector(vol, DirRootAdr, a);
- FSAllocSector(vol, DirRootAdr, oldroot); FSPutSector(vol, oldroot, a);
- a.mark := DirMark; a.m := 1; a.p0 := oldroot; CopyDirEntry(U, a.e[0]);
- FSPutSector(vol, DirRootAdr, a)
- END
- END Insert;
- PROCEDURE underflow(vol: Volume; VAR c: DirPage; (*ancestor page*) dpg0: LONGINT;
- s: LONGINT; (*insertion point in c*) VAR h: BOOLEAN); (*c undersize*)
- VAR i, k: LONGINT; dpg1: LONGINT; a, b: DirPage; (*a := underflowing page, b := neighbouring page*)
- BEGIN
- FSGetSector(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; FSGetSector(vol, dpg1, b);
- k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
- CopyDirEntry(c.e[s], a.e[N-1]); 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 CopyDirEntry(b.e[i], a.e[i+N]); INC(i) END ;
- CopyDirEntry(b.e[i], c.e[s]); b.p0 := c.e[s].p;
- c.e[s].p := dpg1; b.m := b.m - k; i := 0;
- WHILE i < b.m DO CopyDirEntry(b.e[i+k], b.e[i]); INC(i) END ;
- FSPutSector(vol, dpg1, b); a.m := N-1+k; h := FALSE
- ELSE (*merge pages a and b, discard b*) i := 0;
- WHILE i < N DO CopyDirEntry(b.e[i], a.e[i+N]); INC(i) END ;
- i := s; DEC(c.m);
- WHILE i < c.m DO CopyDirEntry(c.e[i+1], c.e[i]); INC(i) END ;
- a.m := 2*N; h := c.m < N;
- FSFreeSector(vol, dpg1) (* added by tt *)
- END ;
- FSPutSector(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 ;
- FSGetSector(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); CopyDirEntry(a.e[i], a.e[i+k]); END ;
- i := k-1; CopyDirEntry(c.e[s], a.e[i]); a.e[i].p := a.p0;
- (*move k-1 items from b to a, one to c*) b.m := b.m -k;
- WHILE i > 0 DO DEC(i); CopyDirEntry(b.e[i+b.m+1], a.e[i]); END ;
- CopyDirEntry(b.e[b.m], c.e[s]); a.p0 := c.e[s].p;
- c.e[s].p := dpg0; a.m := N-1+k; h := FALSE;
- FSPutSector(vol, dpg0, a)
- ELSE (*merge pages a and b, discard a*)
- c.e[s].p := a.p0; CopyDirEntry(c.e[s], b.e[N]); i := 0;
- WHILE i < N-1 DO CopyDirEntry(a.e[i], b.e[i+N+1]); INC(i) END ;
- b.m := 2*N; DEC(c.m); h := c.m < N;
- FSFreeSector(vol, dpg0) (* added by tt*)
- END ;
- FSPutSector(vol, dpg1, b)
- END
- END underflow;
- PROCEDURE del(vol: Volume; dpg1: LONGINT; VAR h: BOOLEAN; VAR a: DirPage; VAR R: LONGINT);
- VAR dpg2: LONGINT; (*global: a, R*) b: DirPage;
- BEGIN
- FSGetSector(vol, dpg1, b); dpg2 := b.e[b.m-1].p;
- IF dpg2 # 0 THEN del(vol, dpg2, h, a, R);
- IF h THEN underflow(vol, b, dpg2, b.m, h); FSPutSector(vol, dpg1, b) END
- ELSE
- b.e[b.m-1].p := a.e[R].p; CopyDirEntry(b.e[b.m-1], a.e[R]);
- DEC(b.m); h := b.m < N; FSPutSector(vol, dpg1, b)
- END
- END del;
- PROCEDURE delete(vol: Volume; CONST name: FileName; dpg0: LONGINT; VAR h: BOOLEAN; VAR fad: LONGINT);
- (*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: LONGINT; a: DirPage;
- BEGIN (*~h*)
- FSGetSector(vol, dpg0, a);
- L := 0; R := a.m; (*binary search*)
- WHILE L < R DO
- i := (L+R) DIV 2;
- IF Strings.Compare(name, a.e[i].name) <= 0 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 CopyDirEntry(a.e[i+1], a.e[i]); INC(i) END
- ELSE del(vol, dpg1, h, a, R);
- IF h THEN underflow(vol, a, dpg1, R, h) END
- END ;
- FSPutSector(vol, dpg0, a)
- ELSIF dpg1 # 0 THEN
- delete(vol, name, dpg1, h, fad);
- IF h THEN underflow(vol, a, dpg1, R, h); FSPutSector(vol, dpg0, a) END
- ELSE (*not in tree*) fad := 0
- END
- END delete;
- (* Remove the file "name" from the volume "vol". "fad" contains the address
- of the found file header block or 0 if the file was not found *)
- PROCEDURE DirDelete(vol: Volume; CONST name: FileName; VAR fad: LONGINT);
- VAR h: BOOLEAN; newroot: LONGINT; a: DirPage;
- BEGIN
- h := FALSE;
- delete(vol, name, DirRootAdr, h, fad);
- IF h THEN (*root underflow*)
- FSGetSector(vol, DirRootAdr, a);
- IF (a.m = 0) & (a.p0 # 0) THEN
- newroot := a.p0; FSGetSector(vol, newroot, a);
- FSPutSector(vol, DirRootAdr, a); (*discard newroot*)
- FSFreeSector(vol, newroot); (* added by tt *)
- END
- END
- END DirDelete;
- PROCEDURE MatchPrefix(CONST mask, name: ARRAY OF CHAR; VAR pos, diff: LONGINT);
- VAR
- done: BOOLEAN;
- BEGIN
- done := FALSE;
- pos := 0;
- REPEAT
- IF mask[pos] = 0X THEN
- pos := -1; diff := 0;
- done := TRUE;
- ELSIF mask[pos] = '*' THEN
- IF mask[pos+1] = 0X THEN pos := -1 END;
- diff := 0;
- done := TRUE;
- END;
- IF ~done THEN
- diff := ORD(name[pos]) - ORD(mask[pos]);
- IF diff # 0 THEN done := TRUE; ELSE INC(pos); END;
- END;
- UNTIL done;
- END MatchPrefix;
- (* This is procedure is ugly... Should be rewritten some time ... *)
- PROCEDURE Match(pos: LONGINT; CONST pat, name: ARRAY OF CHAR): BOOLEAN;
- VAR i0, i1, j0, j1: LONGINT; f, done: BOOLEAN;
- BEGIN
- f := TRUE;
- done := FALSE;
- IF pos # -1 THEN
- i0 := pos; j0 := pos;
- REPEAT
- IF pat[i0] = '*' THEN
- INC(i0);
- IF pat[i0] = 0X THEN done := TRUE; END
- ELSE
- IF name[j0] # 0X THEN f := FALSE END;
- done := TRUE;
- END;
- IF ~done THEN
- f := FALSE;
- WHILE (name[j0] # 0X) & ~f DO
- i1 := i0; j1 := j0;
- WHILE( (pat[i1] # 0X) & (pat[i1] # '*') & (pat[i1] = name[j1])) DO
- INC(i1); INC(j1)
- END;
-
- IF (pat[i1] = 0X) OR (pat[i1] = '*') THEN
- f := TRUE; j0 := j1; i0 := i1;
- ELSE
- INC(j0)
- END;
- END;
- IF ~f THEN done := TRUE; END
- END;
- UNTIL done;
- END;
- RETURN f & (name[0] # 0X)
- END Match;
- PROCEDURE enumerate(fs: FileSystem; CONST mask: ARRAY OF CHAR; dpg: LONGINT; VAR flags: SET; proc: EntryHandler);
- VAR i, pos, diff: LONGINT; dpg1: LONGINT; a: DirPage; time, date, size: LONGINT;
- BEGIN
- FSGetSector(fs.vol, dpg, a); i := 0;
- WHILE (i < a.m) & ~(EnumStop IN flags) 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, proc) END;
- IF diff = 0 THEN
- IF ~(EnumStop IN flags) & Match(pos, mask, a.e[i].name) THEN
- IF flags * {EnumSize, EnumTime} # {} THEN
- FSGetSector(fs.vol, a.e[i].adr, hp);
- time := hp.time; date := hp.date;
- size := hp.aleng*SS + hp.bleng - HS
- ELSE
- time := 0; date := 0; size := MIN(LONGINT)
- END;
- IF fs = fsroot THEN
- proc(a.e[i].name, time, date, size, flags)
- ELSE
- JoinName(fs.prefix, a.e[i].name, fullname);
- proc(fullname, time, date, size, flags)
- END
- END
- ELSE flags := flags + {EnumStop}
- END
- END;
- INC(i)
- END;
- IF ~(EnumStop IN flags) & (i > 0) & (a.e[i-1].p # 0) THEN
- enumerate(fs, mask, a.e[i-1].p, flags, proc)
- END
- END enumerate;
- PROCEDURE sift(L, R: LONGINT; VAR A: Array);
- VAR i, j: LONGINT; x: LONGINT; exit: BOOLEAN;
- BEGIN
- j := L; x := A[j];
- exit := FALSE;
- REPEAT
- 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 := TRUE;
- ELSE A[i] := A[j] END;
- UNTIL exit;
- A[i] := x
- END sift;
- PROCEDURE MarkSectors( vol: Volume; VAR k: LONGINT; VAR A: Array; VAR bad: BOOLEAN; VAR files: LONGINT );
- VAR L, R, i, j, n: LONGINT; x: LONGINT; hd: FileHeader; sup, sub: IndexSector;
- BEGIN
- Log.vS(" marking");
- L := k DIV 2; R := k; (*heapsort*)
- WHILE L > 0 DO DEC(L); sift(L, R, A) END ;
- WHILE R > 0 DO
- DEC(R); x := A[0]; A[0] := A[R]; A[R] := x; sift(L, R, A)
- END;
- WHILE L < k DO
- bad := FALSE; INC(files);
- IF files MOD 128 = 0 THEN Log.vC('.') END;
- FSGetSector(vol, A[L], hd);
- IF hd.aleng < STS THEN
- j := hd.aleng + 1;
- REPEAT
- DEC(j);
- IF hd.sec[j] # 0 THEN FSMarkSector(vol, hd.sec[j]) ELSE hd.aleng := j-1; bad := TRUE END
- UNTIL j = 0
- ELSE
- j := STS;
- REPEAT
- DEC(j);
- IF hd.sec[j] # 0 THEN FSMarkSector(vol, hd.sec[j]) ELSE hd.aleng := j-1; bad := TRUE END
- UNTIL j = 0;
- IF hd.ext = 0 THEN hd.aleng := STS-1; bad := TRUE END;
- IF ~bad THEN
- FSMarkSector(vol, hd.ext); FSGetSector(vol, hd.ext, sup);
- n := (hd.aleng - STS) DIV XS; i := 0;
- WHILE (i <= n) & ~bad DO
- IF sup.x[i] # 0 THEN
- FSMarkSector(vol, sup.x[i]); FSGetSector(vol, sup.x[i], sub);
- IF i < n THEN j := XS
- ELSE j := (hd.aleng - STS) MOD XS + 1
- END;
- REPEAT
- DEC(j);
- IF (sub.x[j] MOD SF = 0) & (sub.x[j] > 0) THEN
- FSMarkSector(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 := STS-1
- ELSE hd.aleng := STS + (i-1) * XS
- END
- END
- END
- END
- END;
- IF bad THEN
- Log.L; Log.S(hd.name); Log.S(" truncated");
- hd.bleng := SS; IF hd.aleng < 0 THEN hd.aleng := 0 (* really bad *) END;
- FSPutSector(vol, A[L], hd)
- END;
- INC(L)
- END
- END MarkSectors;
- PROCEDURE TraverseDir(vol: Volume; dpg: LONGINT; VAR A: Array; VAR k: LONGINT; VAR bad: BOOLEAN; VAR files: LONGINT);
- VAR i: LONGINT; a: DirPage;
- BEGIN
- FSGetSector(vol, dpg, a); FSMarkSector(vol, dpg); i := 0;
- WHILE i < a.m DO
- A[k] := a.e[i].adr; INC(k); INC(i);
- IF k = 2000 THEN MarkSectors(vol, k, A, bad, files); k := 0 END
- END ;
- IF a.p0 # 0 THEN
- TraverseDir(vol, a.p0, A, k, bad, files); i := 0;
- WHILE i < a.m DO
- TraverseDir(vol, a.e[i].p, A, k, bad, files); INC(i)
- END
- END
- END TraverseDir;
- PROCEDURE DirStartup(vol: Volume; VAR init: BOOLEAN);
- VAR
- j, sec, size, q, free, thres: LONGINT; mi: MapIndex; ms: MapSector;
- found, done: BOOLEAN;
- BEGIN
- size := FSSize(vol); init := FALSE; found := FALSE;
- IF (vol.Available(vol) = size) & (size # 0) THEN (* all sectors available *)
- FSGetSector(vol, size*SF, mi);
- IF mi.mark = MapMark THEN
- j := 0; (* check consistency of index *)
- WHILE (j # MapIndexSize) & (mi.index[j] >= 0) & (mi.index[j] MOD SF = 0) DO
- INC(j)
- END;
- IF j = MapIndexSize THEN
- found := TRUE;
- mi.mark := 0; FSPutSector(vol, size*SF, mi); (* invalidate index *)
- j := 0; sec := 1; q := 0;
- done := FALSE;
- WHILE ~((j = MapIndexSize) OR (mi.index[j] = 0)) & ~done DO
- FSGetSector(vol, mi.index[j], ms);
- REPEAT
- IF (sec MOD 32) IN ms.map[sec DIV 32 MOD MapSize] THEN
- FSMarkSector(vol, sec*SF);
- INC(q)
- END;
- IF sec = size THEN done := TRUE
- ELSE INC(sec) END;
- UNTIL (sec MOD (MapSize*32) = 0) OR done;
- INC(j)
- END;
- (* Kernel.GetConfig("DiskGC", s); todo: What is this??
- 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;
- *)
-
- thres := 10;
- ASSERT(q = size-vol.Available(vol));
- free := vol.Available(vol)*100 DIV size;
- IF (free > thres) & (vol.Available(vol)*SS > 10000H) THEN
- init := TRUE
- ELSE (* undo *)
- FOR j := SF TO size*SF BY SF DO
- IF FSMarked(vol, j) THEN FSFreeSector(vol, j) END
- END;
- ASSERT(vol.Available(vol) = size);
- Log.S(thisModuleName);
- Log.S(': '); Log.I(free);
- Log.S("% free, forcing disk GC on ");
- Log.S(vol.name); Log.L
- END
- END
- END;
- (*IF ~found THEN
- Kernel.WriteString(thisModuleName);
- Kernel.WriteString(": Index not found on ");
- Kernel.WriteString(vol.name); Kernel.WriteLn
- END*)
- END;
- END DirStartup;
- PROCEDURE DirInit(vol: Volume; VAR init: BOOLEAN);
- VAR k: LONGINT; A: ARRAY 2000 OF LONGINT; files: LONGINT; bad: BOOLEAN;
- BEGIN
- IF ~(ReadOnly IN vol.flags) THEN
- k := 0; init := FALSE;
- DirStartup(vol, init);
- IF ~init THEN
- files := 0;
- Log.S(thisModuleName);
- Log.S(": Scanning ");
- Log.S(vol.name); Log.S("...");
- TraverseDir(vol, DirRootAdr, A,k,bad,files);
- MarkSectors(vol, k, A, bad, files);
- init := TRUE;
- Log.I(files); Log.S(" files"); Log.L
- END
- ELSE
- init := TRUE
- END;
- END DirInit;
- PROCEDURE DirCleanup(vol: Volume);
- VAR i, j, p, q, sec, size: LONGINT; mi: MapIndex; ms: MapSector;
- abort, exit: BOOLEAN;
- BEGIN
- abort := FALSE; exit := FALSE;
- size := FSSize(vol); i := size*SF;
- IF ~(ReadOnly IN vol.flags) & ~FSMarked(vol, i) THEN (* last sector is free *)
- j := 0; sec := 1; q := 0;
-
- WHILE ~abort & ~exit DO
- REPEAT DEC(i, SF) UNTIL (i = 0) OR ~FSMarked(vol, i); (* find a free sector *)
- IF i = 0 THEN abort := TRUE;
- ELSE (* 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 FSMarked(vol, sec*SF) THEN
- ms.map[sec DIV 32 MOD MapSize] := ms.map[sec DIV 32 MOD MapSize] + {sec MOD 32};
- INC(q)
- END;
- IF sec = size THEN
- FSPutSector(vol, i, ms);
- exit := TRUE;
- END;
- INC(sec)
- UNTIL (sec MOD (MapSize*32) = 0) OR exit;
- IF ~abort & ~exit THEN
- FSPutSector(vol, i, ms)
- END;
- END;
- END;
- IF ~abort THEN
- WHILE j # MapIndexSize DO mi.index[j] := 0; INC(j) END;
- mi.mark := MapMark;
- FSPutSector(vol, size*SF, mi); (* commit *)
- Log.S(thisModuleName);
- Log.S(": Map saved on ");
- Log.S(vol.name); Log.L
- END;
- END
- END DirCleanup;
- (* Check a file name. *)
- PROCEDURE Check(CONST s: ARRAY OF CHAR; VAR name: FileName; VAR res: LONGINT);
- VAR i: LONGINT; ch: CHAR; exit: BOOLEAN;
- BEGIN
- ch := s[0]; i := 0;
- IF ('A' <= CAP(ch)) & (CAP(ch) <= 'Z') THEN
- exit := FALSE;
- REPEAT
- name[i] := ch; INC(i); ch := s[i];
- IF ch = 0X THEN
- WHILE i < LocalLength DO name[i] := 0X; INC(i) END ;
- res := 0;
- exit := TRUE;
- END ;
- IF ~exit THEN
- IF ~(('A' <= CAP(ch)) & (CAP(ch) <= 'Z')
- OR ('0' <= ch) & (ch <= '9') OR (ch = '.')) THEN res := 3; exit := TRUE;
- END ;
- IF (i = LocalLength-1) & ~exit THEN res := 4; exit := TRUE END
- END;
- UNTIL exit;
- ELSIF ch = 0X THEN name[0] := 0X; res := -1
- ELSE res := 3
- END
- END Check;
- (* Creates a new file with the specified name. *)
- PROCEDURE FSNew(fs: FileSystem; CONST name: ARRAY OF CHAR): File;
- VAR i, res: LONGINT; f: File; head: FileHd; namebuf: FileName; buf: Buffer;
- BEGIN
- f := NIL; Check(name, namebuf, res);
- IF (res <= 0) & (filePool # NIL) THEN
- f := filePool; filePool := filePool.next;
-
- (* Invalidate buffers *)
- buf := f.firstbuf;
- REPEAT buf.apos := InvalidAddress; buf := buf.next; UNTIL buf = f.firstbuf;
-
- buf := f.firstbuf;
-
- FOR i := 0 TO SS - 1 DO
- buf.data.B[i] := 0X;
- END;
- buf.apos := 0; buf.mod := TRUE; buf.lim := HS;
- SYSTEM.PUT (ADDRESSOF (head), ADDRESSOF(buf.data));
- head.mark := HeaderMark;
- head.aleng := 0; head.bleng := HS; Strings.Copy(namebuf, head.name);
- head.time := Kernel.GetTime();
- f.fs := fs; f.key := 0; f.aleng := 0; f.bleng := HS; f.modH := TRUE;
- f.time := head.time;
- Strings.Copy(namebuf, f.name); f.sechint := InitHint;
- f.registered := FALSE;
- f.ext := 0; i := 0;
- REPEAT f.sec[i] := 0; head.sec[i] := 0; INC(i) UNTIL i = STS
- END;
- RETURN f
- END FSNew;
- PROCEDURE CopySectorTable(CONST src: ARRAY OF LONGINT; VAR dest: ARRAY OF LONGINT );
- VAR
- i: LONGINT;
- BEGIN
- FOR i := 0 TO STS -1 DO
- dest[i] := src[i];
- END;
- END CopySectorTable;
- (* Store the fileheader of file f in h *)
- PROCEDURE UpdateHeader(f: File; VAR h: FileHeader);
- BEGIN
- h.aleng := f.aleng; h.bleng := f.bleng;
- CopySectorTable(f.sec, h.sec);
- h.ext := f.ext;
- h.time := f.time
- END UpdateHeader;
- PROCEDURE ReadBuf(f: File; buf: Buffer; pos: LONGINT);
- VAR sec: LONGINT; xpos: LONGINT;
- index: IndexSector;
- BEGIN
- IF pos < STS THEN
- sec := f.sec[pos]
- ELSE
- xpos := pos-STS;
- (* replaced: sec := f.ext.sub[xpos DIV XS].sec.x[xpos MOD XS] *)
- FSGetSector(f.fs.vol, f.ext, index);
- FSGetSector(f.fs.vol, index.x[xpos DIV XS], index);
- sec := index.x[xpos MOD XS];
- END;
- FSGetSector(f.fs.vol, sec, buf.data);
- IF pos < f.aleng THEN buf.lim := SS ELSE buf.lim := f.bleng END;
- buf.apos := pos; buf.mod := FALSE
- END ReadBuf;
- PROCEDURE NewSuper(f: File);
- VAR i: LONGINT; super: LONGINT; sec: IndexSector;
- BEGIN
- FSAllocSector(f.fs.vol, f.key, super);
- f.ext := super;
-
- (* Clear sector *)
- FOR i := 0 TO XS-1 DO sec.x[i] := 0 END;
- FSPutSector(f.fs.vol, super, sec);
- END NewSuper;
- PROCEDURE WriteBuf(f: File; buf: Buffer);
- VAR i, j, k, xpos: LONGINT; secadr: LONGINT; super, sub: LONGINT; vol: Volume;
- ptr: FileHd; src, dst: LONGINT; tempFileHeader: FileHeader;
- superSec, subSec: IndexSector;
- BEGIN
- vol := f.fs.vol;
- f.time := Kernel.GetTime(); f.modH := TRUE;
- IF buf.apos < STS THEN
- secadr := f.sec[buf.apos];
- IF secadr = 0 THEN (* This buffer has never been written to disk, allocate one on the disk *)
- FSAllocSector(vol, f.sechint, secadr);
- f.modH := TRUE; f.sec[buf.apos] := secadr; f.sechint := secadr
- END;
- IF buf.apos = 0 THEN (* If this sector has number 0, it is the FileHeader itself. *)
- SYSTEM.PUT (ADDRESSOF (ptr), ADDRESSOF(buf.data));
- UpdateHeader(f, ptr^);
- f.modH := FALSE
- END
- ELSE
- (* The block is not directly adressable, so get the appropraite super and sub index blocks or allocate them
- if they do not already exist *)
- super := f.ext;
- IF super = 0 THEN NewSuper(f); super := f.ext END;
- xpos := buf.apos-STS;
- i := xpos DIV XS;
- FSGetSector(vol, super, superSec);
- sub := superSec.x[i];
- IF sub = 0 THEN
- FSAllocSector(vol, f.sechint, sub); f.sechint := sub;
- FOR j := 0 TO XS-1 DO subSec.x[j] := 0 END;
- superSec.x[i] := sub;
- FSPutSector(vol, super, superSec);
- ELSE
- FSGetSector(vol, sub, subSec);
- END;
- k := xpos MOD XS; secadr := subSec.x[k];
- IF secadr = 0 THEN
- FSAllocSector(vol, f.sechint, secadr); f.sechint := secadr;
- subSec.x[k] := secadr;
- FSPutSector(vol, sub, subSec);
- END
- END;
- FSPutSector(vol, secadr, buf.data); buf.mod := FALSE;
- END WriteBuf;
- (* Search and get the buffer with number pos if file f. NIL if not found *)
- PROCEDURE SearchBuf(f: File; pos: LONGINT): Buffer;
- VAR buf: Buffer;
- BEGIN
- buf := f.firstbuf;
- WHILE (buf # NIL) & (buf.apos # pos) DO
- buf := buf.next;
- IF buf = f.firstbuf THEN buf := NIL; END
- END;
- RETURN buf
- END SearchBuf;
- (* Get the buffer at position pos. Never returns NIL*)
- PROCEDURE GetBuf(f: File; pos: LONGINT): Buffer;
- VAR buf: Buffer;
- BEGIN
- buf := f.firstbuf;
- WHILE (buf.apos # pos) DO
- IF buf.next = f.firstbuf THEN
- (* take one of the buffers *)
- f.firstbuf := buf;
- IF (buf.mod) & (buf.apos # InvalidAddress) THEN WriteBuf(f, buf) END;
- buf.apos := pos;
- IF pos <= f.aleng THEN ReadBuf(f, buf, pos) END;
- END;
- IF buf.apos # pos THEN buf := buf.next END;
- END;
- RETURN buf
- END GetBuf;
- (* Return unique id for file, or 0 if it does not exist. *)
- PROCEDURE FileKey(fs: FileSystem; CONST name: ARRAY OF CHAR): LONGINT;
- VAR res: LONGINT; namebuf: FileName; header: LONGINT;
- BEGIN
- header := 0;
- Check(name, namebuf, res);
- IF res = 0 THEN
- Search(fs.vol, namebuf, header)
- END;
- RETURN header
- END FileKey;
- (* Open an existing file. *)
- PROCEDURE FSOld(fs: FileSystem; CONST name: ARRAY OF CHAR): File;
- VAR
- i, k, res: LONGINT; f: File; header: LONGINT; buf: Buffer; head: FileHd;
- namebuf: FileName; super: LONGINT; sub: LONGINT; sec: IndexSector; vol: Volume;
- BEGIN
- f := NIL; Check(name, namebuf, res);
- IF res = 0 THEN
- vol := fs.vol;
- Search(vol, namebuf, header);
- IF (header # 0) & (filePool # NIL) THEN
- f := filePool; filePool := filePool.next;
-
- (* Invalidate buffers *)
- buf := f.firstbuf;
- REPEAT buf.apos := InvalidAddress; buf := buf.next; UNTIL buf = f.firstbuf;
-
- buf.apos := 0; buf.mod := FALSE;
- FSGetSector(vol, header, buf.data);
- SYSTEM.PUT (ADDRESSOF (head), ADDRESSOF(buf.data));
- f.fs := fs; f.key := header;
- f.aleng := head.aleng; f.bleng := head.bleng;
- f.time := head.time;
- IF f.aleng = 0 THEN buf.lim := f.bleng ELSE buf.lim := SS END;
- Strings.Copy(namebuf, f.name); f.registered := TRUE;
- CopySectorTable(head.sec, f.sec);
- f.ext := head.ext;
- f.sechint := header; f.modH := FALSE
- END
- END;
- RETURN f
- END FSOld;
- PROCEDURE Unbuffer(f: File); (* f.sec*)
- VAR
- i, k: LONGINT; buf: Buffer; head: FileHeader;
- sec: IndexSector; vol: Volume;
- BEGIN
- vol := f.fs.vol;
-
- (* Flush all data buffers *)
- buf := f.firstbuf;
- REPEAT
- IF buf.mod & (buf.apos # InvalidAddress) THEN
- (*WriteBuf(f, f.firstbuf);*)
- WriteBuf(f, buf);
- buf.apos := InvalidAddress;
- END;
- buf := buf.next;
- UNTIL f.firstbuf = buf;
-
- (* And write file header *)
- IF f.modH THEN
- FSGetSector(vol, f.sec[0], head); UpdateHeader(f, head);
- FSPutSector(vol, f.sec[0], head); f.modH := FALSE
- END
- END Unbuffer;
- (** Find an open file. Can also be used to see if a given file is open. If it is not, then it is safe to purge its sectors when it is deleted or replaced (through rename). *)
- PROCEDURE FindOpenFile*(fs: FileSystem; key: LONGINT): File;
- VAR f: File;
- BEGIN
- f := froot;
- REPEAT f := f.next UNTIL (f = NIL) OR ((f.key = key) & (f.fs = fs));
- RETURN f
- END FindOpenFile;
- PROCEDURE LogGC(CONST procname, prefix, name: ARRAY OF CHAR);
- BEGIN
- Log.vS(thisModuleName);
- Log.vC('.');
- Log.vS(procname);
- Log.vC(' ');
- Log.vS(prefix);
- Log.vC(':');
- Log.vS(name);
- Log.vL;
- END LogGC;
- PROCEDURE Free(vol: Volume; adr: LONGINT);
- BEGIN
- IF (adr # 0) & FSMarked(vol, adr) THEN FSFreeSector(vol, adr) END
- END Free;
- PROCEDURE PurgeOnDisk(fs: FileSystem; hdadr: LONGINT); (*bsm*)
- VAR hd: FileHeader; supi, subi: IndexSector; aleng, i, k: LONGINT;
- secCount, subAdr: LONGINT; vol: Volume;
- BEGIN
- ASSERT(fs.vol # NIL);
- vol := fs.vol;
- FSGetSector(vol, hdadr, hd);
- LogGC("PurgeOnDisk", fs.prefix, hd.name);
- aleng := hd.aleng;
- secCount := aleng+1;
- IF secCount > STS THEN secCount := STS END;
- FOR i := 0 TO secCount-1 DO
- Free(fs.vol, hd.sec[i])
- END;
- aleng := aleng - secCount;
- IF aleng >= 0 THEN
- FSGetSector(vol, hd.ext, supi);
- WHILE (aleng >= 0) DO
- subAdr := supi.x[aleng DIV XS];
- FSGetSector(vol, subAdr, subi);
- FOR i := 0 TO aleng MOD XS DO
- Free(fs.vol, subi.x[i])
- END;
- Free(fs.vol, subAdr);
- aleng := aleng - (aleng MOD XS + 1);
- END;
- Free(fs.vol, hd.ext);
- END;
- END PurgeOnDisk;
- PROCEDURE FSRegister(f: File; VAR res: LONGINT);
- VAR repAdr: LONGINT; (* address of the file replaced through register, 0 if no such file *)
- repFile: File;
- BEGIN
- Unbuffer(f);
- IF ~f.registered & (f.name # "") THEN
- Insert(f.fs.vol, f.name, f.sec[0], repAdr);
- f.registered := TRUE; f.key := f.sec[0];
- IF (repAdr # 0) & (f.sec[0] # repAdr) THEN
- repFile := FindOpenFile(f.fs, repAdr);
- IF repFile = NIL THEN
- PurgeOnDisk(f.fs(FileSystem), repAdr) (* Purge file if it is not open *)
- ELSE
- repFile.registered := FALSE
- END;
- END;
- res := 0
- ELSE
- res := 1
- END
- END FSRegister;
- PROCEDURE PutIntoFilePool(f: File);
- VAR
- temp: File;
- buf: Buffer;
- BEGIN
- (* The File must be removed from the open list, before it can be put into the File Pool for reuse *)
- IF (f.key # 0) & (FindOpenFile(f.fs, f.key) # NIL) THEN
- Log.S("File " ); Log.S(f.name); Log.SL(" is still open!"); Log.Flush(Log.normal);
- HALT(8);
- END;
-
- buf := f.firstbuf;
- REPEAT
- buf.apos := InvalidAddress;
- buf := buf.next;
- UNTIL (buf = f.firstbuf);
-
- temp := filePool;
- WHILE (temp # NIL) & (temp # f) DO temp := temp.next END;
- IF temp = NIL THEN
- f.next := filePool; filePool := f;
- END;
- END PutIntoFilePool;
- PROCEDURE LogFS(fs: FileSystem);
- BEGIN
- IF fs.vol # NIL THEN Log.vS(fs.vol.name); Log.vC(' ') END;
- Log.vS(fs.desc)
- END LogFS;
- PROCEDURE FileCleanup(f: File);
- VAR p, c, temp: File;
- BEGIN
- Log.vS("OFS: Cleanup "); Log.vI(f.key);
- Log.vC(' '); LogFS(f.fs); Log.vL;
-
- (* Remove file from open list, and put it back into the pool *)
- p := froot; c := froot.next;
- WHILE c # NIL DO
- IF c = f THEN
- p.next := c.next;
- temp := c;
- c := c.next;
- PutIntoFilePool(temp);
- ELSE
- p := c;
- c := c.next;
- END;
- END;
- Log.vSL("FileCleanup finished");
- END FileCleanup;
- (* Returns the current length of a file. *)
- PROCEDURE Length*(f: File): LONGINT;
- BEGIN
- RETURN f.aleng*SS + f.bleng - HS
- END Length;
- (* Returns the time (t) and date (d) when a file was last modified. *)
- PROCEDURE GetTime*(f: File; VAR t: LONGINT);
- BEGIN
- t := f.time;
- END GetTime;
- (* Sets the modification time (t) of a file. *)
- PROCEDURE SetTime*(f: File; t: LONGINT);
- BEGIN
- f.modH := TRUE; f.time := t;
- END SetTime;
- (* Positions a Rider at a certain position in a file. Multiple Riders can be positioned at different locations in a file. A Rider cannot be positioned beyond the end of a file. *)
- PROCEDURE Set*(VAR r: Rider; f: File; pos: LONGINT);
- VAR a, b: LONGINT;
- BEGIN
- r.eof := FALSE; r.res := 0; r.file := f; r.fs := f.fs;
- IF pos < 0 THEN
- a := 0; b := HS
- ELSIF pos < f.aleng*SS + f.bleng - HS THEN
- a := (pos + HS) DIV SS; b := (pos + HS) MOD SS
- ELSE
- a := f.aleng; b := f.bleng
- END;
- r.apos := a; r.bpos := b; r.hint := f.firstbuf
- END Set;
- (* Returns the offset of a Rider positioned on a file. *)
- PROCEDURE Pos*(VAR r: Rider): LONGINT;
- BEGIN
- RETURN r.apos*SS + r.bpos - HS
- END Pos;
- (* Read a byte from a file, advancing the Rider one byte further. R.eof indicates if the end of the file has been passed. *)
- PROCEDURE Read*(VAR r: Rider; VAR x: CHAR);
- VAR buf: Buffer; f: File;
- BEGIN
- buf := r.hint(Buffer); f := r.file;
- IF r.apos # buf.apos THEN buf := GetBuf(f, r.apos); r.hint := buf END;
- IF r.bpos < buf.lim THEN
- x := SYSTEM.VAL(CHAR, buf.data.B[r.bpos]); INC(r.bpos)
- ELSIF r.apos < f.aleng THEN
- INC(r.apos);
- buf := SearchBuf(f, r.apos);
- IF buf = NIL THEN
- buf := r.hint(Buffer);
- IF (buf.mod) & (buf.apos # InvalidAddress) THEN WriteBuf(f, buf) END ;
- ReadBuf(f, buf, r.apos)
- ELSE
- r.hint := buf
- END ;
- x := SYSTEM.VAL(CHAR, buf.data.B[0]); r.bpos := 1
- ELSE
- x := 0X; r.eof := TRUE
- END
- END Read;
- (* Reads a sequence of length n bytes into the buffer x, advancing the Rider. Less bytes will be read when reading over the end of the file. r.res indicates the number of unread bytes. x must be big enough to hold all the bytes. *)
- PROCEDURE ReadBytes*(VAR r: Rider; VAR x: ARRAY OF SYSTEM.BYTE; n: LONGINT);
- VAR src, dst, m: LONGINT; buf: Buffer; f: File;
- BEGIN
- IF LEN(x) < n THEN HALT(19) END ;
- IF n > 0 THEN
- dst := ADDRESSOF(x[0]);
- buf := r.hint(Buffer); f := r.file;
- IF r.apos # buf.apos THEN buf := GetBuf(f, r.apos); r.hint := buf END;
- WHILE n > 0 DO
- src := ADDRESSOF(buf.data.B[0]) + r.bpos; m := r.bpos + n;
- IF m <= buf.lim THEN
- Kernel.Move(src,dst, n); r.bpos := m; r.res := 0; n := 0;
- ELSIF buf.lim = SS THEN
- m := buf.lim - r.bpos;
- IF m > 0 THEN Kernel.Move(src, dst, m); dst := dst + m; n := n - m END ;
- IF r.apos < f.aleng THEN
- INC(r.apos); r.bpos := 0; buf := SearchBuf(f, r.apos);
- IF buf = NIL THEN
- buf := r.hint(Buffer);
- IF buf.mod & (buf.apos # InvalidAddress) THEN WriteBuf(f, buf) END ;
- ReadBuf(f, buf, r.apos)
- ELSE
- r.hint := buf
- END
- ELSE
- r.bpos := buf.lim; r.res := n; r.eof := TRUE; n := 0;
- END
- ELSE
- m := buf.lim - r.bpos;
- IF m > 0 THEN Kernel.Move(src, dst, m); r.bpos := buf.lim END ;
- r.res := n - m; r.eof := TRUE; n := 0;
- END
- END
- ELSE
- r.res := 0
- END
- END ReadBytes;
- PROCEDURE ReadInt*(VAR r: Rider; VAR x: LONGINT);
- BEGIN
- ReadBytes(r, x, 4);
- END ReadInt;
- PROCEDURE ReadSet*(VAR r: Rider; VAR x: SET);
- BEGIN
- ReadBytes(r, x, 4);
- END ReadSet;
- PROCEDURE ReadReal*(VAR r: Rider; VAR x: REAL);
- BEGIN
- ReadBytes(r, x, 4);
- END ReadReal;
- PROCEDURE NewSub(f: File);
- VAR i, k: LONGINT; sub: LONGINT; sec: IndexSector;
- vol: Volume;
- BEGIN
- IF f.ext = 0 THEN NewSuper(f) END;
- vol := f.fs.vol;
- k := (f.aleng - STS) DIV XS;
- IF k = XS THEN HALT(18) END;
- FSAllocSector(vol, f.sechint, sub); f.sechint := sub;
- FOR i := 0 TO XS-1 DO sec.x[i] := 0 END;
- FSPutSector(vol, sub, sec);
-
- FSGetSector(vol, f.ext, sec);
- sec.x[k] := sub;
- FSPutSector(vol, f.ext, sec);
- END NewSub;
- (* Writes a byte into the file at the Rider position, advancing the Rider by one. *)
- PROCEDURE Write*(VAR r: Rider; x: CHAR);
- VAR f: File; buf: Buffer;
- BEGIN
- buf := r.hint(Buffer); f := r.file;
- IF r.apos # buf.apos THEN buf := GetBuf(f, r.apos); r.hint := buf END;
- IF r.bpos >= buf.lim THEN
- IF r.bpos < SS THEN
- INC(buf.lim); INC(f.bleng); f.modH := TRUE
- ELSE
- WriteBuf(f, buf); INC(r.apos); buf := SearchBuf(f, r.apos);
- IF buf = NIL THEN
- buf := r.hint(Buffer);
- IF r.apos <= f.aleng THEN
- ReadBuf(f, buf, r.apos);
- ELSE
- buf.apos := r.apos; buf.lim := 1; INC(f.aleng); f.bleng := 1; f.modH := TRUE;
- IF (f.aleng - STS) MOD XS = 0 THEN NewSub(f) 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;
- (* Writes the buffer x containing n bytes into a file at the Rider position. *)
- PROCEDURE WriteBytes*(VAR r: Rider; CONST x: ARRAY OF SYSTEM.BYTE; n: LONGINT);
- VAR src, dst, m: LONGINT; f: File; buf: Buffer;
- BEGIN
- IF LEN(x) < n THEN HALT(19) END;
- IF n > 0 THEN
- src := ADDRESSOF(x[0]);
- buf := r.hint(Buffer); f := r.file;
- IF r.apos # buf.apos THEN buf := GetBuf(f, r.apos); r.hint := buf END;
- WHILE n > 0 DO
- buf.mod := TRUE; dst := ADDRESSOF(buf.data.B[0]) + r.bpos; m := r.bpos + n;
- IF m <= buf.lim THEN
- Kernel.Move(src, dst, n); r.bpos := m; n := 0;
- ELSIF m <= SS THEN
- Kernel.Move(src, dst, n); r.bpos := m;
- f.bleng := m; buf.lim := m; f.modH := TRUE; n := 0;
- ELSE
- m := SS - r.bpos;
- IF m > 0 THEN Kernel.Move(src, dst, m); src := src + m; n := n - m END;
- WriteBuf(f, buf); INC(r.apos); r.bpos := 0; buf := SearchBuf(f, r.apos);
- IF buf = NIL THEN
- buf := r.hint(Buffer);
- IF r.apos <= f.aleng THEN ReadBuf(f, buf, r.apos)
- ELSE
- buf.apos := r.apos; buf.lim := 0; INC(f.aleng); f.bleng := 0; f.modH := TRUE;
- IF (f.aleng - STS) MOD XS = 0 THEN NewSub(f) END
- END
- ELSE
- r.hint := buf
- END
- END
- END
- END
- END WriteBytes;
- PROCEDURE WriteInt*(VAR r: Rider; VAR x: LONGINT);
- BEGIN
- WriteBytes(r, x, 4);
- END WriteInt;
- PROCEDURE WriteSet*(VAR r: Rider; VAR x: SET);
- BEGIN
- WriteBytes(r, x, 4);
- END WriteSet;
- PROCEDURE WriteReal*(VAR r: Rider; VAR x: REAL);
- BEGIN
- WriteBytes(r, x, 4);
- END WriteReal;
- PROCEDURE Purge(fs: FileSystem; f: File); (*bsm*)
- VAR
- k, i, j, aleng, secCount, super, sub: LONGINT;
- superSec, subSec: IndexSector;
- vol: Volume;
- BEGIN
- ASSERT(f.fs = fs);
- vol := fs.vol;
- LogGC("Purge", fs.prefix, f.name);
- aleng := f.aleng;
- secCount := aleng + 1;
- IF secCount > STS THEN secCount := STS END;
- FOR i := 0 TO secCount - 1 DO
- Free(fs.vol, f.sec[i])
- END;
- aleng := aleng - secCount;
- IF aleng >= 0 THEN
- FSGetSector(vol, f.ext, superSec);
- WHILE (aleng >= 0) DO
- sub := superSec.x[aleng DIV XS];
- FSGetSector(vol, sub, subSec);
- FOR i := 0 TO aleng MOD XS DO
- Free(fs.vol, subSec.x[i])
- END;
- Free(fs.vol, sub);
- aleng := aleng - (aleng MOD XS + 1);
- END;
- Free(fs.vol, f.ext);
- END;
- END Purge;
- PROCEDURE Registered(fs: FileSystem; f: File): BOOLEAN; (*bsm*)
- BEGIN
- ASSERT(fs IS FileSystem); ASSERT(f.fs = fs);
- RETURN f.registered
- END Registered;
- (* Deletes a file. res = 0 indicates success. *)
- PROCEDURE FSDelete(fs: FileSystem; CONST name: ARRAY OF CHAR; VAR key: LONGINT; VAR res: LONGINT);
- VAR
- adr: LONGINT; namebuf: FileName; head: FileHeader; vol: Volume; r: LONGINT;
- delFile: File; (*bsm*)
- BEGIN
- Check(name, namebuf, r); res := r;
- IF res = 0 THEN
- vol := fs.vol;
- DirDelete(vol, namebuf, adr);
- key := adr;
- IF adr # 0 THEN
- FSGetSector(vol, adr, head);
- head.mark := HeaderMark+1; (* invalidate mark of file on disk *)
- FSPutSector(vol, adr, head);
- delFile := FindOpenFile(fs, key);
- IF delFile = NIL THEN
- PurgeOnDisk(fs(FileSystem), adr);
- ELSE
- delFile.registered := FALSE
- END;
- ELSE
- res := 2
- END
- ELSE
- key := 0
- END
- END FSDelete;
- (* Renames a file. res = 0 indicates success. *)
- PROCEDURE FSRename(fs: FileSystem; CONST old, new: ARRAY OF CHAR; VAR res: LONGINT);
- VAR
- adr: LONGINT; oldbuf, newbuf: FileName; head: FileHeader;
- vol: Volume; f: File; r: LONGINT;
- repFile: File;
- repAdr: LONGINT; (* address of the file replaced through the rename, 0 if no such file *)
- BEGIN
- Check(old, oldbuf, r); res := r;
- IF res = 0 THEN
- Check(new, newbuf, r); res := r;
- IF res = 0 THEN
- vol := fs.vol;
- DirDelete(vol, oldbuf, adr);
- IF adr # 0 THEN
- f := FindOpenFile(fs, adr);
- IF f # NIL THEN Strings.Copy(newbuf, f.name); END;
- Insert(vol, newbuf, adr, repAdr);
- FSGetSector(vol, adr, head);
- Strings.Copy(newbuf, head.name);
- FSPutSector(vol, adr, head);
- IF (repAdr # 0) & (adr # repAdr) THEN(*bsm*)
- repFile := FindOpenFile(fs, repAdr);
- IF (repFile = NIL) THEN
- PurgeOnDisk(fs(FileSystem), repAdr)
- ELSE
- repFile.registered := FALSE
- END;
- END;
- ELSE res := 2
- END
- END
- END
- END FSRename;
- PROCEDURE GetName*(f: File; VAR name: ARRAY OF CHAR);
- BEGIN
- Strings.Copy(f.name, name)
- END GetName;
- (* File system initialization and finalization *)
- PROCEDURE Finalize(fs: FileSystem);
- VAR
- tempVol : Volume;
- BEGIN
- fs := fs(FileSystem); (* Safety Check *)
- DirCleanup(fs.vol);
- tempVol := fs.vol;
- tempVol.Finalize(fs.vol);
- fs.vol := NIL (* prevent access in case user still has file handles *)
- END Finalize;
- (** Find file system with specified prefix. *)
- PROCEDURE This*(CONST prefix: ARRAY OF CHAR): FileSystem;
- VAR fs: FileSystem;
- BEGIN
- fs := fsroot; WHILE (fs # NIL) & (fs.prefix # prefix) DO fs := fs.next END;
- RETURN fs
- END This;
- (** Add file system at end of list, with specified prefix, which must be unique. *)
- PROCEDURE Add(fs: FileSystem; CONST prefix: ARRAY OF CHAR);
- VAR p, c: FileSystem;
- BEGIN
- Log.vS("OFS: Adding "); LogFS(fs); Log.vL;
- COPY(prefix, fs.prefix);
- p := NIL; c := fsroot;
- WHILE c # NIL DO
- ASSERT((c # fs) & (c.prefix # fs.prefix)); (* duplicate insertion not allowed *)
- p := c; c := c.next
- END;
- IF p = NIL THEN fsroot := fs ELSE p.next := fs END;
- fs.next := NIL;
- fs.link := fsmount; fsmount := fs (* push on mount list *)
- END Add;
- PROCEDURE Format(vol: Volume);
- VAR
- i: LONGINT;
- block: DataSector;
- BEGIN
- IF (vol#NIL) & (vol.blockSize = SS) & (vol.size >= MinVolSize) THEN
- FSGetSector(vol, DirRootAdr, block);
- FOR i := 0 TO SS - 1 DO
- block.B[i] := 0X;
- END;
-
- block.B[0] := CHR(DirMark);
- block.B[1] := CHR(DirMark DIV 100H);
- block.B[2] := CHR(DirMark DIV 10000H);
- block.B[3] := CHR(DirMark DIV 1000000H);
- FSPutSector(vol, DirRootAdr, block);
- END;
- END Format;
- (** Generate a new file system object. *)
- PROCEDURE NewFS*(CONST prefix: Prefix; format: BOOLEAN; vol: Volume);
- VAR
- fs: FileSystem; init: BOOLEAN;
- BEGIN
- IF vol # NIL THEN
- IF This(prefix) = NIL THEN
- IF (vol.blockSize = SS) & (vol.size >= MinVolSize) THEN
- IF format THEN Format(vol); END;
-
- FSGetSector(vol, DirRootAdr, hp);
-
- IF hp.mark = DirMark THEN (* assume it is an Aos filesystem *)
- NEW(fs); fs.vol := vol;
- ASSERT(vol.size < MAX(LONGINT) DIV SF);
- Strings.Copy( "GCAosFS", fs.desc);
- DirInit(vol, init);
- ASSERT(init); (* will have to undo changes to vol before continuing *)
- Add(fs, prefix);
- ELSE
- Log.S(thisModuleName);
- Log.S(": File system not found on ");
- Log.S(vol.name); Log.L;
- Log.S("Directory mark: "); Log.H( hp.mark); Log.L;
- END
- ELSE
- Log.S(thisModuleName);
- Log.S(": Bad volume size"); Log.L
- END
- ELSE
- Log.S(thisModuleName);
- Log.S(": "); Log.S(prefix);
- Log.S(" already in use"); Log.L
- END;
- ELSE
- Log.S(thisModuleName);
- Log.S(": "); Log.S(prefix);
- Log.S(" volume is NIL"); Log.L
- END;
- END NewFS;
- (* (* Clean up when module freed. *)
- PROCEDURE Cleanup;
- VAR fs: FileSystem;
- BEGIN
- IF Kernel.shutdown = 0 THEN
- REPEAT (* unmount all AosFSs *)
- fs := First(); (* look for fs to unmount *)
- WHILE (fs # NIL) & ~(fs IS FileSystem) DO
- fs := Next(fs)
- END;
- IF fs # NIL THEN Remove(fs) END
- UNTIL fs = NIL
- END
- END Cleanup;
- *)
- PROCEDURE DefaultAllocBlock*(vol: Volume; hint: LONGINT; VAR adr: LONGINT; VAR res: LONGINT);
- VAR
- found: BOOLEAN;
- BEGIN
- found := FALSE;
- IF ReadOnly IN vol.flags THEN HALT(21) END;
- ASSERT(hint >= 0);
- IF hint > vol.size THEN hint := 0 END;
- adr := hint+1;
- REPEAT
- IF adr > vol.size THEN adr := 0 END;
-
- IF (adr MOD 32) IN vol.map[adr DIV 32] THEN
- INC(adr) (* Block in use *)
- ELSE
- vol.map[adr DIV 32] := vol.map[adr DIV 32] + {adr MOD 32};
- found := TRUE;
- END;
- IF (adr = hint) & (~found) THEN HALT(20) END
- UNTIL found;
- INC(vol.used);
- res := Ok;
- END DefaultAllocBlock;
- PROCEDURE DefaultFreeBlock*(vol: Volume; adr: LONGINT; VAR res: LONGINT);
- BEGIN
- IF (adr < 1) OR (adr > vol.size) THEN HALT(15) END;
- IF ReadOnly IN vol.flags THEN HALT(21) END;
- vol.map[adr DIV 32] := vol.map[adr DIV 32] - {adr MOD 32};
- DEC(vol.used);
- res := Ok;
- END DefaultFreeBlock;
- PROCEDURE DefaultMarkBlock*(vol: Volume; adr: LONGINT; VAR res: LONGINT);
- BEGIN
- IF (adr < 1) OR (adr > vol.size) THEN HALT(15) END;
- IF ReadOnly IN vol.flags THEN HALT(21) END;
- vol.map[adr DIV 32] := vol.map[adr DIV 32] + {adr MOD 32};
- INC(vol.used);
- res := Ok;
- END DefaultMarkBlock;
- PROCEDURE DefaultMarked*(vol: Volume; adr: LONGINT; VAR res: LONGINT): BOOLEAN;
- BEGIN
- IF (adr < 1) OR (adr > vol.size) THEN HALT(15) END;
- IF ReadOnly IN vol.flags THEN HALT(21) END;
- res := Ok;
- RETURN (adr MOD 32) IN vol.map[adr DIV 32]
- END DefaultMarked;
- PROCEDURE DefaultAvailable*(vol: Volume): LONGINT;
- BEGIN
- RETURN vol.size-vol.used
- END DefaultAvailable;
- PROCEDURE DefaultSync*(vol: Volume);
- BEGIN
- END DefaultSync;
- (** Init procedure for private data of above methods only. vol.flags and vol.size must be set before. *)
- PROCEDURE InitVol*(vol: Volume);
- VAR maplen: LONGINT;
- BEGIN
- IF ~(ReadOnly IN vol.flags) THEN
- maplen := (vol.size+1+31) DIV 32;
- (*ASSERT( BitmapSize >= maplen); (* todo: replace this with: NEW(vol.map, maplen); *)*)
- NEW(vol.map, maplen);
- WHILE maplen > 0 DO DEC(maplen); vol.map[maplen] := {} END;
- vol.map[0] := vol.map[0] + {0}; (* reserve sector 0 (illegal to use) *)
- vol.used := 0
- ELSE
- vol.used := vol.size
- END
- END InitVol;
- (** Finalize procedure for volumes. *)
- PROCEDURE DefaultFinalizeVol*(vol: Volume);
- BEGIN
- (* vol.map := NIL; *) (* todo: uncomment this *)
- vol.AllocBlock := NIL; vol.FreeBlock := NIL; vol.MarkBlock := NIL; vol.Marked := NIL;
- vol.Available := NIL; vol.GetBlock := NIL; vol.PutBlock := NIL; vol.Sync := NIL;
- vol.Finalize := NIL
- END DefaultFinalizeVol;
- (** File name prefix support. *)
- (** Split fullname = ( prefix ":" name ) into prefix and name *)
- PROCEDURE SplitName(CONST fullname: ARRAY OF CHAR; VAR prefix, name: ARRAY OF CHAR);
- VAR i, j: LONGINT;
- BEGIN
- i := 0; WHILE (fullname[i] # ':') & (fullname[i] # 0X) DO INC(i) END;
- IF (fullname[i] # ':') OR (i >= LEN(prefix)) THEN
- Strings.Copy("", prefix); Strings.Copy (fullname, name);
- ELSE
- j := 0; WHILE j # i DO prefix[j] := fullname[j]; INC(j) END;
- prefix[j] := 0X;
- j := 0; REPEAT INC(i); name[j] := fullname[i]; INC(j) UNTIL fullname[i] = 0X
- END
- END SplitName;
- (** File system list support. *)
- PROCEDURE DeleteFS(fs: FileSystem);
- VAR p, c: FileSystem;
- BEGIN
- p := NIL; c := fsroot;
- WHILE c # fs DO p := c; c := c.next END; (* fs must be in list *)
- IF p = NIL THEN fsroot := c.next ELSE p.next := c.next END;
- c.next := NIL
- END DeleteFS;
- (** Promote fs to the start of the list. *)
- PROCEDURE Promote*(fs: FileSystem);
- BEGIN
- DeleteFS(fs); fs.next := fsroot; fsroot := fs
- END Promote;
- PROCEDURE Close*(f: File);
- VAR
- temp: File;
- BEGIN
- Unbuffer(f);
-
- IF (f.fs # NIL) & (f.fs.vol # NIL) THEN
- IF ~(ReadOnly IN f.fs.vol.flags) THEN
- IF ~Registered(f.fs, f) THEN
- Purge(f.fs, f)
- END;
- END;
- END;
- (* Put File in filePool *)
- FileCleanup(f);
- PutIntoFilePool(f);
- END Close;
- (** Remove the file system and finalize it. *)
- PROCEDURE Remove*(fs: FileSystem);
- VAR f: File; count: LONGINT; p, c: FileSystem;
- BEGIN
- Log.vS("OFS: Removing "); LogFS(fs); Log.vL;
- f := froot.next; count := 0;
- WHILE f # NIL DO
- IF f.fs = fs THEN INC(count); Close(f); f.fs := NIL END;
- f := f.next
- END;
- IF count # 0 THEN
- Log.S("OFS: "); Log.I(count);
- Log.S(" open files");
- IF fs.vol # NIL THEN
- Log.S(" on "); Log.S(fs.vol.name)
- END;
- Log.L
- END;
- Finalize(fs); DeleteFS(fs);
- p := NIL; c := fsmount;
- WHILE c # fs DO p := c; c := c.link END;
- IF p = NIL THEN fsmount := c.link ELSE p.link := c.link END;
- c.link := NIL
- END Remove;
- (** Return next file system. *)
- PROCEDURE Next*(fs: FileSystem): FileSystem;
- BEGIN
- RETURN fs.next
- END Next;
- (* Find file in open file list, or open and add it. *)
- PROCEDURE Open(fs: FileSystem; CONST fname: ARRAY OF CHAR): File;
- VAR f: File; key: LONGINT;
- BEGIN
- f := NIL;
- IF (fs # NIL) & (fname # "") THEN
- key := FileKey(fs, fname);
- IF key # 0 THEN
- f := froot.next;
- WHILE (f # NIL) & ((f.fs # fs) OR (f.key # key)) DO f := f.next END
- END;
- IF f = NIL THEN
- f := FSOld(fs, fname);
- IF f # NIL THEN
- ASSERT(f.key # 0); (* key must be set *)
- f.next := froot.next; froot.next := f;
- (* Kernel.RegisterObject(f, Collect, FALSE); *)
- (* Kernel.RegisterObject(f, FileCleanup, FALSE) *)
- END
- END
- END;
- RETURN f
- END Open;
- (** Open an existing file, searching through the mounted file system list if no prefix is specified. *)
- PROCEDURE Old*(CONST name: ARRAY OF CHAR): File;
- VAR fs: FileSystem; f: File; prefix: Prefix; fname: LocalName;
- BEGIN
- f := NIL;
- SplitName(name, prefix, fname);
- IF prefix = "" THEN
- fs := fsroot;
- WHILE (fs # NIL) & (f = NIL) DO
- f := Open(fs, fname); fs := Next(fs);
- END
- ELSE
- f := Open(This(prefix), fname)
- END;
- RETURN f
- END Old;
- (** Create a new file. If no prefix is specified, create the file on the first file system in the mounted list.*)
- PROCEDURE New*(CONST name: ARRAY OF CHAR): File;
- VAR fs: FileSystem; f: File; prefix: Prefix; fname: LocalName;
- BEGIN
- f := NIL; SplitName(name, prefix, fname);
- IF prefix = "" THEN
- fs := fsroot; (* use default file system *)
- IF fname = "" THEN (* anonymous file on unspecified file system *)
- WHILE (fs # NIL) & ((fs.vol = NIL) OR (ReadOnly IN fs.vol.flags)) DO
- fs := Next(fs) (* find a writable file system *)
- END;
- IF fs = NIL THEN fs := fsroot END (* none found, relapse to default *)
- END
- ELSE
- fs := This(prefix)
- END;
- IF fs # NIL THEN
- IF (fs.vol = NIL) OR ~(ReadOnly IN fs.vol.flags) THEN
- f := FSNew(fs, fname);
- ELSE
- Log.vS("Could not create file "); Log.vL;
- END;
- ELSE
- Log.vS("In OFS.New, no filesystem found"); Log.vL;
- END;
- RETURN f
- END New;
- (** Delete a file. res = 0 indicates success. *)
- PROCEDURE Delete*(VAR name: ARRAY OF CHAR; VAR res: LONGINT);
- VAR fs: FileSystem; p, c, temp: File; key: LONGINT; prefix: Prefix; fname: LocalName;
- BEGIN
- SplitName(name, prefix, fname);
- IF prefix = "" THEN fs := fsroot ELSE fs := This(prefix) END;
- IF fs # NIL THEN
- IF (fs.vol = NIL) OR ~(ReadOnly IN fs.vol.flags) THEN
- FSDelete(fs, fname, key, res);
- IF key # 0 THEN
- p := froot; c := froot.next;
- WHILE c # NIL DO
- IF (c.fs = fs) & (c.key = key) THEN
- p.next := c.next;
- temp := c;
- c := c.next;
- PutIntoFilePool(temp)
- ELSE
- p := c;
- c := c.next;
- END;
- END
- END
- ELSE
- res := 3 (* can not modify read-only volume *)
- END
- ELSE
- res := 2 (* file system not found *)
- END
- END Delete;
- (** Rename a file. res = 0 indicates success. *)
- PROCEDURE Rename*(CONST old, new: ARRAY OF CHAR; VAR res: LONGINT);
- VAR ofs, nfs: FileSystem; pold, pnew: Prefix; fold, fnew: LocalName;
- BEGIN
- SplitName(old, pold, fold);
- SplitName(new, pnew, fnew);
- IF pold = "" THEN ofs := fsroot; ELSE ofs := This(pold) END;
- IF pnew = "" THEN nfs := fsroot; ELSE nfs := This(pnew) END;
- IF (nfs # NIL) & (ofs = nfs) THEN
- IF (nfs.vol = NIL) OR ~(ReadOnly IN nfs.vol.flags) THEN
- FSRename(nfs, fold, fnew, res)
- ELSE
- res := 3 (* can not modify read-only volume *)
- END
- ELSE
- res := 2
- END
- END Rename;
- (** Register a file created with New in the directory, replacing the previous file in the directory with the same name. The file is automatically closed. *)
- PROCEDURE Register*(f: File);
- VAR res: LONGINT;
- c,p: File;
- BEGIN
- IF f # NIL THEN
- FSRegister(f, res);
- IF res = 0 THEN (* new file was registered *)
- ASSERT(f.key # 0);
- (* f.next := froot.next; froot.next := f; *) (* Do not put that file into the open list!! *)
- FileCleanup(f); (* Kernel.RegisterObject(f, FileCleanup, FALSE) *)
- ELSE
- IF res = 1 THEN (* file was registered already *)
- (* Remove file from open list *)
- FileCleanup(f);
-
- (* p := froot; c := froot.next;
- WHILE c # NIL DO
- IF c = f THEN p.next := c.next; ELSE p := c; END;
- c := c.next;
- END
- *) ELSE (* error occured while registering *)
- HALT(17)
- END;
- END;
-
- (* Put File in filePool *)
- PutIntoFilePool(f);
- END;
- END Register;
- PROCEDURE Available*(CONST name: ARRAY OF CHAR ): LONGINT;
- VAR
- fs: FileSystem;
- avail: LONGINT;
- temp: PROCEDURE (vol: Volume): LONGINT;
- BEGIN
- IF name = "" THEN fs := fsroot ELSE fs := This(name) END;
-
- IF fs.vol # NIL THEN
- temp := fs.vol.Available;
- avail := temp(fs.vol)*fs.vol.blockSize;
- ELSE
- avail := 0;
- END;
- RETURN avail
- END Available;
- (** Enumerates files matching mask by upcalling proc for every file. If EnumSize flags is set, size parameter of proc upcall will be valid. If EnumTime is set, time will be valid. All flags are passed through to the upcall in the flags parameter. *)
- PROCEDURE Enumerate*(CONST mask: ARRAY OF CHAR; VAR flags: SET; proc: EntryHandler);
- VAR fs: FileSystem; prefix: Prefix; fmask: LocalName;
- BEGIN
- SplitName(mask, prefix, fmask);
- IF prefix = "" THEN
- fs := fsroot;
- WHILE fs # NIL DO
- flags := flags - {EnumStop};
- enumerate(fs, fmask, DirRootAdr, flags, proc);
- fs := Next(fs)
- END
- ELSE
- fs := This(prefix);
- IF fs # NIL THEN enumerate(fs, fmask, DirRootAdr, flags, proc) END
- END
- END Enumerate;
- (** Checks if a file system has open files. *)
- PROCEDURE HasOpenFiles*(fs: FileSystem): BOOLEAN;
- VAR f: File;
- BEGIN
- f := froot;
- REPEAT f := f.next UNTIL (f = NIL) OR (f.fs = fs);
- RETURN f # NIL
- END HasOpenFiles;
- (* Clean up file systems when shutting down or unloading module. File systems are cleaned up in reverse order of installation. *)
- PROCEDURE FSCleanup;
- BEGIN
- WHILE fsmount # NIL DO
- Remove(fsmount)
- END
- END FSCleanup;
- PROCEDURE InitFilePool;
- VAR
- i, j: LONGINT;
- f: File;
- buf: Buffer;
- BEGIN
- filePool := NIL;
- FOR i := 0 TO MaxFiles - 1 DO
- NEW(f);
- f.key := 0; (* Invalidate file *)
- f.next := filePool; filePool := f; f.firstbuf := NIL;
- FOR j := 0 TO MaxBuffers -1 DO
- NEW(buf);
- buf.next := f.firstbuf; f.firstbuf := buf;
- buf.apos := InvalidAddress
- END;
-
- buf := f.firstbuf;
- WHILE buf.next # NIL DO
- buf := buf.next;
- END;
-
- buf.next := f.firstbuf;
- END;
- END InitFilePool;
- BEGIN
- Trace.StringLn("Entering Init() OFS.");
- fsroot := NIL; fsmount := NIL; filePool := NIL;
- NEW(froot); froot.next := NIL; froot.key := 0; froot.fs := NIL;
-
- InitFilePool;
- ASSERT((SIZEOF(FileHeader) = SS) & (SIZEOF(IndexSector) = SS) & (SIZEOF(DataSector) = SS) &
- (SIZEOF(DirPage) = SS) & (SIZEOF(MapIndex) = SS) & (SIZEOF(MapSector) = SS));
- ASSERT((MapSize MOD 32) = 0);
- Trace.StringLn("Completed, exiting.");
- END OFS.
- (**
- On-the-fly GC by bsm
- In order to be non-leaking, a file system must provide the following:
- - FileSystem.Purge -- to reclaim blocks of an open (being closed) file
- - FileSystem.Registered -- reports if a particular open file is registered in the file directory
-
- The following procedures need to be modified to purge file blocks when appropriate.
- - FileSystem.Register -- if an entry to a file, F, which is not open is replaced, purge F.
- - FileSystem.Rename -- same as register.
- - FileSystem.Delete -- if the entry being deleted refers to a file, F, which is not open, purge F.
-
- The procedure FindOpenFile may be used to see if a given file is open or not.
- *)
- (*
- aleng * SS + bleng = length (including header)
- apos * SS + bpos = current position
- 0 <= bpos <= lim <= SS
- 0 <= apos <= aleng < STS
- (apos < aleng) & (lim = SS) OR (apos = aleng)
- *)
|