OFS.Mos 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086
  1. (* ported version of Minos to work with the ARM backend of the Fox Compiler Suite *)
  2. (* ETH Oberon, Copyright 2006 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
  3. Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
  4. MODULE OFS; (** non-portable *) (* pjm/bsm/be *)
  5. (* Oberon file system base - Support for file system and volume implementations. *)
  6. IMPORT SYSTEM, Kernel, Log, Strings, Trace;
  7. CONST
  8. ReadOnly* = 0; Removable* = 1; (** Volume property flags *)
  9. (** Enumerate flags supported by *)
  10. EnumSize* = 0; (** enable size parameter of EntryHandler. *)
  11. EnumTime* = 1; (** enable time and data parameters of EntryHandler. *)
  12. EnumRecursive* = 2; (** enumerate recursively into subdirectories. *)
  13. EnumStop* = 15; (** stop enumeration (can be set in EntryHandler). *)
  14. (** return values for a FileSystem's handler procedure *)
  15. Ok* = 0;
  16. Unsupported* = -1;
  17. Error* = 2;
  18. (* Reserved = 32; (* Blocks reserved for system on Boot volumes *) *)
  19. InvalidAddress = -1;
  20. (* Length of filenames *)
  21. PreLength = 16; (* Length of filesystem identifier *)
  22. LocalLength = 128; (* Length of filename without filesystem identifier *)
  23. FileNameLength = PreLength + LocalLength;
  24. (* TODO: Do this better. At the moment the Bitmap is statically preallocated *)
  25. BitmapSize = 4096;
  26. DEBUG = FALSE;
  27. thisModuleName = "OFS"; (* used for kernel log messages *)
  28. (* min number of block a valume can have *)
  29. MinVolSize = 4;
  30. SF = 29; (* SectorFactor *)
  31. DirMark* = LONGINT(9B1EA38DH); (* The main directory *)
  32. HeaderMark = LONGINT(9BA71D86H); (* a data file *)
  33. MapMark = LONGINT(9C2F977FH); (* a bitmap block *)
  34. DirRootAdr = 1*SF;
  35. MaxFiles = 10;
  36. MaxBuffers = 2;
  37. InitHint = 200*SF;
  38. (* Change the following parameters to get a different sectorsize *)
  39. (* From here: 4096 byte blocks *)
  40. (*SS* = 4096; (* SectorSize *)
  41. STS = 128; (* SecTabSize *)
  42. XS = SS DIV 4; (* IndexSize *)
  43. HS = 568; (* HeaderSize *)
  44. DirPgSize = 102;
  45. N = DirPgSize DIV 2;
  46. FillerSize = 4;*)
  47. STS = 128;
  48. SS* = 4096;
  49. XS = SS DIV 4;
  50. DiskAdrSize = 4; (* bytes *)
  51. HS = 4 (* mark *) + LocalLength + 4*4 (* aleng, bleng, time, date *) + (STS+1)*DiskAdrSize;
  52. DirEntrySize = LocalLength + 2*DiskAdrSize (* adr, p *);
  53. DirPgHeaderSize = 2*4 (* mark, m *) + DiskAdrSize (* p0 *) + 4 (* min. FillerSize *);
  54. DirPgSize = (SS - DirPgHeaderSize) DIV DirEntrySize;
  55. FillerSize = (SS - DirPgHeaderSize) MOD DirEntrySize + 4 (* min. FillerSize *);
  56. N = DirPgSize DIV 2;
  57. (* generic bitmap size *)
  58. MapIndexSize = (SS-4) DIV 4;
  59. MapSize = SS DIV 4; (* {MapSize MOD 32 = 0} *)
  60. TYPE
  61. Array = ARRAY 2000 OF LONGINT;
  62. (* A callback function which is used in Enumerate *)
  63. EntryHandler* = PROCEDURE(CONST name: ARRAY OF CHAR; time, date, size: LONGINT; VAR flags: SET);
  64. (* The basic disc sector, all "real" used disksector types are derived from this *)
  65. DiskSector* = RECORD END;
  66. (* Volume is the base type of all volumes. It provides operations on an abstract array of file system
  67. data blocks of blockSize bytes, numbered from 1 to size. *)
  68. Volume* = POINTER TO VolumeDesc;
  69. VolumeDesc* = RECORD
  70. name*: ARRAY 32 OF CHAR; (* descriptive name - e.g. for matching with Partitions.Show *)
  71. blockSize*: LONGINT; (* block size in bytes *)
  72. size*: LONGINT; (* size in blocks *)
  73. flags*: SET; (* ReadOnly, Removable, Boot *)
  74. AllocBlock*: PROCEDURE (vol: Volume; hint: LONGINT; VAR adr: LONGINT; VAR res: LONGINT);
  75. FreeBlock*, MarkBlock*: PROCEDURE (vol: Volume; adr: LONGINT; VAR res: LONGINT);
  76. (* A the block with number "adr" as used *)
  77. Marked*: PROCEDURE (vol: Volume; adr: LONGINT; VAR res: LONGINT): BOOLEAN;
  78. (* Returns the number of available blocks *)
  79. Available*: PROCEDURE (vol: Volume): LONGINT;
  80. GetBlock*, PutBlock*: PROCEDURE (vol: Volume; adr: LONGINT; VAR blk: ARRAY OF SYSTEM.BYTE; ofs: LONGINT; VAR res: LONGINT);
  81. Finalize*: PROCEDURE (vol: Volume);
  82. Sync*: PROCEDURE(vol: Volume);
  83. (* used blocks *)
  84. used: LONGINT;
  85. (* This bitmap is used to mark in memory if a block is used or not. Currently static allocated... todo: change this *)
  86. map: POINTER TO ARRAY (*BitmapSize*) OF SET
  87. END;
  88. (* The name of a mounted filesystem which can be used to address it *)
  89. Prefix* = ARRAY PreLength OF CHAR;
  90. (* The physically stored filename without prefix *)
  91. LocalName* = ARRAY LocalLength OF CHAR;
  92. (* Filename with prefix and LocalName *)
  93. FileName* = ARRAY FileNameLength OF CHAR;
  94. FileSystem* = POINTER TO FileSystemDesc;
  95. File* = POINTER TO FileDesc;
  96. Buffer* = POINTER TO BufferRecord;
  97. Rider* = RECORD
  98. eof*: BOOLEAN; (** has end of file been passed *)
  99. res*: LONGINT; (** leftover byte count for ReadBytes/WriteBytes *)
  100. (** private fields for implementors *)
  101. apos*, bpos*: LONGINT; (* apos: The sectornumber, bpos: The offset in the sector *)
  102. hint*: Buffer; (* used as a hint for new block allocation. This is basically the current data block *)
  103. file*: File; (* The file this filesystem is working on *)
  104. fs*: FileSystem (* The mounted filesystem, this file is working on *)
  105. END;
  106. FileHd = POINTER TO FileHeader;
  107. (* Generic disk sector *)
  108. DataSector = RECORD (DiskSector)
  109. B: ARRAY SS OF CHAR
  110. END;
  111. (* A disk sector buffer, used in RAM *)
  112. BufferRecord* = RECORD
  113. apos*, lim*: LONGINT; (* apos: sectornumber, lin: current pos in buffer *)
  114. mod*: BOOLEAN;
  115. next*: Buffer;
  116. data*: DataSector
  117. END;
  118. (* A physical Index sector *)
  119. IndexSector = RECORD (DiskSector)
  120. x: ARRAY XS OF LONGINT
  121. END;
  122. SectorTable = ARRAY STS OF LONGINT;
  123. FileDesc* = RECORD
  124. key*: LONGINT; (* unique id for registered file, never 0, basically the blocknumber of the file header *)
  125. fs*: FileSystem; (* file system containing file *)
  126. next*: File;
  127. aleng, bleng: LONGINT; (* aleng: number of used sectors, blen: pos in last used sector *)
  128. nofbufs: LONGINT; (* number of data buffers *)
  129. modH, registered: BOOLEAN; (* modH: File header modified flag, registered: This file has already been registered *)
  130. sechint: LONGINT; (* the number of the last block allocated to this file *)
  131. name: FileName; (* The filename *)
  132. time: LONGINT; (* The last access time *)
  133. ext: LONGINT; (* number of sector representing the superIndex *)
  134. firstbuf*: Buffer; (* the one and only data buffer *)
  135. sec: SectorTable (* table that contains the addresses of the directly addressable sectors *)
  136. END;
  137. FileSystemDesc* = RECORD
  138. next: FileSystem; (* file system search path *)
  139. link: FileSystem; (* list of mounted file systems *)
  140. prefix*: Prefix; (** mount prefix *)
  141. desc*: ARRAY 32 OF CHAR; (** description of file system *)
  142. vol*: Volume (** underlying volume, if any (a boot FS must have a volume) *)
  143. END;
  144. FileHeader = RECORD (DiskSector) (* allocated in the first page of each file on disk *)
  145. mark: LONGINT; (* The mark distinguishes the type of the sector. In this case always a file *)
  146. name: LocalName;
  147. aleng, bleng: LONGINT;
  148. date, time: LONGINT;
  149. sec: SectorTable; (* direct addressable sectors *)
  150. ext: LONGINT; (* number of sector that contains the indirect addressable sectors *)
  151. data: ARRAY SS - HS OF CHAR (* File data to fill the FileHeader *)
  152. END;
  153. DirEntry = RECORD (*B-tree node*) (* a directory entry *)
  154. name: LocalName;
  155. adr: LONGINT; (*sec no of file header*)
  156. p: LONGINT (*sec no of left descendant (DirPage) in directory, used if the name is *)
  157. END;
  158. DirPage = RECORD (DiskSector) (* A disk sector that contains Directory entries *)
  159. mark: LONGINT; (* The mark distinguishes the type of the sector. In this case a directory *)
  160. m: LONGINT; (* number of stored elements *)
  161. p0: LONGINT; (* sec no of left descendant (DirPage) in directory. Contains the files with names
  162. "smaller" than e[0] *)
  163. fill: ARRAY FillerSize OF CHAR;
  164. e: ARRAY DirPgSize OF DirEntry
  165. END;
  166. (* Header of the bitmap which is stored to disk. *)
  167. (* The bitmap is always indirectly addressed *)
  168. MapIndex = RECORD (DiskSector)
  169. mark: LONGINT;
  170. index: ARRAY MapIndexSize OF LONGINT
  171. END;
  172. (* a sector which actually contains a part of the bitmap *)
  173. MapSector = RECORD (DiskSector)
  174. map: ARRAY MapSize OF SET
  175. END;
  176. VAR
  177. (* The one and only directory *)
  178. hp: FileHeader;
  179. fullname: FileName;
  180. fsroot: FileSystem; (* file system search path root *)
  181. fsmount: FileSystem; (* list of known file systems *)
  182. froot: File; (* list of known open files, with dummy head *)
  183. filePool: File; (* A list of files. The number of elements in this pool determine the maximum number of files that can be opened *)
  184. (* bufPool: Buffer; (* A list of buffers. Contains MaxFiles*MaxBuffers elements *) *)
  185. (** Predefined volume methods (need not be used). *)
  186. PROCEDURE First*(): FileSystem;
  187. BEGIN
  188. RETURN fsroot
  189. END First;
  190. (* Join prefix and name to fullname = ( prefix ":" name ) *)
  191. PROCEDURE JoinName*(CONST prefix, name: ARRAY OF CHAR; VAR fullname: ARRAY OF CHAR);
  192. VAR
  193. i, j: LONGINT;
  194. BEGIN
  195. i := 0; WHILE prefix[i] # 0X DO fullname[i] := prefix[i]; INC(i) END;
  196. fullname[i] := ':'; INC(i);
  197. j := 0; WHILE name[j] # 0X DO fullname[i] := name[j]; INC(i); INC(j) END;
  198. fullname[i] := 0X
  199. END JoinName;
  200. PROCEDURE FSGetSector(vol: Volume; src: LONGINT; VAR dest: DiskSector);
  201. VAR
  202. res: LONGINT;
  203. BEGIN
  204. (*
  205. IF src MOD SF # 0 THEN
  206. HALT(15)
  207. END;
  208. *)
  209. vol.GetBlock(vol, src DIV SF, dest, 0, res);
  210. END FSGetSector;
  211. PROCEDURE FSPutSector(vol: Volume; dest: LONGINT; VAR src: DiskSector);
  212. VAR
  213. res: LONGINT;
  214. BEGIN
  215. (*
  216. ASSERT(~(ReadOnly IN vol.flags));
  217. IF dest MOD SF # 0 THEN
  218. HALT(15)
  219. END;
  220. *)
  221. vol.PutBlock(vol, dest DIV SF, src, 0, res);
  222. END FSPutSector;
  223. PROCEDURE FSAllocSector(vol: Volume; hint: LONGINT; VAR sec: LONGINT);
  224. VAR
  225. res: LONGINT;
  226. BEGIN
  227. ASSERT(~(ReadOnly IN vol.flags));
  228. vol.AllocBlock(vol, hint DIV SF, sec, res);
  229. sec := sec * SF
  230. END FSAllocSector;
  231. PROCEDURE FSMarkSector(vol: Volume; sec: LONGINT);
  232. VAR
  233. res: LONGINT;
  234. BEGIN
  235. ASSERT(~(ReadOnly IN vol.flags));
  236. vol.MarkBlock(vol, sec DIV SF, res)
  237. END FSMarkSector;
  238. PROCEDURE FSFreeSector(vol: Volume; sec: LONGINT);
  239. VAR
  240. res: LONGINT;
  241. BEGIN
  242. ASSERT(~(ReadOnly IN vol.flags));
  243. vol.FreeBlock(vol, sec DIV SF, res)
  244. END FSFreeSector;
  245. PROCEDURE FSMarked(vol: Volume; sec: LONGINT): BOOLEAN;
  246. VAR
  247. res: LONGINT;
  248. BEGIN
  249. ASSERT(~(ReadOnly IN vol.flags));
  250. RETURN vol.Marked(vol, sec DIV SF, res)
  251. END FSMarked;
  252. PROCEDURE FSSize(vol: Volume): LONGINT;
  253. BEGIN
  254. ASSERT(vol.size >= MinVolSize);
  255. RETURN vol.size
  256. END FSSize;
  257. (* Search a file in a specific volume, A contains the address of the file. 0 if not found *)
  258. PROCEDURE Search(vol: Volume; CONST name: FileName; VAR A: LONGINT);
  259. VAR i, L, R: LONGINT; dadr: LONGINT; a: DirPage;
  260. BEGIN
  261. A := -1;
  262. dadr := DirRootAdr;
  263. REPEAT
  264. FSGetSector(vol, dadr, a);
  265. ASSERT(a.mark = DirMark);
  266. L := 0; R := a.m; (*binary search*)
  267. WHILE L < R DO
  268. i := (L+R) DIV 2;
  269. IF Strings.Compare(name, a.e[i].name) <= 0 THEN R := i ELSE L := i+1 END
  270. END ;
  271. IF (R < a.m) & (name = a.e[R].name) THEN
  272. A := a.e[R].adr;
  273. ELSE
  274. IF R = 0 THEN dadr := a.p0 ELSE dadr := a.e[R-1].p END ;
  275. IF dadr = 0 THEN A := 0; END;
  276. END;
  277. UNTIL A >= 0;
  278. END Search;
  279. PROCEDURE CopyDirEntry(CONST src: DirEntry; VAR dest: DirEntry );
  280. BEGIN
  281. Strings.Copy( src.name, dest.name);
  282. dest.adr := src.adr;
  283. dest.p := src.p;
  284. END CopyDirEntry;
  285. PROCEDURE insert(vol: Volume; CONST name: FileName; dpg0: LONGINT;
  286. VAR h: BOOLEAN; VAR v: DirEntry; fad: LONGINT; VAR replacedFad: LONGINT (*gc*));
  287. (*h = "tree has become higher and v is ascending element"*)
  288. VAR ch: CHAR; i, j, L, R: LONGINT; dpg1: LONGINT; u: DirEntry; a: DirPage;
  289. BEGIN (*~h*)
  290. FSGetSector(vol, dpg0, a);
  291. L := 0; R := a.m; (*binary search*)
  292. WHILE L < R DO
  293. i := (L+R) DIV 2;
  294. IF Strings.Compare(name, a.e[i].name) <= 0 THEN R := i ELSE L := i+1 END
  295. END ;
  296. replacedFad := 0;
  297. IF (R < a.m) & (name = a.e[R].name) THEN
  298. replacedFad := a.e[R].adr; (*gc*)
  299. a.e[R].adr := fad; FSPutSector(vol, dpg0, a) (*replace*)
  300. ELSE (*not on this page*)
  301. IF R = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[R-1].p END ;
  302. IF dpg1 = 0 THEN (*not in tree, insert*)
  303. u.adr := fad; u.p := 0; h := TRUE; j := 0;
  304. REPEAT ch := name[j]; u.name[j] := ch; INC(j)
  305. UNTIL ch = 0X;
  306. WHILE j < LocalLength DO u.name[j] := 0X; INC(j) END
  307. ELSE
  308. insert(vol, name, dpg1, h, u, fad, replacedFad)
  309. END ;
  310. IF h THEN (*insert u to the left of e[R]*)
  311. IF a.m < DirPgSize THEN
  312. h := FALSE; i := a.m;
  313. WHILE i > R DO DEC(i); CopyDirEntry( a.e[i], a.e[i+1]) END;
  314. CopyDirEntry(u, a.e[R]); INC(a.m)
  315. ELSE (*split page and assign the middle element to v*)
  316. a.m := N; a.mark := DirMark;
  317. IF R < N THEN (*insert in left half*)
  318. CopyDirEntry( a.e[N-1], v); i := N-1;
  319. WHILE i > R DO DEC(i); CopyDirEntry( a.e[i], a.e[i+1]) END ;
  320. CopyDirEntry(u, a.e[R]); FSPutSector(vol, dpg0, a);
  321. FSAllocSector(vol, dpg0, dpg0); i := 0;
  322. WHILE i < N DO CopyDirEntry(a.e[i+N], a.e[i]); INC(i) END
  323. ELSE (*insert in right half*)
  324. FSPutSector(vol, dpg0, a);
  325. FSAllocSector(vol, dpg0, dpg0); DEC(R, N); i := 0;
  326. IF R = 0 THEN CopyDirEntry(u, v);
  327. ELSE CopyDirEntry(a.e[N], v);
  328. WHILE i < R-1 DO CopyDirEntry(a.e[N+1+i], a.e[i]); INC(i) END ;
  329. CopyDirEntry(u, a.e[i]); INC(i)
  330. END ;
  331. WHILE i < N DO CopyDirEntry(a.e[N+i], a.e[i]); INC(i) END
  332. END ;
  333. a.p0 := v.p; v.p := dpg0
  334. END ;
  335. FSPutSector(vol, dpg0, a)
  336. END
  337. END
  338. END insert;
  339. PROCEDURE Insert(vol: Volume; CONST name: FileName; fad: LONGINT; VAR replacedFad: LONGINT);
  340. VAR oldroot: LONGINT; h: BOOLEAN; U: DirEntry; a: DirPage;
  341. BEGIN
  342. h := FALSE;
  343. insert(vol, name, DirRootAdr, h, U, fad, replacedFad); (*gc*)
  344. IF h THEN (*root overflow*)
  345. FSGetSector(vol, DirRootAdr, a);
  346. FSAllocSector(vol, DirRootAdr, oldroot); FSPutSector(vol, oldroot, a);
  347. a.mark := DirMark; a.m := 1; a.p0 := oldroot; CopyDirEntry(U, a.e[0]);
  348. FSPutSector(vol, DirRootAdr, a)
  349. END
  350. END Insert;
  351. PROCEDURE underflow(vol: Volume; VAR c: DirPage; (*ancestor page*) dpg0: LONGINT;
  352. s: LONGINT; (*insertion point in c*) VAR h: BOOLEAN); (*c undersize*)
  353. VAR i, k: LONGINT; dpg1: LONGINT; a, b: DirPage; (*a := underflowing page, b := neighbouring page*)
  354. BEGIN
  355. FSGetSector(vol, dpg0, a);
  356. (*h & a.m = N-1 & dpg0 = c.e[s-1].p*)
  357. IF s < c.m THEN (*b := page to the right of a*)
  358. dpg1 := c.e[s].p; FSGetSector(vol, dpg1, b);
  359. k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
  360. CopyDirEntry(c.e[s], a.e[N-1]); a.e[N-1].p := b.p0;
  361. IF k > 0 THEN
  362. (*move k-1 items from b to a, one to c*)
  363. i := 0;
  364. WHILE i < k-1 DO CopyDirEntry(b.e[i], a.e[i+N]); INC(i) END ;
  365. CopyDirEntry(b.e[i], c.e[s]); b.p0 := c.e[s].p;
  366. c.e[s].p := dpg1; b.m := b.m - k; i := 0;
  367. WHILE i < b.m DO CopyDirEntry(b.e[i+k], b.e[i]); INC(i) END ;
  368. FSPutSector(vol, dpg1, b); a.m := N-1+k; h := FALSE
  369. ELSE (*merge pages a and b, discard b*) i := 0;
  370. WHILE i < N DO CopyDirEntry(b.e[i], a.e[i+N]); INC(i) END ;
  371. i := s; DEC(c.m);
  372. WHILE i < c.m DO CopyDirEntry(c.e[i+1], c.e[i]); INC(i) END ;
  373. a.m := 2*N; h := c.m < N;
  374. FSFreeSector(vol, dpg1) (* added by tt *)
  375. END ;
  376. FSPutSector(vol, dpg0, a)
  377. ELSE (*b := page to the left of a*) DEC(s);
  378. IF s = 0 THEN dpg1 := c.p0 ELSE dpg1 := c.e[s-1].p END ;
  379. FSGetSector(vol, dpg1, b);
  380. k := (b.m-N+1) DIV 2; (*k = no. of items available on page b*)
  381. IF k > 0 THEN
  382. i := N-1;
  383. WHILE i > 0 DO DEC(i); CopyDirEntry(a.e[i], a.e[i+k]); END ;
  384. i := k-1; CopyDirEntry(c.e[s], a.e[i]); a.e[i].p := a.p0;
  385. (*move k-1 items from b to a, one to c*) b.m := b.m -k;
  386. WHILE i > 0 DO DEC(i); CopyDirEntry(b.e[i+b.m+1], a.e[i]); END ;
  387. CopyDirEntry(b.e[b.m], c.e[s]); a.p0 := c.e[s].p;
  388. c.e[s].p := dpg0; a.m := N-1+k; h := FALSE;
  389. FSPutSector(vol, dpg0, a)
  390. ELSE (*merge pages a and b, discard a*)
  391. c.e[s].p := a.p0; CopyDirEntry(c.e[s], b.e[N]); i := 0;
  392. WHILE i < N-1 DO CopyDirEntry(a.e[i], b.e[i+N+1]); INC(i) END ;
  393. b.m := 2*N; DEC(c.m); h := c.m < N;
  394. FSFreeSector(vol, dpg0) (* added by tt*)
  395. END ;
  396. FSPutSector(vol, dpg1, b)
  397. END
  398. END underflow;
  399. PROCEDURE del(vol: Volume; dpg1: LONGINT; VAR h: BOOLEAN; VAR a: DirPage; VAR R: LONGINT);
  400. VAR dpg2: LONGINT; (*global: a, R*) b: DirPage;
  401. BEGIN
  402. FSGetSector(vol, dpg1, b); dpg2 := b.e[b.m-1].p;
  403. IF dpg2 # 0 THEN del(vol, dpg2, h, a, R);
  404. IF h THEN underflow(vol, b, dpg2, b.m, h); FSPutSector(vol, dpg1, b) END
  405. ELSE
  406. b.e[b.m-1].p := a.e[R].p; CopyDirEntry(b.e[b.m-1], a.e[R]);
  407. DEC(b.m); h := b.m < N; FSPutSector(vol, dpg1, b)
  408. END
  409. END del;
  410. PROCEDURE delete(vol: Volume; CONST name: FileName; dpg0: LONGINT; VAR h: BOOLEAN; VAR fad: LONGINT);
  411. (*search and delete entry with key name; if a page underflow arises,
  412. balance with adjacent page or merge; h := "page dpg0 is undersize"*)
  413. VAR i, L, R: LONGINT; dpg1: LONGINT; a: DirPage;
  414. BEGIN (*~h*)
  415. FSGetSector(vol, dpg0, a);
  416. L := 0; R := a.m; (*binary search*)
  417. WHILE L < R DO
  418. i := (L+R) DIV 2;
  419. IF Strings.Compare(name, a.e[i].name) <= 0 THEN R := i ELSE L := i+1 END
  420. END ;
  421. IF R = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[R-1].p END ;
  422. IF (R < a.m) & (name = a.e[R].name) THEN
  423. (*found, now delete*) fad := a.e[R].adr;
  424. IF dpg1 = 0 THEN (*a is a leaf page*)
  425. DEC(a.m); h := a.m < N; i := R;
  426. WHILE i < a.m DO CopyDirEntry(a.e[i+1], a.e[i]); INC(i) END
  427. ELSE del(vol, dpg1, h, a, R);
  428. IF h THEN underflow(vol, a, dpg1, R, h) END
  429. END ;
  430. FSPutSector(vol, dpg0, a)
  431. ELSIF dpg1 # 0 THEN
  432. delete(vol, name, dpg1, h, fad);
  433. IF h THEN underflow(vol, a, dpg1, R, h); FSPutSector(vol, dpg0, a) END
  434. ELSE (*not in tree*) fad := 0
  435. END
  436. END delete;
  437. (* Remove the file "name" from the volume "vol". "fad" contains the address
  438. of the found file header block or 0 if the file was not found *)
  439. PROCEDURE DirDelete(vol: Volume; CONST name: FileName; VAR fad: LONGINT);
  440. VAR h: BOOLEAN; newroot: LONGINT; a: DirPage;
  441. BEGIN
  442. h := FALSE;
  443. delete(vol, name, DirRootAdr, h, fad);
  444. IF h THEN (*root underflow*)
  445. FSGetSector(vol, DirRootAdr, a);
  446. IF (a.m = 0) & (a.p0 # 0) THEN
  447. newroot := a.p0; FSGetSector(vol, newroot, a);
  448. FSPutSector(vol, DirRootAdr, a); (*discard newroot*)
  449. FSFreeSector(vol, newroot); (* added by tt *)
  450. END
  451. END
  452. END DirDelete;
  453. PROCEDURE MatchPrefix(CONST mask, name: ARRAY OF CHAR; VAR pos, diff: LONGINT);
  454. VAR
  455. done: BOOLEAN;
  456. BEGIN
  457. done := FALSE;
  458. pos := 0;
  459. REPEAT
  460. IF mask[pos] = 0X THEN
  461. pos := -1; diff := 0;
  462. done := TRUE;
  463. ELSIF mask[pos] = '*' THEN
  464. IF mask[pos+1] = 0X THEN pos := -1 END;
  465. diff := 0;
  466. done := TRUE;
  467. END;
  468. IF ~done THEN
  469. diff := ORD(name[pos]) - ORD(mask[pos]);
  470. IF diff # 0 THEN done := TRUE; ELSE INC(pos); END;
  471. END;
  472. UNTIL done;
  473. END MatchPrefix;
  474. (* This is procedure is ugly... Should be rewritten some time ... *)
  475. PROCEDURE Match(pos: LONGINT; CONST pat, name: ARRAY OF CHAR): BOOLEAN;
  476. VAR i0, i1, j0, j1: LONGINT; f, done: BOOLEAN;
  477. BEGIN
  478. f := TRUE;
  479. done := FALSE;
  480. IF pos # -1 THEN
  481. i0 := pos; j0 := pos;
  482. REPEAT
  483. IF pat[i0] = '*' THEN
  484. INC(i0);
  485. IF pat[i0] = 0X THEN done := TRUE; END
  486. ELSE
  487. IF name[j0] # 0X THEN f := FALSE END;
  488. done := TRUE;
  489. END;
  490. IF ~done THEN
  491. f := FALSE;
  492. WHILE (name[j0] # 0X) & ~f DO
  493. i1 := i0; j1 := j0;
  494. WHILE( (pat[i1] # 0X) & (pat[i1] # '*') & (pat[i1] = name[j1])) DO
  495. INC(i1); INC(j1)
  496. END;
  497. IF (pat[i1] = 0X) OR (pat[i1] = '*') THEN
  498. f := TRUE; j0 := j1; i0 := i1;
  499. ELSE
  500. INC(j0)
  501. END;
  502. END;
  503. IF ~f THEN done := TRUE; END
  504. END;
  505. UNTIL done;
  506. END;
  507. RETURN f & (name[0] # 0X)
  508. END Match;
  509. PROCEDURE enumerate(fs: FileSystem; CONST mask: ARRAY OF CHAR; dpg: LONGINT; VAR flags: SET; proc: EntryHandler);
  510. VAR i, pos, diff: LONGINT; dpg1: LONGINT; a: DirPage; time, date, size: LONGINT;
  511. BEGIN
  512. FSGetSector(fs.vol, dpg, a); i := 0;
  513. WHILE (i < a.m) & ~(EnumStop IN flags) DO
  514. MatchPrefix(mask, a.e[i].name, pos, diff);
  515. IF i = 0 THEN dpg1 := a.p0 ELSE dpg1 := a.e[i-1].p END;
  516. IF diff >= 0 THEN (* matching prefix *)
  517. IF dpg1 # 0 THEN enumerate(fs, mask, dpg1, flags, proc) END;
  518. IF diff = 0 THEN
  519. IF ~(EnumStop IN flags) & Match(pos, mask, a.e[i].name) THEN
  520. IF flags * {EnumSize, EnumTime} # {} THEN
  521. FSGetSector(fs.vol, a.e[i].adr, hp);
  522. time := hp.time; date := hp.date;
  523. size := hp.aleng*SS + hp.bleng - HS
  524. ELSE
  525. time := 0; date := 0; size := MIN(LONGINT)
  526. END;
  527. IF fs = fsroot THEN
  528. proc(a.e[i].name, time, date, size, flags)
  529. ELSE
  530. JoinName(fs.prefix, a.e[i].name, fullname);
  531. proc(fullname, time, date, size, flags)
  532. END
  533. END
  534. ELSE flags := flags + {EnumStop}
  535. END
  536. END;
  537. INC(i)
  538. END;
  539. IF ~(EnumStop IN flags) & (i > 0) & (a.e[i-1].p # 0) THEN
  540. enumerate(fs, mask, a.e[i-1].p, flags, proc)
  541. END
  542. END enumerate;
  543. PROCEDURE sift(L, R: LONGINT; VAR A: Array);
  544. VAR i, j: LONGINT; x: LONGINT; exit: BOOLEAN;
  545. BEGIN
  546. j := L; x := A[j];
  547. exit := FALSE;
  548. REPEAT
  549. i := j; j := 2*j + 1;
  550. IF (j+1 < R) & (A[j] < A[j+1]) THEN INC(j) END ;
  551. IF (j >= R) OR (x > A[j]) THEN exit := TRUE;
  552. ELSE A[i] := A[j] END;
  553. UNTIL exit;
  554. A[i] := x
  555. END sift;
  556. PROCEDURE MarkSectors( vol: Volume; VAR k: LONGINT; VAR A: Array; VAR bad: BOOLEAN; VAR files: LONGINT );
  557. VAR L, R, i, j, n: LONGINT; x: LONGINT; hd: FileHeader; sup, sub: IndexSector;
  558. BEGIN
  559. Log.vS(" marking");
  560. L := k DIV 2; R := k; (*heapsort*)
  561. WHILE L > 0 DO DEC(L); sift(L, R, A) END ;
  562. WHILE R > 0 DO
  563. DEC(R); x := A[0]; A[0] := A[R]; A[R] := x; sift(L, R, A)
  564. END;
  565. WHILE L < k DO
  566. bad := FALSE; INC(files);
  567. IF files MOD 128 = 0 THEN Log.vC('.') END;
  568. FSGetSector(vol, A[L], hd);
  569. IF hd.aleng < STS THEN
  570. j := hd.aleng + 1;
  571. REPEAT
  572. DEC(j);
  573. IF hd.sec[j] # 0 THEN FSMarkSector(vol, hd.sec[j]) ELSE hd.aleng := j-1; bad := TRUE END
  574. UNTIL j = 0
  575. ELSE
  576. j := STS;
  577. REPEAT
  578. DEC(j);
  579. IF hd.sec[j] # 0 THEN FSMarkSector(vol, hd.sec[j]) ELSE hd.aleng := j-1; bad := TRUE END
  580. UNTIL j = 0;
  581. IF hd.ext = 0 THEN hd.aleng := STS-1; bad := TRUE END;
  582. IF ~bad THEN
  583. FSMarkSector(vol, hd.ext); FSGetSector(vol, hd.ext, sup);
  584. n := (hd.aleng - STS) DIV XS; i := 0;
  585. WHILE (i <= n) & ~bad DO
  586. IF sup.x[i] # 0 THEN
  587. FSMarkSector(vol, sup.x[i]); FSGetSector(vol, sup.x[i], sub);
  588. IF i < n THEN j := XS
  589. ELSE j := (hd.aleng - STS) MOD XS + 1
  590. END;
  591. REPEAT
  592. DEC(j);
  593. IF (sub.x[j] MOD SF = 0) & (sub.x[j] > 0) THEN
  594. FSMarkSector(vol, sub.x[j])
  595. ELSE
  596. bad := TRUE
  597. END
  598. UNTIL j = 0;
  599. INC(i)
  600. ELSE bad := TRUE
  601. END;
  602. IF bad THEN
  603. IF i = 0 THEN hd.aleng := STS-1
  604. ELSE hd.aleng := STS + (i-1) * XS
  605. END
  606. END
  607. END
  608. END
  609. END;
  610. IF bad THEN
  611. Log.L; Log.S(hd.name); Log.S(" truncated");
  612. hd.bleng := SS; IF hd.aleng < 0 THEN hd.aleng := 0 (* really bad *) END;
  613. FSPutSector(vol, A[L], hd)
  614. END;
  615. INC(L)
  616. END
  617. END MarkSectors;
  618. PROCEDURE TraverseDir(vol: Volume; dpg: LONGINT; VAR A: Array; VAR k: LONGINT; VAR bad: BOOLEAN; VAR files: LONGINT);
  619. VAR i: LONGINT; a: DirPage;
  620. BEGIN
  621. FSGetSector(vol, dpg, a); FSMarkSector(vol, dpg); i := 0;
  622. WHILE i < a.m DO
  623. A[k] := a.e[i].adr; INC(k); INC(i);
  624. IF k = 2000 THEN MarkSectors(vol, k, A, bad, files); k := 0 END
  625. END ;
  626. IF a.p0 # 0 THEN
  627. TraverseDir(vol, a.p0, A, k, bad, files); i := 0;
  628. WHILE i < a.m DO
  629. TraverseDir(vol, a.e[i].p, A, k, bad, files); INC(i)
  630. END
  631. END
  632. END TraverseDir;
  633. PROCEDURE DirStartup(vol: Volume; VAR init: BOOLEAN);
  634. VAR
  635. j, sec, size, q, free, thres: LONGINT; mi: MapIndex; ms: MapSector;
  636. found, done: BOOLEAN;
  637. BEGIN
  638. size := FSSize(vol); init := FALSE; found := FALSE;
  639. IF (vol.Available(vol) = size) & (size # 0) THEN (* all sectors available *)
  640. FSGetSector(vol, size*SF, mi);
  641. IF mi.mark = MapMark THEN
  642. j := 0; (* check consistency of index *)
  643. WHILE (j # MapIndexSize) & (mi.index[j] >= 0) & (mi.index[j] MOD SF = 0) DO
  644. INC(j)
  645. END;
  646. IF j = MapIndexSize THEN
  647. found := TRUE;
  648. mi.mark := 0; FSPutSector(vol, size*SF, mi); (* invalidate index *)
  649. j := 0; sec := 1; q := 0;
  650. done := FALSE;
  651. WHILE ~((j = MapIndexSize) OR (mi.index[j] = 0)) & ~done DO
  652. FSGetSector(vol, mi.index[j], ms);
  653. REPEAT
  654. IF (sec MOD 32) IN ms.map[sec DIV 32 MOD MapSize] THEN
  655. FSMarkSector(vol, sec*SF);
  656. INC(q)
  657. END;
  658. IF sec = size THEN done := TRUE
  659. ELSE INC(sec) END;
  660. UNTIL (sec MOD (MapSize*32) = 0) OR done;
  661. INC(j)
  662. END;
  663. (* Kernel.GetConfig("DiskGC", s); todo: What is this??
  664. thres := 0; j := 0;
  665. WHILE s[j] # 0X DO thres := thres*10+(ORD(s[j])-48); INC(j) END;
  666. IF thres < 10 THEN thres := 10
  667. ELSIF thres > 100 THEN thres := 100
  668. END;
  669. *)
  670. thres := 10;
  671. ASSERT(q = size-vol.Available(vol));
  672. free := vol.Available(vol)*100 DIV size;
  673. IF (free > thres) & (vol.Available(vol)*SS > 10000H) THEN
  674. init := TRUE
  675. ELSE (* undo *)
  676. FOR j := SF TO size*SF BY SF DO
  677. IF FSMarked(vol, j) THEN FSFreeSector(vol, j) END
  678. END;
  679. ASSERT(vol.Available(vol) = size);
  680. Log.S(thisModuleName);
  681. Log.S(': '); Log.I(free);
  682. Log.S("% free, forcing disk GC on ");
  683. Log.S(vol.name); Log.L
  684. END
  685. END
  686. END;
  687. (*IF ~found THEN
  688. Kernel.WriteString(thisModuleName);
  689. Kernel.WriteString(": Index not found on ");
  690. Kernel.WriteString(vol.name); Kernel.WriteLn
  691. END*)
  692. END;
  693. END DirStartup;
  694. PROCEDURE DirInit(vol: Volume; VAR init: BOOLEAN);
  695. VAR k: LONGINT; A: ARRAY 2000 OF LONGINT; files: LONGINT; bad: BOOLEAN;
  696. BEGIN
  697. IF ~(ReadOnly IN vol.flags) THEN
  698. k := 0; init := FALSE;
  699. DirStartup(vol, init);
  700. IF ~init THEN
  701. files := 0;
  702. Log.S(thisModuleName);
  703. Log.S(": Scanning ");
  704. Log.S(vol.name); Log.S("...");
  705. TraverseDir(vol, DirRootAdr, A,k,bad,files);
  706. MarkSectors(vol, k, A, bad, files);
  707. init := TRUE;
  708. Log.I(files); Log.S(" files"); Log.L
  709. END
  710. ELSE
  711. init := TRUE
  712. END;
  713. END DirInit;
  714. PROCEDURE DirCleanup(vol: Volume);
  715. VAR i, j, p, q, sec, size: LONGINT; mi: MapIndex; ms: MapSector;
  716. abort, exit: BOOLEAN;
  717. BEGIN
  718. abort := FALSE; exit := FALSE;
  719. size := FSSize(vol); i := size*SF;
  720. IF ~(ReadOnly IN vol.flags) & ~FSMarked(vol, i) THEN (* last sector is free *)
  721. j := 0; sec := 1; q := 0;
  722. WHILE ~abort & ~exit DO
  723. REPEAT DEC(i, SF) UNTIL (i = 0) OR ~FSMarked(vol, i); (* find a free sector *)
  724. IF i = 0 THEN abort := TRUE;
  725. ELSE (* no more space, don't commit *)
  726. mi.index[j] := i; INC(j);
  727. FOR p := 0 TO MapSize-1 DO ms.map[p] := {} END;
  728. REPEAT
  729. IF FSMarked(vol, sec*SF) THEN
  730. ms.map[sec DIV 32 MOD MapSize] := ms.map[sec DIV 32 MOD MapSize] + {sec MOD 32};
  731. INC(q)
  732. END;
  733. IF sec = size THEN
  734. FSPutSector(vol, i, ms);
  735. exit := TRUE;
  736. END;
  737. INC(sec)
  738. UNTIL (sec MOD (MapSize*32) = 0) OR exit;
  739. IF ~abort & ~exit THEN
  740. FSPutSector(vol, i, ms)
  741. END;
  742. END;
  743. END;
  744. IF ~abort THEN
  745. WHILE j # MapIndexSize DO mi.index[j] := 0; INC(j) END;
  746. mi.mark := MapMark;
  747. FSPutSector(vol, size*SF, mi); (* commit *)
  748. Log.S(thisModuleName);
  749. Log.S(": Map saved on ");
  750. Log.S(vol.name); Log.L
  751. END;
  752. END
  753. END DirCleanup;
  754. (* Check a file name. *)
  755. PROCEDURE Check(CONST s: ARRAY OF CHAR; VAR name: FileName; VAR res: LONGINT);
  756. VAR i: LONGINT; ch: CHAR; exit: BOOLEAN;
  757. BEGIN
  758. ch := s[0]; i := 0;
  759. IF ('A' <= CAP(ch)) & (CAP(ch) <= 'Z') THEN
  760. exit := FALSE;
  761. REPEAT
  762. name[i] := ch; INC(i); ch := s[i];
  763. IF ch = 0X THEN
  764. WHILE i < LocalLength DO name[i] := 0X; INC(i) END ;
  765. res := 0;
  766. exit := TRUE;
  767. END ;
  768. IF ~exit THEN
  769. IF ~(('A' <= CAP(ch)) & (CAP(ch) <= 'Z')
  770. OR ('0' <= ch) & (ch <= '9') OR (ch = '.')) THEN res := 3; exit := TRUE;
  771. END ;
  772. IF (i = LocalLength-1) & ~exit THEN res := 4; exit := TRUE END
  773. END;
  774. UNTIL exit;
  775. ELSIF ch = 0X THEN name[0] := 0X; res := -1
  776. ELSE res := 3
  777. END
  778. END Check;
  779. (* Creates a new file with the specified name. *)
  780. PROCEDURE FSNew(fs: FileSystem; CONST name: ARRAY OF CHAR): File;
  781. VAR i, res: LONGINT; f: File; head: FileHd; namebuf: FileName; buf: Buffer;
  782. BEGIN
  783. f := NIL; Check(name, namebuf, res);
  784. IF (res <= 0) & (filePool # NIL) THEN
  785. f := filePool; filePool := filePool.next;
  786. (* Invalidate buffers *)
  787. buf := f.firstbuf;
  788. REPEAT buf.apos := InvalidAddress; buf := buf.next; UNTIL buf = f.firstbuf;
  789. buf := f.firstbuf;
  790. FOR i := 0 TO SS - 1 DO
  791. buf.data.B[i] := 0X;
  792. END;
  793. buf.apos := 0; buf.mod := TRUE; buf.lim := HS;
  794. SYSTEM.PUT (ADDRESSOF (head), ADDRESSOF(buf.data));
  795. head.mark := HeaderMark;
  796. head.aleng := 0; head.bleng := HS; Strings.Copy(namebuf, head.name);
  797. head.time := Kernel.GetTime();
  798. f.fs := fs; f.key := 0; f.aleng := 0; f.bleng := HS; f.modH := TRUE;
  799. f.time := head.time;
  800. Strings.Copy(namebuf, f.name); f.sechint := InitHint;
  801. f.registered := FALSE;
  802. f.ext := 0; i := 0;
  803. REPEAT f.sec[i] := 0; head.sec[i] := 0; INC(i) UNTIL i = STS
  804. END;
  805. RETURN f
  806. END FSNew;
  807. PROCEDURE CopySectorTable(CONST src: ARRAY OF LONGINT; VAR dest: ARRAY OF LONGINT );
  808. VAR
  809. i: LONGINT;
  810. BEGIN
  811. FOR i := 0 TO STS -1 DO
  812. dest[i] := src[i];
  813. END;
  814. END CopySectorTable;
  815. (* Store the fileheader of file f in h *)
  816. PROCEDURE UpdateHeader(f: File; VAR h: FileHeader);
  817. BEGIN
  818. h.aleng := f.aleng; h.bleng := f.bleng;
  819. CopySectorTable(f.sec, h.sec);
  820. h.ext := f.ext;
  821. h.time := f.time
  822. END UpdateHeader;
  823. PROCEDURE ReadBuf(f: File; buf: Buffer; pos: LONGINT);
  824. VAR sec: LONGINT; xpos: LONGINT;
  825. index: IndexSector;
  826. BEGIN
  827. IF pos < STS THEN
  828. sec := f.sec[pos]
  829. ELSE
  830. xpos := pos-STS;
  831. (* replaced: sec := f.ext.sub[xpos DIV XS].sec.x[xpos MOD XS] *)
  832. FSGetSector(f.fs.vol, f.ext, index);
  833. FSGetSector(f.fs.vol, index.x[xpos DIV XS], index);
  834. sec := index.x[xpos MOD XS];
  835. END;
  836. FSGetSector(f.fs.vol, sec, buf.data);
  837. IF pos < f.aleng THEN buf.lim := SS ELSE buf.lim := f.bleng END;
  838. buf.apos := pos; buf.mod := FALSE
  839. END ReadBuf;
  840. PROCEDURE NewSuper(f: File);
  841. VAR i: LONGINT; super: LONGINT; sec: IndexSector;
  842. BEGIN
  843. FSAllocSector(f.fs.vol, f.key, super);
  844. f.ext := super;
  845. (* Clear sector *)
  846. FOR i := 0 TO XS-1 DO sec.x[i] := 0 END;
  847. FSPutSector(f.fs.vol, super, sec);
  848. END NewSuper;
  849. PROCEDURE WriteBuf(f: File; buf: Buffer);
  850. VAR i, j, k, xpos: LONGINT; secadr: LONGINT; super, sub: LONGINT; vol: Volume;
  851. ptr: FileHd; src, dst: LONGINT; tempFileHeader: FileHeader;
  852. superSec, subSec: IndexSector;
  853. BEGIN
  854. vol := f.fs.vol;
  855. f.time := Kernel.GetTime(); f.modH := TRUE;
  856. IF buf.apos < STS THEN
  857. secadr := f.sec[buf.apos];
  858. IF secadr = 0 THEN (* This buffer has never been written to disk, allocate one on the disk *)
  859. FSAllocSector(vol, f.sechint, secadr);
  860. f.modH := TRUE; f.sec[buf.apos] := secadr; f.sechint := secadr
  861. END;
  862. IF buf.apos = 0 THEN (* If this sector has number 0, it is the FileHeader itself. *)
  863. SYSTEM.PUT (ADDRESSOF (ptr), ADDRESSOF(buf.data));
  864. UpdateHeader(f, ptr^);
  865. f.modH := FALSE
  866. END
  867. ELSE
  868. (* The block is not directly adressable, so get the appropraite super and sub index blocks or allocate them
  869. if they do not already exist *)
  870. super := f.ext;
  871. IF super = 0 THEN NewSuper(f); super := f.ext END;
  872. xpos := buf.apos-STS;
  873. i := xpos DIV XS;
  874. FSGetSector(vol, super, superSec);
  875. sub := superSec.x[i];
  876. IF sub = 0 THEN
  877. FSAllocSector(vol, f.sechint, sub); f.sechint := sub;
  878. FOR j := 0 TO XS-1 DO subSec.x[j] := 0 END;
  879. superSec.x[i] := sub;
  880. FSPutSector(vol, super, superSec);
  881. ELSE
  882. FSGetSector(vol, sub, subSec);
  883. END;
  884. k := xpos MOD XS; secadr := subSec.x[k];
  885. IF secadr = 0 THEN
  886. FSAllocSector(vol, f.sechint, secadr); f.sechint := secadr;
  887. subSec.x[k] := secadr;
  888. FSPutSector(vol, sub, subSec);
  889. END
  890. END;
  891. FSPutSector(vol, secadr, buf.data); buf.mod := FALSE;
  892. END WriteBuf;
  893. (* Search and get the buffer with number pos if file f. NIL if not found *)
  894. PROCEDURE SearchBuf(f: File; pos: LONGINT): Buffer;
  895. VAR buf: Buffer;
  896. BEGIN
  897. buf := f.firstbuf;
  898. WHILE (buf # NIL) & (buf.apos # pos) DO
  899. buf := buf.next;
  900. IF buf = f.firstbuf THEN buf := NIL; END
  901. END;
  902. RETURN buf
  903. END SearchBuf;
  904. (* Get the buffer at position pos. Never returns NIL*)
  905. PROCEDURE GetBuf(f: File; pos: LONGINT): Buffer;
  906. VAR buf: Buffer;
  907. BEGIN
  908. buf := f.firstbuf;
  909. WHILE (buf.apos # pos) DO
  910. IF buf.next = f.firstbuf THEN
  911. (* take one of the buffers *)
  912. f.firstbuf := buf;
  913. IF (buf.mod) & (buf.apos # InvalidAddress) THEN WriteBuf(f, buf) END;
  914. buf.apos := pos;
  915. IF pos <= f.aleng THEN ReadBuf(f, buf, pos) END;
  916. END;
  917. IF buf.apos # pos THEN buf := buf.next END;
  918. END;
  919. RETURN buf
  920. END GetBuf;
  921. (* Return unique id for file, or 0 if it does not exist. *)
  922. PROCEDURE FileKey(fs: FileSystem; CONST name: ARRAY OF CHAR): LONGINT;
  923. VAR res: LONGINT; namebuf: FileName; header: LONGINT;
  924. BEGIN
  925. header := 0;
  926. Check(name, namebuf, res);
  927. IF res = 0 THEN
  928. Search(fs.vol, namebuf, header)
  929. END;
  930. RETURN header
  931. END FileKey;
  932. (* Open an existing file. *)
  933. PROCEDURE FSOld(fs: FileSystem; CONST name: ARRAY OF CHAR): File;
  934. VAR
  935. i, k, res: LONGINT; f: File; header: LONGINT; buf: Buffer; head: FileHd;
  936. namebuf: FileName; super: LONGINT; sub: LONGINT; sec: IndexSector; vol: Volume;
  937. BEGIN
  938. f := NIL; Check(name, namebuf, res);
  939. IF res = 0 THEN
  940. vol := fs.vol;
  941. Search(vol, namebuf, header);
  942. IF (header # 0) & (filePool # NIL) THEN
  943. f := filePool; filePool := filePool.next;
  944. (* Invalidate buffers *)
  945. buf := f.firstbuf;
  946. REPEAT buf.apos := InvalidAddress; buf := buf.next; UNTIL buf = f.firstbuf;
  947. buf.apos := 0; buf.mod := FALSE;
  948. FSGetSector(vol, header, buf.data);
  949. SYSTEM.PUT (ADDRESSOF (head), ADDRESSOF(buf.data));
  950. f.fs := fs; f.key := header;
  951. f.aleng := head.aleng; f.bleng := head.bleng;
  952. f.time := head.time;
  953. IF f.aleng = 0 THEN buf.lim := f.bleng ELSE buf.lim := SS END;
  954. Strings.Copy(namebuf, f.name); f.registered := TRUE;
  955. CopySectorTable(head.sec, f.sec);
  956. f.ext := head.ext;
  957. f.sechint := header; f.modH := FALSE
  958. END
  959. END;
  960. RETURN f
  961. END FSOld;
  962. PROCEDURE Unbuffer(f: File); (* f.sec*)
  963. VAR
  964. i, k: LONGINT; buf: Buffer; head: FileHeader;
  965. sec: IndexSector; vol: Volume;
  966. BEGIN
  967. vol := f.fs.vol;
  968. (* Flush all data buffers *)
  969. buf := f.firstbuf;
  970. REPEAT
  971. IF buf.mod & (buf.apos # InvalidAddress) THEN
  972. (*WriteBuf(f, f.firstbuf);*)
  973. WriteBuf(f, buf);
  974. buf.apos := InvalidAddress;
  975. END;
  976. buf := buf.next;
  977. UNTIL f.firstbuf = buf;
  978. (* And write file header *)
  979. IF f.modH THEN
  980. FSGetSector(vol, f.sec[0], head); UpdateHeader(f, head);
  981. FSPutSector(vol, f.sec[0], head); f.modH := FALSE
  982. END
  983. END Unbuffer;
  984. (** 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). *)
  985. PROCEDURE FindOpenFile*(fs: FileSystem; key: LONGINT): File;
  986. VAR f: File;
  987. BEGIN
  988. f := froot;
  989. REPEAT f := f.next UNTIL (f = NIL) OR ((f.key = key) & (f.fs = fs));
  990. RETURN f
  991. END FindOpenFile;
  992. PROCEDURE LogGC(CONST procname, prefix, name: ARRAY OF CHAR);
  993. BEGIN
  994. Log.vS(thisModuleName);
  995. Log.vC('.');
  996. Log.vS(procname);
  997. Log.vC(' ');
  998. Log.vS(prefix);
  999. Log.vC(':');
  1000. Log.vS(name);
  1001. Log.vL;
  1002. END LogGC;
  1003. PROCEDURE Free(vol: Volume; adr: LONGINT);
  1004. BEGIN
  1005. IF (adr # 0) & FSMarked(vol, adr) THEN FSFreeSector(vol, adr) END
  1006. END Free;
  1007. PROCEDURE PurgeOnDisk(fs: FileSystem; hdadr: LONGINT); (*bsm*)
  1008. VAR hd: FileHeader; supi, subi: IndexSector; aleng, i, k: LONGINT;
  1009. secCount, subAdr: LONGINT; vol: Volume;
  1010. BEGIN
  1011. ASSERT(fs.vol # NIL);
  1012. vol := fs.vol;
  1013. FSGetSector(vol, hdadr, hd);
  1014. LogGC("PurgeOnDisk", fs.prefix, hd.name);
  1015. aleng := hd.aleng;
  1016. secCount := aleng+1;
  1017. IF secCount > STS THEN secCount := STS END;
  1018. FOR i := 0 TO secCount-1 DO
  1019. Free(fs.vol, hd.sec[i])
  1020. END;
  1021. aleng := aleng - secCount;
  1022. IF aleng >= 0 THEN
  1023. FSGetSector(vol, hd.ext, supi);
  1024. WHILE (aleng >= 0) DO
  1025. subAdr := supi.x[aleng DIV XS];
  1026. FSGetSector(vol, subAdr, subi);
  1027. FOR i := 0 TO aleng MOD XS DO
  1028. Free(fs.vol, subi.x[i])
  1029. END;
  1030. Free(fs.vol, subAdr);
  1031. aleng := aleng - (aleng MOD XS + 1);
  1032. END;
  1033. Free(fs.vol, hd.ext);
  1034. END;
  1035. END PurgeOnDisk;
  1036. PROCEDURE FSRegister(f: File; VAR res: LONGINT);
  1037. VAR repAdr: LONGINT; (* address of the file replaced through register, 0 if no such file *)
  1038. repFile: File;
  1039. BEGIN
  1040. Unbuffer(f);
  1041. IF ~f.registered & (f.name # "") THEN
  1042. Insert(f.fs.vol, f.name, f.sec[0], repAdr);
  1043. f.registered := TRUE; f.key := f.sec[0];
  1044. IF (repAdr # 0) & (f.sec[0] # repAdr) THEN
  1045. repFile := FindOpenFile(f.fs, repAdr);
  1046. IF repFile = NIL THEN
  1047. PurgeOnDisk(f.fs(FileSystem), repAdr) (* Purge file if it is not open *)
  1048. ELSE
  1049. repFile.registered := FALSE
  1050. END;
  1051. END;
  1052. res := 0
  1053. ELSE
  1054. res := 1
  1055. END
  1056. END FSRegister;
  1057. PROCEDURE PutIntoFilePool(f: File);
  1058. VAR
  1059. temp: File;
  1060. buf: Buffer;
  1061. BEGIN
  1062. (* The File must be removed from the open list, before it can be put into the File Pool for reuse *)
  1063. IF (f.key # 0) & (FindOpenFile(f.fs, f.key) # NIL) THEN
  1064. Log.S("File " ); Log.S(f.name); Log.SL(" is still open!"); Log.Flush(Log.normal);
  1065. HALT(8);
  1066. END;
  1067. buf := f.firstbuf;
  1068. REPEAT
  1069. buf.apos := InvalidAddress;
  1070. buf := buf.next;
  1071. UNTIL (buf = f.firstbuf);
  1072. temp := filePool;
  1073. WHILE (temp # NIL) & (temp # f) DO temp := temp.next END;
  1074. IF temp = NIL THEN
  1075. f.next := filePool; filePool := f;
  1076. END;
  1077. END PutIntoFilePool;
  1078. PROCEDURE LogFS(fs: FileSystem);
  1079. BEGIN
  1080. IF fs.vol # NIL THEN Log.vS(fs.vol.name); Log.vC(' ') END;
  1081. Log.vS(fs.desc)
  1082. END LogFS;
  1083. PROCEDURE FileCleanup(f: File);
  1084. VAR p, c, temp: File;
  1085. BEGIN
  1086. Log.vS("OFS: Cleanup "); Log.vI(f.key);
  1087. Log.vC(' '); LogFS(f.fs); Log.vL;
  1088. (* Remove file from open list, and put it back into the pool *)
  1089. p := froot; c := froot.next;
  1090. WHILE c # NIL DO
  1091. IF c = f THEN
  1092. p.next := c.next;
  1093. temp := c;
  1094. c := c.next;
  1095. PutIntoFilePool(temp);
  1096. ELSE
  1097. p := c;
  1098. c := c.next;
  1099. END;
  1100. END;
  1101. Log.vSL("FileCleanup finished");
  1102. END FileCleanup;
  1103. (* Returns the current length of a file. *)
  1104. PROCEDURE Length*(f: File): LONGINT;
  1105. BEGIN
  1106. RETURN f.aleng*SS + f.bleng - HS
  1107. END Length;
  1108. (* Returns the time (t) and date (d) when a file was last modified. *)
  1109. PROCEDURE GetTime*(f: File; VAR t: LONGINT);
  1110. BEGIN
  1111. t := f.time;
  1112. END GetTime;
  1113. (* Sets the modification time (t) of a file. *)
  1114. PROCEDURE SetTime*(f: File; t: LONGINT);
  1115. BEGIN
  1116. f.modH := TRUE; f.time := t;
  1117. END SetTime;
  1118. (* 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. *)
  1119. PROCEDURE Set*(VAR r: Rider; f: File; pos: LONGINT);
  1120. VAR a, b: LONGINT;
  1121. BEGIN
  1122. r.eof := FALSE; r.res := 0; r.file := f; r.fs := f.fs;
  1123. IF pos < 0 THEN
  1124. a := 0; b := HS
  1125. ELSIF pos < f.aleng*SS + f.bleng - HS THEN
  1126. a := (pos + HS) DIV SS; b := (pos + HS) MOD SS
  1127. ELSE
  1128. a := f.aleng; b := f.bleng
  1129. END;
  1130. r.apos := a; r.bpos := b; r.hint := f.firstbuf
  1131. END Set;
  1132. (* Returns the offset of a Rider positioned on a file. *)
  1133. PROCEDURE Pos*(VAR r: Rider): LONGINT;
  1134. BEGIN
  1135. RETURN r.apos*SS + r.bpos - HS
  1136. END Pos;
  1137. (* Read a byte from a file, advancing the Rider one byte further. R.eof indicates if the end of the file has been passed. *)
  1138. PROCEDURE Read*(VAR r: Rider; VAR x: CHAR);
  1139. VAR buf: Buffer; f: File;
  1140. BEGIN
  1141. buf := r.hint(Buffer); f := r.file;
  1142. IF r.apos # buf.apos THEN buf := GetBuf(f, r.apos); r.hint := buf END;
  1143. IF r.bpos < buf.lim THEN
  1144. x := SYSTEM.VAL(CHAR, buf.data.B[r.bpos]); INC(r.bpos)
  1145. ELSIF r.apos < f.aleng THEN
  1146. INC(r.apos);
  1147. buf := SearchBuf(f, r.apos);
  1148. IF buf = NIL THEN
  1149. buf := r.hint(Buffer);
  1150. IF (buf.mod) & (buf.apos # InvalidAddress) THEN WriteBuf(f, buf) END ;
  1151. ReadBuf(f, buf, r.apos)
  1152. ELSE
  1153. r.hint := buf
  1154. END ;
  1155. x := SYSTEM.VAL(CHAR, buf.data.B[0]); r.bpos := 1
  1156. ELSE
  1157. x := 0X; r.eof := TRUE
  1158. END
  1159. END Read;
  1160. (* 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. *)
  1161. PROCEDURE ReadBytes*(VAR r: Rider; VAR x: ARRAY OF SYSTEM.BYTE; n: LONGINT);
  1162. VAR src, dst, m: LONGINT; buf: Buffer; f: File;
  1163. BEGIN
  1164. IF LEN(x) < n THEN HALT(19) END ;
  1165. IF n > 0 THEN
  1166. dst := ADDRESSOF(x[0]);
  1167. buf := r.hint(Buffer); f := r.file;
  1168. IF r.apos # buf.apos THEN buf := GetBuf(f, r.apos); r.hint := buf END;
  1169. WHILE n > 0 DO
  1170. src := ADDRESSOF(buf.data.B[0]) + r.bpos; m := r.bpos + n;
  1171. IF m <= buf.lim THEN
  1172. Kernel.Move(src,dst, n); r.bpos := m; r.res := 0; n := 0;
  1173. ELSIF buf.lim = SS THEN
  1174. m := buf.lim - r.bpos;
  1175. IF m > 0 THEN Kernel.Move(src, dst, m); dst := dst + m; n := n - m END ;
  1176. IF r.apos < f.aleng THEN
  1177. INC(r.apos); r.bpos := 0; buf := SearchBuf(f, r.apos);
  1178. IF buf = NIL THEN
  1179. buf := r.hint(Buffer);
  1180. IF buf.mod & (buf.apos # InvalidAddress) THEN WriteBuf(f, buf) END ;
  1181. ReadBuf(f, buf, r.apos)
  1182. ELSE
  1183. r.hint := buf
  1184. END
  1185. ELSE
  1186. r.bpos := buf.lim; r.res := n; r.eof := TRUE; n := 0;
  1187. END
  1188. ELSE
  1189. m := buf.lim - r.bpos;
  1190. IF m > 0 THEN Kernel.Move(src, dst, m); r.bpos := buf.lim END ;
  1191. r.res := n - m; r.eof := TRUE; n := 0;
  1192. END
  1193. END
  1194. ELSE
  1195. r.res := 0
  1196. END
  1197. END ReadBytes;
  1198. PROCEDURE ReadInt*(VAR r: Rider; VAR x: LONGINT);
  1199. BEGIN
  1200. ReadBytes(r, x, 4);
  1201. END ReadInt;
  1202. PROCEDURE ReadSet*(VAR r: Rider; VAR x: SET);
  1203. BEGIN
  1204. ReadBytes(r, x, 4);
  1205. END ReadSet;
  1206. PROCEDURE ReadReal*(VAR r: Rider; VAR x: REAL);
  1207. BEGIN
  1208. ReadBytes(r, x, 4);
  1209. END ReadReal;
  1210. PROCEDURE NewSub(f: File);
  1211. VAR i, k: LONGINT; sub: LONGINT; sec: IndexSector;
  1212. vol: Volume;
  1213. BEGIN
  1214. IF f.ext = 0 THEN NewSuper(f) END;
  1215. vol := f.fs.vol;
  1216. k := (f.aleng - STS) DIV XS;
  1217. IF k = XS THEN HALT(18) END;
  1218. FSAllocSector(vol, f.sechint, sub); f.sechint := sub;
  1219. FOR i := 0 TO XS-1 DO sec.x[i] := 0 END;
  1220. FSPutSector(vol, sub, sec);
  1221. FSGetSector(vol, f.ext, sec);
  1222. sec.x[k] := sub;
  1223. FSPutSector(vol, f.ext, sec);
  1224. END NewSub;
  1225. (* Writes a byte into the file at the Rider position, advancing the Rider by one. *)
  1226. PROCEDURE Write*(VAR r: Rider; x: CHAR);
  1227. VAR f: File; buf: Buffer;
  1228. BEGIN
  1229. buf := r.hint(Buffer); f := r.file;
  1230. IF r.apos # buf.apos THEN buf := GetBuf(f, r.apos); r.hint := buf END;
  1231. IF r.bpos >= buf.lim THEN
  1232. IF r.bpos < SS THEN
  1233. INC(buf.lim); INC(f.bleng); f.modH := TRUE
  1234. ELSE
  1235. WriteBuf(f, buf); INC(r.apos); buf := SearchBuf(f, r.apos);
  1236. IF buf = NIL THEN
  1237. buf := r.hint(Buffer);
  1238. IF r.apos <= f.aleng THEN
  1239. ReadBuf(f, buf, r.apos);
  1240. ELSE
  1241. buf.apos := r.apos; buf.lim := 1; INC(f.aleng); f.bleng := 1; f.modH := TRUE;
  1242. IF (f.aleng - STS) MOD XS = 0 THEN NewSub(f) END
  1243. END
  1244. ELSE
  1245. r.hint := buf
  1246. END;
  1247. r.bpos := 0
  1248. END
  1249. END;
  1250. buf.data.B[r.bpos] := x; INC(r.bpos); buf.mod := TRUE;
  1251. END Write;
  1252. (* Writes the buffer x containing n bytes into a file at the Rider position. *)
  1253. PROCEDURE WriteBytes*(VAR r: Rider; CONST x: ARRAY OF SYSTEM.BYTE; n: LONGINT);
  1254. VAR src, dst, m: LONGINT; f: File; buf: Buffer;
  1255. BEGIN
  1256. IF LEN(x) < n THEN HALT(19) END;
  1257. IF n > 0 THEN
  1258. src := ADDRESSOF(x[0]);
  1259. buf := r.hint(Buffer); f := r.file;
  1260. IF r.apos # buf.apos THEN buf := GetBuf(f, r.apos); r.hint := buf END;
  1261. WHILE n > 0 DO
  1262. buf.mod := TRUE; dst := ADDRESSOF(buf.data.B[0]) + r.bpos; m := r.bpos + n;
  1263. IF m <= buf.lim THEN
  1264. Kernel.Move(src, dst, n); r.bpos := m; n := 0;
  1265. ELSIF m <= SS THEN
  1266. Kernel.Move(src, dst, n); r.bpos := m;
  1267. f.bleng := m; buf.lim := m; f.modH := TRUE; n := 0;
  1268. ELSE
  1269. m := SS - r.bpos;
  1270. IF m > 0 THEN Kernel.Move(src, dst, m); src := src + m; n := n - m END;
  1271. WriteBuf(f, buf); INC(r.apos); r.bpos := 0; buf := SearchBuf(f, r.apos);
  1272. IF buf = NIL THEN
  1273. buf := r.hint(Buffer);
  1274. IF r.apos <= f.aleng THEN ReadBuf(f, buf, r.apos)
  1275. ELSE
  1276. buf.apos := r.apos; buf.lim := 0; INC(f.aleng); f.bleng := 0; f.modH := TRUE;
  1277. IF (f.aleng - STS) MOD XS = 0 THEN NewSub(f) END
  1278. END
  1279. ELSE
  1280. r.hint := buf
  1281. END
  1282. END
  1283. END
  1284. END
  1285. END WriteBytes;
  1286. PROCEDURE WriteInt*(VAR r: Rider; VAR x: LONGINT);
  1287. BEGIN
  1288. WriteBytes(r, x, 4);
  1289. END WriteInt;
  1290. PROCEDURE WriteSet*(VAR r: Rider; VAR x: SET);
  1291. BEGIN
  1292. WriteBytes(r, x, 4);
  1293. END WriteSet;
  1294. PROCEDURE WriteReal*(VAR r: Rider; VAR x: REAL);
  1295. BEGIN
  1296. WriteBytes(r, x, 4);
  1297. END WriteReal;
  1298. PROCEDURE Purge(fs: FileSystem; f: File); (*bsm*)
  1299. VAR
  1300. k, i, j, aleng, secCount, super, sub: LONGINT;
  1301. superSec, subSec: IndexSector;
  1302. vol: Volume;
  1303. BEGIN
  1304. ASSERT(f.fs = fs);
  1305. vol := fs.vol;
  1306. LogGC("Purge", fs.prefix, f.name);
  1307. aleng := f.aleng;
  1308. secCount := aleng + 1;
  1309. IF secCount > STS THEN secCount := STS END;
  1310. FOR i := 0 TO secCount - 1 DO
  1311. Free(fs.vol, f.sec[i])
  1312. END;
  1313. aleng := aleng - secCount;
  1314. IF aleng >= 0 THEN
  1315. FSGetSector(vol, f.ext, superSec);
  1316. WHILE (aleng >= 0) DO
  1317. sub := superSec.x[aleng DIV XS];
  1318. FSGetSector(vol, sub, subSec);
  1319. FOR i := 0 TO aleng MOD XS DO
  1320. Free(fs.vol, subSec.x[i])
  1321. END;
  1322. Free(fs.vol, sub);
  1323. aleng := aleng - (aleng MOD XS + 1);
  1324. END;
  1325. Free(fs.vol, f.ext);
  1326. END;
  1327. END Purge;
  1328. PROCEDURE Registered(fs: FileSystem; f: File): BOOLEAN; (*bsm*)
  1329. BEGIN
  1330. ASSERT(fs IS FileSystem); ASSERT(f.fs = fs);
  1331. RETURN f.registered
  1332. END Registered;
  1333. (* Deletes a file. res = 0 indicates success. *)
  1334. PROCEDURE FSDelete(fs: FileSystem; CONST name: ARRAY OF CHAR; VAR key: LONGINT; VAR res: LONGINT);
  1335. VAR
  1336. adr: LONGINT; namebuf: FileName; head: FileHeader; vol: Volume; r: LONGINT;
  1337. delFile: File; (*bsm*)
  1338. BEGIN
  1339. Check(name, namebuf, r); res := r;
  1340. IF res = 0 THEN
  1341. vol := fs.vol;
  1342. DirDelete(vol, namebuf, adr);
  1343. key := adr;
  1344. IF adr # 0 THEN
  1345. FSGetSector(vol, adr, head);
  1346. head.mark := HeaderMark+1; (* invalidate mark of file on disk *)
  1347. FSPutSector(vol, adr, head);
  1348. delFile := FindOpenFile(fs, key);
  1349. IF delFile = NIL THEN
  1350. PurgeOnDisk(fs(FileSystem), adr);
  1351. ELSE
  1352. delFile.registered := FALSE
  1353. END;
  1354. ELSE
  1355. res := 2
  1356. END
  1357. ELSE
  1358. key := 0
  1359. END
  1360. END FSDelete;
  1361. (* Renames a file. res = 0 indicates success. *)
  1362. PROCEDURE FSRename(fs: FileSystem; CONST old, new: ARRAY OF CHAR; VAR res: LONGINT);
  1363. VAR
  1364. adr: LONGINT; oldbuf, newbuf: FileName; head: FileHeader;
  1365. vol: Volume; f: File; r: LONGINT;
  1366. repFile: File;
  1367. repAdr: LONGINT; (* address of the file replaced through the rename, 0 if no such file *)
  1368. BEGIN
  1369. Check(old, oldbuf, r); res := r;
  1370. IF res = 0 THEN
  1371. Check(new, newbuf, r); res := r;
  1372. IF res = 0 THEN
  1373. vol := fs.vol;
  1374. DirDelete(vol, oldbuf, adr);
  1375. IF adr # 0 THEN
  1376. f := FindOpenFile(fs, adr);
  1377. IF f # NIL THEN Strings.Copy(newbuf, f.name); END;
  1378. Insert(vol, newbuf, adr, repAdr);
  1379. FSGetSector(vol, adr, head);
  1380. Strings.Copy(newbuf, head.name);
  1381. FSPutSector(vol, adr, head);
  1382. IF (repAdr # 0) & (adr # repAdr) THEN(*bsm*)
  1383. repFile := FindOpenFile(fs, repAdr);
  1384. IF (repFile = NIL) THEN
  1385. PurgeOnDisk(fs(FileSystem), repAdr)
  1386. ELSE
  1387. repFile.registered := FALSE
  1388. END;
  1389. END;
  1390. ELSE res := 2
  1391. END
  1392. END
  1393. END
  1394. END FSRename;
  1395. PROCEDURE GetName*(f: File; VAR name: ARRAY OF CHAR);
  1396. BEGIN
  1397. Strings.Copy(f.name, name)
  1398. END GetName;
  1399. (* File system initialization and finalization *)
  1400. PROCEDURE Finalize(fs: FileSystem);
  1401. VAR
  1402. tempVol : Volume;
  1403. BEGIN
  1404. fs := fs(FileSystem); (* Safety Check *)
  1405. DirCleanup(fs.vol);
  1406. tempVol := fs.vol;
  1407. tempVol.Finalize(fs.vol);
  1408. fs.vol := NIL (* prevent access in case user still has file handles *)
  1409. END Finalize;
  1410. (** Find file system with specified prefix. *)
  1411. PROCEDURE This*(CONST prefix: ARRAY OF CHAR): FileSystem;
  1412. VAR fs: FileSystem;
  1413. BEGIN
  1414. fs := fsroot; WHILE (fs # NIL) & (fs.prefix # prefix) DO fs := fs.next END;
  1415. RETURN fs
  1416. END This;
  1417. (** Add file system at end of list, with specified prefix, which must be unique. *)
  1418. PROCEDURE Add(fs: FileSystem; CONST prefix: ARRAY OF CHAR);
  1419. VAR p, c: FileSystem;
  1420. BEGIN
  1421. Log.vS("OFS: Adding "); LogFS(fs); Log.vL;
  1422. COPY(prefix, fs.prefix);
  1423. p := NIL; c := fsroot;
  1424. WHILE c # NIL DO
  1425. ASSERT((c # fs) & (c.prefix # fs.prefix)); (* duplicate insertion not allowed *)
  1426. p := c; c := c.next
  1427. END;
  1428. IF p = NIL THEN fsroot := fs ELSE p.next := fs END;
  1429. fs.next := NIL;
  1430. fs.link := fsmount; fsmount := fs (* push on mount list *)
  1431. END Add;
  1432. PROCEDURE Format(vol: Volume);
  1433. VAR
  1434. i: LONGINT;
  1435. block: DataSector;
  1436. BEGIN
  1437. IF (vol#NIL) & (vol.blockSize = SS) & (vol.size >= MinVolSize) THEN
  1438. FSGetSector(vol, DirRootAdr, block);
  1439. FOR i := 0 TO SS - 1 DO
  1440. block.B[i] := 0X;
  1441. END;
  1442. block.B[0] := CHR(DirMark);
  1443. block.B[1] := CHR(DirMark DIV 100H);
  1444. block.B[2] := CHR(DirMark DIV 10000H);
  1445. block.B[3] := CHR(DirMark DIV 1000000H);
  1446. FSPutSector(vol, DirRootAdr, block);
  1447. END;
  1448. END Format;
  1449. (** Generate a new file system object. *)
  1450. PROCEDURE NewFS*(CONST prefix: Prefix; format: BOOLEAN; vol: Volume);
  1451. VAR
  1452. fs: FileSystem; init: BOOLEAN;
  1453. BEGIN
  1454. IF vol # NIL THEN
  1455. IF This(prefix) = NIL THEN
  1456. IF (vol.blockSize = SS) & (vol.size >= MinVolSize) THEN
  1457. IF format THEN Format(vol); END;
  1458. FSGetSector(vol, DirRootAdr, hp);
  1459. IF hp.mark = DirMark THEN (* assume it is an Aos filesystem *)
  1460. NEW(fs); fs.vol := vol;
  1461. ASSERT(vol.size < MAX(LONGINT) DIV SF);
  1462. Strings.Copy( "GCAosFS", fs.desc);
  1463. DirInit(vol, init);
  1464. ASSERT(init); (* will have to undo changes to vol before continuing *)
  1465. Add(fs, prefix);
  1466. ELSE
  1467. Log.S(thisModuleName);
  1468. Log.S(": File system not found on ");
  1469. Log.S(vol.name); Log.L;
  1470. Log.S("Directory mark: "); Log.H( hp.mark); Log.L;
  1471. END
  1472. ELSE
  1473. Log.S(thisModuleName);
  1474. Log.S(": Bad volume size"); Log.L
  1475. END
  1476. ELSE
  1477. Log.S(thisModuleName);
  1478. Log.S(": "); Log.S(prefix);
  1479. Log.S(" already in use"); Log.L
  1480. END;
  1481. ELSE
  1482. Log.S(thisModuleName);
  1483. Log.S(": "); Log.S(prefix);
  1484. Log.S(" volume is NIL"); Log.L
  1485. END;
  1486. END NewFS;
  1487. (* (* Clean up when module freed. *)
  1488. PROCEDURE Cleanup;
  1489. VAR fs: FileSystem;
  1490. BEGIN
  1491. IF Kernel.shutdown = 0 THEN
  1492. REPEAT (* unmount all AosFSs *)
  1493. fs := First(); (* look for fs to unmount *)
  1494. WHILE (fs # NIL) & ~(fs IS FileSystem) DO
  1495. fs := Next(fs)
  1496. END;
  1497. IF fs # NIL THEN Remove(fs) END
  1498. UNTIL fs = NIL
  1499. END
  1500. END Cleanup;
  1501. *)
  1502. PROCEDURE DefaultAllocBlock*(vol: Volume; hint: LONGINT; VAR adr: LONGINT; VAR res: LONGINT);
  1503. VAR
  1504. found: BOOLEAN;
  1505. BEGIN
  1506. found := FALSE;
  1507. IF ReadOnly IN vol.flags THEN HALT(21) END;
  1508. ASSERT(hint >= 0);
  1509. IF hint > vol.size THEN hint := 0 END;
  1510. adr := hint+1;
  1511. REPEAT
  1512. IF adr > vol.size THEN adr := 0 END;
  1513. IF (adr MOD 32) IN vol.map[adr DIV 32] THEN
  1514. INC(adr) (* Block in use *)
  1515. ELSE
  1516. vol.map[adr DIV 32] := vol.map[adr DIV 32] + {adr MOD 32};
  1517. found := TRUE;
  1518. END;
  1519. IF (adr = hint) & (~found) THEN HALT(20) END
  1520. UNTIL found;
  1521. INC(vol.used);
  1522. res := Ok;
  1523. END DefaultAllocBlock;
  1524. PROCEDURE DefaultFreeBlock*(vol: Volume; adr: LONGINT; VAR res: LONGINT);
  1525. BEGIN
  1526. IF (adr < 1) OR (adr > vol.size) THEN HALT(15) END;
  1527. IF ReadOnly IN vol.flags THEN HALT(21) END;
  1528. vol.map[adr DIV 32] := vol.map[adr DIV 32] - {adr MOD 32};
  1529. DEC(vol.used);
  1530. res := Ok;
  1531. END DefaultFreeBlock;
  1532. PROCEDURE DefaultMarkBlock*(vol: Volume; adr: LONGINT; VAR res: LONGINT);
  1533. BEGIN
  1534. IF (adr < 1) OR (adr > vol.size) THEN HALT(15) END;
  1535. IF ReadOnly IN vol.flags THEN HALT(21) END;
  1536. vol.map[adr DIV 32] := vol.map[adr DIV 32] + {adr MOD 32};
  1537. INC(vol.used);
  1538. res := Ok;
  1539. END DefaultMarkBlock;
  1540. PROCEDURE DefaultMarked*(vol: Volume; adr: LONGINT; VAR res: LONGINT): BOOLEAN;
  1541. BEGIN
  1542. IF (adr < 1) OR (adr > vol.size) THEN HALT(15) END;
  1543. IF ReadOnly IN vol.flags THEN HALT(21) END;
  1544. res := Ok;
  1545. RETURN (adr MOD 32) IN vol.map[adr DIV 32]
  1546. END DefaultMarked;
  1547. PROCEDURE DefaultAvailable*(vol: Volume): LONGINT;
  1548. BEGIN
  1549. RETURN vol.size-vol.used
  1550. END DefaultAvailable;
  1551. PROCEDURE DefaultSync*(vol: Volume);
  1552. BEGIN
  1553. END DefaultSync;
  1554. (** Init procedure for private data of above methods only. vol.flags and vol.size must be set before. *)
  1555. PROCEDURE InitVol*(vol: Volume);
  1556. VAR maplen: LONGINT;
  1557. BEGIN
  1558. IF ~(ReadOnly IN vol.flags) THEN
  1559. maplen := (vol.size+1+31) DIV 32;
  1560. (*ASSERT( BitmapSize >= maplen); (* todo: replace this with: NEW(vol.map, maplen); *)*)
  1561. NEW(vol.map, maplen);
  1562. WHILE maplen > 0 DO DEC(maplen); vol.map[maplen] := {} END;
  1563. vol.map[0] := vol.map[0] + {0}; (* reserve sector 0 (illegal to use) *)
  1564. vol.used := 0
  1565. ELSE
  1566. vol.used := vol.size
  1567. END
  1568. END InitVol;
  1569. (** Finalize procedure for volumes. *)
  1570. PROCEDURE DefaultFinalizeVol*(vol: Volume);
  1571. BEGIN
  1572. (* vol.map := NIL; *) (* todo: uncomment this *)
  1573. vol.AllocBlock := NIL; vol.FreeBlock := NIL; vol.MarkBlock := NIL; vol.Marked := NIL;
  1574. vol.Available := NIL; vol.GetBlock := NIL; vol.PutBlock := NIL; vol.Sync := NIL;
  1575. vol.Finalize := NIL
  1576. END DefaultFinalizeVol;
  1577. (** File name prefix support. *)
  1578. (** Split fullname = ( prefix ":" name ) into prefix and name *)
  1579. PROCEDURE SplitName(CONST fullname: ARRAY OF CHAR; VAR prefix, name: ARRAY OF CHAR);
  1580. VAR i, j: LONGINT;
  1581. BEGIN
  1582. i := 0; WHILE (fullname[i] # ':') & (fullname[i] # 0X) DO INC(i) END;
  1583. IF (fullname[i] # ':') OR (i >= LEN(prefix)) THEN
  1584. Strings.Copy("", prefix); Strings.Copy (fullname, name);
  1585. ELSE
  1586. j := 0; WHILE j # i DO prefix[j] := fullname[j]; INC(j) END;
  1587. prefix[j] := 0X;
  1588. j := 0; REPEAT INC(i); name[j] := fullname[i]; INC(j) UNTIL fullname[i] = 0X
  1589. END
  1590. END SplitName;
  1591. (** File system list support. *)
  1592. PROCEDURE DeleteFS(fs: FileSystem);
  1593. VAR p, c: FileSystem;
  1594. BEGIN
  1595. p := NIL; c := fsroot;
  1596. WHILE c # fs DO p := c; c := c.next END; (* fs must be in list *)
  1597. IF p = NIL THEN fsroot := c.next ELSE p.next := c.next END;
  1598. c.next := NIL
  1599. END DeleteFS;
  1600. (** Promote fs to the start of the list. *)
  1601. PROCEDURE Promote*(fs: FileSystem);
  1602. BEGIN
  1603. DeleteFS(fs); fs.next := fsroot; fsroot := fs
  1604. END Promote;
  1605. PROCEDURE Close*(f: File);
  1606. VAR
  1607. temp: File;
  1608. BEGIN
  1609. Unbuffer(f);
  1610. IF (f.fs # NIL) & (f.fs.vol # NIL) THEN
  1611. IF ~(ReadOnly IN f.fs.vol.flags) THEN
  1612. IF ~Registered(f.fs, f) THEN
  1613. Purge(f.fs, f)
  1614. END;
  1615. END;
  1616. END;
  1617. (* Put File in filePool *)
  1618. FileCleanup(f);
  1619. PutIntoFilePool(f);
  1620. END Close;
  1621. (** Remove the file system and finalize it. *)
  1622. PROCEDURE Remove*(fs: FileSystem);
  1623. VAR f: File; count: LONGINT; p, c: FileSystem;
  1624. BEGIN
  1625. Log.vS("OFS: Removing "); LogFS(fs); Log.vL;
  1626. f := froot.next; count := 0;
  1627. WHILE f # NIL DO
  1628. IF f.fs = fs THEN INC(count); Close(f); f.fs := NIL END;
  1629. f := f.next
  1630. END;
  1631. IF count # 0 THEN
  1632. Log.S("OFS: "); Log.I(count);
  1633. Log.S(" open files");
  1634. IF fs.vol # NIL THEN
  1635. Log.S(" on "); Log.S(fs.vol.name)
  1636. END;
  1637. Log.L
  1638. END;
  1639. Finalize(fs); DeleteFS(fs);
  1640. p := NIL; c := fsmount;
  1641. WHILE c # fs DO p := c; c := c.link END;
  1642. IF p = NIL THEN fsmount := c.link ELSE p.link := c.link END;
  1643. c.link := NIL
  1644. END Remove;
  1645. (** Return next file system. *)
  1646. PROCEDURE Next*(fs: FileSystem): FileSystem;
  1647. BEGIN
  1648. RETURN fs.next
  1649. END Next;
  1650. (* Find file in open file list, or open and add it. *)
  1651. PROCEDURE Open(fs: FileSystem; CONST fname: ARRAY OF CHAR): File;
  1652. VAR f: File; key: LONGINT;
  1653. BEGIN
  1654. f := NIL;
  1655. IF (fs # NIL) & (fname # "") THEN
  1656. key := FileKey(fs, fname);
  1657. IF key # 0 THEN
  1658. f := froot.next;
  1659. WHILE (f # NIL) & ((f.fs # fs) OR (f.key # key)) DO f := f.next END
  1660. END;
  1661. IF f = NIL THEN
  1662. f := FSOld(fs, fname);
  1663. IF f # NIL THEN
  1664. ASSERT(f.key # 0); (* key must be set *)
  1665. f.next := froot.next; froot.next := f;
  1666. (* Kernel.RegisterObject(f, Collect, FALSE); *)
  1667. (* Kernel.RegisterObject(f, FileCleanup, FALSE) *)
  1668. END
  1669. END
  1670. END;
  1671. RETURN f
  1672. END Open;
  1673. (** Open an existing file, searching through the mounted file system list if no prefix is specified. *)
  1674. PROCEDURE Old*(CONST name: ARRAY OF CHAR): File;
  1675. VAR fs: FileSystem; f: File; prefix: Prefix; fname: LocalName;
  1676. BEGIN
  1677. f := NIL;
  1678. SplitName(name, prefix, fname);
  1679. IF prefix = "" THEN
  1680. fs := fsroot;
  1681. WHILE (fs # NIL) & (f = NIL) DO
  1682. f := Open(fs, fname); fs := Next(fs);
  1683. END
  1684. ELSE
  1685. f := Open(This(prefix), fname)
  1686. END;
  1687. RETURN f
  1688. END Old;
  1689. (** Create a new file. If no prefix is specified, create the file on the first file system in the mounted list.*)
  1690. PROCEDURE New*(CONST name: ARRAY OF CHAR): File;
  1691. VAR fs: FileSystem; f: File; prefix: Prefix; fname: LocalName;
  1692. BEGIN
  1693. f := NIL; SplitName(name, prefix, fname);
  1694. IF prefix = "" THEN
  1695. fs := fsroot; (* use default file system *)
  1696. IF fname = "" THEN (* anonymous file on unspecified file system *)
  1697. WHILE (fs # NIL) & ((fs.vol = NIL) OR (ReadOnly IN fs.vol.flags)) DO
  1698. fs := Next(fs) (* find a writable file system *)
  1699. END;
  1700. IF fs = NIL THEN fs := fsroot END (* none found, relapse to default *)
  1701. END
  1702. ELSE
  1703. fs := This(prefix)
  1704. END;
  1705. IF fs # NIL THEN
  1706. IF (fs.vol = NIL) OR ~(ReadOnly IN fs.vol.flags) THEN
  1707. f := FSNew(fs, fname);
  1708. ELSE
  1709. Log.vS("Could not create file "); Log.vL;
  1710. END;
  1711. ELSE
  1712. Log.vS("In OFS.New, no filesystem found"); Log.vL;
  1713. END;
  1714. RETURN f
  1715. END New;
  1716. (** Delete a file. res = 0 indicates success. *)
  1717. PROCEDURE Delete*(VAR name: ARRAY OF CHAR; VAR res: LONGINT);
  1718. VAR fs: FileSystem; p, c, temp: File; key: LONGINT; prefix: Prefix; fname: LocalName;
  1719. BEGIN
  1720. SplitName(name, prefix, fname);
  1721. IF prefix = "" THEN fs := fsroot ELSE fs := This(prefix) END;
  1722. IF fs # NIL THEN
  1723. IF (fs.vol = NIL) OR ~(ReadOnly IN fs.vol.flags) THEN
  1724. FSDelete(fs, fname, key, res);
  1725. IF key # 0 THEN
  1726. p := froot; c := froot.next;
  1727. WHILE c # NIL DO
  1728. IF (c.fs = fs) & (c.key = key) THEN
  1729. p.next := c.next;
  1730. temp := c;
  1731. c := c.next;
  1732. PutIntoFilePool(temp)
  1733. ELSE
  1734. p := c;
  1735. c := c.next;
  1736. END;
  1737. END
  1738. END
  1739. ELSE
  1740. res := 3 (* can not modify read-only volume *)
  1741. END
  1742. ELSE
  1743. res := 2 (* file system not found *)
  1744. END
  1745. END Delete;
  1746. (** Rename a file. res = 0 indicates success. *)
  1747. PROCEDURE Rename*(CONST old, new: ARRAY OF CHAR; VAR res: LONGINT);
  1748. VAR ofs, nfs: FileSystem; pold, pnew: Prefix; fold, fnew: LocalName;
  1749. BEGIN
  1750. SplitName(old, pold, fold);
  1751. SplitName(new, pnew, fnew);
  1752. IF pold = "" THEN ofs := fsroot; ELSE ofs := This(pold) END;
  1753. IF pnew = "" THEN nfs := fsroot; ELSE nfs := This(pnew) END;
  1754. IF (nfs # NIL) & (ofs = nfs) THEN
  1755. IF (nfs.vol = NIL) OR ~(ReadOnly IN nfs.vol.flags) THEN
  1756. FSRename(nfs, fold, fnew, res)
  1757. ELSE
  1758. res := 3 (* can not modify read-only volume *)
  1759. END
  1760. ELSE
  1761. res := 2
  1762. END
  1763. END Rename;
  1764. (** 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. *)
  1765. PROCEDURE Register*(f: File);
  1766. VAR res: LONGINT;
  1767. c,p: File;
  1768. BEGIN
  1769. IF f # NIL THEN
  1770. FSRegister(f, res);
  1771. IF res = 0 THEN (* new file was registered *)
  1772. ASSERT(f.key # 0);
  1773. (* f.next := froot.next; froot.next := f; *) (* Do not put that file into the open list!! *)
  1774. FileCleanup(f); (* Kernel.RegisterObject(f, FileCleanup, FALSE) *)
  1775. ELSE
  1776. IF res = 1 THEN (* file was registered already *)
  1777. (* Remove file from open list *)
  1778. FileCleanup(f);
  1779. (* p := froot; c := froot.next;
  1780. WHILE c # NIL DO
  1781. IF c = f THEN p.next := c.next; ELSE p := c; END;
  1782. c := c.next;
  1783. END
  1784. *) ELSE (* error occured while registering *)
  1785. HALT(17)
  1786. END;
  1787. END;
  1788. (* Put File in filePool *)
  1789. PutIntoFilePool(f);
  1790. END;
  1791. END Register;
  1792. PROCEDURE Available*(CONST name: ARRAY OF CHAR ): LONGINT;
  1793. VAR
  1794. fs: FileSystem;
  1795. avail: LONGINT;
  1796. temp: PROCEDURE (vol: Volume): LONGINT;
  1797. BEGIN
  1798. IF name = "" THEN fs := fsroot ELSE fs := This(name) END;
  1799. IF fs.vol # NIL THEN
  1800. temp := fs.vol.Available;
  1801. avail := temp(fs.vol)*fs.vol.blockSize;
  1802. ELSE
  1803. avail := 0;
  1804. END;
  1805. RETURN avail
  1806. END Available;
  1807. (** 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. *)
  1808. PROCEDURE Enumerate*(CONST mask: ARRAY OF CHAR; VAR flags: SET; proc: EntryHandler);
  1809. VAR fs: FileSystem; prefix: Prefix; fmask: LocalName;
  1810. BEGIN
  1811. SplitName(mask, prefix, fmask);
  1812. IF prefix = "" THEN
  1813. fs := fsroot;
  1814. WHILE fs # NIL DO
  1815. flags := flags - {EnumStop};
  1816. enumerate(fs, fmask, DirRootAdr, flags, proc);
  1817. fs := Next(fs)
  1818. END
  1819. ELSE
  1820. fs := This(prefix);
  1821. IF fs # NIL THEN enumerate(fs, fmask, DirRootAdr, flags, proc) END
  1822. END
  1823. END Enumerate;
  1824. (** Checks if a file system has open files. *)
  1825. PROCEDURE HasOpenFiles*(fs: FileSystem): BOOLEAN;
  1826. VAR f: File;
  1827. BEGIN
  1828. f := froot;
  1829. REPEAT f := f.next UNTIL (f = NIL) OR (f.fs = fs);
  1830. RETURN f # NIL
  1831. END HasOpenFiles;
  1832. (* Clean up file systems when shutting down or unloading module. File systems are cleaned up in reverse order of installation. *)
  1833. PROCEDURE FSCleanup;
  1834. BEGIN
  1835. WHILE fsmount # NIL DO
  1836. Remove(fsmount)
  1837. END
  1838. END FSCleanup;
  1839. PROCEDURE InitFilePool;
  1840. VAR
  1841. i, j: LONGINT;
  1842. f: File;
  1843. buf: Buffer;
  1844. BEGIN
  1845. filePool := NIL;
  1846. FOR i := 0 TO MaxFiles - 1 DO
  1847. NEW(f);
  1848. f.key := 0; (* Invalidate file *)
  1849. f.next := filePool; filePool := f; f.firstbuf := NIL;
  1850. FOR j := 0 TO MaxBuffers -1 DO
  1851. NEW(buf);
  1852. buf.next := f.firstbuf; f.firstbuf := buf;
  1853. buf.apos := InvalidAddress
  1854. END;
  1855. buf := f.firstbuf;
  1856. WHILE buf.next # NIL DO
  1857. buf := buf.next;
  1858. END;
  1859. buf.next := f.firstbuf;
  1860. END;
  1861. END InitFilePool;
  1862. BEGIN
  1863. Trace.StringLn("Entering Init() OFS.");
  1864. fsroot := NIL; fsmount := NIL; filePool := NIL;
  1865. NEW(froot); froot.next := NIL; froot.key := 0; froot.fs := NIL;
  1866. InitFilePool;
  1867. ASSERT((SIZEOF(FileHeader) = SS) & (SIZEOF(IndexSector) = SS) & (SIZEOF(DataSector) = SS) &
  1868. (SIZEOF(DirPage) = SS) & (SIZEOF(MapIndex) = SS) & (SIZEOF(MapSector) = SS));
  1869. ASSERT((MapSize MOD 32) = 0);
  1870. Trace.StringLn("Completed, exiting.");
  1871. END OFS.
  1872. (**
  1873. On-the-fly GC by bsm
  1874. In order to be non-leaking, a file system must provide the following:
  1875. - FileSystem.Purge -- to reclaim blocks of an open (being closed) file
  1876. - FileSystem.Registered -- reports if a particular open file is registered in the file directory
  1877. The following procedures need to be modified to purge file blocks when appropriate.
  1878. - FileSystem.Register -- if an entry to a file, F, which is not open is replaced, purge F.
  1879. - FileSystem.Rename -- same as register.
  1880. - FileSystem.Delete -- if the entry being deleted refers to a file, F, which is not open, purge F.
  1881. The procedure FindOpenFile may be used to see if a given file is open or not.
  1882. *)
  1883. (*
  1884. aleng * SS + bleng = length (including header)
  1885. apos * SS + bpos = current position
  1886. 0 <= bpos <= lim <= SS
  1887. 0 <= apos <= aleng < STS
  1888. (apos < aleng) & (lim = SS) OR (apos = aleng)
  1889. *)