-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved.
-- NetDirFileTajo.mesa, HGM, 9-May-86 16:16:29
DIRECTORY
Ascii USING [CR],
Checksum USING [ComputeChecksum],
Environment USING [bytesPerPage],
Inline USING [LongCOPY, LowHalf],
MFile USING [
AcquireTemp, AddNotifyProc, CopyFileHandle, Error, Filter, GetLength, Handle,
ReadOnly, Release, ReleaseChoice, RemoveNotifyProc, Rename, SetAccess,
SwapNames],
MSegment USING [Address, Create, Delete, Handle],
MStream USING [Create],
Put USING [Text],
Stream USING [Handle],
String USING [
AppendChar, AppendDecimal, AppendString, Equivalent, WordsForString],
System USING [Pulses, GetClockPulses, PulsesToMicroseconds],
Time USING [AppendCurrent],
NameServerDefs USING [CacheEntry, FlushWholeCache, KickProber, msg],
NetDirDefs,
PupTypes USING [PupAddress],
ServerHeap USING [CopyString, Node],
Stats USING [StatCounterIndex, StatBump];
NetDirFileTajo: MONITOR
IMPORTS
Checksum, Inline, MFile, MSegment, MStream, Put, String, System,
Time, Stats, NameServerDefs, ServerHeap
EXPORTS NameServerDefs =
BEGIN OPEN NetDirDefs;
PupAddress: TYPE = PupTypes.PupAddress;
CacheEntry: TYPE = NameServerDefs.CacheEntry;
verbose: BOOLEAN = TRUE;
useCount: CARDINAL ← 0;
newVersion, oldVersion: CARDINAL ← 0; -- 0 if none/unknown
newInTransit, oldInTransit: BOOLEAN ← FALSE;
oldFile, newFile: MFile.Handle ← NIL;
seg: MSegment.Handle ← NIL;
header: LONG POINTER TO NetDirDefs.NewHeader;
first: LONG POINTER TO NetDirDefs.NewEntry;
Entry: TYPE = LONG POINTER TO NetDirDefs.NewEntry;
statMsScanningFile: PUBLIC Stats.StatCounterIndex;
oldFileName: STRING = "Pup-network.directory";
newFileName: STRING = "Pup-network.big";
CruftyNetworkDirectoryFile: ERROR = CODE;
GetStreamForOldDirectoryFile: PUBLIC ENTRY PROCEDURE RETURNS [Stream.Handle] =
BEGIN
IF oldFile = NIL THEN RETURN[NIL];
RETURN[MStream.Create[MFile.CopyFileHandle[oldFile, []], []]];
END;
GetStreamForNewDirectoryFile: PUBLIC ENTRY PROCEDURE RETURNS [Stream.Handle] =
BEGIN
IF newFile = NIL THEN RETURN[NIL];
RETURN[MStream.Create[MFile.CopyFileHandle[newFile, []], []]];
END;
CreateTempFile: PUBLIC PROCEDURE RETURNS [fh: MFile.Handle, sh: Stream.Handle] =
BEGIN
fh ← MFile.AcquireTemp[binary, 256*LONG[Environment.bytesPerPage]];
sh ← MStream.Create[MFile.CopyFileHandle[fh, [], writeOnly], []];
END;
MakeTempFileIntoOldDirectoryFile: PUBLIC PROCEDURE [fh: MFile.Handle]
RETURNS [ok: BOOLEAN] =
BEGIN
IF ~BlessOldDirectoryFile[fh, TRUE] THEN { MFile.Release[fh]; RETURN[FALSE]; };
ok ← TRUE;
MFile.SetAccess[fh, rename];
IF oldFile = NIL THEN BEGIN MFile.Rename[fh, oldFileName]; END
ELSE
BEGIN
old: MFile.Handle ← MFile.CopyFileHandle[oldFile, []];
MFile.SetAccess[
old, rename ! MFile.Error => BEGIN ok ← FALSE; CONTINUE; END];
IF ok THEN MFile.SwapNames[fh, old];
MFile.Release[old];
END;
MFile.Release[fh];
END;
MakeTempFileIntoNewDirectoryFile: PUBLIC PROCEDURE [fh: MFile.Handle]
RETURNS [ok: BOOLEAN] =
BEGIN
IF ~BlessNewDirectoryFile[fh, TRUE] THEN { MFile.Release[fh]; RETURN[FALSE]; };
ok ← TRUE;
MFile.SetAccess[fh, rename];
IF newFile = NIL THEN BEGIN MFile.Rename[fh, newFileName]; END
ELSE
BEGIN
old: MFile.Handle ← MFile.CopyFileHandle[newFile, []];
MFile.SetAccess[
old, rename ! MFile.Error => BEGIN ok ← FALSE; CONTINUE; END];
IF ok THEN MFile.SwapNames[fh, old];
MFile.Release[old];
END;
MFile.Release[fh];
END;
DeleteTempFile: PUBLIC PROCEDURE [fh: MFile.Handle] =
BEGIN MFile.Release[fh]; END;
SearchNetDirForName: PUBLIC ENTRY PROCEDURE [key: LONG STRING, ce: CacheEntry]
RETURNS [BOOLEAN] =
BEGIN
entry: LONG POINTER TO NetDirDefs.NewEntry ← first;
pulses: System.Pulses ← System.GetClockPulses[];
IF seg = NIL THEN RETURN[FALSE];
FOR i: CARDINAL IN [0..header.numberOfEntries) DO
name: LONG STRING ← FirstName[entry];
FOR j: CARDINAL IN [0..entry.numberOfNames) DO
IF String.Equivalent[key, name] THEN
BEGIN CopyThingsFromFile[ce, entry]; GOTO Foundit; END;
name ← NextName[name];
ENDLOOP;
entry ← entry + entry.words;
REPEAT
Foundit => NULL;
FINISHED => CopyMissedName[ce, key];
ENDLOOP;
pulses ← System.Pulses[System.GetClockPulses[] - pulses];
Stats.StatBump[ -- This might overflow 60 serconds
statMsScanningFile, Inline.LowHalf[
System.PulsesToMicroseconds[pulses]/1000]];
RETURN[TRUE];
END;
SearchNetDirForAddress: PUBLIC ENTRY PROCEDURE [key: PupAddress, ce: CacheEntry]
RETURNS [BOOLEAN] =
BEGIN
entry: LONG POINTER TO NetDirDefs.NewEntry ← first;
pulses: System.Pulses ← System.GetClockPulses[];
IF seg = NIL THEN RETURN[FALSE];
FOR i: CARDINAL IN [0..header.numberOfEntries) DO
aaddress: LONG POINTER TO PupAddress ← Address[entry];
FOR j: CARDINAL IN [0..entry.numberOfAddresses) DO
IF key = aaddress↑ THEN
BEGIN CopyThingsFromFile[ce, entry]; GOTO Foundit; END;
aaddress ← aaddress + SIZE[PupAddress];
ENDLOOP;
entry ← entry + entry.words;
REPEAT
Foundit => NULL;
FINISHED => CopyMissedAddress[ce, key];
ENDLOOP;
pulses ← System.Pulses[System.GetClockPulses[] - pulses];
Stats.StatBump[ -- This might overflow 60 serconds
statMsScanningFile, Inline.LowHalf[
System.PulsesToMicroseconds[pulses]/1000]];
RETURN[TRUE];
END;
CopyThingsFromFile: PROCEDURE [ce: CacheEntry, entry: Entry] =
BEGIN
name: LONG STRING;
words: CARDINAL;
words ← SIZE[PupAddress, entry.numberOfAddresses];
ce.size ← ce.size + words;
ce.addrs ← DESCRIPTOR[ServerHeap.Node[words], entry.numberOfAddresses];
Inline.LongCOPY[to: BASE[ce.addrs], from: Address[entry], nwords: words];
words ← SIZE[LONG STRING, entry.numberOfNames];
ce.size ← ce.size + words;
ce.names ← DESCRIPTOR[ServerHeap.Node[words], entry.numberOfNames];
name ← FirstName[entry];
FOR i: CARDINAL IN [0..entry.numberOfNames) DO
ce.size ← ce.size + String.WordsForString[name.length];
ce.names[i] ← ServerHeap.CopyString[name];
name ← NextName[name];
ENDLOOP;
END;
CopyMissedName: PROCEDURE [ce: CacheEntry, key: LONG STRING] =
BEGIN
words: CARDINAL = SIZE[LONG STRING];
ce.names ← DESCRIPTOR[ServerHeap.Node[words], 1];
ce.names[0] ← ServerHeap.CopyString[key];
ce.size ← ce.size + words + String.WordsForString[key.length];
END;
CopyMissedAddress: PROCEDURE [ce: CacheEntry, key: PupAddress] =
BEGIN
words: CARDINAL = SIZE[PupAddress];
ce.addrs ← DESCRIPTOR[ServerHeap.Node[words], 1];
ce.addrs[0] ← key;
ce.size ← ce.size + words;
END;
FirstName: PROCEDURE [entry: Entry] RETURNS [name: LONG STRING] =
BEGIN
name ← LOOPHOLE[entry];
name ← name + SIZE[NetDirDefs.NewEntry];
END;
NextName: PROCEDURE [name: LONG STRING] RETURNS [LONG STRING] =
BEGIN
RETURN[name + String.WordsForString[name.length]];
END;
Address: PROCEDURE [entry: Entry] RETURNS [address: LONG POINTER TO PupAddress] =
BEGIN
name: LONG STRING ← FirstName[entry];
FOR i: CARDINAL IN [0..entry.numberOfNames) DO
name ← NextName[name];
ENDLOOP;
address ← LOOPHOLE[name];
END;
-- These routines are actually called from PupDirServer
GetOldDirectoryVersion: PUBLIC PROCEDURE RETURNS [CARDINAL, BOOLEAN] =
BEGIN RETURN[oldVersion, oldInTransit]; END;
GetNewDirectoryVersion: PUBLIC PROCEDURE RETURNS [CARDINAL, BOOLEAN] =
BEGIN RETURN[newVersion, newInTransit]; END;
OpenDirectoryFiles: PUBLIC ENTRY PROCEDURE =
BEGIN
IF (useCount ← useCount + 1) = 1 THEN
BEGIN
MFile.AddNotifyProc[InspectOld, [oldFileName, null, readOnly], NIL];
MFile.AddNotifyProc[InspectNew, [newFileName, null, readOnly], NIL];
MFile.AddNotifyProc[InspectOld, [oldFileName, null, writeOnly], NIL];
MFile.AddNotifyProc[InspectNew, [newFileName, null, writeOnly], NIL];
ResetDirectoryFiles[];
END;
END;
CloseDirectoryFiles: PUBLIC ENTRY PROCEDURE =
BEGIN
IF useCount # 0 AND (useCount ← useCount - 1) = 0 THEN
BEGIN
MFile.RemoveNotifyProc[InspectOld, [oldFileName, null, readOnly], NIL];
MFile.RemoveNotifyProc[InspectNew, [newFileName, null, readOnly], NIL];
MFile.RemoveNotifyProc[InspectOld, [oldFileName, null, writeOnly], NIL];
MFile.RemoveNotifyProc[InspectNew, [newFileName, null, writeOnly], NIL];
IF seg # NIL THEN BEGIN MSegment.Delete[seg]; seg ← NIL; header ← NIL; END;
IF oldFile # NIL THEN BEGIN MFile.Release[oldFile]; oldFile ← NIL; END;
IF newFile # NIL THEN BEGIN MFile.Release[newFile]; newFile ← NIL; END;
END;
END;
ResetDirectoryFiles: INTERNAL PROCEDURE =
BEGIN
ResetNewDirectoryFile[];
ResetOldDirectoryFile[];
END;
ResetNewDirectoryFile: INTERNAL PROCEDURE =
BEGIN
newVersion ← 0;
IF newFile = NIL THEN
BEGIN
newFile ← MFile.ReadOnly[newFileName, [ReleaseNewFile] ! MFile.Error => CONTINUE];
IF newFile = NIL THEN RETURN;
END;
IF ~BlessNewDirectoryFile[newFile, FALSE] THEN RETURN;
IF seg # NIL THEN BEGIN MSegment.Delete[seg]; seg ← NIL; END;
seg ← MSegment.Create[MFile.CopyFileHandle[newFile, []], [ReleaseSeg], 0];
header ← MSegment.Address[seg];
first ← LOOPHOLE[header];
first ← first + SIZE[NetDirDefs.NewHeader];
newVersion ← header.version;
IF verbose THEN
BEGIN
text: STRING = [75];
Time.AppendCurrent[text];
String.AppendString[text, " Using "L];
String.AppendString[text, newFileName];
String.AppendString[text, "!"L];
String.AppendDecimal[text, newVersion];
LogString[text];
END;
END;
ResetOldDirectoryFile: INTERNAL PROCEDURE =
BEGIN
seg: MSegment.Handle;
header: LONG POINTER TO NetDirDefs.Header;
oldVersion ← 0;
IF oldFile = NIL THEN
BEGIN
oldFile ← MFile.ReadOnly[oldFileName, [ReleaseOldFile] ! MFile.Error => CONTINUE];
IF oldFile = NIL THEN RETURN;
END;
IF ~BlessOldDirectoryFile[oldFile, FALSE] THEN RETURN;
seg ← MSegment.Create[MFile.CopyFileHandle[oldFile, []], [ReleaseSeg], 0];
header ← MSegment.Address[seg];
oldVersion ← header.version;
IF verbose THEN
BEGIN
text: STRING = [75];
Time.AppendCurrent[text];
String.AppendString[text, " Using "L];
String.AppendString[text, oldFileName];
String.AppendString[text, "!"L];
String.AppendDecimal[text, oldVersion];
LogString[text];
END;
MSegment.Delete[seg];
END;
LogString: PROCEDURE [text: LONG STRING] =
BEGIN
String.AppendChar[text, '.];
String.AppendChar[text, Ascii.CR];
Put.Text[NIL, text];
IF NameServerDefs.msg # NIL THEN Put.Text[NameServerDefs.msg, text];
END;
BlessOldDirectoryFile: PROCEDURE [file: MFile.Handle, new: BOOLEAN]
RETURNS [ok: BOOLEAN] =
BEGIN
seg: MSegment.Handle;
h: LONG POINTER TO Header;
e: EntryBase;
n: NameBase;
a: AddrBase;
at: AddrTableBase;
nt: NameTableBase;
bytes: LONG CARDINAL ← MFile.GetLength[file];
message: STRING ← NIL;
CheckPosition: PROCEDURE [offset: Offset] =
BEGIN
off: LONG CARDINAL = LONG[LOOPHOLE[offset, CARDINAL]];
IF off > bytes THEN ERROR CruftyNetworkDirectoryFile;
END;
ok ← TRUE;
seg ← MSegment.Create[MFile.CopyFileHandle[file, []], [NIL, NIL]];
h ← MSegment.Address[seg];
e ← LOOPHOLE[h];
n ← LOOPHOLE[h];
a ← LOOPHOLE[h];
at ← LOOPHOLE[h];
nt ← LOOPHOLE[h];
BEGIN
ENABLE CruftyNetworkDirectoryFile => GOTO Bad;
message ← "File Too Short"L;
IF bytes < 2*SIZE[Header] THEN ERROR CruftyNetworkDirectoryFile;
message ← "File Too Long"L;
IF bytes > 2*LONG[LAST[CARDINAL]] THEN ERROR CruftyNetworkDirectoryFile;
message ← "Software Checksum Mismatch"L;
IF ~TestFileChecksum[file] THEN ERROR CruftyNetworkDirectoryFile;
message ← "Bad internal pointer"L;
IF h.numberOfNames > maxNamesInFile THEN GOTO Bad;
FOR i: CARDINAL IN [0..h.numberOfNames) DO
CheckPosition[nt[h.nameLookupTable][i]]; ENDLOOP;
IF h.numberOfAddrs > maxAddrsInFile THEN GOTO Bad;
FOR i: CARDINAL IN [0..h.numberOfAddrs) DO
CheckPosition[at[h.addrLookupTable][i]]; ENDLOOP;
EXITS
Bad =>
BEGIN
ok ← FALSE;
IF verbose THEN
BEGIN
text: STRING = [100];
Time.AppendCurrent[text];
String.AppendString[text, " "L];
String.AppendString[text, IF new THEN "New "L ELSE "Old "L];
String.AppendString[text, oldFileName];
String.AppendString[text, " is crufty: "L];
String.AppendString[text, message];
LogString[text];
END;
END;
END;
MSegment.Delete[seg];
END;
BlessNewDirectoryFile: PROCEDURE [file: MFile.Handle, new: BOOLEAN]
RETURNS [ok: BOOLEAN] =
BEGIN
seg: MSegment.Handle;
header: LONG POINTER TO NewHeader;
entry: LONG POINTER TO NewEntry;
bytes: LONG CARDINAL ← MFile.GetLength[file];
words: LONG CARDINAL = bytes/2;
message: STRING ← NIL;
CheckPosition: PROCEDURE [p: LONG POINTER] =
BEGIN
off: LONG CARDINAL ← LOOPHOLE[p];
off ← off - LOOPHOLE[header, LONG CARDINAL];
IF off > words THEN ERROR CruftyNetworkDirectoryFile;
END;
ok ← TRUE;
seg ← MSegment.Create[MFile.CopyFileHandle[file, []], [NIL, NIL]];
header ← MSegment.Address[seg];
entry ← LOOPHOLE[header];
entry ← entry + SIZE[NewHeader];
BEGIN
ENABLE CruftyNetworkDirectoryFile => GOTO Bad;
message ← "File Too Short"L;
IF bytes < 2*SIZE[Header] THEN ERROR CruftyNetworkDirectoryFile;
message ← "File Too Long"L;
message ← "Software Checksum Mismatch"L;
IF ~TestFileChecksum[file] THEN ERROR CruftyNetworkDirectoryFile;
message ← "Entry Too Long"L;
FOR i: CARDINAL IN [0..header.numberOfEntries) DO
name: LONG STRING;
CheckPosition[entry];
name ← FirstName[entry];
FOR i: CARDINAL IN [0..entry.numberOfNames) DO
CheckPosition[name];
name ← NextName[name];
ENDLOOP;
entry ← entry + entry.words;
ENDLOOP;
EXITS
Bad =>
BEGIN
ok ← FALSE;
IF verbose THEN
BEGIN
text: STRING = [100];
Time.AppendCurrent[text];
String.AppendString[text, " "L];
String.AppendString[text, IF new THEN "New "L ELSE "Old "L];
String.AppendString[text, newFileName];
String.AppendString[text, " is crufty: "L];
String.AppendString[text, message];
LogString[text];
END;
END;
END;
MSegment.Delete[seg];
END;
TestFileChecksum: PROCEDURE [file: MFile.Handle] RETURNS [ok: BOOLEAN] =
BEGIN
bytes: LONG CARDINAL = MFile.GetLength[file];
words: LONG CARDINAL;
seg: MSegment.Handle;
p: LONG POINTER;
checksum: WORD ← 0;
IF (bytes MOD 2) # 0 THEN RETURN[FALSE];
words ← bytes/2;
words ← words - 1; -- Checksum
seg ← MSegment.Create[MFile.CopyFileHandle[file, []], [], 0];
p ← MSegment.Address[seg];
UNTIL words = 0 DO
clump: CARDINAL = Inline.LowHalf[MIN[words, 10000]];
checksum ← Checksum.ComputeChecksum[checksum, clump, p];
p ← p + clump;
words ← words - clump;
ENDLOOP;
ok ← p↑ = checksum;
MSegment.Delete[seg];
END;
ReleaseOldFile: ENTRY PROCEDURE [file: MFile.Handle, instanceData: LONG POINTER]
RETURNS [MFile.ReleaseChoice] =
BEGIN
oldFile ← NIL;
oldInTransit ← TRUE;
RETURN[goAhead];
END;
ReleaseNewFile: ENTRY PROCEDURE [file: MFile.Handle, instanceData: LONG POINTER]
RETURNS [MFile.ReleaseChoice] =
BEGIN
newFile ← NIL;
newInTransit ← TRUE;
RETURN[goAhead];
END;
ReleaseSeg: ENTRY PROCEDURE [
segment: MSegment.Handle, instanceData: LONG POINTER]
RETURNS [MFile.ReleaseChoice] = BEGIN seg ← NIL; RETURN[goAhead]; END;
InspectOld: PROCEDURE [
name: LONG STRING, file: MFile.Handle, clientInstanceData: LONG POINTER]
RETURNS [BOOLEAN] =
BEGIN
IF InspectOldLocked[] THEN NameServerDefs.KickProber[];
RETURN[FALSE];
END;
InspectOldLocked: ENTRY PROCEDURE RETURNS [BOOLEAN] =
BEGIN
IF oldFile # NIL THEN
BEGIN oldInTransit ← FALSE; RETURN[FALSE]; END; -- duplicate supression
ResetOldDirectoryFile[];
oldInTransit ← FALSE;
RETURN[TRUE];
END;
InspectNew: PROCEDURE [
name: LONG STRING, file: MFile.Handle, clientInstanceData: LONG POINTER]
RETURNS [BOOLEAN] =
BEGIN
IF InspectNewLocked[] THEN
BEGIN
IF newVersion # 0 THEN NameServerDefs.FlushWholeCache[];
NameServerDefs.KickProber[];
END;
RETURN[FALSE];
END;
InspectNewLocked: ENTRY PROCEDURE RETURNS [BOOLEAN] =
BEGIN
IF newFile # NIL THEN
BEGIN newInTransit ← FALSE; RETURN[FALSE]; END; -- duplicate supression
ResetNewDirectoryFile[];
newInTransit ← FALSE;
RETURN[TRUE];
END;
END.