-- file CoreSS.Mesa -- Last edited by Levin, January 12, 1981 11:58 AM. -- Last edited by Schroeder, January 19, 1981 1:00 PM. -- Last edited by Brotz, February 20, 1981 9:11 AM. -- CoreSS supports calls done at open & close time. SS = Start/Stop. DIRECTORY AltoFileDefs, CoreSwapDefs, crD: FROM "CoreDefs", crID: FROM "CoreImpDefs", DirectoryDefs, DiskDefs, DiskKDDefs, exD: FROM "ExceptionDefs", ImageDefs, ovD: FROM "OverviewDefs", SegmentDefs, Storage, StringDefs, TimeDefs; CoreSS: PROGRAM IMPORTS crD, crID, DirectoryDefs, DiskDefs, DiskKDDefs, exD, ImageDefs, SegmentDefs, Storage, StringDefs EXPORTS crD SHARES crD = PUBLIC BEGIN OPEN crD, crID; -- Purpose: provides a Laurel style interface to files and raw storage/structures. -- Distributed File Department of the Core Division. This is a mini file system -- for Laurel. 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. IF filename.length > 0 AND filename[0] = '[ THEN [erc, uFH] _ LeafOpenFile[user, filename, mode] ELSE [erc, uFH] _ AltoOpenFile[user, filename, mode]; -- [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: PageByte] = -- 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: PageByte, 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. -- GetUFileTimes: PROCEDURE [uFH: UFileHandle] RETURNS [read, write, create: TimeDefs.PackedTime, ec: ovD.ErrorCode] = -- Reads the read, write, and create times associated with the file handle. -- Error Codes: diskError. {[read, write, create, ec] _ uFH.getTimes[uFH]}; SetUFileTimes: PROCEDURE [uFH: UFileHandle, read, write, create: TimeDefs.PackedTime _ TimeDefs.DefaultTime] RETURNS [ovD.ErrorCode] = -- Sets the read, write, and create times associated with the file handle. -- If a time is set to TimeDefs.DefaultTime then the current time is used. -- Error Codes: diskError. {RETURN[uFH.setTimes[uFH, read, write, create]]}; -- INTERNAL MODULE: AltoCore.Mesa -- Purpose: provides support for Alto file system in Laurel. -- 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: PROC [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; i: CARDINAL; BEGIN -- For EXITS. SELECT mode FROM read => altoMode _ SegmentDefs.Read; update => altoMode _ SegmentDefs.ReadWriteAppend; ENDCASE => exD.SysBug[]; -- Get mesaFH and open file, one way or another. mesaFH _ LookupInFileCache[filename]; -- Try for a quick open. IF mesaFH = NIL THEN --i.e. it wasn't cached BEGIN IF filename.length = 0 THEN GOTO CBadFilename; IF (IF filename[filename.length - 1] = '. THEN filename.length ELSE filename.length + 1) > AltoFileDefs.FilenameChars THEN GOTO CBadFilename; FOR i IN [0 .. filename.length) DO SELECT filename[i] FROM IN ['a .. 'z], IN ['A .. 'Z], IN['0 .. '9], '<, '>, '+, '-, '., '!, '$ => NULL; ENDCASE => GOTO CBadFilename; ENDLOOP; mesaFH _ SegmentDefs.NewFile[filename, altoMode, SegmentDefs.DefaultVersion ! DirectoryDefs.BadDirectory=> GOTO CBadDirectory; SegmentDefs.FileNameError => IF mode = read THEN GO TO CFileNotFound ELSE GO TO CBadFilename; DirectoryDefs.BadFilename => GOTO CBadFilename; DiskDefs.UnrecoverableDiskError => GOTO CUDErr; DiskKDDefs.DiskFull => GOTO CDiskFull]; InsertInFileCache[filename, mesaFH]; END; -- We would like the invariant for all files in use by Laurel to be that -- mesaFH.lock > 0. However, this doesn't work for Swatee, because it is locked by -- the Mesa system at start-up and Hardcopy wants to be able to use Swatee. -- Fortunately, Swatee isn't opened by Mesa, so we make the invariant include -- the open requirement. -- Allow read (but not update) of SysDir, which unfortunately doesn't satisfy the invariant. IF ~(mesaFH.fp = AltoFileDefs.DirFP AND mode = read) AND mesaFH.lock > 0 AND mesaFH.open THEN RETURN[fileInUse, NIL]; SegmentDefs.SetFileAccess[mesaFH, altoMode]; -- ensure in-use invariant SegmentDefs.LockFile[mesaFH]; SegmentDefs.OpenFile[mesaFH]; [eofPage, eofByte] _ SegmentDefs.GetEndOfFile[mesaFH]; [eofPage, eofByte] _ MakeFileIndexCanonical[eofPage, eofByte]; aFH _ Storage.Node[SIZE[alto UFileObject]]; aFH^ _ UFileObject [access: mode, lastFilePage: MapAltoToUPage[eofPage], byteFF: eofByte, close: AltoCloseFile, delete: AltoDeleteFile, getTimes: AltoGetTimes, setTimes: AltoSetTimes, 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; 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 => erc _ diskCorrupted; CBadFilename => erc _ illegalFilename; CFileNotFound => erc _ fileNotFound; CUDErr => erc _ diskError; CDiskFull => erc _ diskFull; END; -- of block for EXITS. RETURN[erc, NIL]; 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 => exD.SysBug[]; [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 OPEN SegmentDefs; MakeFileGoAway: PROCEDURE = BEGIN CloseFile[aFH.handle ! DiskDefs.UnrecoverableDiskError => {erc _ diskError; CONTINUE}; InvalidFP => {erc _ diskCorrupted; CONTINUE}]; ReleaseFile[aFH.handle]; END; ThisIsSwatee: PROCEDURE RETURNS [BOOLEAN] = INLINE {OPEN CoreSwapDefs; RETURN [PuntInfo^ ~= NIL AND aFH.handle = PuntInfo^.puntESV.drumFile]}; erc _ ovD.ok; -- Asssume the best. UnlockFile[aFH.handle]; SELECT aFH.handle.lock FROM 0 => MakeFileGoAway[]; 1 => IF ThisIsSwatee[] THEN MakeFileGoAway[]; ENDCASE; FreeAFHStorage[aFH]; UpdateFreePageCount[]; 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 => exD.SysBug[]; FreeCacheEntry[aFH.handle]; SegmentDefs.UnlockFile[aFH.handle]; SegmentDefs.DestroyFile[aFH.handle ! DiskDefs.UnrecoverableDiskError, SegmentDefs.FileError => {erc_diskError; CONTINUE}; SegmentDefs.InvalidFP => {erc_diskCorrupted; CONTINUE}]; FreeAFHStorage[aFH]; UpdateFreePageCount[]; RETURN[erc]; END; -- of AltoDeleteFile -- AltoGetTimes: PROCEDURE [uFH: UFileHandle] RETURNS [read, write, create: TimeDefs.PackedTime, ec: ovD.ErrorCode] = BEGIN aFH: AltoFileHandle; WITH fh: uFH SELECT FROM alto => aFH _ @fh; ENDCASE => exD.SysBug[]; ec _ ok; -- Assume all will go well. [read, write, create] _ SegmentDefs.GetFileTimes[aFH.handle ! DiskDefs.UnrecoverableDiskError => {ec _ ovD.diskError; CONTINUE}; SegmentDefs.InvalidFP => {ec _ ovD.diskCorrupted; CONTINUE} ]; END; -- AltoGetTimes-- AltoSetTimes: PROCEDURE [uFH: UFileHandle, read, write, create: TimeDefs.PackedTime] RETURNS [ec: ovD.ErrorCode] = BEGIN aFH: AltoFileHandle; WITH fh: uFH SELECT FROM alto => aFH _ @fh; ENDCASE => exD.SysBug[]; ec _ ovD.ok; SegmentDefs.SetFileTimes[aFH.handle, read, write, create ! DiskDefs.UnrecoverableDiskError => {ec _ ovD.diskError; CONTINUE}; SegmentDefs.InvalidFP => {ec _ ovD.diskCorrupted; CONTINUE} ]; END; -- AltoGetTimes-- AltoFileLength: PROCEDURE [uFH: UFileHandle] RETURNS [erc: ovD.ErrorCode, lastPage: UPageNumber, byteFF: PageByte] = -- 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: PageByte, 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 => exD.SysBug[]; [lastPage, byteFF] _ MakeFileIndexCanonical[lastPage, byteFF]; SegmentDefs.SetEndOfFile[aFH.handle, MapUToAltoPage[lastPage], byteFF ! DiskDefs.UnrecoverableDiskError => {erc _ diskError; CONTINUE}; SegmentDefs.InvalidFP => {erc _ diskCorrupted; CONTINUE}]; 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]; Storage.Free[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 _ Storage.Node[SIZE[VDATable] + 2] + 1; FOR i IN [-1 .. numberOfChunks] 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 Storage.Free[base + (i * chunkSpan) - lowerPad]; ENDLOOP; Storage.Free[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, fH: SegmentDefs.FileHandle] = -- Return immediately iff fH = NIL. Else insert the name/fp into the cache. BEGIN oldFH: SegmentDefs.FileHandle; node: CacheNodePtr; copyOfName: STRING; hasDot: BOOLEAN = (name[name.length - 1] = '.); 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.fp = fH.fp THEN RETURN -- already cached. ELSE exD.SysBug[]; -- Inconsistent data. copyOfName _ Storage.String[name.length + (IF hasDot THEN 0 ELSE 1)]; StringDefs.AppendString[copyOfName, name]; IF ~hasDot THEN StringDefs.AppendChar[copyOfName, '.]; IF fileCacheEntries >= maxFileCacheEntries THEN BEGIN prev: CacheNodePtr; FOR node _ fileCacheHeader, node.next UNTIL node.next = NIL DO prev _ node; ENDLOOP; prev.next _ NIL; Storage.FreeString[node.key]; Storage.Free[node]; END ELSE fileCacheEntries _ fileCacheEntries + 1; node _ Storage.Node[SIZE[CacheNode]]; node^ _ [next: fileCacheHeader, fp: fH.fp, key: copyOfName]; 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. IF fH = NIL THEN exD.SysBug[]; FOR node _ fileCacheHeader, node.next UNTIL node = NIL DO IF node.fp = fH.fp THEN BEGIN IF prev # NIL THEN prev.next _ node.next ELSE fileCacheHeader _ node.next; Storage.FreeString[node.key]; Storage.Free[node]; fileCacheEntries _ fileCacheEntries - 1; RETURN END; prev _ node; ENDLOOP; END; -- of FreeCacheEntry. -- LookupInFileCache: PROCEDURE [name: STRING] RETURNS [fH: SegmentDefs.FileHandle] = -- 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; prev: CacheNodePtr _ NIL; newSsd, oldSsd: StringDefs.SubStringDescriptor; newSsd _ [base: name, offset: 0, length: name.length - (IF name[name.length - 1] = '. THEN 1 ELSE 0)]; FOR node _ fileCacheHeader, node.next UNTIL node = NIL DO oldSsd _ [base: node.key, offset: 0, length: node.key.length - 1]; IF StringDefs.EquivalentSubString[@newSsd, @oldSsd] THEN GOTO gotIt; prev _ node; REPEAT gotIt => BEGIN fH _ SegmentDefs.InsertFile[@node.fp, SegmentDefs.DefaultAccess]; IF prev # NIL THEN BEGIN -- move node to head of file cache for LRU. prev.next _ node.next; node.next _ fileCacheHeader; fileCacheHeader _ node; END; END; FINISHED => fH _ NIL; -- not found. ENDLOOP; END; -- of LookupInFileCache. -- -- Data Structures and Types for FilenameCache: INTERNAL MODULE. fileCacheHeader: CacheNodePtr _ NIL; fileCacheEntries: CARDINAL _ 0; maxFileCacheEntries: CARDINAL = 25; CleanUpFileNameCache: ImageDefs.CleanupProcedure = BEGIN next: CacheNodePtr; IF why ~= Save THEN RETURN; UNTIL fileCacheHeader = NIL DO next _ fileCacheHeader.next; Storage.FreeString[fileCacheHeader.key]; Storage.Free[fileCacheHeader]; fileCacheHeader _ next; ENDLOOP; fileCacheEntries _ 0; END; -- of CleanUpFileNameCache. -- END of FilenameCache: INTERNAL MODULE; cleanup: ImageDefs.CleanupItem _ [link:, mask: ImageDefs.CleanupMask[Save], proc: CleanUpFileNameCache]; ImageDefs.AddCleanupProcedure[@cleanup]; END. -- of CoreSS -- z20461(635)\f1 z20461\f1