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]; }. FSFileOpsImpl.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Schroeder, November 30, 1983 9:07 am Levin, September 22, 1983 1:21 pm Bob Hagmann, May 9, 1985 4:45:18 pm PDT Russ Atkinson (RRA) May 13, 1985 8:34:04 pm PDT Exported to FSBackdoor Exported to FSFileOps Internal procedures can't make new ones now, so don't even look want system volume and already have descriptor found VolumeDesc in list new tree file, but can't have validation stamp in property page no SIGNALs or ERRORs turn off volume flusher if any; (RRA) must do this before suspending the BTree assumes that tree already is suspended special steps for the system volume; (RRA) must close this before closing the BTree a little earlier so used times always will be updated after scavenge put file in directory/cache Start Code Bob Hagmann January 29, 1985 3:39:51 pm PST Cedar 6.0 interface changes Bob Hagmann April 30, 1985 7:39:58 am PDT changes to: ScavengeDirectoryAndCache, SetUpBTree, Scavenge, InnerGetVolumeDesc, ActivateVolume ΚV– "Cedar" style˜codešœ™Kšœ Οmœ7™BK™$K™!K™'K™/—K˜šΟk ˜ Kšœ žœžœ˜#Kšœžœ;˜HKšœžœb˜mKšœžœ9˜FKšœžœ?žœu˜ΐKšœ žœM˜_Kšžœžœ#˜+Kšœ žœZ˜jKšœžœP˜[Kšœ žœs˜‚Kšœžœ ˜Kšœ žœ˜*Kšœžœžœ˜3K˜—šœžœž˜Kšžœ9žœ7˜yKšžœ˜Kšœ˜K˜Kšžœžœžœ˜K˜—™K˜šΟnœžœžœ žœ˜:šŸœžœžœ˜Kšžœžœžœ˜Kšœ˜Kšœ˜—Kšœ žœ ˜Kšœ˜Kšœ,Οc$˜Pšœ%žœ *˜TKšœžœžœ˜.—šžœžœ˜Kšœžœžœ˜BKš œ žœ žœžœžœ˜UKšœ˜—Kšœ(˜(Kšžœžœžœžœ˜7Kšœ˜—K˜šŸ œžœžœ žœ˜.Kšœ,žœ˜1Kšžœ žœžœžœ˜0KšœZ˜ZKšœ˜—K˜šŸ œžœžœ žœžœžœžœ˜WKšœ5˜5Kšžœ žœžœžœ˜0šœ@˜@Kšœ*˜*—Kšœ˜—K˜š Ÿœžœžœžœžœ˜CKšœžœ˜Kšœ˜šœžœ'˜/Kšœ-˜-Kš œ žœžœžœžœ2˜hKšœ˜—Kšœ<˜˜>K˜šŸ œžœžœ˜,šžœ˜K˜>KšœS˜SKšœžœžœ ˜PK˜K˜—Kšœ˜—K˜šŸ œžœ)žœžœ žœžœžœžœ˜†Kšœ žœžœ˜Kšœ žœ ˜Kšœ˜šžœ žœ0˜@šœ˜šžœ˜Kšžœp˜tKšžœ˜——Kšœ˜—šžœ˜šžœ˜Kšœ?™?šœ0ž˜3Kšœ*˜*—Kšœ žœ˜Kšœ˜—Kšžœ(˜,—šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—šœ˜šžœžœ˜Kšœ˜Kšžœ žœžœ žœ˜Kšœ™šžœž˜KšœO™OKšœ žœ˜%—Kšœ' ˜:šžœžœžœ˜Kšœ"˜"Kšœžœ˜Kšœ˜—Kšœ˜—K˜šŸœžœžœ"˜?Kšœžœ%˜?Kšžœžœ)˜?Kšœ˜—K˜šŸœžœžœ#˜CKšœ&™&Kšœžœ˜!šžœ:žœ žœž˜QKšžœžœžœ˜Kšœ ˜ Kšžœžœžœ˜Kšžœ˜—šžœžœ˜KšœT™TKšœ žœ˜%Kšœ žœ˜ Kšœ˜—Kšœ$˜$šžœž˜ Kšžœ˜!Kšžœ˜—Kšœ˜—K˜šœ,˜,Kšœ*˜*Kšœ˜—K˜šœžœ˜2Kšžœžœžœ˜Kšœžœ˜šžœ:žœ žœž˜QKšœ˜Kšžœ˜—Kšœ˜—K˜šΟbœžœ˜.Kšžœžœžœ˜šžœ:žœ žœž˜QKšœžœ žœ˜.Kšžœ˜—Kšœžœ˜Kšž œ ˜Kšœ˜—K˜šœžœžœ˜Kšœžœ˜Kšœ˜K˜Kšœžœ˜Kšœžœ˜K˜K˜—K˜šŸœžœžœ˜-K˜š Ÿœžœžœžœ žœž œ˜XKšœ žœ˜0Kšœ žœ˜šžœžœž˜šœžœ˜+Kšœ”˜”—Kšžœ˜—Kšœ˜—šœžœ-˜HKšœD™D—Kšœ žœžœ˜!Kšœžœ˜?Kšœ žœ˜Kšœžœ˜Kšœžœ˜K˜Kšœ˜Kšœžœ˜%Kšœ7˜7Kšœ#˜#Kšœ$ ˜7Kšœ ˜0Kšœž œ˜+Kšœ$žœžœ <˜mKšœ&žœ˜,šœ˜šžœžœ˜Kšœ&žœ˜-Kšœ$˜$—šžœ!žœ žœž˜:Kšžœžœ"žœžœ˜2Kšœˆ˜ˆKšžœ˜—šžœ;ž˜BK˜K˜šœ'˜'Kšœžœ žœ˜—˜7Kš œžœ žœ#žœžœ˜?—šžœ˜Kšžœ *˜=šžœ˜Kšœ™Kšœžœžœ˜šžœ˜šžœ˜Kšœ@˜@Kš œžœžœ žœžœ˜KKšœ˜—šžœ˜Kšœ2˜2Kš œžœžœ žœžœ˜KKšœ˜——Kšžœžœ˜Kšœ˜——Kšžœ˜—Kšœ˜—Kšœ&žœ˜-KšœR˜RKšœ'žœ˜1Kšœ"˜"Kšœ˜K˜——™ K˜Kšœ>˜>K˜—Kšœ˜K˜K˜™+K™K™—™)Kšœ ΟrS™_——…—2ΔG*