<> <> <> <> <> <> DIRECTORY BasicTime USING [GMT, Now, Update], Booting USING [Deregister, CheckpointProc, RegisterProcs, RollbackProc], BTree USING [Entry, Error, New, Open, SalvageEntries, SetState, SetUpdateInProgress, Tree, UpdateInProgress], BTreeVM USING [FreeBuffers, Handle, Open, ReferencePage, ReleasePage], File USING [Create, Delete, Error, FindVolumeFromID, FindVolumeFromName, FP, GetVolumeName, Handle, Info, nullFP, Open, PageCount, PageNumber, Reason, SetSize, SystemVolume, Volume, VolumeID], FileBackdoor USING [CreateVMBacking, GetRoot, GetVolumePages, NextFile, SetFreeboard, SetRoot], FS USING [Error, ErrorDesc, WordsForPages], FSBackdoor USING [Entry, EntryPtr, EntryType, MakeFName, TextFromTextRep, TextRep, ProduceError, Version], FSDir USING [Compare, EntrySize, UpdateAttachedEntry, UpdateCachedEntry, UpdateLocalEntry], FSFileOps USING [GetNameBodyAndVersion, GetProps, InitializePropertyStorage, RegisterVolumeFlusher, VolumeDesc, VolumeDescObject], FSName USING [IsLocal], FSReport USING [FileError, UnknownVolume], Rope USING [Cat, Equal, Fetch, Length, ROPE, Text]; FSFileOpsImpl: CEDAR MONITOR IMPORTS BasicTime, Booting, BTree, BTreeVM, File, FileBackdoor, FS, FSBackdoor, FSDir, FSFileOps, FSName, FSReport, Rope EXPORTS FSBackdoor, FSFileOps = { ROPE: TYPE = Rope.ROPE; <> ScavengeDirectoryAndCache: PUBLIC PROC [volName: ROPE] = { Activate: ENTRY PROC = { ENABLE UNWIND => NULL; ActivateVolume[vDesc]; }; errorDesc: FS.ErrorDesc; vDesc: FSFileOps.VolumeDesc; Booting.RegisterProcs[c: RejectCheckpoint]; -- mutual exclusion with checkpoints vDesc _ InnerGetVolumeDesc[ volName, TRUE -- suspension occurred only if no FS.Error ! FS.Error => {errorDesc _ error; CONTINUE} ]; IF errorDesc.group = ok THEN { Scavenge[ vDesc.vol ! FS.Error => {errorDesc _ error; CONTINUE} ]; Activate[ ! FS.Error => {IF errorDesc.group = ok THEN errorDesc _ error; CONTINUE} ]; }; Booting.Deregister[c: RejectCheckpoint]; IF errorDesc.group # ok THEN ERROR FS.Error[errorDesc]; }; SetFreeboard: PUBLIC PROC [freeboard: INT] = { vDesc: FSFileOps.VolumeDesc = GetVolumeDesc[NIL]; IF vDesc = NIL THEN FSReport.UnknownVolume[NIL]; FileBackdoor.SetFreeboard[ vDesc.vol, freeboard ! File.Error => FSReport.FileError[why] ]; }; VolumePages: PUBLIC PROC [volName: ROPE _ NIL] RETURNS [size, free, freeboard: INT] = { vDesc: FSFileOps.VolumeDesc = GetVolumeDesc[volName]; IF vDesc = NIL THEN FSReport.UnknownVolume[NIL]; [size, free, freeboard] _ FileBackdoor.GetVolumePages[ vDesc.vol ! File.Error => FSReport.FileError[why] ]; }; FNameFromHandle: PUBLIC PROC [file: File.Handle] RETURNS [ROPE] = { nameBody, prefix: ROPE; version: FSBackdoor.Version; { ENABLE File.Error => FSReport.FileError[why]; volume: File.Volume = File.Info[file].volume; prefix _ IF volume = File.SystemVolume[] THEN NIL ELSE Rope.Cat["[]<", File.GetVolumeName[volume], ">"]; }; [nameBody, version] _ FSFileOps.GetNameBodyAndVersion[file]; RETURN [ FSBackdoor.MakeFName[nameBody, version, prefix] ]; }; CloseVolume: PUBLIC ENTRY PROC [v: File.Volume] = { ENABLE UNWIND => NULL; prev, vDesc: FSFileOps.VolumeDesc _ NIL; FOR vDesc _ volumeDescList, vDesc.next UNTIL vDesc = NIL DO IF vDesc.vol = v THEN { IF prev = NIL THEN volumeDescList _ vDesc.next ELSE prev.next _ vDesc.next; IF vDesc = svDesc THEN svDesc _ NIL; BTree.SetState[vDesc.tree, closed]; IF vDesc.treeVM # NIL THEN { BTreeVM.FreeBuffers[vDesc.treeVM]; vDesc.treeVM _ NIL; }; EXIT; }; prev _ vDesc; ENDLOOP; }; <> GetVolumeDesc: PUBLIC PROC [vName: ROPE] RETURNS [vDesc: FSFileOps.VolumeDesc] = { errorDesc: FS.ErrorDesc; vDesc _ InnerGetVolumeDesc[vName, FALSE ! FS.Error => {errorDesc _ error; CONTINUE} ]; IF errorDesc.group # ok THEN ERROR FS.Error[errorDesc]; }; LPCreatedTime: PUBLIC PROC [vol: File.Volume, fp: File.FP] RETURNS [BasicTime.GMT] = { handle: File.Handle; handle _ File.Open[ vol, fp ! File.Error => FSReport.FileError[why] ]; RETURN [ FSFileOps.GetProps[handle].created ]; }; OpenFile: PUBLIC PROC [vol: File.Volume, fp: File.FP] RETURNS [h: File.Handle] = { RETURN [ File.Open[ vol, fp ! File.Error => FSReport.FileError[why] ] ]; }; CreateFile: PUBLIC PROC [vol: File.Volume, pages: INT, VMBackingFile: BOOL _ FALSE] RETURNS [fp: File.FP, h: File.Handle] = { ENABLE File.Error => FSReport.FileError[why]; IF VMBackingFile THEN h _ FileBackdoor.CreateVMBacking[vol, pages, FSFileOps.InitializePropertyStorage] ELSE h _ File.Create[vol, pages, FSFileOps.InitializePropertyStorage]; fp _ File.Info[h].fp; }; DeleteFile: PUBLIC PROC [h: File.Handle] = { File.Delete[ h ! File.Error => FSReport.FileError[why] ]; }; SetFilePages: PUBLIC PROC [h: File.Handle, pages: INT] = { File.SetSize[h, pages ! File.Error => FSReport.FileError[why] ]; }; GetFileInfo: PUBLIC PROC [h: File.Handle] RETURNS [pages: INT, fp: File.FP] = { [fp: fp, size: pages] _ File.Info[h ! File.Error => FSReport.FileError[why] ]; }; <> waitForRollback: BOOL _ FALSE; rollback: CONDITION; svDesc, volumeDescList: FSFileOps.VolumeDesc _ NIL; InnerGetVolumeDesc: ENTRY PROC [vName: ROPE, suspendVolume: BOOL] RETURNS [FSFileOps.VolumeDesc] = { ENABLE UNWIND => NULL; vDesc: FSFileOps.VolumeDesc; vol: File.Volume _ NIL; WHILE waitForRollback DO <> WAIT rollback; ENDLOOP; IF Rope.Length[vName] = 0 AND svDesc # NIL THEN { <> IF suspendVolume THEN SuspendVolume[svDesc]; RETURN [svDesc]; }; SELECT TRUE FROM Rope.Length[vName] = 0 => { -- system volume vol _ File.SystemVolume[]; IF vol = NIL THEN { -- no system volume is available IF suspendVolume THEN FSReport.UnknownVolume[NIL] ELSE RETURN [NIL]; }; vName _ File.GetVolumeName[vol]; }; (Rope.Fetch[vName, 0] = '#) => { -- volume number vol _ File.FindVolumeFromID[ MakeVolumeID[vName] ]; IF vol = NIL THEN FSReport.UnknownVolume[vName]; vName _ File.GetVolumeName[vol]; }; ENDCASE; FOR vDesc _ volumeDescList, vDesc.next UNTIL vDesc = NIL DO IF Rope.Equal[vName, vDesc.vName, FALSE] THEN { <> IF suspendVolume THEN SuspendVolume[vDesc]; RETURN [vDesc]; }; ENDLOOP; IF vol = NIL THEN { vol _ File.FindVolumeFromName[vName]; IF vol = NIL THEN FSReport.UnknownVolume[vName]; }; vDesc _ NEW[ FSFileOps.VolumeDescObject _ [ NIL, vName, NIL, vol, NIL, NIL ] ]; vDesc.tree _ AllocateBTree[]; IF NOT suspendVolume THEN [] _ SetUpBTree[vDesc]; IF vol = File.SystemVolume[] THEN { FSFileOps.RegisterVolumeFlusher[vDesc]; svDesc _ vDesc; } ELSE vDesc.prefix _ Rope.Cat["[]<", vName, ">"]; vDesc.next _ volumeDescList; volumeDescList _ vDesc; RETURN [vDesc]; }; bufferSize: CARDINAL = 4; buffers: CARDINAL = 32; basePage: File.PageNumber = [0]; -- first page of the directory/cache btree initialPages: File.PageCount = basePage + buffers*bufferSize; AllocateBTree: PROC RETURNS [BTree.Tree] = { RETURN [ BTree.New [ repPrim: [compare: FSDir.Compare, entrySize: FSDir.EntrySize], storPrim: [referencePage: BTreeVM.ReferencePage, releasePage: BTreeVM.ReleasePage], minEntrySize: SIZE[FSBackdoor.Entry.local] + SIZE[FSBackdoor.TextRep[1]], -- "a" initialState: suspended ] ]; }; SetUpBTree: PROC [vDesc: FSFileOps.VolumeDesc, scavenge: BOOL _ FALSE, newTemp: BOOL _ FALSE] RETURNS [treeFile: File.Handle _ NIL]= { newFile: BOOL _ FALSE; treeFP: File.FP _ File.nullFP; treeVM: BTreeVM.Handle; IF ~newTemp THEN treeFP _ FileBackdoor.GetRoot[vDesc.vol, client ! File.Error => IF why = nonCedarVolume THEN FSBackdoor.ProduceError[nonCedarVolume, Rope.Cat["Local volume \"", vDesc.vName, "\" not formatted for Cedar"]] ELSE FSReport.FileError[why] ].fp; IF treeFP = File.nullFP THEN { <> treeFile _ File.Create[vDesc.vol, initialPages, NIL ! File.Error => FSReport.FileError[why] ]; newFile _ TRUE; } ELSE treeFile _ OpenFile[vDesc.vol, treeFP]; treeVM _ BTreeVM.Open[ file: treeFile, filePagesPerPage: bufferSize, cacheSize: buffers, base: basePage ]; { ENABLE UNWIND => { BTreeVM.FreeBuffers[treeVM]; IF newFile THEN DeleteFile[treeFile ! FS.Error => CONTINUE]; }; OpenBTree[vDesc.tree, treeVM, newFile, scavenge]; IF newFile AND ~newTemp THEN FileBackdoor.SetRoot[client, treeFile ! File.Error => FSReport.FileError[why] ]; }; vDesc.treeVM _ treeVM; }; OpenBTree: PROC [tree: BTree.Tree, treeVM: BTreeVM.Handle, new, scavenge: BOOL] = { BTree.Open[ tree: tree, storage: treeVM, pageSize: FS.WordsForPages[bufferSize], initialize: new, maintainRecomputableState: TRUE ! BTree.UpdateInProgress => IF scavenge THEN RESUME ELSE FSBackdoor.ProduceError[badBTree, "Update in progress when opening the directory/cache BTree"]; BTree.Error => FSBackdoor.ProduceError[badBTree, "BTree.Error when opening the directory/cache BTree"]; File.Error => FSReport.FileError[why]; ]; }; MakeVolumeID: PROC [vName: ROPE] RETURNS [id: File.VolumeID] = { FSBackdoor.ProduceError[notImplemented, "Can't handle hex volume ID's yet"]; }; SuspendVolume: INTERNAL PROC [vDesc: FSFileOps.VolumeDesc] = { <> IF vDesc = svDesc THEN <> FSFileOps.RegisterVolumeFlusher[NIL]; BTree.SetState[vDesc.tree, suspended]; -- may wait forever IF vDesc.treeVM # NIL THEN { BTreeVM.FreeBuffers[vDesc.treeVM]; vDesc.treeVM _ NIL; }; }; ActivateVolume: INTERNAL PROC [vDesc: FSFileOps.VolumeDesc] = { [] _ SetUpBTree[vDesc ! FS.Error => DestroyVolumeDesc[vDesc] ]; IF vDesc = svDesc THEN FSFileOps.RegisterVolumeFlusher[svDesc]; }; DestroyVolumeDesc: INTERNAL PROC [victim: FSFileOps.VolumeDesc] = { <> prev: FSFileOps.VolumeDesc _ NIL; FOR vDesc: FSFileOps.VolumeDesc _ volumeDescList, vDesc.next UNTIL vDesc = NIL DO IF victim = vDesc THEN EXIT; prev _ vDesc; REPEAT FINISHED => ERROR; ENDLOOP; IF victim = svDesc THEN { <> FSFileOps.RegisterVolumeFlusher[NIL]; svDesc _ NIL; }; BTree.SetState[victim.tree, closed]; IF prev = NIL THEN volumeDescList _ victim.next ELSE prev.next _ victim.next; }; RejectCheckpoint: Booting.CheckpointProc = { rejection _ "FS scavenge is in progress."; }; FSCheckpointProc: ENTRY Booting.CheckpointProc = { ENABLE UNWIND => NULL; waitForRollback _ TRUE; FOR vDesc: FSFileOps.VolumeDesc _ volumeDescList, vDesc.next UNTIL vDesc = NIL DO SuspendVolume[vDesc]; ENDLOOP; }; FSRollbackProc: ENTRY Booting.RollbackProc = { ENABLE UNWIND => NULL; FOR vDesc: FSFileOps.VolumeDesc _ volumeDescList, vDesc.next UNTIL vDesc = NIL DO ActivateVolume[vDesc ! FS.Error => CONTINUE ]; ENDLOOP; waitForRollback _ FALSE; BROADCAST rollback; }; AttachedEntry: TYPE = RECORD [ next: REF AttachedEntry, nameBody: Rope.Text, version: FSBackdoor.Version, keep: CARDINAL, created: BasicTime.GMT, attachedTo: Rope.Text ]; Scavenge: PROC [vol: File.Volume] = TRUSTED { SaveAttachments: UNSAFE PROC [entry: BTree.Entry] RETURNS [continue: BOOL] = UNCHECKED { entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; continue _ TRUE; WITH e: entryPtr^ SELECT FROM attached => attHead _ NEW [ AttachedEntry _ [ attHead, FSBackdoor.TextFromTextRep[@entryPtr[e.nameBody]], e.version, e.keep, e.created, FSBackdoor.TextFromTextRep[@entryPtr[e.attachedTo]] ] ]; ENDCASE; }; fakeUsedTime: BasicTime.GMT = BasicTime.Update[BasicTime.Now[], -60*20]; <> attHead: REF AttachedEntry _ NIL; vDesc: FSFileOps.VolumeDesc = NEW [FSFileOps.VolumeDescObject]; fp: File.FP _ File.nullFP; newFSBTree: File.Handle _ NIL; oldFSBTree: File.Handle _ NIL; vDesc.vol _ vol; vDesc.tree _ AllocateBTree[]; oldFSBTree _ SetUpBTree[vDesc, TRUE]; [] _ BTree.SalvageEntries[vDesc.tree, SaveAttachments]; BTree.SetState[vDesc.tree, closed]; BTreeVM.FreeBuffers[vDesc.treeVM]; -- dump old buffers vDesc.tree _ AllocateBTree[]; -- get a new BTree newFSBTree _ SetUpBTree[vDesc, TRUE, TRUE]; OpenBTree[vDesc.tree, vDesc.treeVM, TRUE, TRUE]; -- initialize the BTree  this file is not the root file yet BTree.SetUpdateInProgress[vDesc.tree, TRUE]; { ENABLE UNWIND => { BTree.SetUpdateInProgress[vDesc.tree, FALSE]; BTreeVM.FreeBuffers[vDesc.treeVM] }; FOR attHead _ attHead, attHead.next UNTIL attHead = NIL DO IF NOT FSName.IsLocal[attHead.nameBody] THEN LOOP; FSDir.UpdateAttachedEntry[vDesc, attHead.nameBody, attHead.version, attHead.keep, attHead.created, attHead.attachedTo, insertOrReplace]; ENDLOOP; UNTIL (fp _ FileBackdoor.NextFile[vDesc.vol, fp]) = File.nullFP DO nameBody: Rope.Text; version: FSBackdoor.Version; h: File.Handle = OpenFile[vDesc.vol, fp ! FS.Error => LOOP ]; [nameBody, version] _ FSFileOps.GetNameBodyAndVersion[h ! FS.Error => IF error.code = $invalidPropertyPage THEN LOOP ]; IF Rope.Length[nameBody] = 0 THEN DeleteFile[h] -- creation of this file was not completed ELSE { <> exists: BOOL _ FALSE; IF FSName.IsLocal[nameBody] THEN FSDir.UpdateLocalEntry[ vDesc, nameBody, version, FSFileOps.GetProps[h].keep, fp, insert ! BTree.Error => IF reason = wrongUpdateType THEN {exists _ TRUE; CONTINUE} ] ELSE FSDir.UpdateCachedEntry[ vDesc, nameBody, version, fakeUsedTime, fp, insert ! BTree.Error => IF reason = wrongUpdateType THEN {exists _ TRUE; CONTINUE} ]; IF exists THEN DeleteFile[h]; }; ENDLOOP; }; BTree.SetUpdateInProgress[vDesc.tree, FALSE]; FileBackdoor.SetRoot[client, newFSBTree ! File.Error => FSReport.FileError[why] ]; File.Delete[oldFSBTree ! File.Error => CONTINUE]; BTreeVM.FreeBuffers[vDesc.treeVM]; }; <> Booting.RegisterProcs[c: FSCheckpointProc, r: FSRollbackProc]; }. <> <> <<>> <> <>