-- file CoreImpl.Mesa -- Last edited by Brotz, June 1, 1982 12:47 PM. DIRECTORY AltoFile USING [FP, IllegalFileName, MapFileNameToFP, NoSuchFile], AltoFileDefs USING [FP, NullFP], Core USING [CacheEntry, CacheEntryBlk, CacheHeader, CacheHeaderBlk, DMSUser, LocalCacheEntry, OpenMode], exD: FROM "ExceptionDefs" USING [SysBug], intCommon USING [leafOk, remoteFilePath, retrieveHandle], NameInfoDefs USING [IsMemberClosure, Membership], NameInfoSpecialDefs USING [CleanUp], RetrieveDefs USING [MailboxState], Storage USING [CopyString, Free, FreeString, Node, String], String USING [AppendChar, AppendString, AppendSubString, EquivalentString, EquivalentSubString, SubStringDescriptor], VMDefs USING [CantOpen, CloseFile, DestroyFile, Error, FileHandle, FileSystem, FSAlto, GetFileSystem, Login, Logout, OpenFile, StartFile, UnableToLogin], VMSpecial USING [OpenAltoFileFromFP]; CoreImpl: MONITOR IMPORTS AltoFile, exD, intC: intCommon, NameInfoDefs, NameInfoSpecialDefs, RetrieveDefs, Storage, String, VMDefs, VMSpecial EXPORTS Core = BEGIN OPEN Core; -- Implementation of Core. See Core.mesa for details. -- MONITOR protects the file name cache. (We don't want someone setting the open bit of -- a cache entry while someone else is opening that file.) -- **** Global Variables **** -- localCacheHead: PUBLIC CacheHeader; remoteCacheHead: CacheHeader _ NIL; currentLoginEpoch: CARDINAL _ 0; localCacheSize: CARDINAL _ 0; localCacheLimit: CARDINAL = 25; currentName: STRING _ NIL; currentSimpleName: STRING _ NIL; currentPassword: STRING _ NIL; leafOk: CARDINAL _ 0; Login: PUBLIC ENTRY PROCEDURE [user: DMSUser] = -- Notifies the Core of the new name, registry and password to be used on subsequent -- Open's. BEGIN currentLoginEpoch _ currentLoginEpoch + 1; IF currentName # NIL THEN Storage.FreeString[currentName]; IF currentSimpleName # NIL THEN Storage.FreeString[currentSimpleName]; IF currentPassword # NIL THEN Storage.FreeString[currentPassword]; currentName _ Storage.String[user.name.length + 1 + user.registry.length]; String.AppendString[currentName, user.name]; String.AppendChar[currentName, '.]; String.AppendString[currentName, user.registry]; currentSimpleName _ Storage.CopyString[user.name]; currentPassword _ Storage.CopyString[user.password]; END; -- of Login -- Open: PUBLIC ENTRY PROCEDURE [filename: STRING, mode: OpenMode] RETURNS [handle: VMDefs.FileHandle] = -- Opens "filename" in "mode", and returns a handle to the opened file. -- ERRORS: -- VMDefs.CantOpen (notFound, alreadyExists, accessDenied, illegalFileName) -- VMDefs.Error (io, resources, credentials) [io = unrecoverable disk error or communications -- failure for remote files, resources = disk full or directory limit on remote server, -- credentials = credentials presented at Login were not sufficient to permit this Open]. BEGIN ENABLE UNWIND => NULL; IF filename = NIL OR filename.length = 0 THEN ERROR VMDefs.CantOpen[illegalFileName]; handle _ IF filename[0] = '[ THEN OpenRemoteFile[filename, mode] ELSE OpenLocalFile[filename, mode]; END; -- of Open -- OpenLocalFile: INTERNAL PROCEDURE [name: STRING, mode: OpenMode] RETURNS [handle: VMDefs.FileHandle] = BEGIN entry: LocalCacheEntry _ LookupInLocalCache[name, NIL]; fp: AltoFileDefs.FP; IF entry = NIL THEN BEGIN e: CacheEntry; fp _ AltoFile.MapFileNameToFP[name, IF mode = read THEN old ELSE oldOrNew ! AltoFile.NoSuchFile => VMDefs.CantOpen[notFound]; AltoFile.IllegalFileName => VMDefs.CantOpen[illegalFileName]]; e _ InsertInCache[localCacheHead, name, fp, FALSE]; WITH le: e SELECT FROM local => entry _ @le; ENDCASE => exD.SysBug[]; END ELSE {fp _ entry.fp; IF entry.open THEN ERROR VMDefs.CantOpen[alreadyExists]}; handle _ VMSpecial.OpenAltoFileFromFP[fp: fp, writable: mode = update, cacheFraction: 0]; entry.handle _ handle; entry.open _ TRUE; END; -- of OpenLocalFile -- OpenRemoteFile: INTERNAL PROCEDURE [name: STRING, mode: OpenMode] RETURNS [handle: VMDefs.FileHandle] = BEGIN madeTemp, newFileSystem: BOOLEAN _ FALSE; fileSSD, serverSSD: String.SubStringDescriptor; i: CARDINAL; server: STRING _ [50]; file: STRING _ [99]; head: CacheHeader _ NIL; fileSystem: VMDefs.FileSystem; entry: CacheEntry; Cleanup: INTERNAL PROCEDURE = BEGIN IF madeTemp THEN {Storage.FreeString[name]; madeTemp _ FALSE}; IF head # NIL AND head.firstEntry = NIL THEN RemoveCacheHead[head]; IF newFileSystem THEN VMDefs.Logout[fileSystem]; END; -- of Cleanup -- IF ~intC.leafOk THEN ERROR VMDefs.Error[credentials]; IF name.length < 2 THEN ERROR VMDefs.CantOpen[illegalFileName]; IF leafOk # 12795 THEN BEGIN mbr: NameInfoDefs.Membership; SELECT RetrieveDefs.MailboxState[intC.retrieveHandle] FROM badName, badPwd => ERROR VMDefs.Error[credentials]; ENDCASE; mbr _ NameInfoDefs.IsMemberClosure["CoreLeafUsers.ms"L, currentName]; NameInfoSpecialDefs.CleanUp[]; SELECT mbr FROM yes, allDown => leafOk _ 12795; ENDCASE => ERROR VMDefs.Error[credentials]; END; IF name[1] = '] THEN BEGIN -- Handle name of form []Filename, replacing the [] with intC.remoteFilePrefix. temp: STRING; dollarSignCount: CARDINAL _ 0; remoteFilePath: STRING = intC.remoteFilePath; IF remoteFilePath = NIL THEN ERROR VMDefs.CantOpen[illegalFileName]; FOR i: CARDINAL IN [0 .. remoteFilePath.length) DO IF remoteFilePath[i] = '$ THEN dollarSignCount _ dollarSignCount + 1; ENDLOOP; temp _ Storage.String[name.length - 2 + remoteFilePath.length + dollarSignCount * (currentSimpleName.length - 1)]; IF dollarSignCount > 0 THEN FOR i: CARDINAL IN [0 .. remoteFilePath.length) DO IF remoteFilePath[i] = '$ THEN String.AppendString[temp, currentSimpleName] ELSE String.AppendChar[temp, remoteFilePath[i]]; ENDLOOP ELSE String.AppendString[temp, remoteFilePath]; fileSSD _ String.SubStringDescriptor[name, 2, name.length - 2]; String.AppendSubString[temp, @fileSSD]; name _ temp; madeTemp _ TRUE; END; -- Parse name into server and file. FOR i IN [1 .. name.length) DO IF name[i] = '] THEN EXIT; REPEAT FINISHED => ERROR VMDefs.CantOpen[illegalFileName]; ENDLOOP; serverSSD _ String.SubStringDescriptor[name, 1, i - 1]; String.AppendSubString[server, @serverSSD]; fileSSD _ String.SubStringDescriptor[name, i + 1, name.length - i - 1]; String.AppendSubString[file, @fileSSD]; head _ GetRemoteCacheHead[server]; IF SearchCache[head, file, NIL] # NIL THEN {Cleanup[]; ERROR VMDefs.CantOpen[alreadyExists]}; [fileSystem, newFileSystem] _ GetCurrentFileSystem[head]; IF fileSystem = NIL THEN {Cleanup[]; ERROR VMDefs.CantOpen[io]}; handle _ VMDefs.OpenFile [fileSystem, file, IF mode = update THEN oldOrNew ELSE oldReadOnly ! VMDefs.CantOpen => Cleanup[]]; entry _ InsertInCache[head, name, AltoFileDefs.NullFP]; entry.handle _ handle; IF madeTemp THEN Storage.FreeString[name]; END; -- of OpenRemoteFile -- Close: PUBLIC ENTRY PROCEDURE [handle: VMDefs.FileHandle] = -- Closes the file associated with "handle". "handle" must not be re-used by the caller. BEGIN ENABLE UNWIND => NULL; fileSystem: VMDefs.FileSystem _ VMDefs.GetFileSystem[handle]; VMDefs.StartFile[handle]; IF fileSystem = VMDefs.FSAlto THEN BEGIN entry: LocalCacheEntry _ LookupInLocalCache[NIL, handle]; VMDefs.CloseFile[handle]; entry.open _ FALSE; entry.handle _ NIL; END ELSE BEGIN entry: CacheEntry; head: CacheHeader; [head, entry] _ LookupRemoteFile[handle]; VMDefs.CloseFile[handle]; head _ RemoveRemoteCacheEntry[head, entry]; IF ~FileSystemExists[head, fileSystem] THEN VMDefs.Logout[fileSystem]; END; END; -- of Close -- Delete: PUBLIC ENTRY PROCEDURE [handle: VMDefs.FileHandle] = -- Deletes the file associated with "handle". "handle" must not be re-used by the caller. BEGIN ENABLE UNWIND => NULL; fileSystem: VMDefs.FileSystem _ VMDefs.GetFileSystem[handle]; VMDefs.StartFile[handle]; IF fileSystem = VMDefs.FSAlto THEN BEGIN entry: LocalCacheEntry _ LookupInLocalCache[NIL, handle]; VMDefs.DestroyFile[handle]; RemoveEntryFromCache[localCacheHead, entry]; END ELSE BEGIN entry: CacheEntry; head: CacheHeader; [head, entry] _ LookupRemoteFile[handle]; VMDefs.DestroyFile[handle]; head _ RemoveRemoteCacheEntry[head, entry]; IF ~FileSystemExists[head, fileSystem] THEN VMDefs.Logout[fileSystem]; END; END; -- of Delete -- InsertInFileCache: PUBLIC ENTRY PROCEDURE [name: STRING, fp: AltoFileDefs.FP, open: BOOLEAN _ TRUE] RETURNS [entry: LocalCacheEntry] = -- Inserts "name" and "fp" in the Alto file name cache. -- To protect a file from being opened, "open" should be TRUE. In this case, subsequent -- Open's on this file will fail with VMDefs.Error[alreadyExists]. -- VMDefs.Error[alreadyExists]. -- Possible uses of this procedure are to provide faster Open's and to protect critical files as -- described above. BEGIN e: CacheEntry _ InsertInCache[localCacheHead, name, fp, open]; WITH le: e SELECT FROM local => RETURN[@le]; ENDCASE => exD.SysBug[]; END; -- of InsertInFileCache -- InsertInCache: INTERNAL PROCEDURE [head: CacheHeader, name: STRING, fp: AltoFileDefs.FP, open: BOOLEAN _ TRUE] RETURNS [entry: CacheEntry] = -- Inserts "name" and "fp" in the Alto file name cache. -- To protect a file from being opened, "open" should be TRUE. In this case, subsequent -- Open's on this file will fail with VMDefs.Error[alreadyExists]. -- Possible uses of this procedure are to provide faster Open's and to protect critical files as -- described above. BEGIN hasDot: BOOLEAN = name[name.length - 1] = '.; nameCopy: STRING _ Storage.String[name.length + (IF hasDot THEN 0 ELSE 1)]; IF head = localCacheHead THEN BEGIN entry _ GetReusableLocalCacheEntry[]; IF entry # NIL THEN BEGIN UnlinkEntryFromCache[head, entry]; IF entry.name # NIL THEN Storage.FreeString[entry.name]; END ELSE {entry _ Storage.Node[SIZE[local CacheEntryBlk]]; localCacheSize _ localCacheSize + 1}; END ELSE entry _ Storage.Node[SIZE[remote CacheEntryBlk]]; String.AppendString[nameCopy, name]; IF ~hasDot THEN String.AppendChar[nameCopy, '.]; IF head = localCacheHead THEN entry^ _ CacheEntryBlk [next: NIL, prev: NIL, name: NIL, handle: NIL, open: open, vp: local[fp: fp]] ELSE entry^ _ CacheEntryBlk[next: NIL, prev: NIL, name: NIL, handle: NIL, open: TRUE, vp: remote[loginEpoch: currentLoginEpoch]]; entry.name _ nameCopy; -- Link entry in at the beginning of the cache list. InsertEntryAtHeadOfCache[head, entry]; END; -- of InsertInCache -- GetRemoteCacheHead: PROCEDURE [server: STRING] RETURNS [head: CacheHeader] = BEGIN FOR head _ remoteCacheHead, head.next UNTIL head = NIL DO IF String.EquivalentString[server, head.server] THEN RETURN; ENDLOOP; head _ Storage.Node[SIZE[CacheHeaderBlk]]; head^ _ CacheHeaderBlk[server: Storage.CopyString[server], firstEntry: NIL, lastEntry: NIL, next: remoteCacheHead, prev: NIL]; IF remoteCacheHead # NIL THEN remoteCacheHead.prev _ head; remoteCacheHead _ head; END; -- of GetRemoteCacheHead -- UnlinkEntryFromCache: INTERNAL PROCEDURE [head: CacheHeader, entry: CacheEntry] = BEGIN IF entry.next = NIL THEN head.lastEntry _ entry.prev ELSE entry.next.prev _ entry.prev; IF entry.prev = NIL THEN head.firstEntry _ entry.next ELSE entry.prev.next _ entry.next; END; -- of UnlinkEntryFromCache -- InsertEntryAtHeadOfCache: INTERNAL PROC [head: CacheHeader, entry: CacheEntry] = BEGIN IF head.firstEntry # NIL THEN head.firstEntry.prev _ entry ELSE head.lastEntry _ entry; entry.next _ head.firstEntry; entry.prev _ NIL; head.firstEntry _ entry; END; -- of InsertEntryAtHeadOfCache -- RemoveEntryFromCache: INTERNAL PROCEDURE [head: CacheHeader, entry: CacheEntry] = BEGIN UnlinkEntryFromCache[head, entry]; IF entry.name # NIL THEN Storage.FreeString[entry.name]; Storage.Free[entry]; IF head = localCacheHead THEN localCacheSize _ localCacheSize - 1; END; -- of RemoveEntryFromCache -- GetReusableLocalCacheEntry: INTERNAL PROCEDURE RETURNS [entry: LocalCacheEntry] = BEGIN e: CacheEntry; IF localCacheSize < localCacheLimit THEN RETURN[NIL]; FOR e _ localCacheHead.lastEntry, e.prev UNTIL e = NIL DO IF ~e.open THEN EXIT; REPEAT FINISHED => RETURN[NIL]; ENDLOOP; WITH le: e SELECT FROM local => RETURN[@le]; ENDCASE => exD.SysBug[]; END; -- of GetReusableLocalCacheEntry -- LookupInFileCache: PUBLIC ENTRY PROCEDURE [name: STRING] RETURNS [fp: AltoFileDefs.FP] = -- Returns the FP associated with the name (or NullFP if no such file). BEGIN entry: LocalCacheEntry; IF name = NIL THEN RETURN[AltoFileDefs.NullFP]; entry _ LookupInLocalCache[name, NIL]; RETURN[IF entry = NIL THEN AltoFileDefs.NullFP ELSE entry.fp]; END; -- of LookupInFileCache -- LookupInLocalCache: INTERNAL PROCEDURE [name: STRING, handle: VMDefs.FileHandle] RETURNS [entry: LocalCacheEntry] = -- Returns the CacheEntry associated with the name (or NIL if no such file). BEGIN e: CacheEntry _ SearchCache[localCacheHead, name, handle]; IF e = NIL THEN RETURN[NIL]; UnlinkEntryFromCache[localCacheHead, e]; InsertEntryAtHeadOfCache[localCacheHead, e]; WITH le: e SELECT FROM local => RETURN[@le]; ENDCASE => exD.SysBug[]; END; -- of LookupInLocalCache -- SearchCache: INTERNAL PROCEDURE [head: CacheHeader, name: STRING, handle: VMDefs.FileHandle] RETURNS [entry: CacheEntry] = -- Searches one cache list, the one corresponding to the particular server for the file "name". -- If "name" is NIL, then searches the list for handle. -- Returns the CacheEntry associated with the name (or NIL if no such file). BEGIN nameSSD, cacheSSD: String.SubStringDescriptor; IF head = NIL THEN RETURN[NIL]; IF name # NIL THEN BEGIN IF name.length = 0 THEN RETURN[NIL]; nameSSD _ [base: name, offset: 0, length: name.length - (IF name[name.length - 1] = '. THEN 1 ELSE 0)]; END; FOR entry _ head.firstEntry, entry.next UNTIL entry = NIL DO IF name = NIL THEN {IF entry.handle = handle THEN EXIT} ELSE BEGIN cacheSSD _ [base: entry.name, offset: 0, length: entry.name.length - 1]; IF String.EquivalentSubString[@nameSSD, @cacheSSD] THEN EXIT; END; ENDLOOP; END; -- of SearchCache -- GetCurrentFileSystem: INTERNAL PROCEDURE [head: CacheHeader] RETURNS [fileSystem: VMDefs.FileSystem, new: BOOLEAN] = BEGIN name: STRING; IF head = NIL THEN RETURN[NIL, FALSE]; FOR entry: CacheEntry _ head.firstEntry, entry.next UNTIL entry = NIL DO WITH re: entry SELECT FROM remote => IF re.loginEpoch = currentLoginEpoch THEN RETURN[VMDefs.GetFileSystem[re.handle], FALSE]; ENDCASE => exD.SysBug[]; ENDLOOP; fileSystem _ NIL; new _ TRUE; name _ IF String.EquivalentString[head.server, "Cherry"L] THEN currentSimpleName ELSE currentName; fileSystem _ VMDefs.Login[IFS, head.server, name, currentPassword ! VMDefs.UnableToLogin => {new _ FALSE; CONTINUE}]; END; -- of GetCurrentFileSystem -- FreeCacheEntry: PUBLIC ENTRY PROCEDURE [name: STRING] = -- Removes the cache entry for "name" from the local file cache (if it existed). -- May call SysBug if that file is marked open. BEGIN entry: CacheEntry = LookupInLocalCache[name, NIL]; IF entry = NIL THEN RETURN; IF entry.open THEN exD.SysBug[]; RemoveEntryFromCache[localCacheHead, entry]; END; -- of FreeCacheEntry -- LookupRemoteFile: INTERNAL PROCEDURE [handle: VMDefs.FileHandle] RETURNS [head: CacheHeader, entry: CacheEntry] = BEGIN entry _ NIL; FOR head _ remoteCacheHead, head.next UNTIL head = NIL DO FOR entry _ head.firstEntry, entry.next UNTIL entry = NIL DO IF entry.handle = handle THEN RETURN; ENDLOOP; ENDLOOP; END; -- of LookupRemoteFile -- FileSystemExists: INTERNAL PROCEDURE [head: CacheHeader, fileSystem: VMDefs.FileSystem] RETURNS [exists: BOOLEAN] = BEGIN IF head = NIL THEN RETURN[FALSE]; FOR entry: CacheEntry _ head.firstEntry, entry.next UNTIL entry = NIL DO IF VMDefs.GetFileSystem[entry.handle] = fileSystem THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; END; -- of FileSystemExists -- RemoveRemoteCacheEntry: INTERNAL PROCEDURE [head: CacheHeader, entry: CacheEntry] RETURNS [newHead: CacheHeader] = BEGIN RemoveEntryFromCache[head, entry]; IF head.firstEntry = NIL THEN {RemoveCacheHead[head]; newHead _ NIL} ELSE newHead _ head; END; -- of RemoveRemoteCacheEntry -- RemoveCacheHead: INTERNAL PROCEDURE [head: CacheHeader] = BEGIN IF head.next # NIL THEN head.next.prev _ head.prev; IF head.prev # NIL THEN head.prev.next _ head.next ELSE remoteCacheHead _ head.next; END; -- of RemoveCacheHead -- -- Start code -- localCacheHead _ Storage.Node[SIZE[CacheHeaderBlk]]; localCacheHead^ _ CacheHeaderBlk [server: NIL, firstEntry: NIL, lastEntry: NIL, next: NIL, prev: NIL]; END. -- of CoreImpl --z20461(635)\f1