-- DirectoryPropsImpl.mesa (last edited by: Keith on: January 4, 1981 1:53 PM)

DIRECTORY
CommonSoftwareFileTypes USING [CommonSoftwareFileType, tDirectory],
DCSFileTypes USING [tLeaderPage],
Directory,
DirectoryContexts,
DirectoryFiles,
DirectoryInternal,
DirectoryTrees,
DirectoryProps,
Environment USING [bytesPerPage, wordsPerPage],
File USING [Capability, GetAttributes, GetSize, grow, LimitPermissions, nullCapability, read,
shrink, Type, Unknown, Permissions, write],
FileStream USING [Subtype],
Inline USING [BITAND, LongCOPY, LongMult, LowHalf],
PropertyTypes,
Space USING [Create, Handle, LongPointer, Map, Unmap, virtualMemory],
System USING [GetGreenwichMeanTime, gmtEpoch, GreenwichMeanTime],
Volume USING [Unknown];

DirectoryPropsImpl: MONITOR
LOCKS DirectoryInternal.DirectoryLock
IMPORTS Directory, DirectoryContexts, DirectoryFiles, DirectoryTrees, DirectoryInternal, File,
Inline, Space, System, Volume
EXPORTS Directory, DirectoryProps
SHARES File =
BEGIN

eType: Directory.ErrorType;

leaderPageSpace: Space.Handle;
leaderPageWords: CARDINAL = Environment.wordsPerPage*DirectoryInternal.leaderPageSize;

pLeaderPage: LONG POINTER TO LPEntry;
LPEntry: TYPE = MACHINE DEPENDENT RECORD [
type(0: 0.. 15): Directory.PropertyType,
length(1: 0.. 15): CARDINAL,
value(2: 0.. 15): UNSPECIFIED];

leaderVersionID: CARDINAL =25280;
pVersion: LONG POINTER TO CARDINAL;

-- file stream impl information
-- if file stream can no longer read leader pages, the error may lie here! Check the formats


fsLeaderVersionID: CARDINAL = 01240;
pFSLeaderPage: LONG POINTER TO FSLeaderPage;
FSLeaderPage: TYPE = MACHINE DEPENDENT RECORD [
versionID: CARDINAL,
dataType: FileStream.Subtype,
create, write, read: System.GreenwichMeanTime,
length: LONG CARDINAL,
nameLength: CARDINAL,
name: PACKED ARRAY [0.. Directory.maxDirectoryNameLength) OF CHARACTER];

FileType: TYPE = {noLP, fsLP, fullLP};
-- indicates how to construct leader page:
-- noLP indicates file has no leader page
-- fsLP indicates that File Stream maintains some properties
-- fullLP indicates that Directory maintains the full property list


--
entry procedures

GetNextProperty: PUBLIC ENTRY PROC [file: File.Capability, currentProperty: Directory.PropertyType]
RETURNS [nextProperty: Directory.PropertyType] =
--
Stateless Property List Type enumerator
BEGIN ENABLE {UNWIND => NULL; Directory.Error => {eType ← type; GOTO AnError}};
pLP: LONG POINTER TO LPEntry ← pLeaderPage+DirectoryInternal.leaderPageOffset+1;
SELECT TestFileType[file] FROM
= fullLP => {
MapCap[file, fullLP];
IF currentProperty = Directory.nullProperty THEN nextProperty ← pLP.type
ELSE {
pLP ← FindProp[currentProperty];
IF pLP = NIL THEN nextProperty ← Directory.nullProperty
ELSE {pLP ← pLP + pLP.length+2; nextProperty ← pLP.type}};
Space.Unmap[leaderPageSpace];
RETURN [nextProperty]};
= fsLP => {
SELECT currentProperty FROM
= Directory.nullProperty => RETURN [PropertyTypes.tReadDate];
= PropertyTypes.tReadDate => RETURN [PropertyTypes.tWriteDate];
= PropertyTypes.tWriteDate => RETURN [PropertyTypes.tCreateDate];
= PropertyTypes.tCreateDate => RETURN [PropertyTypes.tByteLength];
= PropertyTypes.tByteLength => RETURN [PropertyTypes.tFileName];
= PropertyTypes.tFileName => RETURN [PropertyTypes.tParentDirectory];
ENDCASE;
MapCap[file, fullLP];
pLP ← FindProp[currentProperty];
IF pLP = NIL THEN nextProperty ← Directory.nullProperty
ELSE {pLP ← pLP + pLP.length+2; nextProperty ← pLP.type};
Space.Unmap[leaderPageSpace];
RETURN [nextProperty]};
ENDCASE => RETURN [Directory.nullProperty];
EXITS AnError => RETURN WITH ERROR Directory.Error[eType];
END;

GetProperty: PUBLIC ENTRY PROC [file: File.Capability, property: Directory.PropertyType,
propertyValue: LONG DESCRIPTOR FOR ARRAY OF UNSPECIFIED] =
--
Return the value of a property
BEGIN ENABLE {UNWIND => NULL; Directory.Error => {eType ← type; GOTO AnError}};
pLP: LONG POINTER TO LPEntry;
SELECT TestFileType[file] FROM
= fullLP => {
IF property = Directory.nullProperty THEN
RETURN WITH ERROR Directory.Error[invalidProperty];
MapCap[file, fullLP];
pLP ← FindProp[property];
IF pLP = NIL THEN
{Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
Inline.LongCOPY[
from: @pLP.value, to: BASE[propertyValue],
nwords: MIN[LENGTH[propertyValue], pLP.length]];
IF LENGTH[propertyValue] < pLP.length THEN {
Space.Unmap[leaderPageSpace];
RETURN WITH ERROR Directory.Error[propertyTooSmall]};
Space.Unmap[leaderPageSpace];
RETURN};
= fsLP => {
time: System.GreenwichMeanTime;
name: STRING ← [Directory.maxDirectoryNameLength];
MapCap[file, fsLP];
SELECT property FROM
= Directory.nullProperty => {
Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
= PropertyTypes.tReadDate, = PropertyTypes.tWriteDate, = PropertyTypes.tCreateDate => {
SELECT property FROM
= PropertyTypes.tReadDate => time ← pFSLeaderPage.read;
= PropertyTypes.tWriteDate => time ← pFSLeaderPage.write;
= PropertyTypes.tCreateDate => time ← pFSLeaderPage.create;
ENDCASE;
Inline.LongCOPY[
to: BASE[propertyValue], from: @time, nwords:
MIN[LENGTH[propertyValue], SIZE[System.GreenwichMeanTime]]];
Space.Unmap[leaderPageSpace];
IF LENGTH[propertyValue] < SIZE[System.GreenwichMeanTime] THEN
RETURN WITH ERROR Directory.Error[propertyTooSmall];
RETURN};
= PropertyTypes.tByteLength => {
Inline.LongCOPY[
to: BASE[propertyValue], from: @pFSLeaderPage.length, nwords:
MIN[LENGTH[propertyValue], 2]];
Space.Unmap[leaderPageSpace];
IF LENGTH[propertyValue] < 2 THEN
RETURN WITH ERROR Directory.Error[propertyTooSmall];
RETURN};
= PropertyTypes.tFileName => {
FOR i: CARDINAL IN [0.. pFSLeaderPage.nameLength) DO name[i]←pFSLeaderPage.name[i]
ENDLOOP;
name.length←pFSLeaderPage.nameLength;
Inline.LongCOPY[
to: BASE[propertyValue], from: name, nwords:
MIN[LENGTH[propertyValue], SIZE[StringBody[name.maxlength]]]];
Space.Unmap[leaderPageSpace];
IF LENGTH[propertyValue] < SIZE[StringBody[name.maxlength]] THEN
RETURN WITH ERROR Directory.Error[propertyTooSmall];
RETURN};
ENDCASE;
pLP ← FindProp[property];
IF pLP = NIL THEN
{Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
Inline.LongCOPY[from: @pLP.value, to: BASE[propertyValue],
nwords: MIN[LENGTH[propertyValue], pLP.length]];
IF LENGTH[propertyValue] < pLP.length THEN {
Space.Unmap[leaderPageSpace];
RETURN WITH ERROR Directory.Error[propertyTooSmall]};
Space.Unmap[leaderPageSpace];
RETURN};
ENDCASE => {
-- file has no leader page! Grovel around directories for values
name: STRING ← [Directory.maxDirectoryNameLength];
parentCap: File.Capability;
SELECT property FROM
= PropertyTypes.tFileName => {
propertyValue[0] ← 0; -- in case SearchForName bombs
parentCap ← SearchForName[file, name];
Inline.LongCOPY[
to: BASE[propertyValue], from: name, nwords:
MIN[LENGTH[propertyValue], SIZE[StringBody[name.maxlength]]]];
IF LENGTH[propertyValue] < SIZE[StringBody[name.maxlength]] THEN
RETURN WITH ERROR Directory.Error[propertyTooSmall];
RETURN};
= PropertyTypes.tParentDirectory => {
parentCap ← SearchForName[file, name];
IF LENGTH[propertyValue] < SIZE[File.Capability] THEN
RETURN WITH ERROR Directory.Error[propertyTooSmall];
Inline.LongCOPY[from: @parentCap, to: BASE[propertyValue], nwords: SIZE[File.Capability]]};
ENDCASE => FOR i: CARDINAL IN [0.. LENGTH[propertyValue]) DO propertyValue[i] ← 0 ENDLOOP};
EXITS AnError => RETURN WITH ERROR Directory.Error[eType];
END;

GetProps: PUBLIC ENTRY PROC [file: File.Capability, name: LONG STRING]
RETURNS [readDate, writeDate, createDate: System.GreenwichMeanTime,
byteLength: LONG CARDINAL, parent: File.Capability] =
--
Return the value of a set of properties
BEGIN ENABLE {UNWIND => NULL; Directory.Error => {eType ← type; GOTO AnError}};
[readDate: readDate, writeDate: writeDate, createDate: createDate, byteLength: byteLength, parent:
parent] ← GetPropsInternal[file: file, name: name];
EXITS AnError => RETURN WITH ERROR Directory.Error[eType];
END;

GetPropsInternal: PUBLIC INTERNAL PROC [file: File.Capability, name: LONG STRING] RETURNS
[readDate, writeDate, createDate: System.GreenwichMeanTime, byteLength: LONG CARDINAL,
parent: File.Capability] =
--
Return the value of a set of properties
BEGIN
pLP: LONG POINTER TO LPEntry;
SELECT TestFileType[file] FROM
= fullLP => {
MapCap[file, fullLP];
pLP ← FindProp[PropertyTypes.tReadDate];
IF pLP = NIL THEN
{Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
Inline.LongCOPY[from: @pLP.value, to: @readDate, nwords: SIZE[System.GreenwichMeanTime]];
pLP ← FindProp[PropertyTypes.tWriteDate];
IF pLP = NIL THEN
{Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
Inline.LongCOPY[from: @pLP.value, to: @readDate, nwords: SIZE[System.GreenwichMeanTime]];
pLP ← FindProp[PropertyTypes.tCreateDate];
IF pLP = NIL THEN
{Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
Inline.LongCOPY[from: @pLP.value, to: @readDate, nwords: SIZE[System.GreenwichMeanTime]];
pLP ← FindProp[PropertyTypes.tParentDirectory];
IF pLP = NIL THEN
{Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
Inline.LongCOPY[from: @pLP.value, to: @parent, nwords: SIZE[File.Capability]];
pLP ← FindProp[PropertyTypes.tFileName];
IF pLP = NIL THEN
{Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
DirectoryTrees.MoveLongString[from: LOOPHOLE[@pLP.value], to: name];
Space.Unmap[leaderPageSpace];
RETURN};
= fsLP => {
MapCap[file, fsLP];
readDate ← pFSLeaderPage.read;
writeDate ← pFSLeaderPage.write;
createDate ← pFSLeaderPage.create;
byteLength ← pFSLeaderPage.length;
FOR i: CARDINAL IN [0.. pFSLeaderPage.nameLength) DO
name[i] ← pFSLeaderPage.name[i] ENDLOOP;
name.length ← pFSLeaderPage.nameLength;
pLP ← FindProp[PropertyTypes.tParentDirectory];
IF pLP = NIL THEN
{Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
Inline.LongCOPY[from: @pLP.value, to: @parent, nwords: SIZE[File.Capability]];
Space.Unmap[leaderPageSpace];
RETURN};
ENDCASE => {
readDate ← writeDate ← createDate ← System.gmtEpoch;
parent ← File.nullCapability; -- in case SearchForName bombs
parent ← SearchForName[file, name];
RETURN};
END;

PutProperty: PUBLIC ENTRY PROC [file: File.Capability, property: Directory.PropertyType,
propertyValue: LONG DESCRIPTOR FOR ARRAY OF UNSPECIFIED, new: BOOLEAN] =
--
Puts a property in the Property list
BEGIN ENABLE {UNWIND => NULL; Directory.Error => {eType ← type; GOTO AnError}};
PutPropertyInternal[file: file, property: property, propertyValue: propertyValue, new: new];
EXITS AnError => RETURN WITH ERROR Directory.Error[eType];
END;

PutPropertyInternal: PUBLIC INTERNAL PROC [file: File.Capability, property: Directory.PropertyType,
propertyValue: LONG DESCRIPTOR FOR ARRAY OF UNSPECIFIED, new: BOOLEAN] =
--
Puts a property in the Property list
BEGIN
pLP: LONG POINTER TO LPEntry;
SELECT TestFileType[file] FROM
= fullLP => {
MapCap[file, fullLP];
pLP ← FindProp[property];
IF (pLP = NIL) AND ~ new THEN
{Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
PutProp[property, propertyValue];
Space.Unmap[leaderPageSpace];
RETURN};
= fsLP => {
name: STRING ← [Directory.maxDirectoryNameLength];
MapCap[file, fsLP];
SELECT property FROM
= Directory.nullProperty => RETURN WITH ERROR Directory.Error[invalidProperty];
= PropertyTypes.tReadDate =>
Inline.LongCOPY[
from: BASE[propertyValue],
to: @pFSLeaderPage.read, nwords: SIZE[System.GreenwichMeanTime]];
= PropertyTypes.tWriteDate =>
Inline.LongCOPY[
from: BASE[propertyValue],
to: @pFSLeaderPage.write, nwords: SIZE[System.GreenwichMeanTime]];
= PropertyTypes.tCreateDate =>
Inline.LongCOPY[
from: BASE[propertyValue],
to: @pFSLeaderPage.create, nwords: SIZE[System.GreenwichMeanTime]];
= PropertyTypes.tByteLength =>
Inline.LongCOPY[from: BASE[propertyValue], to: @pFSLeaderPage.length, nwords: 2];
= PropertyTypes.tFileName => {
Inline.LongCOPY[
from: BASE[propertyValue], to: name, nwords: LENGTH[propertyValue]];
FOR i: CARDINAL IN [0.. name.length) DO
pFSLeaderPage.name[i] ← name[i] ENDLOOP;
pFSLeaderPage.nameLength ← name.length};
ENDCASE => {
pLP ← FindProp[property];
IF (pLP = NIL) AND ~ new THEN {
Space.Unmap[leaderPageSpace]; RETURN WITH ERROR Directory.Error[invalidProperty]};
PutProp[property, propertyValue]};
Space.Unmap[leaderPageSpace];
RETURN};
ENDCASE;
END;

PutProps: PUBLIC ENTRY PROC [file: File.Capability,
readDate, writeDate, createDate: System.GreenwichMeanTime] =
--
Sets the dates of a file
BEGIN ENABLE {UNWIND => NULL; Directory.Error => {eType ← type; GOTO AnError}};
SELECT TestFileType[file] FROM
= fullLP => {
MapCap[file, fullLP];
PutProp[PropertyTypes.tReadDate, DESCRIPTOR[@readDate, SIZE[System.GreenwichMeanTime]]];
PutProp[
PropertyTypes.tWriteDate, DESCRIPTOR[@writeDate, SIZE[System.GreenwichMeanTime]]];
PutProp[
PropertyTypes.tCreateDate, DESCRIPTOR[@createDate, SIZE[System.GreenwichMeanTime]]];
Space.Unmap[leaderPageSpace];
RETURN};
= fsLP => {
MapCap[file, fsLP];
pFSLeaderPage.read ← readDate;
pFSLeaderPage.write ← writeDate;
pFSLeaderPage.create ← createDate;
Space.Unmap[leaderPageSpace];
RETURN};
ENDCASE => RETURN;
EXITS AnError => RETURN WITH ERROR Directory.Error[eType];
END;

UpdateDates: PUBLIC ENTRY PROC [file: File.Capability, permissions: File.Permissions]
RETURNS [File.Capability] =
--
Updates the dates of a file
BEGIN ENABLE {UNWIND => NULL; Directory.Error => {eType ← type; GOTO AnError}};
newCap: File.Capability;
newCap ← IF permissions = Directory.ignore THEN file ELSE File.LimitPermissions[file, permissions];
SetTimes[newCap];
RETURN [newCap];
EXITS AnError => RETURN WITH ERROR Directory.Error[eType];
END;

--
internal procedures

FindProp: INTERNAL PROC [type: Directory.PropertyType] RETURNS [pLP: LONG POINTER TO LPEntry] =
--
Return a pointer to leader page entry of indicated property (or NIL if doesn’t exist)
BEGIN
size: CARDINAL ← DirectoryInternal.leaderPageOffset;
pLP ← pLeaderPage+DirectoryInternal.leaderPageOffset+1;
WHILE pLP.type # Directory.nullProperty DO
IF pLP.type = type THEN RETURN;
pLP ← pLP+pLP.length+2;
size ← size + pLP.length+2;
IF size >= leaderPageWords THEN {
-- a size field is wrong, so zero this leader page and continue
pLP ← pLeaderPage+DirectoryInternal.leaderPageOffset+1;
pLP.type ← Directory.nullProperty;
RETURN [NIL]};
ENDLOOP;
RETURN [NIL]
END;

MapCap: INTERNAL PROC[file: File.Capability, type: FileType] =
--
map the file leader page to the leader page space
BEGIN
pLP: LONG POINTER TO LPEntry ← pLeaderPage+DirectoryInternal.leaderPageOffset+1;
file.permissions ← Directory.fileMaxPermissions;
Space.Map[leaderPageSpace, [file, 0]];
IF pVersion↑ # leaderVersionID THEN {
-- set up an empty leader page (and you can kiss page zero good-bye!)
pVersion↑ ← leaderVersionID;
pLP↑ ← [type: Directory.nullProperty, length: 0, value:]};
IF type = fsLP AND (pFSLeaderPage.versionID # fsLeaderVersionID) THEN {
pFSLeaderPage.versionID ← fsLeaderVersionID;
pFSLeaderPage.nameLength ← 0;
pFSLeaderPage.read ← pFSLeaderPage.write ← pFSLeaderPage.create ← System.GreenwichMeanTime[0];
pFSLeaderPage.dataType ← unknown;
pFSLeaderPage.length ← Inline.LongMult[Inline.LowHalf[File.GetSize[file]],Environment.bytesPerPage]};
RETURN
END;

PutProp: INTERNAL PROC
[type: Directory.PropertyType, value: LONG DESCRIPTOR FOR ARRAY OF UNSPECIFIED] =
--
place/replace property with value
BEGIN
pLP: LONG POINTER TO LPEntry ← pLeaderPage+DirectoryInternal.leaderPageOffset+1;
copySize: CARDINAL ← 0;
addToCopy: BOOLEAN ← FALSE;
pCopyLP: LONG POINTER TO LPEntry;
WHILE pLP.type # Directory.nullProperty DO
IF pLP.type = type THEN {addToCopy ← TRUE; pCopyLP ← pLP};
IF addToCopy THEN copySize ← copySize+pLP.length+2;
pLP ← pLP+pLP.length+2;
ENDLOOP;
IF addToCopy THEN {
pLP ← pLP - pCopyLP.length - 2;
Inline.LongCOPY[from: pCopyLP+pCopyLP.length+2, to: pCopyLP, nwords: copySize]};
pLP↑ ← [type: type, length: LENGTH[value], value:];
Inline.LongCOPY[from: BASE[value], to: @pLP.value, nwords: LENGTH[value]];
pLP ← pLP+pLP.length+2;
pLP↑ ← [type: Directory.nullProperty, length: 0, value:];
RETURN
END;

SearchForName: PUBLIC INTERNAL PROC
[file: File.Capability, name: LONG STRING] RETURNS [parent: File.Capability] =
--
returns a file’s name and parent capability by searching the directories for an entry with
-- the same ID as
file.
--
Current Limitation: the only directory searched is the default working directory
BEGIN
pDD: LONG POINTER TO DirectoryInternal.DirectoryDescriptor;
gnFile: File.Capability;
pDD ← @DirectoryFiles.directoryCache[DirectoryContexts.contextTable[0].pDC].dir;
name.length ← 0;
DO
gnFile ← DirectoryTrees.BTreeGetNext[pDD, name, name];
IF name.length = 0 THEN ERROR Directory.Error[invalidProperty]; --
couldn’t find the file!
IF gnFile.fID = file.fID THEN EXIT;
ENDLOOP;
parent ← DirectoryFiles.directoryCache[DirectoryContexts.contextTable[0].pDC].cap;
RETURN
END;

SetCreateDate: PUBLIC INTERNAL PROC
[file: File.Capability, name: LONG STRING, parent: File.Capability] =
--
Define all the properties for this file
BEGIN
time: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
emptyByteLength: LONG CARDINAL ←
(File.GetSize[file]-DirectoryInternal.leaderPageSize)*Environment.wordsPerPage*2;
pLP: LONG POINTER TO LPEntry ← pLeaderPage+DirectoryInternal.leaderPageOffset+1;
SELECT TestFileType[file] FROM
= fsLP => {
file.permissions ← Directory.fileMaxPermissions;
Space.Map[leaderPageSpace, [file, 0]];
pFSLeaderPage.versionID ← fsLeaderVersionID;
pVersion↑ ← leaderVersionID;
pFSLeaderPage.read ← pFSLeaderPage.write ← pFSLeaderPage.create ← time;
pFSLeaderPage.dataType ← unknown;
pFSLeaderPage.length ← emptyByteLength;
FOR i: CARDINAL IN [0.. name.length) DO
pFSLeaderPage.name[i] ← name[i] ENDLOOP;
pFSLeaderPage.nameLength ← name.length;
pLP↑ ← [type: Directory.nullProperty, length: 0, value:];
PutProp[PropertyTypes.tParentDirectory, DESCRIPTOR[@parent, SIZE[File.Capability]]];
Space.Unmap[leaderPageSpace]};
= fullLP => {
file.permissions ← Directory.fileMaxPermissions;
Space.Map[leaderPageSpace, [file, 0]];
pVersion↑ ← leaderVersionID;
pLP↑ ← [type: Directory.nullProperty, length: 0, value:];
PutProp[PropertyTypes.tReadDate, DESCRIPTOR[@time, SIZE[System.GreenwichMeanTime]]];
PutProp[PropertyTypes.tWriteDate, DESCRIPTOR[@time, SIZE[System.GreenwichMeanTime]]];
PutProp[PropertyTypes.tCreateDate, DESCRIPTOR[@time, SIZE[System.GreenwichMeanTime]]];
PutProp[PropertyTypes.tByteLength, DESCRIPTOR[@emptyByteLength, 2]];
PutProp[PropertyTypes.tFileName, DESCRIPTOR[name, SIZE[StringBody[name.length]]]];
PutProp[PropertyTypes.tParentDirectory, DESCRIPTOR[@parent, SIZE[File.Capability]]];
Space.Unmap[leaderPageSpace]};
ENDCASE;
RETURN
END;

SetNameAndCap: PUBLIC INTERNAL PROC
[file: File.Capability, name: LONG STRING, parent: File.Capability] =
--
set the leader page name and parent capability
BEGIN
SELECT TestFileType[file] FROM
= fsLP => {
MapCap[file, fsLP];
FOR i: CARDINAL IN [0.. name.length) DO
pFSLeaderPage.name[i] ← name[i] ENDLOOP;
pFSLeaderPage.nameLength ← name.length;
PutProp[PropertyTypes.tParentDirectory, DESCRIPTOR[@parent, SIZE[File.Capability]]];
Space.Unmap[leaderPageSpace]};
= fullLP => {
MapCap[file, fullLP];
PutProp[PropertyTypes.tFileName, DESCRIPTOR[name, SIZE[StringBody[name.length]]]];
PutProp[PropertyTypes.tParentDirectory, DESCRIPTOR[@parent, SIZE[File.Capability]]];
Space.Unmap[leaderPageSpace]};
ENDCASE;
RETURN
END;

SetTimes: PUBLIC INTERNAL PROC [file: File.Capability] =
--
set the leader page times from the file capability
BEGIN
time: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
setRead: BOOLEAN ← Inline.BITAND[file.permissions, File.read];
setCreate: BOOLEAN ← Inline.BITAND[file.permissions, File.write+File.grow+File.shrink];
SELECT TestFileType[file] FROM
= fsLP => {
MapCap[file, fsLP];
IF setRead THEN pFSLeaderPage.read ← time;
IF setCreate THEN pFSLeaderPage.write ← pFSLeaderPage.create ← time;
Space.Unmap[leaderPageSpace]};
= fullLP => {
MapCap[file, fullLP];
IF setRead THEN
PutProp[PropertyTypes.tReadDate, DESCRIPTOR[@time, SIZE[System.GreenwichMeanTime]]];
IF setCreate THEN {
PutProp[PropertyTypes.tWriteDate, DESCRIPTOR[@time, SIZE[System.GreenwichMeanTime]]];
PutProp[PropertyTypes.tCreateDate, DESCRIPTOR[@time, SIZE[System.GreenwichMeanTime]]]};
Space.Unmap[leaderPageSpace]};
ENDCASE;
RETURN
END;

TestFileType: INTERNAL PROC [file: File.Capability] RETURNS [FileType] =
--
return how leader page is to be constructed
BEGIN
ft: File.Type;
ftValue: CARDINAL;
IF File.GetSize[file ! File.Unknown => ERROR Directory.Error[fileNotFound];
Volume.Unknown => ERROR Directory.Error[volumeNotFound]] = 0 THEN RETURN [noLP];
ft ← File.GetAttributes[file].type;
IF ft = DCSFileTypes.tLeaderPage THEN RETURN [fsLP];
[ftValue] ← ft;
IF ftValue IN CommonSoftwareFileTypes.CommonSoftwareFileType THEN RETURN [fullLP];
RETURN [noLP]
END;

ValidLP: PUBLIC INTERNAL PROC [file: File.Capability] RETURNS [BOOLEAN] =
--
Check if the file is of type tDirectory and has a good leader page
BEGIN
ft: File.Type;
ft ← File.GetAttributes[file ! File.Unknown => GOTO NotValid].type;
IF ft # CommonSoftwareFileTypes.tDirectory THEN GOTO NotValid;
Space.Map[leaderPageSpace, [file, 0]];
IF pVersion↑ # leaderVersionID THEN {Space.Unmap[leaderPageSpace]; GOTO NotValid};
Space.Unmap[leaderPageSpace];
RETURN [TRUE];
EXITS NotValid => RETURN [FALSE]
END;

-- Initialization

leaderPageSpace ← Space.Create[DirectoryInternal.leaderPageSize, Space.virtualMemory];
pLeaderPage ← Space.LongPointer[leaderPageSpace];
pFSLeaderPage ← LOOPHOLE[pLeaderPage];
pVersion ← LOOPHOLE[pLeaderPage];
pVersion ← pVersion + DirectoryInternal.leaderPageOffset;

END.


LOG

Time: August 28, 1980 11:25 AM By: Keith
Action: Created File
Time: October 8, 1980 10:13 AM By: KeithAction: MapCap initializes leader page rather than raising DirectoryNeedsScavenging if leader page is trashed (ARs 6046, 6183).
Time: October 20, 1980 6:28 PM By: KeithAction: Changed GetProps and GetProperty so that files with no leader pages will return somewhat meaningful property values for parent directory and file name.
Time: December 7, 1980 4:34 PM By: FayAction: Changed GetProps and PutProperty to call monitor-internal procedures, so that the scavenger can call these internal procs from inside the monitor.
Time: AugustJanuary 4, 1981 2:05 PM By: Keith Action: Fixed FindProp so that a funny property length dies gracefully.