FSFileOpsImpl.mesa
Last Edited by: Schroeder, November 15, 1983 1:27 pm
Last Edited by: Levin, September 22, 1983 1:21 pm
DIRECTORY
BasicTime USING [GMT, Now],
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, GetRoot, GetVolumeName, GetVolumePages, Handle, Info, NextFile, nullFP, Open, PageCount, PageNumber, Reason, SetFreeboard, SetRoot, SetFlusher, SetSize, SystemVolume, Volume, VolumeID],
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, FS, FSBackdoor, FSDir, FSFileOps, FSName, FSReport, Rope
EXPORTS FSBackdoor, FSFileOps
= BEGIN
Exported to FSBackdoor
ScavengeDirectoryAndCache: PUBLIC PROC [volName: Rope.ROPE] =
BEGIN
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 BEGIN
Scavenge[ vDesc.vol
! FS.Error => {errorDesc ← error; CONTINUE} ];
Unsuspend[ vDesc
! FS.Error => {IF errorDesc.group = ok THEN errorDesc ← error; CONTINUE} ];
END;
Booting.Deregister[c: RejectCheckpoint];
IF errorDesc.group # ok THEN ERROR FS.Error[errorDesc];
END;
SetFreeboard: PUBLIC PROC [freeboard: INT] =
BEGIN
vDesc: FSFileOps.VolumeDesc = GetVolumeDesc[NIL];
IF vDesc = NIL THEN FSReport.UnknownVolume[NIL];
File.SetFreeboard[ vDesc.vol, freeboard
! File.Error => FSReport.FileError[why] ];
END;
VolumePages: PUBLIC PROC [volName: Rope.ROPENIL] RETURNS [size, free, freeboard: INT] =
BEGIN
vDesc: FSFileOps.VolumeDesc = GetVolumeDesc[volName];
IF vDesc = NIL THEN FSReport.UnknownVolume[NIL];
[size, free, freeboard] ← File.GetVolumePages[ vDesc.vol
! File.Error => FSReport.FileError[why] ];
END;
FNameFromHandle: PUBLIC PROC [file: File.Handle] RETURNS [Rope.ROPE] =
BEGIN
nameBody, prefix: Rope.ROPE;
version: FSBackdoor.Version;
BEGIN 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], ">"];
END;
[nameBody, version] ← FSFileOps.GetNameBodyAndVersion[file];
RETURN [ FSBackdoor.MakeFName[nameBody, version, prefix] ];
END;
CloseVolume: PUBLIC ENTRY PROC [v: File.Volume] =
BEGIN
prev, vDesc: FSFileOps.VolumeDesc ← NIL;
FOR vDesc ← volumeDescList, vDesc.next UNTIL vDesc = NIL DO
IF vDesc.vol = v
THEN BEGIN
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 BEGIN
BTreeVM.FreeBuffers[vDesc.treeVM];
vDesc.treeVM ← NIL;
END;
EXIT;
END;
prev ← vDesc;
ENDLOOP;
END;
Exported to FSFileOps
GetVolumeDesc: PUBLIC PROC [vName: Rope.ROPE] RETURNS [vDesc: FSFileOps.VolumeDesc] =
BEGIN
errorDesc: FS.ErrorDesc;
vDesc ← InnerGetVolumeDesc[vName, FALSE
! FS.Error => {errorDesc ← error; CONTINUE} ];
IF errorDesc.group # ok THEN ERROR FS.Error[errorDesc];
END;
LPCreatedTime: PUBLIC PROC [vol: File.Volume, fp: File.FP] RETURNS [BasicTime.GMT] =
BEGIN
handle: File.Handle;
handle ← File.Open[ vol, fp
! File.Error => FSReport.FileError[why] ];
RETURN [ FSFileOps.GetProps[handle].created ];
END;
OpenFile: PUBLIC PROC [vol: File.Volume, fp: File.FP] RETURNS [h: File.Handle] =
BEGIN
RETURN [ File.Open[ vol, fp
! File.Error => FSReport.FileError[why] ] ];
END;
CreateFile: PUBLIC PROC [vol: File.Volume, pages: INT] RETURNS [fp: File.FP, h: File.Handle] =
BEGIN ENABLE File.Error => FSReport.FileError[why];
h ← File.Create[vol, pages, FSFileOps.InitializePropertyStorage];
fp ← File.Info[h].fp;
END;
DeleteFile: PUBLIC PROC [h: File.Handle] =
BEGIN
File.Delete[ h
! File.Error => FSReport.FileError[why] ];
END;
SetFilePages: PUBLIC PROC [h: File.Handle, pages: INT] =
BEGIN
File.SetSize[h, pages
! File.Error => FSReport.FileError[why] ];
END;
GetFileInfo: PUBLIC PROC [h: File.Handle] RETURNS [pages: INT, fp: File.FP] =
BEGIN
[fp: fp, size: pages] ← File.Info[h
! File.Error => FSReport.FileError[why] ];
END;
Internal procedures
waitForRollback: BOOLEANFALSE;
rollback: CONDITION;
svDesc, volumeDescList: FSFileOps.VolumeDesc ← NIL;
InnerGetVolumeDesc: ENTRY PROC [vName: Rope.ROPE, suspendTree: BOOLEAN] RETURNS [FSFileOps.VolumeDesc] =
BEGIN ENABLE UNWIND => NULL;
vDesc: FSFileOps.VolumeDesc;
vol: File.Volume ← NIL;
WHILE waitForRollback DO
WAIT rollback ENDLOOP; -- can't make new ones now, so don't even look
IF Rope.Length[vName] = 0 AND svDesc # NIL
THEN BEGIN -- want system volume and already have descriptor
IF suspendTree THEN Suspend[svDesc];
RETURN [svDesc];
END;
SELECT TRUE FROM
Rope.Length[vName] = 0 => BEGIN -- system volume
vol ← File.SystemVolume[];
IF vol = NIL
THEN BEGIN -- no system volume is available
IF suspendTree
THEN FSReport.UnknownVolume[NIL]
ELSE RETURN [NIL];
END;
vName ← File.GetVolumeName[vol];
END;
(Rope.Fetch[vName, 0] = '#) => BEGIN -- volume number
vol ← File.FindVolumeFromID[ MakeVolumeID[vName] ];
IF vol = NIL THEN FSReport.UnknownVolume[vName];
vName ← File.GetVolumeName[vol];
END;
ENDCASE;
FOR vDesc ← volumeDescList, vDesc.next UNTIL vDesc = NIL DO
IF Rope.Equal[vName, vDesc.vName, FALSE]
THEN BEGIN -- found VolumeDesc in list
IF suspendTree THEN Suspend[vDesc];
RETURN [vDesc];
END;
ENDLOOP;
IF vol = NIL
THEN BEGIN
vol ← File.FindVolumeFromName[vName];
IF vol = NIL THEN FSReport.UnknownVolume[vName];
END;
vDesc ← NEW[ FSFileOps.VolumeDescObject ← [ NIL, vName, NIL, vol, NIL, NIL ] ];
vDesc.tree ← AllocateBTree[];
IF NOT suspendTree
THEN SetUpBTree[vDesc];
IF vol = File.SystemVolume[]
THEN BEGIN
FSFileOps.RegisterVolumeFlusher[vDesc];
svDesc ← vDesc;
END
ELSE vDesc.prefix ← Rope.Cat["[]<", vName, ">"];
vDesc.next ← volumeDescList;
volumeDescList ← vDesc;
RETURN [vDesc];
END;
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] =
BEGIN
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
] ];
END;
SetUpBTree: PROC [vDesc: FSFileOps.VolumeDesc, scavenge: BOOLEANFALSE] =
BEGIN
newFile: BOOLEANFALSE;
treeFile: File.Handle;
treeFP: File.FP;
treeVM: BTreeVM.Handle;
treeFP ← File.GetRoot[vDesc.vol, client
! File.Error =>
IF why = nonCedarVolume
THEN FSBackdoor.ProduceError[nonCedarVolume, Rope.Cat["The local volume \"", vDesc.vName, "\" is not formatted for Cedar."]]
ELSE FSReport.FileError[why]
].fp;
IF treeFP = File.nullFP
THEN BEGIN -- new tree file, but can't have validation stamp in property page
treeFile ← File.Create[vDesc.vol, initialPages, NIL
! File.Error => FSReport.FileError[why] ];
newFile ← TRUE;
END
ELSE treeFile ← OpenFile[vDesc.vol, treeFP];
treeVM ← BTreeVM.Open[
file: treeFile,
filePagesPerPage: bufferSize,
cacheSize: buffers,
base: basePage
];
BEGIN
ENABLE UNWIND =>
BEGIN
BTreeVM.FreeBuffers[treeVM];
IF newFile THEN DeleteFile[treeFile ! FS.Error => CONTINUE];
END;
OpenBTree[vDesc.tree, treeVM, newFile, scavenge];
IF newFile THEN File.SetRoot[client, treeFile
! File.Error => FSReport.FileError[why] ];
END;
vDesc.treeVM ← treeVM;
END;
OpenBTree: PROC [tree: BTree.Tree, treeVM: BTreeVM.Handle, new, scavenge: BOOLEAN] =
BEGIN
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 discovered to be in progress when opening the directory/cache BTree."];
BTree.Error =>
FSBackdoor.ProduceError[badBTree, "Error from the BTree package when opening the directory/cache BTree."];
File.Error =>
FSReport.FileError[why];
];
END;
MakeVolumeID: PROC [vName: Rope.ROPE] RETURNS [id: File.VolumeID] =
BEGIN
FSBackdoor.ProduceError[notImplemented, "Can't handle hex volume ID's yet."];
END;
Suspend: INTERNAL PROC [vDesc: FSFileOps.VolumeDesc] =
BEGIN -- no SIGNALs or ERRORs
BTree.SetState[vDesc.tree, suspended]; -- may wait forever
IF vDesc.treeVM # NIL
THEN BEGIN
BTreeVM.FreeBuffers[vDesc.treeVM];
vDesc.treeVM ← NIL;
END;
END;
Unsuspend: ENTRY PROC [vDesc: FSFileOps.VolumeDesc] =
BEGIN ENABLE UNWIND => NULL;
SetUpBTree[vDesc
! FS.Error => DestroyVolumeDesc[vDesc]
];
END;
DestroyVolumeDesc: INTERNAL PROC [victim: FSFileOps.VolumeDesc] =
BEGIN -- assumes that tree already is suspended
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;
BTree.SetState[victim.tree, closed];
IF prev = NIL
THEN volumeDescList ← victim.next
ELSE prev.next ← victim.next;
IF victim = svDesc
THEN BEGIN
File.SetFlusher[svDesc.vol, NIL, NIL];
svDesc ← NIL;
END;
END;
RejectCheckpoint: Booting.CheckpointProc =
{ rejection ← "FS scavenge is in progress." };
FSCheckpointProc: ENTRY Booting.CheckpointProc =
BEGIN
waitForRollback ← TRUE;
FOR vDesc: FSFileOps.VolumeDesc ← volumeDescList, vDesc.next UNTIL vDesc = NIL DO
Suspend[vDesc];
ENDLOOP;
END;
FSRollbackProc: ENTRY Booting.RollbackProc =
BEGIN
FOR vDesc: FSFileOps.VolumeDesc ← volumeDescList, vDesc.next UNTIL vDesc = NIL DO
SetUpBTree[vDesc
! FS.Error => { DestroyVolumeDesc[vDesc]; LOOP }
];
ENDLOOP;
waitForRollback ← FALSE;
BROADCAST rollback;
END;
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
BEGIN
SaveAttachments: UNSAFE PROC [entry: BTree.Entry] RETURNS [continue: BOOLEAN] = UNCHECKED
BEGIN
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;
END;
attHead: REF AttachedEntry ← NIL;
vDesc: FSFileOps.VolumeDesc = NEW [FSFileOps.VolumeDescObject];
fp: File.FP ← File.nullFP;
vDesc.vol ← vol;
vDesc.tree ← AllocateBTree[];
SetUpBTree[vDesc, TRUE];
[] ← BTree.SalvageEntries[vDesc.tree, SaveAttachments];
OpenBTree[vDesc.tree, vDesc.treeVM, TRUE, TRUE]; -- reinitialize the BTree
BTree.SetUpdateInProgress[vDesc.tree, TRUE];
BEGIN
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 ← File.NextFile[vDesc.vol, fp]) = File.nullFP DO
nameBody: Rope.Text;
version: FSBackdoor.Version;
h: File.Handle = OpenFile[vDesc.vol, fp];
[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 BEGIN -- put file in directory/cache
exists: BOOLEANFALSE;
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, BasicTime.Now[], fp, insert
! BTree.Error => IF reason = wrongUpdateType THEN {exists ← TRUE; CONTINUE} ];
IF exists THEN DeleteFile[h];
END;
ENDLOOP;
END;
BTree.SetUpdateInProgress[vDesc.tree, FALSE];
BTreeVM.FreeBuffers[vDesc.treeVM];
END;
Start Code
Booting.RegisterProcs[c: FSCheckpointProc, r: FSRollbackProc];
END.