Cedar Nucleus (files): Public interface.
File: File.mesa
Last edited by: Andrew Birrell, September 20, 1983 2:04 pm
Introduction
This interface provides for creating, accessing and deleting files. Files are permanent objects recorded on backing storage. The backing storage consists of (logical) volumes. Each file consists of a number of pages allocated on a particular volume. There are provisions for a root file in a volume, intended to be the origin of a higher level directory system. The data pages of a file can be read and written. Files can be extended or contracted. Files also have properties (recorded in leader pages which are otherwise invisible to clients of this interface). This interface allows the entire property storage of a file to be read or written. The structure and contents of that storage are beyond this interface.
A volume is identified by a unique identifier, or by a human sensible string. Files are identified by an FP, which identifies a file uniquely relative to the volume on which it is allocated. Data pages within a file are numbered consecutively from 0.
This interface does not provide for creating, formatting, initializing or scavenging volumes.
DIRECTORY
Rope USING[ ROPE ];
File: CEDAR DEFINITIONS =
BEGIN
Errors
RC: TYPE = { -- extension of "Reason" for internal use.
ok,
wentOffline, -- volume is not accessible (some drive has been offline)
nonCedarVolume, -- the operation is not possible on a non-cedar (Pilot?) volume.
inconsistent, -- the volume's (or file's) permanent data structures look inconsistent
software, -- i.e. label-check. The page on disk is not the one the software expected.
hardware, -- the disk hardware/microcode detected a hard disk error
unknownFile, -- the file does not exist (possibly deleted since it was opened)
unknownPage, -- the page would be beyond the end of the file
volumeFull, -- the volume has no free pages (and one is needed!)
fragmented, -- the file requires too many non-contiguous areas of disk ( > 84 today )
mixedDevices -- a bootable file is not entirely on one device, or not on correct device
};
Reason: TYPE = RC[SUCC[ok]..LAST[RC]]; -- error reason
Error: ERROR[why: Reason];
This error may be raised by most of the procedures. The possible values of "why" are detailed with each procedure. This error is raised after releasing internal locks: the facilities of this interface may be used from catch-phrases. For debugging, extra information may be available in the frame that raises this error.
Types and Constants
VolumeID: TYPE[5];
The permanent identification of a logical volume. Universally permanently unique.
NullVolumeRep: PRIVATE TYPE = RECORD[a,b,c,d,e:CARDINAL];
nullVolumeID: VolumeID = LOOPHOLE[NullVolumeRep[a:0,b:0,c:0,d:0,e:0]];
Guaranteed to be the UID of no volume.
Volume: TYPE = REF VolumeObject;
VolumeObject: TYPE;
The runtime representation of a volume. NIL never represents a valid volume.
VolumeFile: TYPE = MACHINE DEPENDENT { -- root files of a volume
checkpoint(0),
microcode(1),
germ(2),
bootFile(3),
debugger(4), -- outload file --
debuggee(5), -- outload file --
VM(6), -- virtual memory backing file --
VAM(7), -- volume allocation map --
client(8), -- client directory system root file --
alpine(9), -- for use by Alpine file servers --
(15) -- spare root page slots --
};
VolumeFlusher: TYPE = PROC[Volume, INT, REF ANY] RETURNS[BOOL];
A procedure provided by a client which may be called occasionally to try to free up space on the volume. See "SetFlusher" below.
FP: TYPE = MACHINE DEPENDENT RECORD[
The permanent identification of a file. "id" is permanently unique relative to a volume.
id(0): FileID,
da(2): DA ];
FileID: TYPE[2];
DA: TYPE[2];
NullFileIDRep: PRIVATE TYPE = RECORD[a,b:CARDINAL];
NullDARep: PRIVATE TYPE = RECORD[a,b:CARDINAL];
nullDA: PRIVATE DA = LOOPHOLE[NullDARep[0,0]];
nullFP: FP = [id: nullFileID, da: nullDA];
Guaranteed to be the FP of no file.
nullFileID: FileID = LOOPHOLE[NullFileIDRep[0,0]];
Guaranteed to be the FileID of no file.
PageNumber: TYPE = RECORD[INT];
Actually, [0..LAST[INT]). The file-relative number of a data page. The first data page of a file is numbered 0.
PageCount: TYPE = INT;
Actually [0..LAST[INT]]. Represents file sizes.
Add: PROC[p: PageNumber, n: PageCount] RETURNS[PageNumber] = INLINE
{ RETURN[ [p+n] ] }; -- "n" may be negative --
wordsPerPage: INT = 256;
The number of words in each data page of a file. Might not equal VM.wordsPerPage. Should really say "DiskFace.wordsPerPage", but we don't want that compilation dependency.
PagesForWords: PROC[w: INT] RETURNS[PageCount] = INLINE
{ RETURN[ (w+wordsPerPage-1) / wordsPerPage ] };
Handle: TYPE = REF Object;
Object: TYPE;
The runtime representation of a file. NIL never represents a valid file.
propertyWords: INT = 256; -- the number of words in a file's property storage.
PropertyStorage: TYPE = LONG POINTER TO ARRAY [0..propertyWords) OF WORD;
Volumes
Locating and naming volumes (Only these operations work for non-Cedar volumes)
NextVolume: PROC[volume: Volume, wait: BOOLFALSE] RETURNS[Volume];
The enumerator for volumes. This gives the volumes in time sequence of the containing physical volumes coming online (omitting volumes that are no longer online). A "VolumeObject" is created when the requisite containing physical volumes all come online, and remains valid until one of them goes offline. NextVolume[NIL] gives the first currently valid volume. If there are no more volumes and "wait" is FALSE, returns NIL. If there are no more volumes and "wait" is TRUE, waits until a new "VolumeObject" is created. Note that this enumeration includes volumes which may raise Error[inconsistent, software, hardware] if you try to access them, and a volume may go offline immediately after it has been returned by this enumeration.
LogicalInfo: PROC[volume: File.Volume] RETURNS[
id: VolumeID,
size: INT,
rootStatus: RC,
Following information is valid only if rootStatus is ok or nonCedarVolume
name: Rope.ROPE,
vamStatus: RC,
Following information is valid only if vamStatus is ok
free: INT,
freeboard: INT];
Misc info about the volume. This procedure raises no errors. Note the caveats about which results are valid. See also GetVolumeID[...], GetVolumeName[...], and GetVolumePages[...].
GetVolumeID: PROC[volume: Volume] RETURNS[id: VolumeID];
! no errors
Returns the volume's permanent UID.
GetVolumeName: PROC[volume: Volume] RETURNS[Rope.ROPE];
! Error[wentOffline, inconsistent, software, hardware]
Returns the human-sensible name of this volume (not necessarily unique).
FindVolumeFromID: PROC[id: VolumeID] RETURNS[Volume];
Obtain the handle for accessing the specified volume, by enumerating with "NextVolume" and "GetVolumeID", omitting volumes no longer online. If no suitable volume is found, then NIL is returned.
FindVolumeFromName: PROC[name: Rope.ROPE] RETURNS[Volume];
Obtain the handle for accessing the specified volume, by enumerating with "NextVolume" and "GetVolumeName", omitting volumes no longer online and volumes that raise an error in GetVolumeName. If no suitable volume is found, then NIL is returned.
SystemVolume: PROC RETURNS[Volume];
Obtain the handle for accessing the volume which was booted to start this run. Returns NIL if there is no such volume.
SetSystemVolume: PROC[Volume];
Asserts that this is the system volume. This may also determine the VM backing file if we don't yet have one. It's rather dangerous to call this unless you know that there was previously no system volume.
FindSystemVolume: PROC RETURNS[found: BOOL];
If there is currently no system volume, try to find one. Returns whether there is now a system volume. This is a no-op if there is presently a system volume.
FindVM: PROC RETURNS[found: BOOL];
If there is currently no VM backing file, try to find one. Returns whether there is now a VM backing file. This is a no-op if there is presently a VM backing file.
Management of volume free pages
GetVolumePages: PROC[volume: Volume] RETURNS[size, free, freeboard: INT];
! Error[wentOffline, nonCedarVolume, inconsistent, software, hardware]
Returns the size and present free page count of this volume. Also returns the "freeboard" parameter set by a client (see volume flusher arrangements below).
SetFlusher: PROC[volume: Volume, flusher: VolumeFlusher, data: REF ANY];
! no errors
Specifies a volume flusher for this volume. Hereafter, the procedure will be called from inside this implementation when the implementation thinks it is a good time to free up some pages on the volume. When called, the procedure is handed "data", "volume" and a page count indicating how many pages it should try to free. The flusher procedure should return TRUE iff it thinks it is making progress. No internal locks are held synchronously with the call out to the flusher procedure. Setting a flusher of NIL disables this facility. If the client has never specified a freeboard, then this call sets a suitable default freeboard.
GetFlusher: PROC[volume: Volume] RETURNS[VolumeFlusher, REF ANY];
! no errors
Returns NIL if there is currently no flusher for this volume.
SetFreeboard: PROC[volume: Volume, freeboard: INT];
Requests that the implementation try to maintain that number of free pages by calling the volume flusher (if there is one) from time to time.
Volume root page operations
SetRoot: PROC[root: VolumeFile, file: Handle, page: PageNumber ← [0]];
! Error[wentOffline, nonCedarVolume, unknownFile, inconsistent, software, hardware, mixedDevices]
Installs the file as a root file of its volume, for subsequent access by GetRoot or when inloading/booting. "page", if provided, is the page number of the first data page used for inloading/booting from this file.
GetRoot: PROC[volume: Volume, root: VolumeFile] RETURNS[fp: FP, page: PageNumber];
! Error[wentOffline, nonCedarVolume, inconsistent, software, hardware]
Returns the appropriate root file of the volume, or nullFP if no root has been installed.
MarkDebugger: PROC[volume: Volume, isDebugger: BOOL];
! Error[wentOffline, nonCedarVolume, inconsistent, software, hardware]
Marks the volume as being one which is expected to be the system volume of a world-swap debugger. If you don't understand that, don't call this procedure.
IsDebugger: PROC[volume: Volume] RETURNS[BOOL];
! Error[wentOffline, inconsistent, software, hardware]
Tells whether the volume is one which is expected to be the system volume of a world-swap debugger. If you don't understand that, don't call this procedure.
EraseVolume: PROC[volume: Volume];
! Error[wentOffline, nonCedarVolume, inconsistent, software, hardware]
Initialises the entire volume. This implicitly deletes every file on that volume, and sets all its volume root files to nullFP (except the VAM). Makes no assumptions about the previous contents of the volume. Error[inconsistent] probably indicates that a containing physical volume needs to be scavenged. Calling this for the volume that contains the current VM backing file is probably a bad idea.
Files
Open: PROC[volume: Volume, fp: FP] RETURNS[Handle];
! Error[wentOffline, nonCedarVolume, unknownFile, inconsistent, software, hardware]
Prepares for runtime access to the file. Verifies that the FP is for an existing file, and reports "unknownFile" if not. Note that there is no Close operation, since it would have no effect.
Create: PROC[volume: Volume, size: PageCount, report: PROC[FP,PropertyStorage] ← NIL] RETURNS[Handle];
! Error[wentOffline, nonCedarVolume, volumeFull, fragmented, inconsistent, software, hardware]
Creates a file having the given number of data pages and zero'ed properties. If "report" is not NIL then it is called with the new file's FP and PropertyStorage immediately before the file is actually created. However, "report" may be called multiple times with differing FP's (if writing the file's new pages encounters a hard disk error) - only the last FPis correct. Note that at even after the reporter has been called, the creation may fail. During a call of "report", no internal locks are held, except an exclusive lock on the file being created. The client may initialize the file's PropertyStorage during a call of "report".
Delete: PROC[file: Handle];
! Error[wentOffline, unknownFile, inconsistent, software, hardware]
Destroys the file.
Info: PROC[file: Handle] RETURNS[volume: Volume, fp: FP, size: PageCount];
! Error[unknownFile]
The size is the page number of data pages in the file. That is, a file with no data pages has size 0.
SetSize: PROC[file: Handle, size: PageCount];
! Error[wentOffline, nonCedarVolume, unknownFile, volumeFull, fragmented, inconsistent, software, hardware]
Extends or contracts the file (synchronously).
Read: UNSAFE PROC[file: Handle, from: PageNumber, nPages: PageCount, to: LONG POINTER];
! Error[wentOffline, unknownFile, unknownPage, inconsistent, software, hardware]
Copies pages from the file starting at "from" into the VM specified by "to". If Error[unknownPage] is raised, no pages were transferred.
Write: PROC[file: Handle, to: PageNumber, nPages: PageCount, from: LONG POINTER];
! Error[wentOffline, unknownFile, unknownPage, inconsistent, software, hardware]
Copies pages from the VM specified by "from" into the file starting at "to". If Error[unknownPage] is raised, no pages were transferred.
GetProperties: PROC[file: Handle] RETURNS[PropertyStorage];
! Error[unknownFile]
Returns storage containing the properties of a file. The property storage is uninterpreted at this level and lives as long as the file handle. This call never involves disk IO.
WriteProperties: PROC[file: Handle];
! Error[wentOffline, unknownFile, inconsistent, software, hardware]
Writes the property storage of the file back to disk,
NextFile: PROC[volume: Volume, prev: FP] RETURNS[FP];
! Error[wentOffline]
Returns the FP of the "next" file in existence on the volume. Giving prev=nullFP returns the "first" file. Returns nullFP if there is no "next" file. The enumeration order is determined by the physical layout of the disk. This is intended as the primitive operation for higher level scavengers.
END.