FSFileOpsImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Schroeder, November 30, 1983 9:07 am
Levin, September 22, 1983 1:21 pm
Bob Hagmann, May 10, 1984 10:14:41 am PDT
Russ Atkinson, November 7, 1984 11:07:28 am PST
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, GetRoot, GetVolumeName, GetVolumePages, Handle, Info, NextFile, nullFP, Open, PageCount, PageNumber, Reason, SetFreeboard, SetRoot, SetSize, SystemVolume, Volume, VolumeID],
FileExtrasForFS USING [CreateVMBacking],
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, FileExtrasForFS, FS, FSBackdoor, FSDir, FSFileOps, FSName, FSReport, Rope
EXPORTS FSBackdoor, FSFileOps
= {
Exported to FSBackdoor
ScavengeDirectoryAndCache: PUBLIC PROC [volName: Rope.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];
File.SetFreeboard[ vDesc.vol, freeboard ! File.Error => FSReport.FileError[why] ];
};
VolumePages: PUBLIC PROC
[volName: Rope.ROPENIL] RETURNS [size, free, freeboard: INT] = {
vDesc: FSFileOps.VolumeDesc = GetVolumeDesc[volName];
IF vDesc = NIL THEN FSReport.UnknownVolume[NIL];
[size, free, freeboard] ← File.GetVolumePages[ vDesc.vol
! File.Error => FSReport.FileError[why] ];
};
FNameFromHandle: PUBLIC PROC [file: File.Handle] RETURNS [Rope.ROPE] = {
nameBody, prefix: Rope.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;
};
Exported to FSFileOps
GetVolumeDesc: PUBLIC PROC
[vName: Rope.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: BOOLFALSE]
RETURNS [fp: File.FP, h: File.Handle] = {
ENABLE File.Error => FSReport.FileError[why];
IF VMBackingFile
THEN h ← FileExtrasForFS.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] ];
};
Internal procedures
waitForRollback: BOOLFALSE;
rollback: CONDITION;
svDesc, volumeDescList: FSFileOps.VolumeDesc ← NIL;
InnerGetVolumeDesc: ENTRY PROC [vName: Rope.ROPE, suspendVolume: BOOL] RETURNS [FSFileOps.VolumeDesc] = {
ENABLE UNWIND => NULL;
vDesc: FSFileOps.VolumeDesc;
vol: File.Volume ← NIL;
WHILE waitForRollback DO
can't make new ones now, so don't even look
WAIT rollback;
ENDLOOP;
IF Rope.Length[vName] = 0 AND svDesc # NIL THEN {
want system volume and already have descriptor
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 {
found VolumeDesc in list
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: BOOLFALSE] = {
newFile: BOOLFALSE;
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 {
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;
}
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 THEN File.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 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];
];
};
MakeVolumeID: PROC [vName: Rope.ROPE] RETURNS [id: File.VolumeID] = {
FSBackdoor.ProduceError[notImplemented, "Can't handle hex volume ID's yet."];
};
SuspendVolume: INTERNAL PROC [vDesc: FSFileOps.VolumeDesc] = {
no SIGNALs or ERRORs
IF vDesc = svDesc THEN
turn off volume flusher if any; (RRA) must do this before suspending the BTree
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] = {
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;
IF victim = svDesc THEN {
special steps for the system volume; (RRA) must close this before closing the BTree
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];
a little earlier so used times always will be updated after scavenge
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];
{
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
! FS.Error => IF error.code = $badFP THEN 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 {
put file in directory/cache
exists: BOOLFALSE;
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];
BTreeVM.FreeBuffers[vDesc.treeVM];
};
Start Code
Booting.RegisterProcs[c: FSCheckpointProc, r: FSRollbackProc];
}.