DIRECTORY
Ascii USING [Upper],
Basics USING [Comparison],
BasicTime USING [GMT, nullGMT],
BTree USING [DeleteKey, Entry, EntSize, EnumerateEntries, Key, PathStk, ReadEntry, Relation, Tree, UpdateEntry, UpdateType],
File USING [FP, nullFP, Volume],
FS USING [Error, ErrorDesc, maxFNameLength],
FSBackdoor USING [Entry, EntryPtr, EntryType, highestVersion, lowestVersion, MakeFName, TextRep, TextRP, ProduceError, Version],
FSDir USING [VersionMatching],
FSFileOps USING [DeleteFile, LPCreatedTime, OpenFile, VolumeDesc],
FSLock USING [ActiveFile, ReleaseRecord, LockRecord, WaitForRecord],
PrincOpsUtils USING [ByteBlt],
Rope USING [Cat, Length, NewText, ROPE, Text];
Directory Entry Representation
AppendText:
UNSAFE PROC [eP: FSBackdoor.EntryPtr, text: Rope.Text]
RETURNS [textRP: FSBackdoor.TextRP] =
UNCHECKED
BEGIN
textRep: LONG POINTER TO FSBackdoor.TextRep;
textRP ← LOOPHOLE[eP.size];
textRep ← @eP[textRP];
LOOPHOLE[textRep, LONG POINTER TO CARDINAL]^ ← Rope.Length[text]; -- sets textRep.length
[] ← PrincOpsUtils.ByteBlt [
to: [ BASE[DESCRIPTOR[textRep]], 0, textRep.length ],
from: [ BASE[DESCRIPTOR[text]], 0, textRep.length ]
];
eP.size ← eP.size+SIZE[FSBackdoor.TextRep[textRep.length]];
END;
GetText:
UNSAFE PROC [textRep:
LONG
POINTER
TO FSBackdoor.TextRep, text:
REF
TEXT] =
UNCHECKED
BEGIN
text.length ← textRep.length;
[] ← PrincOpsUtils.ByteBlt [
to: [ BASE[DESCRIPTOR[text]], 0, textRep.length ],
from: [ BASE[DESCRIPTOR[textRep]], 0, textRep.length ]
];
END;
EntryNameMatches:
UNSAFE PROC [name: Rope.Text, entryPtr: FSBackdoor.EntryPtr]
RETURNS[
BOOLEAN] =
UNCHECKED
BEGIN
entryName: LONG POINTER TO FSBackdoor.TextRep = @entryPtr[entryPtr.nameBody];
nameBody: REF READONLY TEXT = LOOPHOLE[name];
IF entryName.length # nameBody.length THEN RETURN[FALSE];
FOR i:
CARDINAL
IN [ 0 .. nameBody.length )
DO
IF Ascii.Upper[entryName[i]] # Ascii.Upper[nameBody[i]] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
END;
Directory/cache manipulation procedures, exported to FSDir
EnumerateEntries:
PUBLIC
PROC [ vDesc: FSFileOps.VolumeDesc, start: Rope.Text, versions: FSDir.VersionMatching, matchProc:
UNSAFE
PROC [entry: FSBackdoor.EntryPtr]
RETURNS [accept, stop:
BOOLEAN], acceptProc:
PROC
RETURNS [stop:
BOOLEAN] ] =
TRUSTED
BEGIN
GetAll:
UNSAFE
PROC [entry: BTree.Entry]
RETURNS [continue:
BOOLEAN] =
BEGIN
entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry];
[accept, stop] ← matchProc[ entryPtr ];
IF stop
THEN continue ← FALSE
ELSE
BEGIN
IF accept
THEN
BEGIN
-- save nameBody for restarting enumeration
GetText[@entryPtr[entryPtr.nameBody], keyText];
keyRef.version ← entryPtr.version;
continue ← FALSE;
END
ELSE continue ← TRUE;
END;
END;
MatchEntry:
UNSAFE
PROC [entry: BTree.Entry] =
BEGIN
entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry];
IF entryPtr = NIL
THEN stop ← TRUE
ELSE
BEGIN
GetText[@entryPtr[entryPtr.nameBody], keyText];
[accept, stop] ← matchProc[ entryPtr ];
END;
END;
FindL:
UNSAFE
PROC [entry: BTree.Entry] =
BEGIN
entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry];
IF entryPtr = NIL
THEN stop ← TRUE
ELSE GetText[@entryPtr[entryPtr.nameBody], keyText];
END;
keyText: REF TEXT = NEW [ TEXT[FS.maxFNameLength] ];
keyRef: REF KeyObject = CheckoutKey[FSBackdoor.lowestVersion, start];
stop, accept: BOOLEAN;
SELECT versions
FROM
all =>
BEGIN
UNTIL BTree.EnumerateEntries[tree: vDesc.tree, relation: greater, key: keyRef, Proc: GetAll]
DO
IF stop OR (accept AND acceptProc[]) THEN EXIT;
keyRef.nameBody ← LOOPHOLE[keyText]; -- key from last iteration
ENDLOOP;
END;
bangLOnly =>
BEGIN
BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: MatchEntry];
keyRef^ ← [FSBackdoor.highestVersion, LOOPHOLE[keyText]]; -- for later interations
UNTIL stop
DO
IF accept AND acceptProc[] THEN EXIT;
BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: MatchEntry];
ENDLOOP;
END;
bangHOnly =>
BEGIN
stop ← FALSE;
BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: FindL];
keyRef^ ← [FSBackdoor.highestVersion, LOOPHOLE[keyText]]; -- for later interations
UNTIL stop
DO
BTree.ReadEntry[tree: vDesc.tree, relation: lessEqual, key: keyRef, Proc: MatchEntry];
IF accept AND acceptProc[] THEN EXIT;
BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: FindL];
ENDLOOP;
END;
ENDCASE => ERROR;
RecycleKey[keyRef];
END;
AcquireOldFName:
PUBLIC
PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, wantedVersion: FSBackdoor.Version, wantedTime: BasicTime.
GMT]
RETURNS [type: FSBackdoor.EntryType, version: FSBackdoor.Version, keep:
CARDINAL, fp: File.
FP, time: BasicTime.
GMT, attachedTo: Rope.Text, a: FSLock.ActiveFile] =
TRUSTED
BEGIN
GetEntry:
UNSAFE PROC [entry: BTree.Entry] =
BEGIN
entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry];
type ← notFound;
IF entry = NIL OR NOT EntryNameMatches[nameBody, entryPtr]
THEN {outcome ← notFound; RETURN};
version ← entryPtr.version;
IF wantedTime # BasicTime.nullGMT
THEN
BEGIN
time ←
WITH e: entryPtr^
SELECT
FROM
local => FSFileOps.LPCreatedTime[vDesc.vol, e.fp],
attached => e.created,
cached => FSFileOps.LPCreatedTime[vDesc.vol, e.fp],
ENDCASE => ERROR;
IF wantedTime # time THEN {outcome ← wrongTime; RETURN};
END;
type ← entryPtr.type;
[a, lockSet] ← FSLock.LockRecord[vDesc.prefix, nameBody, version];
IF lockSet
THEN
WITH e: entryPtr^
SELECT
FROM
local =>
BEGIN
keep ← e.keep;
fp ← e.fp;
END;
attached =>
BEGIN
keep ← e.keep;
time ← e.created;
attachedTo ←TextFromTextRep[@entryPtr[e.attachedTo]];
END;
cached =>
BEGIN
time ← e.used;
fp ← e.fp;
END;
ENDCASE => ERROR;
outcome ← seeLockSet;
END;
FindAndLock:
PROC
RETURNS [
BOOLEAN] =
TRUSTED
BEGIN
DO
errorDesc: FS.ErrorDesc; -- this GetEntry checks the created-time from a leader page sometimes
BTree.ReadEntry[tree: vDesc.tree, relation: search, key: keyRef, Proc: GetEntry
! FS.Error => {errorDesc ← error; CONTINUE} ];
IF errorDesc.group # ok THEN ERROR FS.Error[errorDesc];
SELECT outcome
FROM
notFound =>
IF search = equal AND wantedTime # BasicTime.nullGMT
THEN RETURN[TRUE] -- useful to keep looking
ELSE RETURN[FALSE];
wrongTime =>
RETURN[TRUE];
seeLockSet =>
IF lockSet
THEN RETURN[FALSE]
ELSE FSLock.WaitForRecord[a];
ENDLOOP;
END;
outcome: {notFound, wrongTime, seeLockSet};
lockSet, keepLooking: BOOLEAN;
keyRef: REF KeyObject = CheckoutKey [wantedVersion, nameBody];
search: BTree.Relation ←
SELECT wantedVersion
FROM
FSBackdoor.highestVersion => lessEqual,
FSBackdoor.lowestVersion => greater,
ENDCASE => equal;
IF keepLooking ← FindAndLock[]
THEN
BEGIN
-- search for version with desired createdTime
IF search = lessEqual
THEN keyRef.version ← [version - 1] -- already found highest version
ELSE { keyRef.version ← FSBackdoor.highestVersion; search ← lessEqual };
WHILE keepLooking
DO
keepLooking ← FindAndLock[];
keyRef.version ← [version - 1];
ENDLOOP;
END;
RecycleKey[keyRef];
END;
AcquireNextLName:
PUBLIC
PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, newKeep:
CARDINAL]
RETURNS [a: FSLock.ActiveFile, keep:
CARDINAL, fp: File.
FP] =
TRUSTED
BEGIN
GetEntry:
UNSAFE
PROC [entry: BTree.Entry] =
BEGIN
entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry];
type ← notFound;
IF entry # NIL AND EntryNameMatches[nameBody, entryPtr]
THEN
BEGIN
keyRef.version ← entryPtr.version;
type ← entryPtr.type;
WITH e: entryPtr^
SELECT
FROM
local =>
BEGIN
IF keep = 0 THEN keep ← e.keep;
entryFP ← e.fp;
END;
attached =>
BEGIN
IF keep = 0 THEN keep ← e.keep;
END;
ENDCASE => ERROR;
END
ELSE keyRef.version ← FSBackdoor.lowestVersion;
IF count = 0
THEN
BEGIN
IF keyRef.version + 1 = FSBackdoor.highestVersion
THEN { noMoreVersions ← TRUE; RETURN }; -- version too big
[a, nextLockSet] ← FSLock.LockRecord[vDesc.prefix, nameBody, [keyRef.version + 1]];
IF NOT nextLockSet OR keyRef.version < keep
THEN RETURN;
END;
IF type # notFound
THEN
BEGIN
count ← count + 1;
IF count < keep
THEN entryLockSet ← FALSE
ELSE [entryA, entryLockSet] ← FSLock.LockRecord[vDesc.prefix, nameBody, keyRef.version];
END;
END;
keyRef: REF KeyObject = CheckoutKey[ , nameBody];
entryFP: File.FP;
entryA: FSLock.ActiveFile;
entryLockSet, nextLockSet: BOOLEAN;
noMoreVersions: BOOLEAN ← FALSE;
type: FSBackdoor.EntryType;
count: CARDINAL ← 0;
fp ← File.nullFP;
keep ← newKeep; -- will get reset if =0 and any entries are found
DO
-- get a name lock on !N
keyRef.version ← FSBackdoor.highestVersion;
BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry];
IF noMoreVersions THEN NoMoreVersions[vDesc.prefix, nameBody, FSBackdoor.highestVersion];
IF nextLockSet THEN EXIT ELSE FSLock.WaitForRecord[a];
ENDLOOP;
IF keyRef.version >= keep
THEN
UNTIL type = notFound
DO
-- do keep processing
IF entryLockSet
THEN
BEGIN
IF entryA.fileLock = none
THEN
BEGIN
-- can delete or reuse this file
IF NOT BTree.DeleteKey[tree: vDesc.tree, key: keyRef] THEN ERROR;
IF type = local
THEN
BEGIN
IF fp = File.nullFP
THEN fp ← entryFP
ELSE FSFileOps.DeleteFile[ FSFileOps.OpenFile[vDesc.vol, entryFP] ];
END;
END;
FSLock.ReleaseRecord[entryA];
END;
BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry];
ENDLOOP;
RecycleKey[keyRef];
END;
DoKeeps:
PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text] =
TRUSTED
BEGIN
GetEntry:
UNSAFE
PROC [entry: BTree.Entry] =
BEGIN
entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry];
IF entry = NIL OR NOT EntryNameMatches[nameBody, entryPtr]
THEN {type ← notFound; RETURN};
keyRef.version ← entryPtr.version;
type ← entryPtr.type;
WITH e: entryPtr^
SELECT
FROM
local => { IF count = 0 THEN keep ← e.keep; fp ← e.fp };
attached => { IF count = 0 THEN keep ← e.keep };
ENDCASE => ERROR;
count ← count + 1;
IF count <= keep
THEN lockSet ← FALSE
ELSE [a, lockSet] ← FSLock.LockRecord[vDesc.prefix, nameBody, keyRef.version];
END;
keyRef: REF KeyObject = CheckoutKey[FSBackdoor.highestVersion, nameBody];
count: CARDINAL ← 0;
fp: File.FP;
a: FSLock.ActiveFile;
lockSet: BOOLEAN;
type: FSBackdoor.EntryType;
keep: CARDINAL;
BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry];
IF type # notFound AND keyRef.version > keep
THEN
DO
-- do keep processing
BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry];
IF type = notFound THEN EXIT;
IF lockSet
THEN
BEGIN
IF a.fileLock = none
THEN
BEGIN
-- can delete this file
IF NOT BTree.DeleteKey[tree: vDesc.tree, key: keyRef] THEN ERROR;
IF type = local THEN FSFileOps.DeleteFile[ FSFileOps.OpenFile[vDesc.vol, fp] ];
END;
FSLock.ReleaseRecord[a];
END;
ENDLOOP;
RecycleKey[keyRef];
END;
AcquireOldGName:
PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, wantedVersion: FSBackdoor.Version]
RETURNS [type: FSBackdoor.EntryType, time: BasicTime.
GMT, fp: File.
FP, a: FSLock.ActiveFile] =
TRUSTED
BEGIN
GetEntry:
UNSAFE
PROC [entry: BTree.Entry] =
BEGIN
entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry];
lockSet: BOOLEAN;
IF entry = NIL
THEN type ← notFound
ELSE
BEGIN
type ← entryPtr.type;
IF type = cached
THEN
BEGIN
[a, lockSet] ← FSLock.LockRecord[vDesc.prefix, nameBody, wantedVersion];
IF lockSet
THEN BEGIN
WITH e: entryPtr^ SELECT FROM
cached => { time ← e.used; fp ← e.fp };
ENDCASE => ERROR;
END
ELSE a ← NIL;
END;
END;
END;
keyRef: REF KeyObject = CheckoutKey[wantedVersion, nameBody];
BTree.ReadEntry[tree: vDesc.tree, key: keyRef, Proc: GetEntry];
RecycleKey[keyRef];
END;
AcquireOldOrNewGName:
PUBLIC
PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, wantedVersion: FSBackdoor.Version]
RETURNS [fp: File.
FP, a: FSLock.ActiveFile] =
TRUSTED
BEGIN
GetEntry:
UNSAFE
PROC [entry: BTree.Entry] =
BEGIN
entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry];
type ← notFound;
fp ← File.nullFP;
IF entry # NIL
THEN
BEGIN
type ← entryPtr.type;
WITH e: entryPtr^
SELECT
FROM
cached => fp ← e.fp;
ENDCASE => ERROR;
END;
[a, lockSet] ← FSLock.LockRecord[vDesc.prefix, nameBody, wantedVersion]
END;
keyRef: REF KeyObject = CheckoutKey[wantedVersion, nameBody];
type: FSBackdoor.EntryType;
lockSet: BOOLEAN;
DO
BTree.ReadEntry[tree: vDesc.tree, key: keyRef, Proc: GetEntry];
IF NOT lockSet THEN FSLock.WaitForRecord[a] ELSE EXIT;
ENDLOOP;
RecycleKey[keyRef];
END;
UpdateLocalEntry:
PUBLIC
PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version, keep:
CARDINAL, fp: File.
FP, update: BTree.UpdateType] =
TRUSTED
BEGIN
WriteEntry:
UNSAFE
PROCEDURE [entry: BTree.Entry] =
BEGIN
localPtr: LONG POINTER TO FSBackdoor.Entry.local = LOOPHOLE[entry];
localPtr^ ← [ SIZE[FSBackdoor.Entry.local], version, , local [keep, fp] ];
localPtr.nameBody ← AppendText[localPtr, nameBody];
END;
keyRef: REF KeyObject = CheckoutKey[version, nameBody];
BTree.UpdateEntry[tree: vDesc.tree, key: keyRef, words: SIZE[FSBackdoor.Entry.local] + SIZE[FSBackdoor.TextRep[Rope.Length[nameBody]]], Proc: WriteEntry, updateType: update];
RecycleKey[keyRef];
END;
UpdateCachedEntry:
PUBLIC
PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version, used: BasicTime.GMT, fp: File.
FP, update: BTree.UpdateType] =
TRUSTED
BEGIN
WriteEntry:
UNSAFE
PROCEDURE [entry: BTree.Entry] =
BEGIN
cachedPtr: LONG POINTER TO FSBackdoor.Entry.cached = LOOPHOLE[entry];
cachedPtr^ ← [ SIZE[FSBackdoor.Entry.cached], version, , cached [used, fp] ];
cachedPtr.nameBody ← AppendText[cachedPtr, nameBody];
END;
keyRef: REF KeyObject = CheckoutKey[version, nameBody];
BTree.UpdateEntry[tree: vDesc.tree, key: keyRef, words: SIZE[FSBackdoor.Entry.cached] + SIZE[FSBackdoor.TextRep[Rope.Length[nameBody]]], Proc: WriteEntry, updateType: update];
RecycleKey[keyRef];
END;
UpdateAttachedEntry:
PUBLIC
PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version, keep:
CARDINAL, created: BasicTime.GMT, attachedTo: Rope.Text, update: BTree.UpdateType] =
TRUSTED
BEGIN
WriteEntry:
UNSAFE
PROCEDURE [entry: BTree.Entry] =
BEGIN
attachedPtr: LONG POINTER TO FSBackdoor.Entry.attached = LOOPHOLE[entry];
attachedPtr^ ← [ SIZE[FSBackdoor.Entry.attached], version, , attached [keep, created, ] ];
attachedPtr.nameBody ← AppendText[attachedPtr, nameBody];
attachedPtr.attachedTo ← AppendText[attachedPtr, attachedTo];
END;
keyRef: REF KeyObject = CheckoutKey[version, nameBody];
BTree.UpdateEntry[tree: vDesc.tree, key: keyRef, words: SIZE[FSBackdoor.Entry.attached] + SIZE[FSBackdoor.TextRep[Rope.Length[nameBody]]] + SIZE[FSBackdoor.TextRep[Rope.Length[attachedTo]]], Proc: WriteEntry, updateType: update];
RecycleKey[keyRef];
END;
DeleteEntry:
PUBLIC
PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version] =
BEGIN
keyRef: REF KeyObject = CheckoutKey[version, nameBody];
[] ← BTree.DeleteKey[tree: vDesc.tree, key: keyRef];
RecycleKey[keyRef];
END;