-- file CoreSS.Mesa (See also Core*.mesa.)
-- Last edited by Levin, April 25, 1979 9:28 AM.
-- Converted to Mesa 5.0, March 26, 1979 6:13 PM
-- CoreSS supports calls done at open & close time. SS = Start/Stop.
-- This file is part of a set. See [IFS]<DMS>Overview.mesa for more information.
DIRECTORY
DiskKDDefs: FROM "DiskKDDefs",
DiskDefs: FROM "DiskDefs",
SystemDefs: FROM "SystemDefs",
AltoFileDefs: FROM "AltoFileDefs",
DirectoryDefs: FROM "DirectoryDefs",
SegmentDefs: FROM "SegmentDefs",
StringDefs: FROM "StringDefs",
crD: FROM "CoreDefs",
crID: FROM "CoreImpDefs",
ovD: FROM "OverviewDefs";
CoreSS: PROGRAM -- [6] Core Division --
IMPORTS DiskKDDefs, DiskDefs, SystemDefs, SegmentDefs, DirectoryDefs, StringDefs, ovD, crID
EXPORTS crD
SHARES crD = PUBLIC BEGIN
OPEN crD, crID;
-- Purpose: provides an MC style interface to files and raw storage/structures.
--Topology of this file:
--
Core: PROGRAM
--Attachment Department
--Distributed File Department
--
AltoCore: INTERNAL MODULE
--
VDATable: INTERNAL MODULE
--
FilenameCache: INTERNAL MODULE
--
FreePageMonitor: INTERNAL MODULE
---------------------------------------------------------------
-- Attachment Department of the Core Division. This is a stub; attachments will not be implemented initially.
-- (Not Implemented) FileAttachmentCore: PROCEDURE [capability: Capability, user: DMSUser, filename: UFilename] RETURNS [ovD.ErrorCode];
-- Exchanges messages with the appropriate attachment manager to obtain the file corresponding to the capability. Creates a file named filename and moves the file into it. Catches all Signals generated by communication procedures and turns them into ErrorCode’s and Strings. Possible errors: "Can’t open file", "Can’t find attachment manager", "Invalid capability".
-- (Not Implemented) FileToCapability: PROCEDURE [user: DMSUser, filename: UFilename] RETURNS [ovD.ErrorCode, Capability];
-- Exchanges messages with the appropriate attachment manager to obtain a capability for the attachment. Sends the file named filename to the attachment manager. Catches all Signals generated by communication procedures and turns them into ErrorCode’s and Strings. Possible errors: "Can’t find file", "Can’t find attachment manager".
-------------------------------------------
-- Distributed File Department of the Core Division. This is a mini file system for DMS. It uses several, and as yet unspecified, file servers. The name of the file server is encoded in the UFilename string (i.e. the DMS generalized filename).
OpenFile: PROCEDURE [user: DMSUser, filename: UFilename, mode: OpenMode]
RETURNS [erc: ovD.ErrorCode, uFH: UFileHandle]=
-- Opens the specified file in the specified mode, and returns a handle to the opened file. (Handles are useable until a CloseFile or DeleteFile. When OpenFile fails, i.e. ErrorCode # ok, a special handle, NIL, is returned.)
-- Error Codes: diskError, diskFull, diskCorrupted, illegalFilename, fileInUse, fileTooBig.
BEGIN
-- ##DO: Parse the filename; determine the server (e.g. local disk, IFS, whatever); then dispatch to the correct open file routine. For now, assume the local disk.

[erc, uFH] ← AltoOpenFile[user, filename, mode];
END; -- of OpenFile.
CloseFile: PROCEDURE [uFH: UFileHandle] RETURNS[ovD.ErrorCode]=
-- Closes the specified file. (Special Case: a nop if handle = NIL.) The handle is closed and should not be re-used by the caller.
-- Error Codes: (##DO: update) diskError.
BEGIN
IF uFH=NIL THEN RETURN[ovD.ok] ELSE RETURN [uFH.close[uFH]];
END; -- of CloseFile.
DeleteFile: PROCEDURE [uFH: UFileHandle] RETURNS[ovD.ErrorCode]=
-- Deletes the file associated with the file handle. (Special case: a nop if handle = NIL.) The handle is closed and should not be re-used by the caller.
-- Error Codes: (##DO: update) diskError.
BEGIN
IF uFH=NIL THEN RETURN[ovD.ok] ELSE RETURN [uFH.delete[uFH]];
END; -- of DeleteFile.
UFileLength: PROCEDURE [uFH: UFileHandle]
RETURNS [erc: ovD.ErrorCode, lastPage: PageNumber, byteFF: CARDINAL[0..512)]=
-- Returns the first free byte in the file, i.e. its length, via the formula (lastPage*512 + byteFF). Any data that has been written since the file was opened is included. (Note, empty files return [0,0].)
-- Error Codes: (None for Alto.)
BEGIN
[erc, lastPage, byteFF] ← uFH.length[uFH];
END; -- of UFileLength.
UFileTruncate: PROCEDURE [lastPage: PageNumber, byteFF: CARDINAL[0..512), uFH: UFileHandle] RETURNS[ovD.ErrorCode]=
-- Shorten a file which has been opened by OpenFile. The position has the same semantics as UFileLength.
-- Do NOT use to LENGTHEN a file!
-- Error Codes: (##DO: update) diskError.
BEGIN
RETURN [uFH.truncate[lastPage, byteFF, uFH]];
END; -- of UFileTruncate.
----------------------------------------------
-- INTERNAL MODULE: AltoCore.Mesa
-- Purpose: provides support for Alto file system in MC.
-- This file is part of a set. See [IFS/MAXC]<DMS>OverviewDefs.mesa for more info.
-- ErrorCodes from ovD:
ok: ovD.ErrorCode=ovD.ok;
diskError: ovD.ErrorCode=ovD.diskError;
diskCorrupted: ovD.ErrorCode=ovD.diskCorrupted;
diskFull: ovD.ErrorCode=ovD.diskFull;
fileInUse: ovD.ErrorCode=ovD.fileInUse;
illegalFilename: ovD.ErrorCode=ovD.illegalFilename;
fileNotFound: ovD.ErrorCode=ovD.fileNotFound;
AltoOpenFile: PUBLIC PROCEDURE [user: DMSUser, filename: UFilename, mode: OpenMode]
RETURNS [ovD.ErrorCode, UFileHandle] =
-- Opens the specified file in the specified mode, and returns a handle to the opened file. (Handles are useable until a CloseFile or DeleteFile. When OpenFile fails, i.e. ErrorCode # ok, a special handle, NIL, is returned.)
-- The maximun size of a filename is AltoFileDefs.FilenameChars (=39).
-- Error Codes: diskError, diskFull, diskCorrupted, illegalFilename, fileInUse, fileTooBig.
BEGIN
mesaFH: SegmentDefs.FileHandle ← NIL;
altoMode: SegmentDefs.AccessOptions;
aFH: AltoFileHandle;
eofPage: AltoPageNumber;
eofByte: CARDINAL[0 .. 512];
eofFA: AltoFileDefs.FA;
erc: ovD.ErrorCode;

BEGIN -- This block is for EXITS.
IF filename.length > AltoFileDefs.FilenameChars THEN
RETURN[illegalFilename, NIL];

SELECT mode FROM
read => altoMode ← SegmentDefs.Read;
update => altoMode ← SegmentDefs.Read+SegmentDefs.Write+SegmentDefs.Append;
ENDCASE => ovD.SysBug[NIL];

-- Get mesaFH to and opened file, one way or another.
mesaFH ← LookupInFileCache[filename]; -- Try for a quick open.
IF mesaFH#NIL --i.e. it WAS cached-- THEN
IF mesaFH.open THEN RETURN[fileInUse,NIL]
ELSE --not in use-- SegmentDefs.SetFileAccess[mesaFH, altoMode]
ELSE -- i.e. mesaFH=NIL, it WASN’T cached-- BEGIN
mesaFH ← SegmentDefs.NewFile[filename, altoMode, SegmentDefs.DefaultVersion !
-- catch phrases:
DirectoryDefs.BadDirectory=> GOTO CBadDirectory;
SegmentDefs.FileNameError,
DirectoryDefs.BadFilename => GOTO CBadFilename;
DiskDefs.UnrecoverableDiskError => GOTO CUDErr;
SegmentDefs.InsufficientVM => GOTO CIVM;
DiskKDDefs.DiskFull => GOTO CDiskFull];
InsertInFileCache[filename, mesaFH];
END;

SegmentDefs.OpenFile[mesaFH];
[eofPage, eofByte] ← SegmentDefs.GetEndOfFile[mesaFH];
[eofPage, eofByte] ← MakeFileIndexCanonical[eofPage, eofByte]; -- For aFH’s copy.
aFH ← SystemDefs.AllocateHeapNode[SIZE[alto UFileObject]];
aFH↑ ← UFileObject[
access: mode,
lastFilePage: MapAltoToUPage[eofPage],
byteFF: eofByte,
close: AltoCloseFile,
delete: AltoDeleteFile,
length: AltoFileLength,
truncate: AltoTruncateFile,
read: AltoReadPages,
write: AltoWritePages,
varpart: alto[handle: mesaFH, vDATable: CreateVDATable[] ]];
IF eofPage>maxVDAIndex THEN BEGIN -- Oops, too long!
erc←AltoCloseFileF[aFH]; -- Undo this open.
RETURN [IF erc#ovD.ok THEN erc ELSE ovD.fileTooBig, NIL];
END; -- of IF too long.
TablePutVDA[aFH.vDATable, 0, aFH.handle.fp.leaderDA];
SegmentDefs.GetFileLength[aFH.handle,@eofFA];
TablePutVDA[aFH.vDATable, eofPage, eofFA.da];
UpdateFreePageCount[];
RETURN[ok, aFH];
EXITS
CBadDirectory => RETURN [diskCorrupted, NIL];
CBadFilename => IF mode=read THEN RETURN [fileNotFound, NIL]
ELSE RETURN [illegalFilename, NIL]; -- Bad char(s).
CUDErr => RETURN [diskError, NIL];
CIVM => ovD.SysBug[NIL]; -- Insufficient Virt Mem.
CDiskFull => RETURN [diskFull, NIL];
END;-- of block for EXITS.
ERROR;
-- to satisfy the stupid compiler
END;
-- of AltoOpenFile.
-- Note: all of the following operations require a UFileHandle for a file which is in open state. They have no way of checking the validity of the UFileHandle. Terrible things will happen if they are given an invalid UFileHandle.
AltoCloseFile: PROCEDURE [uFH: UFileHandle] RETURNS[erc: ovD.ErrorCode] =
-- Closes the specified file. The handle is closed and should not be re-used by the caller. (The special Case "A nop if handle = NIL," is fielded by Core.)
-- Error Codes: diskError, diskCorrupted.
BEGIN
aFH:AltoFileHandle;
eofPage: AltoPageNumber = MapUToAltoPage[uFH.lastFilePage];
fA: AltoFileDefs.FA;
vDA: VDA;

WITH fh: uFH↑ SELECT FROM
alto => aFH←@fh;
ENDCASE => ovD.SysBug[NIL];

[erc, vDA] ← GetVDA[eofPage, aFH];
IF erc#ok THEN RETURN [erc];
fA ← [da: vDA, page: eofPage, byte: uFH.byteFF];
SegmentDefs.UpdateFileLength[aFH.handle, @fA];
RETURN[AltoCloseFileF[aFH]];
END; -- of AltoCloseFile.
AltoCloseFileF: PROCEDURE [aFH: AltoFileHandle] RETURNS[erc: ovD.ErrorCode] =
-- Closes the specified file. Used by AltoCloseFile and AltoOpenFile to just close the file.
-- Error Codes: diskError, diskCorrupted.
BEGIN
erc←ovD.ok; -- Asssume the best.
SegmentDefs.CloseFile[aFH.handle !
-- Catch phrases:
DiskDefs.UnrecoverableDiskError,
SegmentDefs.FileError => BEGIN erc←diskError; CONTINUE; END;
SegmentDefs.InvalidFP => BEGIN erc←diskCorrupted; CONTINUE; END;
SegmentDefs.InsufficientVM => ovD.SysBug[NIL] -- Need memory.
];
FreeAFHStorage[aFH];
UpdateFreePageCount[];
RETURN [erc];
END; -- of AltoCloseFileF.
AltoDeleteFile: PROCEDURE [uFH: UFileHandle] RETURNS[ovD.ErrorCode]=
-- Deletes the file associated with the file handle. The handle is closed and should not be re-used by the caller. (The special case "A nop if handle = NIL," is fielded by Core.)
-- Error Codes: diskError, diskCorrupted.
BEGIN aFH:AltoFileHandle;
erc: ovD.ErrorCode←ok; -- Assume all will go well.

WITH fh: uFH↑ SELECT FROM
alto => aFH←@fh;
ENDCASE => ovD.SysBug[NIL];

FreeCacheEntry[aFH.handle];
SegmentDefs.DestroyFile[aFH.handle !
-- Catch phrases:
DiskDefs.UnrecoverableDiskError,
SegmentDefs.FileError => BEGIN erc←diskError; CONTINUE; END;
SegmentDefs.InvalidFP => BEGIN erc←diskCorrupted; CONTINUE; END;
SegmentDefs.InsufficientVM => ovD.SysBug[NIL] -- Need memory.
];
FreeAFHStorage[aFH];
UpdateFreePageCount[];
RETURN [erc];
END; -- of AltoDeleteFile
AltoFileLength: PROCEDURE [uFH: UFileHandle]
RETURNS [erc: ovD.ErrorCode, lastPage: UPageNumber, byteFF: CARDINAL[0..512)]=
-- Returns the first free byte in the file, i.e. its length, via the formula (lastPage*512 + byteFF). Any data that has been written since the file was opened is included. (Note, empty files return [0,0].)
-- Note: The name of this routine may be misleading. The length returned is a DMS file length; the page number component is a UPageNumber, not an AltoPageNumber. (The name falls out of the naming conventions elsewhere, alas.)
-- Error Codes: none for the Alto.
BEGIN
RETURN[ok, uFH.lastFilePage, uFH.byteFF];
END; -- of AltoFileLength.
AltoTruncateFile: PROCEDURE
[lastPage: UPageNumber, byteFF: CARDINAL[0..512), uFH: UFileHandle] RETURNS[ovD.ErrorCode]=
-- Shorten a file which has been opened by OpenFile. The position has the same semantics as UFileLength.
-- Do NOT use to LENGTHEN a file!
-- File must have been opened in "update" mode.
-- Error Codes: diskError, diskCorrupted.
BEGIN aFH: AltoFileHandle;
erc: ovD.ErrorCode←ok; -- Assume all will go well.
WITH fh: uFH↑ SELECT FROM
alto => aFH ← @fh;
ENDCASE => ovD.SysBug[NIL];
[lastPage, byteFF] ← MakeFileIndexCanonical[lastPage, byteFF];
SegmentDefs.SetEndOfFile[aFH.handle, MapUToAltoPage[lastPage], byteFF !
-- Catch phrases:
DiskDefs.UnrecoverableDiskError => BEGIN erc←diskError; CONTINUE; END;
SegmentDefs.InvalidFP => BEGIN erc←diskCorrupted; CONTINUE; END;
SegmentDefs.InsufficientVM => ovD.SysBug[NIL] -- Need memory.
];
uFH.lastFilePage ← lastPage;
uFH.byteFF ← byteFF;
UpdateFreePageCount[];
RETURN [erc];
END; -- of AltoTruncateFile
FreeAFHStorage: PROCEDURE [aFH: AltoFileHandle] =
-- Return to the Mesa runtime all allocated storage associated with aFH.
BEGIN
DeallocateVDATable[aFH];
SystemDefs.FreeHeapNode[aFH];
END; -- of FreeAFHStorage.
-- END of AltoCore: INTERNAL MODULE;
-- VDATable: INTERNAL MODULE =
-- The vDA table is represented as a set of chunks, indexed by a descriptor block.
CreateVDATable: PROCEDURE RETURNS [vDATablePtr: VDATablePtr] =
-- Allocates and initializes a VDATable and returns a pointer to it.
-- Note that the VDATable has 2 extra entries (containing NIL), one before and one after the real data. The purpose of these entries is to obviate the need to bounds check when making shadow copies of the padded entries.
BEGIN
i: INTEGER;
vDATablePtr ← SystemDefs.AllocateHeapNode[SIZE[VDATable]+2]+1;
FOR i IN [-1 .. numberOfChunks+1) DO vDATablePtr[i] ← NIL; ENDLOOP;
END; -- of CreateVDATable
DeallocateVDATable: PROCEDURE [aFH: AltoFileHandle] =
-- Returns all allocated storage of the vDATable to the Mesa runtime (i.e. the chunk index table and the individual chunks).
BEGIN
i: CARDINAL;
base: VDAsPtr;
FOR i IN [0 .. numberOfChunks) DO
base ← aFH.vDATable↑[i];
IF base # NIL THEN SystemDefs.FreeHeapNode[base+(i*chunkSpan)-lowerPad];
ENDLOOP;
SystemDefs.FreeHeapNode[aFH.vDATable - 1];
END; -- of DeallocateVDATable
-- END of VDATable: INTERNAL MODULE;
-- FilenameCache: INTERNAL MODULE =
-- Filenames and their associated Mesa FileHandles are cached for faster opening of a file that has been opened previously or inserted in the cache at startup time.
InsertInFileCache: PUBLIC PROCEDURE
[name: STRING -- to be copied--,
fH: SegmentDefs.FileHandle -- possibly NIL--]=
-- Return immediately iff fH=NIL. Else insert the name/fH into the cache.
BEGIN
oldFH: SegmentDefs.FileHandle; node: CacheNodePtr;
copyOfName: STRING;

IF fH=NIL THEN RETURN;

-- See if this entry has been already made.
-- The consistency check below is removable##.
oldFH←LookupInFileCache[name];
IF oldFH#NIL THEN
IF oldFH↑=fH↑ THEN RETURN -- already cached.
ELSE ovD.SysBug[NIL]; -- Inconsistant data.

copyOfName←SystemDefs.AllocateHeapString[name.length];
StringDefs.AppendString[copyOfName, name];
node←SystemDefs.AllocateHeapNode[SIZE[CacheNode]];
node↑←[next: fileCacheHeader, fH: fH, key: copyOfName];
SegmentDefs.LockFile[fH];
fileCacheHeader←node;
END; -- of InsertFileInCache.
FreeCacheEntry: PROCEDURE [fH: SegmentDefs.FileHandle] =
-- Remove an entry from the cache and free associated storage.
BEGIN
node: CacheNodePtr;
prev: CacheNodePtr←NIL; -- Inited for special case in loop.

FOR node←fileCacheHeader, node.next UNTIL node=NIL DO
IF node.fH=fH THEN GOTO gotIt;
prev←node;
REPEAT
gotIt => BEGIN
IF prev#NIL THEN prev.next←node.next
ELSE fileCacheHeader←node.next;
SegmentDefs.UnlockFile[node.fH];
SystemDefs.FreeHeapString[node.key];
SystemDefs.FreeHeapNode[node];
END;
FINISHED => ovD.SysBug[NIL]; -- oops, not found.
ENDLOOP;
END; -- of FreeCacheEntry.
LookupInFileCache: PROCEDURE [name: STRING]
RETURNS [fH: SegmentDefs.FileHandle -- possibly NIL--] =
-- Look for name in the file cache. Return the associated FileHandle, or NIL iff the name isn’t found in the cache.
BEGIN
node: CacheNodePtr;

FOR node←fileCacheHeader, node.next UNTIL node=NIL DO
IF SameName[name, node.key] THEN GOTO gotIt;
REPEAT
gotIt => fH←node.fH;
FINISHED => fH←NIL; -- oops, not found.
ENDLOOP;
END; -- of LookupInFileCache.
SameName: PROCEDURE [name1, name2: STRING]
RETURNS [BOOLEAN] =
-- Comapres two filenames. For avoiding swapping, this may want to be implemented without using StringDefs.
BEGIN
RETURN [StringDefs.EquivalentString[name1, name2]];
END; -- of SameName.
-- Data Structures and Types for FilenameCache: INTERNAL MODULE.
CacheNode: TYPE = RECORD[
next: CacheNodePtr,
fH: SegmentDefs.FileHandle,
key: STRING];
CacheNodePtr: TYPE = POINTER TO CacheNode;
fileCacheHeader: CacheNodePtr←NIL;
-- END of FilenameCache: INTERNAL MODULE;
--------------------------------------------------------------
-- FreePageMonitor: INTERNAL MODULE;
-- This module is responsible for maintaining a free page count and announcing changes to the client.
-- Empty. (See CoreCom.)
-- END of FreePageMonitor: INTERNAL MODULE;
--------------------------------------------------------------
END.; -- of CoreSS, Core Division implementation module.