FSBackdoor.mesa
Backdoors to the Cedar File System
Created by M. D. Schroeder
Last Edited by: Schroeder, September 15, 1983 1:56 pm
DIRECTORY
BasicTime USING [GMT],
File USING [FP, Handle, Volume],
FS USING [Lock, OpenFile],
FSBackdoor: CEDAR DEFINITIONS =
BEGIN
ROPE: TYPE = Rope.ROPE;
CLIENT-PROVIDED OPEN FILES
FS provides a way for clients to implement objects with the TYPE FS.OpenFile. The client provides procedures that implement the operations on an FS.OpenFile. Refer to FS.mesa for the semantics of these procedures. The client procedures should raise FS.Error as appropriate. Client procedures can generate code ATOM's used by FS and can produce additional code ATOM's if needed. FS.Error's with existing code ATOM's should be generated using FSBackdoor.ProduceError (see below). The client is responsible for associating any new code ATOM's with the appropriate FS.ErrorGroup. Note that no locking is done by FS on client-provided FS.OpenFile's. FS produces FS.Error[client, $notImplemented] when an attempt is make to use any client procedures that are NIL.
FileProcs: TYPE;
CreateFileProcs:
PROC [
GetClass:
PROC [clientFile:
REF]
RETURNS [
ATOM] ←
NIL,
SameFile:
PROC [clientFile1, clientFile2:
REF]
RETURNS [
BOOLEAN] ←
NIL,
GetName:
PROC [clientFile:
REF]
RETURNS [fullFName, attachedTo: Rope.
ROPE] ←
NIL,
GetInfo:
PROC [clientFile:
REF]
RETURNS [keep:
CARDINAL, pages, bytes:
INT, created: BasicTime.
GMT, lock:
FS.Lock] ←
NIL,
SetPageCount:
PROC [clientFile:
REF, pages:
INT] ←
NIL,
SetByteCountAndCreatedTime:
PROC [clientFile:
REF, bytes:
INT, created: BasicTime.
GMT] ←
NIL,
Read:
UNSAFE
PROC [clientFile:
REF, from, nPages:
INT, to:
LONG
POINTER] ←
NIL,
Write:
PROC [clientFile:
REF, to:
INT, nPages:
INT, from:
LONG
POINTER] ←
NIL,
Close:
PROC [clientFile:
REF] ←
NIL
]
RETURNS [
REF FileProcs];
SameFile will only get called for two clientFile's with equal "REF FileProcs".
Client errors: none
User errors: none
CreateProcsOpenFile:
PROC [clientFile:
REF, fileProcs:
REF FileProcs]
RETURNS [
FS.OpenFile];
Whenever any of the procedures above are called for the FS.OpenFile returned by FSBackdoor.CreateProcsOpenFile, the "clientFile" argument to CreateProcsOpenFile is passed on as the identification of the open file that is to be operated upon.
Client errors: none
User errors: none
GetClientFileAndProcs:
PROC [file:
FS.OpenFile]
RETURNS [clientFile:
REF, fileProcs:
REF FileProcs];
If "file" is a client-provided FS.OpenFile then the "clientFile" and "fileProcs" are returned, otherwise NIL is returned for both results.
Client errors: $invalidOpenFile
User error: none
ErrorCode: TYPE = {ok -- used internally-- , inconsistent, software, badFP, wentOffline, hardware, volumeFull, fragmented, noMoreVersions, serverInaccessible, connectionRejected, connectionTimedOut, badCredentials, accessDenied, quotaExceeded, invalidPropertyPage, badBTree, lockConflict, fileBusy, noCache, wrongLock, globalWriteLock, zeroKeep, badByteCount, unknownPage, invalidOpenFile, notImplemented, nonCedarVolume, unknownServer, unknownVolume, unknownFile, unknownCreatedTime, illegalName, patternNotAllowed, versionSpecified, globalCreation, badWorkingDir, noKeeps, cantUpdateTiogaFile};
ProduceError:
PROC [code: ErrorCode, explanation: Rope.
ROPE];
Raises FS.Error for the code ATOM that corresponds lexically to the "code". The FS.ErrorGroup in the error is properly set. The error will contain "explanation". Clients should use this procedure to generate FS.Error's for existing codes to avoid spelling the ATOM's wrong and getting the wrong FS.ErrorGroup.
VOLUME SPACE MANAGEMENT
FS provides access to the File package procedures for setting the volume freeboard on the system volume and reading the size, free and freeboard counts from a named volume.
SetFreeboard:
PROC [freeboard:
INT];
Client errors: none
User errors: $unknownVolume
VolumePages:
PROC [volName:
ROPE ← NIL]
RETURNS [size, free, freeboard:
INT];
"volName" = NIL means the system volume.
Client errors: none
User errors: $nonCedarVolume, $unknownVolume
InfoProc: TYPE = PROC [fullGName: ROPE, created: BasicTime.GMT, bytes: INT, keep: CARDINAL] RETURNS [continue: BOOLEAN];
EnumerateCacheForInfo:
PROC [proc: InfoProc, volName, pattern:
ROPE ←
NIL];
NameProc: TYPE = PROC [fullGName: ROPE] RETURNS [continue: BOOLEAN];
EnumerateCacheForNames:
PROC [proc: NameProc, volName, pattern:
ROPE ←
NIL];
Like FS.EnumerateForInfo and FS.EnumerateForNames, except that only cached GNames in the named volume are enumerated. "volName" = NIL means the system volume. "pattern" = NIL means to match all names.
Client errors: $noCache
User errors: $nonCedarVolume, $unknownVolume, $illegalName
Flush:
PROC [fullGName:
ROPE, volName:
ROPE ←
NIL];
The specified GName is removed from the directory/cache on the named volume and any associated file on that volume is deleted. "volName" = NIL means the system volume. Locking is done only for the system volume. In this case FS.Error[lock, $lockConflict] is generated when a "write" lock connot be obtained. Flushing a cached file from other than the system volume is done independent of any locks that may be set, so BE SURE YOU KNOW WHAT YOU ARE DOING. No FS.Error occurs if the "fullGName" is not in the cache on the indicated volume
Client errors: none
User errors: $nonCedarVolume, $unknownVolume
DIRECT ACCESS TO THE DIRECTORY/CACHE ON ANY VOLUME
The directory of LNames and the cache for GName are implemented together as a BTree on a local volume. The following procedures and TYPES allow direct access to this BTree.
A "nameBody" is an FName without a version part. LNames are recorded in the directory/cache without the "[]<>" prefix. Thus the nameBody for the LName "[]<>foo.mesa!5" will be "foo.mesa", and the nameBody for the GName "[indigo]<Cedar>Top>FS.df!47" will be "[indigo]<Cedar>Top>FS.df". The version part is represented as a scalar. The version for a GName that includes no version is "noVersion", which value otherwise will not occur.
Version: TYPE = RECORD [CARDINAL];
noVersion: Version = [LAST[CARDINAL]];
highestVersion: Version = noVersion;
lowestVersion: Version = [0];
"highestVersion" and "lowestVersion" are used internally as the representation of !L and !H.
An entry in the directory/cache can be gotten at directly via FSBackdoor.Enumerate and the following TYPEs.
*******************************************************************************
WARNING: You should treat Entry's as READONLY, though the compiler won't let me say it.
*******************************************************************************
EntryType: TYPE = {local, attached, cached, notFound}; -- notFound is for internal use only
EntryPtr: TYPE = LONG BASE POINTER TO Entry;
TextRP: TYPE = EntryPtr RELATIVE POINTER TO TextRep;
TextRep: TYPE = MACHINE DEPENDENT RECORD [PACKED SEQUENCE length: CARDINAL OF CHAR];
Entry: TYPE = MACHINE DEPENDENT RECORD [
size(0): CARDINAL, -- size in words of entire entry, including TextRep's at end
version(1): Version, -- version part of FName
nameBody(2): TextRP, -- FName w/o version
rest(3): SELECT type(3): EntryType FROM
local => [
keep(4): CARDINAL,
fp(5): File.FP
],
attached => [
keep(4): CARDINAL,
created(5): BasicTime.GMT,
attachedTo(5+SIZE[BasicTime.GMT]): TextRP -- includes version part
],
cached => [
used(4): BasicTime.GMT,
fp(4+SIZE[BasicTime.GMT]): File.FP
]
ENDCASE
-- here follows the TextRep for nameBody and possibly for attachedTo
];
Enumerate:
PROCEDURE [ volName:
ROPE, nameBodyPattern: Rope.Text, localOnly, allVersions:
BOOLEAN, version: Version, matchProc:
UNSAFE
PROC [entry: EntryPtr]
RETURNS [accept, stop:
BOOLEAN], acceptProc:
PROC
RETURNS [stop:
BOOLEAN] ];
"volName" is the name of a disk volume. A NIL "volName" means the system volume. The directory/cache is enumerated. The order of enumeration is lexical by nameBody (lower case letters are mapped to upper case) then numerical by version. Because LNames in the directory/cache do not have the "[]<>" prefix, and because of the constraints on the characters allowed in FNames, all LNames appear in the directory/cache earlier in the enumeration order than all GNames. The scope of the enumeration is constrained by the "localOnly" argument: if TRUE then only the LNames are enumerated; otherwise the enumeration spans all entries in the directory/cache. If "allVersions" is TRUE then the enumeration includes all versions of entries; otherwise it includes on the version specified by "version". The name body pattern is applied literally, without consideration of any working directories. If you want to enumerate only global names, then be sure the "nameBodyPattern" starts with "[". "matchProc" is called for each entry that matches the "pattern". A NIL "pattern" means to start at the beginning of the directory/cache and match everything. If "stop" is returned as TRUE then the enumeration terminates. Otherwise, if "accept" is returned as TRUE then "acceptProc" is called. The "acceptProc" can also stop the enumeration. The directory/cache is read locked while "matchProc" is called, so other operations on the directory/cache and tasks involving indefinite waits should not be done from this procedure. Do these from the "acceptProc" instead.
Client errors: none
User errors: $nonCedarVolume, $unknownVolume
TextFromTextRep:
PROCEDURE [nameBody:
LONG
POINTER
TO TextRep]
RETURNS [Rope.Text];
If "e" is an "EntryPtr", and "e.t" is a contained "TextRP", then "@e[e.t]" is a "LONG POINTER TO TextRep".
Client errors: none
User errors: none
MakeFName:
PROCEDURE [nameBody:
ROPE, version: Version ← noVersion, prefix:
ROPE ←
NIL]
RETURNS [
ROPE];
Makes an FName out of a "nameBody" and "version", and a "prefix". Does not do a syntax check. The prefix is prepended if the nameBody does not start with the character "[". If the nameBody comes from the directory of some named volume other than the system volume, then a prefix of "[]<volumeName>" should be supplied. If a prefix is needed and NIL is supplied, then "[]<>" is used. For use in converting the "attachedTo" name from a directory entry to a ROPE, just leave "version" and "prefix" defaulted, since "attachedTo" already starts with "[" and includes the version part.
Client errors: none
User errors: none
DIRECTORY/CACHE RECONSTRUCTION
ScavengeDirectoryAndCache:
PROC [ volName:
ROPE ←
NIL ];
The directory/cache BTree for the named volume is rebuilt by enumerating the leader pages of all files on the volume and merging in any attached LNames that can be recovered from an existing BTree. "volName" = NIL means the system volume. While scavenging is occuring all FS operations that access the directory will wait. DO NOT call FSBackdoor.CloseVolume first.
Client errors: none
User errors: $nonCedarVolume, $unknownVolume
CloseVolume:
PROC [v: File.Volume];
For experts only!!! Tells FS to close any directory/cache BTree it has open for the volume. May cause FS operations in progress to raise various strange BTree and File ERROR's. After CloseVolume returns, new FS operations will try to reopen a directory/cache BTree for the volume. Should be called AFTER a volume is erased.
Client errors: none
User errors: none
GetFileHandle:
PROC [file:
FS.OpenFile]
RETURNS [File.Handle];
For experts only!!! Returns the File.Handle that goes with an FS.OpenFile.
Client errors: $invalidOpenFile, $notImplemented (for client-provided FS.OpenFile's)
User errors: none
FNameFromHandle:
PROC [file: File.Handle]
RETURNS [
ROPE];
Returns the full FName of the named "file". Will raise FS.Error[environment, $invalidPropertyPage] if the handle is not for an FS file.
Client errors: none
User errors: none
EventOp: TYPE = {startRetrieving, endRetrieving, startStoring, endStoring, startFlushing, endFlushing};
EventHandle: TYPE = REF EventObject;
EventObject: TYPE;
NextEvent:
PROC [last: EventHandle ←
NIL]
RETURNS [fName:
ROPE, event: EventOp, this: EventHandle] ;
An event is the start or conclusion of retrieving, storing, or flushing a global file. A call on NextEvent returns the FName and EventOp for the next event to occur after the "last" event. When "last" is NIL then the next event in time is returned. Will WAIT if necessary. Storing, retrieving and flushing of several files can occur simultaneously.
Client errors: none
User errors: none
END...