-- Copyright (C) 1981, 1982, 1984, 1985 by Xerox Corporation. All rights reserved. -- PilotFileSystem.mesa -- HGM, 16-Sep-85 22:01:34 -- Wobber, 3-Nov-82 11:30:48 -- Gobbel, 25-Sep-81 15:24:50 -- MJohnson, 1-Feb-82 15:03:33 -- Hankins 8-Aug-84 9:52:49 (Klamath update - space rework) -- TO DO: -- - Remove LOOPHOLEs DIRECTORY Environment USING [bytesPerPage], File USING [File], FileDefs USING [ Buffer, ComparePositions, Comparison, Completer, CompleterArg, FileTime, OpenOptions, Operations, OperationsRecord, PageNumber, Position], Heap USING [systemZone], Inline USING [HighHalf, LDIVMOD, LowHalf], MFile USING [ Acquire, DeleteWhenReleased, dontRelease, Error, GetLength, GetTimes, Handle, Release, SetAccess, SetLength, SetTimes], Process USING [Abort, EnableAborts, InitializeCondition, SecondsToTicks], Space USING [CopyIn, CopyOut, Window], SpecialMFile USING [GetCapaWithAccess], String USING [AppendString, EquivalentStrings], VMDefs USING [AccessFailure, CantOpen, Error, PageByteIndex, PageNumber]; PilotFileSystem: MONITOR IMPORTS FileDefs, Heap, Inline, MFile, Process, Space, SpecialMFile, String, VMDefs EXPORTS FileDefs = BEGIN OPEN FileDefs; -- Types -- FSInstance: PUBLIC TYPE = LONG POINTER TO FSObject; FSObject: PUBLIC TYPE = RECORD [ fileList: FileHandle ¬ NIL, ops: Operations ¬ NIL, loginCount: CARDINAL ¬ 0]; FileHandle: PUBLIC TYPE = LONG POINTER TO FileObject; FileObject: PUBLIC TYPE = RECORD [ pilotFile: File.File, mfh: MFile.Handle, link: FileHandle, name: LONG STRING, openCount: [0..77777B] ¬ 1, lengthKnown: BOOLEAN ¬ FALSE, length: Position]; IORequestPtr: TYPE = LONG POINTER TO IORequest; IORequest: TYPE = RECORD [ direction: Direction, file: FileHandle, filePage: PageNumber, buf: Buffer, completer: Completer, arg: CompleterArg, next: IORequestPtr]; qHead, qFree: IORequestPtr; Direction: TYPE = {read, write}; -- Constants -- bytesPerPage: CARDINAL = Environment.bytesPerPage; nRequests: CARDINAL = 5; ioInProgress: CARDINAL ¬ 0; -- Global variables -- ioRequestFreed, ioRequestWaiting: CONDITION; ioProcess: PROCESS; goingAway: BOOLEAN ¬ FALSE; IllegalDestroy: ERROR = CODE; IllegalExtend: ERROR = CODE; InsufficientLogouts: ERROR = CODE; InvalidFS: ERROR = CODE; InvalidFileHandle: ERROR = CODE; TooManyLogouts: ERROR = CODE; pilotFS: FSInstance; -- Procedures -- -- FileDefs.Operations implementing procedures -- PilotLogin: PUBLIC PROCEDURE [ server, userName, password, secondaryName, secondaryPassword: LONG STRING ¬ NIL] RETURNS [FSInstance] = { pilotFS.loginCount ¬ pilotFS.loginCount + 1; RETURN[pilotFS]}; PilotAbort, PilotCheckpoint: PUBLIC PROCEDURE [fs: FSInstance] = { -- unimplemented -- }; PilotLogout: PUBLIC PROCEDURE [fs: FSInstance] = BEGIN ValidateFS[fs]; IF pilotFS.loginCount = 0 THEN ERROR TooManyLogouts; pilotFS.loginCount ¬ pilotFS.loginCount - 1; END; PilotOpen: PUBLIC ENTRY PROCEDURE [ instance: FSInstance, name: LONG STRING, options: OpenOptions ¬ oldReadOnly] RETURNS [file: FileHandle] = BEGIN ENABLE UNWIND => NULL; error: VMDefs.AccessFailure ¬ ok; BEGIN fHandle: MFile.Handle; exists: BOOLEAN ¬ TRUE; ValidateFS[instance]; file ¬ FindFile[name]; IF file = NIL THEN fHandle ¬ MFile.Acquire[ name, anchor, MFile.dontRelease ! MFile.Error => SELECT code FROM noSuchFile => {exists ¬ FALSE; CONTINUE}; ENDCASE => {error ¬ other; GOTO OpenFailed}]; SELECT options FROM new => BEGIN IF exists THEN { MFile.Release[fHandle]; error ¬ alreadyExists; GOTO OpenFailed}; fHandle ¬ MFile.Acquire[ name, readWrite, MFile.dontRelease ! MFile.Error => {error ¬ other; GOTO OpenFailed}]; END; old, oldReadOnly => BEGIN IF file # NIL THEN GOTO AlreadyOpen; IF ~exists THEN {error ¬ notFound; GOTO OpenFailed}; MFile.SetAccess[fHandle, IF options = old THEN readWrite ELSE readOnly]; END; oldOrNew => BEGIN IF file # NIL THEN GOTO AlreadyOpen; IF exists THEN MFile.SetAccess[fHandle, readWrite] ELSE fHandle ¬ MFile.Acquire[ name, readWrite, MFile.dontRelease ! MFile.Error => {error ¬ other; GOTO OpenFailed}]; END; ENDCASE; file ¬ Heap.systemZone.NEW[FileObject]; file.mfh ¬ fHandle; file.name ¬ Heap.systemZone.NEW[StringBody [name.length]]; file.name.length ¬ 0; String.AppendString[file.name, name]; file.pilotFile ¬ SpecialMFile.GetCapaWithAccess[fHandle]; InsertFile[file]; EXITS OpenFailed => ERROR VMDefs.CantOpen[error]; AlreadyOpen => file.openCount ¬ file.openCount + 1; END; -- 'enabled' END; PilotAbandon, PilotClose: PUBLIC ENTRY PROCEDURE [file: FileHandle] = BEGIN ENABLE UNWIND => NULL; ValidateFile[file]; SELECT file.openCount FROM 0 => ERROR VMDefs.Error[other]; 1 => RemoveFile[file]; ENDCASE => file.openCount ¬ file.openCount - 1; END; PilotDestroy: PUBLIC ENTRY PROCEDURE [file: FileHandle] = BEGIN ENABLE UNWIND => NULL; ValidateFile[file]; IF file.openCount # 1 THEN ERROR IllegalDestroy; MFile.DeleteWhenReleased[file.mfh]; RemoveFile[file, TRUE]; END; PilotGetLength: PUBLIC ENTRY PROCEDURE [file: FileHandle] RETURNS [pos: Position] = BEGIN ENABLE UNWIND => NULL; ValidateFile[file]; IF file.lengthKnown THEN RETURN[file.length]; file.length ¬ MakePosition[MFile.GetLength[file.mfh]]; file.lengthKnown ¬ TRUE; RETURN[file.length]; END; PilotSetLength, PilotTruncate: PUBLIC ENTRY PROCEDURE [ file: FileHandle, pos: Position] = BEGIN ENABLE UNWIND => NULL; ValidateFile[file]; MFile.SetLength[file.mfh, LONG[pos.page] * bytesPerPage + pos.byte]; file.length ¬ pos; file.lengthKnown ¬ TRUE; END; PilotExtend: PUBLIC ENTRY PROCEDURE [ file: FileHandle, pos: Position, buf: Buffer] = BEGIN OPEN Space; ENABLE UNWIND => NULL; ValidateFile[file]; IF ~file.lengthKnown THEN { file.length ¬ MakePosition[MFile.GetLength[file.mfh]]; file.lengthKnown ¬ TRUE}; IF ComparePositions[pos, file.length] # greater OR ~(pos.page = file.length.page OR (pos.page = file.length.page + 1 AND pos.byte = 0)) THEN ERROR IllegalExtend; MFile.SetLength[file.mfh, LONG[pos.page] * bytesPerPage + pos.byte]; -- worry about out of space errors? [] ¬ Space.CopyOut[pointer: buf, window: [file.pilotFile, pos.page, 1]]; -- why does NSFileSystem do Inline.LongCOPY of buf to a space then CopyOut that space?? file.length ¬ pos; END; PilotStartRead: PUBLIC ENTRY PROCEDURE [ file: FileHandle, page: PageNumber, buf: Buffer, completer: Completer, arg: CompleterArg] = BEGIN ENABLE UNWIND => NULL; ValidateFile[file]; RequestTransfer[read, file, page, buf, completer, arg]; END; PilotStartWrite: PUBLIC ENTRY PROCEDURE [ file: FileHandle, page: PageNumber, buf: Buffer, completer: Completer, arg: CompleterArg] = BEGIN ENABLE UNWIND => NULL; ValidateFile[file]; RequestTransfer[write, file, page, buf, completer, arg]; END; PilotGetTimes: PUBLIC ENTRY PROCEDURE [file: FileHandle] RETURNS [read, write, create: FileTime] = BEGIN ENABLE UNWIND => NULL; ValidateFile[file]; [read: read, write: write, create: create] ¬ MFile.GetTimes[file.mfh]; END; PilotSetCreationTime: PUBLIC ENTRY PROCEDURE [ file: FileHandle, create: FileTime] = BEGIN ENABLE UNWIND => NULL; ValidateFile[file]; MFile.SetTimes[file: file.mfh, create: LOOPHOLE[create]]; END; -- Private procedures -- FindFile: INTERNAL PROCEDURE [name: LONG STRING] RETURNS [file: FileHandle] = BEGIN FOR file ¬ pilotFS.fileList, file.link UNTIL file = NIL DO IF String.EquivalentStrings[name, file.name] THEN RETURN ENDLOOP; END; InsertFile: INTERNAL PROCEDURE [file: FileHandle] = BEGIN file.link ¬ pilotFS.fileList; pilotFS.fileList ¬ file; END; MakePosition: INTERNAL PROCEDURE [byteLength: LONG CARDINAL] RETURNS [pos: Position] = { OPEN Inline; [quotient: pos.page, remainder: pos.byte] ¬ LDIVMOD[ numlow: LowHalf[byteLength], numhigh: HighHalf[byteLength], den: bytesPerPage]}; RemoveFile: INTERNAL PROCEDURE [ file: FileHandle, alreadyReleased: BOOLEAN ¬ FALSE] = BEGIN prev: LONG POINTER TO FileHandle ¬ @pilotFS.fileList; FOR cur: FileHandle ¬ pilotFS.fileList, cur.link UNTIL cur = NIL DO IF cur = file THEN {prev­ ¬ file.link; EXIT}; prev ¬ @cur.link; REPEAT FINISHED => ERROR InvalidFileHandle; ENDLOOP; IF ~alreadyReleased THEN MFile.Release[file.mfh]; Heap.systemZone.FREE[@file]; END; ValidateFile: INTERNAL PROCEDURE [file: FileHandle] = BEGIN FOR f: FileHandle ¬ pilotFS.fileList, f.link UNTIL f = NIL DO IF f = file THEN RETURN ENDLOOP; ERROR InvalidFileHandle; END; -- Asynchronous I/O facility PageIOProcess: PROCEDURE = BEGIN GetIORequest: ENTRY PROCEDURE = BEGIN ENABLE UNWIND => NULL; WHILE QueueEmpty[] DO WAIT ioRequestWaiting ENDLOOP; req ¬ qHead­; FreeIORequest[]; ioInProgress ¬ ioInProgress + 1; END; IOCompleted: ENTRY PROCEDURE = INLINE { ENABLE UNWIND => NULL; ioInProgress ¬ ioInProgress - 1; NOTIFY ioRequestFreed}; req: IORequest; w: Space.Window; UNTIL goingAway DO GetIORequest[ ! ABORTED => LOOP]; w ¬ [ file: req.file.pilotFile, base: req.filePage + 1, -- incr to skip over leader page count: 1]; SELECT req.direction FROM read => [] ¬ Space.CopyIn[pointer: req.buf, window: w]; write => [] ¬ Space.CopyOut[pointer: req.buf, window: w]; ENDCASE => ERROR; IOCompleted[]; req.completer[req.arg, ok]; ENDLOOP; END; -- proc. PageIOProcess RequestTransfer: INTERNAL PROCEDURE [ direction: Direction, file: FileHandle, page: PageNumber, buf: Buffer, completer: Completer, arg: CompleterArg] = BEGIN req: IORequestPtr; WHILE QueueFull[] DO WAIT ioRequestFreed ENDLOOP; req ¬ AllocateIORequest[]; req­ ¬ [ direction: direction, file: file, filePage: page, buf: buf, completer: completer, arg: arg, next: req.next]; NOTIFY ioRequestWaiting; END; ValidateFS: PROCEDURE [fs: FSInstance] = {IF fs # pilotFS THEN ERROR InvalidFS}; -- IORequest management AllocateIORequest: INTERNAL PROC RETURNS [req: IORequestPtr] = INLINE { req ¬ qFree; IF (qFree ¬ qFree.next) = qHead THEN qFree ¬ NIL}; FreeIORequest: INTERNAL PROC = INLINE BEGIN IF QueueFull[] THEN qFree ¬ qHead; qHead ¬ qHead.next; NOTIFY ioRequestFreed; END; QueueFull: INTERNAL PROC RETURNS [BOOLEAN] = INLINE {RETURN[qFree = NIL]}; QueueEmpty: INTERNAL PROC RETURNS [BOOLEAN] = INLINE {RETURN[qHead = qFree]}; -- Initialize and finalize -- InitializeLocal: PUBLIC PROCEDURE RETURNS [ops: FileDefs.Operations] = BEGIN req, reqFirst: IORequestPtr; req ¬ reqFirst ¬ Heap.systemZone.NEW[IORequest]; THROUGH (0..nRequests) DO req ¬ req.next ¬ Heap.systemZone.NEW[IORequest] ENDLOOP; req.next ¬ reqFirst; -- close the queue (ring) of requests qHead ¬ qFree ¬ req; -- initial state = queue empty ioProcess ¬ FORK PageIOProcess[]; pilotFS ¬ Heap.systemZone.NEW[FSObject]; pilotFS.ops ¬ Heap.systemZone.NEW[ FileDefs.OperationsRecord ¬ [ login: LOOPHOLE[PilotLogin], logout: LOOPHOLE[PilotLogout], checkpoint: LOOPHOLE[PilotCheckpoint], abort: LOOPHOLE[PilotAbort], open: LOOPHOLE[PilotOpen], close: LOOPHOLE[PilotClose], abandon: LOOPHOLE[PilotAbandon], destroy: LOOPHOLE[PilotDestroy], getLength: LOOPHOLE[PilotGetLength], setLength: LOOPHOLE[PilotSetLength], extend: LOOPHOLE[PilotExtend], truncate: LOOPHOLE[PilotTruncate], startRead: LOOPHOLE[PilotStartRead], startWrite: LOOPHOLE[PilotStartWrite], getTimes: LOOPHOLE[PilotGetTimes], setCreation: LOOPHOLE[PilotSetCreationTime]]]; RETURN[pilotFS.ops]; END; FinalizeLocal: PUBLIC PROCEDURE = BEGIN IF pilotFS.loginCount # 0 THEN ERROR InsufficientLogouts; goingAway ¬ TRUE; Process.Abort[ioProcess]; JOIN ioProcess; Heap.systemZone.FREE[@pilotFS.ops]; Heap.systemZone.FREE[@pilotFS]; END; Process.InitializeCondition[@ioRequestWaiting, Process.SecondsToTicks[600]]; Process.InitializeCondition[@ioRequestFreed, Process.SecondsToTicks[600]]; Process.EnableAborts[@ioRequestWaiting]; END...