XNSRemoteFileOpsImpl.mesa
Copyright Ó 1988, 1989, 1990, 1991, 1992 by Xerox Corporation. All rights reserved.
Tim Diebert: September 14, 1988 11:11:57 am PDT
Willie-sue, December 17, 1992 10:58 am PST
Foote, July 9, 1991 1:52 pm PDT
Christian Jacobi, July 24, 1992 1:33 pm PDT
DIRECTORY
AuthenticationP14V2 USING [CallProblem, Credentials, CredentialsType, Problem, SeqWords, Verifier, Which],
Basics USING [FWORD, HWORD, RawBytes, UnsafeBlock],
BasicTime USING [GetClockPulses, GMT, Now, Pulses, PulsesToMicroseconds],
CrRPC USING [BulkDataCheckAbortProc, BulkDataSink, BulkDataSource, BulkDataValueProc, CreateClientHandle, DestroyClientHandle, Error, ErrorReason, Handle, HandleClass, ReadBulkDataStream],
FilingAttributesP10V5 USING [AccessSequence, Attribute, AttributeSequence, AttributeSequenceObject, AttributeValue, FileID],
FilingP10V5 USING [AccessError, AccessProblem, AttributeTypeSequence, AuthenticationError, ByteRange, ChangeAttributes, ChangeControls, Close, Continue, Control, ControlObject, ControlSequence, ControlSequenceObject, Create, Delete, Deserialize, FilterType, Handle, HandleError, List, Lock, Logoff, Logon, Move, nullHandle, Open, ReplaceBytes, Retrieve, RetrieveBytes, ScopeSequence, ServiceError, ServiceProblem, Session, SessionError, SessionProblem, SessionToken, Store, UndefinedError],
FinalizeOps USING [CallQueue, CreateCallQueue, EnableFinalization, FinalizeProc],
IO USING [EndOf, EndOfStream, Error, GetBlock, PutBlock, PutFR1, STREAM, UnsafeGetBlock, UnsafePutBlock, Value],
PFS USING [AccessOptions, CreateOptions, Error, ErrorGroup, FileType, InfoProc, Mutability, NameConfirmProc, NameFormat, NameProc, nullUniqueID, PathFromRope, PropProc, RetrieveConfirmProc, RopeFromPath, StoreConfirmProc, tUnspecified, UniqueID],
PFSBackdoor USING [ErrorCode, ProduceError],
PFSClass USING [AttachProc, CaseSensitiveProc, CloseProc, CopyProc, DeleteProc, EnumerateClientPropertiesProc, EnumerateForInfoProc, EnumerateForNamesProc, FileInfoProc, FileManipulationProcs, FileManipulationProcsObject, FSHandle, FSObject, GetClientPropertyProc, GetHandleProc, GetInfoProc, LookupNameProc, MaintenanceProcs, MaintenanceProcsObject, OpenFile, OpenFileObject, OpenProc, PFSNameToUnixNameProc, ReadProc, Register, RenameProc, RetrieveProc, SetAttributesProc, SetByteCountAndUniqueIDProc, SetClientPropertyProc, StoreProc, SweepProc, ValidateProc, WriteProc],
PFSNames USING [Cat, IsAbsolute, IsADirectory, PATH, PrivatePathObject, StripVersionNumber, SubName, Version, VersionKind],
RefText USING [ObtainScratch, ReleaseScratch],
Rope USING [ActionType, Cat, Concat, Equal, Fetch, FetchType, IsEmpty, MapType, MaxLen, MoveType, ROPE, Substr],
XNS USING [Address, unknownAddress],
XNSAuth USING [AuthenticationError, CallError, Conversation, Credentials, defaultCredentialsLifetime, GetCredentials, GetNextVerifier, Identity, Initiate, Refresh, SetRecipientHostNumber, Verifier],
XNSCH USING [LookupAddressFromRope],
XNSCHName USING [Name, RopeFromName],
XNSCredentials USING [GetIdentity],
XNSRemoteFileDeserialize USING [],
XNSRemoteFilePrivate USING [BuildAttribute, BuildDirectoryAttribute, BuildScopeDepthAndFilter, BuildScopeFilter, BuildStoreAttributes, CallProtected, ConvertFSNameToXNS, ConvertPathToFile, EncapsulateCard32, EncapsulateFileID, GetAccessErrorMsg, GetAuthCallErrorMsg, GetAuthErrorMsg, GetCedarAttributes, GetConnection, GetCrRPCErrorMsg, GetServiceErrorMsg, GetSessionErrorMsg, InfoFromAttributeStream, InfoFromOpenFile, ParsePFSPath, ReportXNSError, ReturnConnection, Revive, UnknownFile],
XNSRemoteFileTypes USING [AttributeSequence, AttributeSequenceObject, AttributeTypeSequence, FileID, fileID, name, GMT, nullGMT, Op, pathname, ROPE, ServerData, ServerDataObject, Session, STREAM, type, XNSOpenFile, XNSOpenFileObject];
XNSRemoteFileOpsImpl: CEDAR MONITOR
IMPORTS BasicTime, CrRPC, FilingP10V5, FinalizeOps, IO, PFS, PFSBackdoor, PFSClass, PFSNames, RefText, Rope, XNSAuth, XNSCH, XNSCHName, XNSCredentials, XNSRemoteFilePrivate
EXPORTS XNSRemoteFileDeserialize
~ BEGIN OPEN XNSRemoteFilePrivate, XNSRemoteFileTypes;
Copied Types
PATH: TYPE = PFSNames.PATH;
FSHandle: TYPE = PFSClass.FSHandle;
FileType: TYPE ~ PFS.FileType;
Version: TYPE = PFSNames.Version;
Parameters
myFlavor: ATOM ¬ $XNS;
initialConnectionTTL: CARD ¬ 8;
credentialsErrorTTL: CARD ¬ 30;
PFS Class name
xnsFlavor: ATOM ~ $XNS;
crrpcClass: CrRPC.HandleClass ~ $CMUX;
Maintenance Object
mo: PFSClass.MaintenanceProcs ~ NEW [PFSClass.MaintenanceProcsObject ¬ [
sweep: XNSSweep,
validate: XNSValidate
]];
File Manipulation Object
fmo: PFSClass.FileManipulationProcs ~ NEW[PFSClass.FileManipulationProcsObject ¬ [
delete: XNSDelete,
enumerateForInfo: XNSEnumerateForInfo,
enumerateForNames: XNSEnumerateForNames,
fileInfo: XNSFileInfo,
lookupName: XNSLookup,
rename: XNSRename,
copy: XNSCopy,
setAttributes: XNSSetAttributes,
setByteCountAndUniqueID: XNSSetByteCountAndUniqueID,
setClientProperty: XNSSetClientProperty,
getClientProperty: XNSGetClientProperty,
enumerateClientProperties: XNSEnumerateClientProperties,
read: XNSRead,
write: XNSWrite,
open: XNSOpen,
close: XNSClose,
store: XNSStore,
retrieve: XNSRetrieve,
-- attach: XNSAttach,
getInfo: XNSGetInfo
]];
Initialized Data - order is important!
vfsClass: PFSClass.FSHandle ~ NEW [PFSClass.FSObject ¬ [
flavor: xnsFlavor,
name: NIL, -- filled in upon instantiation
maintenanceProcs: mo,
procs: fmo,
data: NIL -- filled in upon instantiation
]];
Transport Management
TransportProc: TYPE ~ PROC; -- [tr: Transport];
Transport: TYPE ~ REF TransportBody;
TransportBody: TYPE ~ MONITORED RECORD [
crH: CrRPC.Handle,
vfs: VFS
];
XNS Virtual File System(s)
VFS: TYPE ~ REF VFSInstance;
VFSInstance: TYPE ~ RECORD [
fs: ROPE,
flavorSpecified: BOOL,
sD: ServerData
];
File Manipulation Procs
XNSDelete: PFSClass.DeleteProc ~ {
[h: FSHandle, file: PATH, wantedUniqueID: UniqueID, proc: PFS.NameConfirmProc]
NameConfirmProc: TYPE ~ PROC [fullName: PATH, uniqueID: UniqueID] RETURNS [continue: BOOLFALSE];
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ¬ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data, h];
doIt: BOOL ¬ TRUE;
{ -- for ENABLE --
ENABLE UNWIND => { data.active ¬ FALSE; ReturnConnection[crH] };
P: PROC = {
fileH: FilingP10V5.Handle; withKids: BOOL; version: Version;
[fileH: fileH, version: version, withKids: withKids] ¬ FindFile[h, crH, file, wantedUniqueID, delete];
IF withKids THEN {
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: fileH, session: data.session­];
ERROR PFS.Error[[client, $illegalDelete, IO.PutFR1["%g is a directory with children", [rope[PFS.RopeFromPath[file]]]]]];
};
IF proc # NIL THEN doIt ¬ proc[file, wantedUniqueID];
IF doIt THEN {
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Delete[h: crH, file: fileH, session: data.session­];
};
data.active ¬ FALSE; ReturnConnection[crH];
};
CallProtected[proc: P, h: h, filePath: file, uid: wantedUniqueID];
};
};
XNSEnumerateForInfo: PFSClass.EnumerateForInfoProc ~ {
[h: FSHandle, pattern: PATH, proc: PFS.InfoProc, lbound: PATH, hbound: PATH]
InfoProc: TYPE ~ PROC [fullFName, attachedTo: PATH, uniqueID: UniqueID, bytes: INT, mutability: Mutability, fileType: FileType]
RETURNS [continue: BOOLTRUE];
P: PROC [file: PATH, bytes: INT, created: GMT, fileType: FileType, version: Version, fID: FileID, isDir: BOOL, withKids: BOOL, pathName: REF TEXT]
RETURNS [continue: BOOL] = {
continue ¬ proc[fullFName: file, attachedTo: NIL, uniqueID: MakeUID[created], bytes: bytes, mutability: immutable, fileType: fileType]};
InnerEnumerate[h, pattern, FALSE, P, enumerate];
};
XNSEnumerateForNames: PFSClass.EnumerateForNamesProc ~ {
[h: FSHandle, pattern: PATH, proc: PFS.NameProc, lbound: PATH, hbound: PATH]
NameProc: TYPE ~ PROC [name: PATH] RETURNS [continue: BOOLTRUE;
P: PROC [file: PATH, bytes: INT, created: GMT, fileType: FileType, version: Version,
fID: FileID, isDir: BOOL, withKids: BOOL, pathName: REF TEXT]
RETURNS [continue: BOOL] = {
continue ¬ proc[file]
};
InnerEnumerate[h, pattern, TRUE, P, enumerateNames];
};
EnumerateProc: TYPE ~ PROC [--file:-- PATH, --bytes:-- INT, --created:-- GMT,
--fileType:-- FileType, --version:-- Version, --fID:-- FileID, --isDir:-- BOOL, --withKids:-- BOOL, --pathName:-- REF TEXT] RETURNS [--continue:-- BOOL];
InnerEnumerate: PROC [h: FSHandle, pattern: PATH, namesOnly: BOOL,
proc: EnumerateProc, op: Op] ~ {
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ¬ vfs.sD;
root: FilingP10V5.Handle ¬ data.root;
we need to prefix names with leadingDir, so that full path names are returned - this is a PFS thing
leadingDir: PATH ~ PFSNames.SubName[pattern, 0, 1, PFSNames.IsAbsolute[pattern], PFSNames.IsADirectory[pattern]];
pat: ROPE ~ RemoveLeadingSlash[PFS.RopeFromPath[pattern]];
newPattern: ROPE ¬ ConvertFSNameToXNS[pat, enumerate];
scope: FilingP10V5.ScopeSequence ¬ BuildScopeFilter[matches, newPattern, pathname];
types: AttributeTypeSequence ~ GetCedarAttributes[op];
crH: CrRPC.Handle ¬ GetConnection[data, h];
Listing: CrRPC.BulkDataSink ~ {
[h: CrRPC.Handle, s: STREAM, checkAbort: CrRPC.BulkDataCheckAbortProc] RETURNS [abort: BOOL]
EachElement: CrRPC.BulkDataValueProc ~ {
[s: STREAM] RETURNS [abort: BOOL ← FALSE]
file: ROPE; continue: BOOL;
pathName: REF TEXT;
filePath: PATH;
bytes: INT ¬ 0; created: GMT ¬ nullGMT;
fileType: PFS.FileType ¬ PFS.tUnspecified;
version: Version ¬ [none, 0];
fileID: FileID; isDir: BOOL; numKids: CARD;
[version, bytes, created, fileType, pathName, fileID, isDir, numKids] ¬ InfoFromAttributeStream[s];
file ¬ ConvertPathToFile[pathName, isDir];
IF file = NIL THEN { RefText.ReleaseScratch[pathName]; RETURN[FALSE] }; -- the server has given us a file that has a bad character in it so go around
filePath ¬ PFSNames.Cat[leadingDir, PFS.PathFromRope[file]];
continue ¬ proc[PFS.PathFromRope[file], bytes, created, fileType, version, fileID, isDir,
(isDir AND (numKids # 0)), pathName];
RefText.ReleaseScratch[pathName];
RETURN[NOT continue];
}; -- EachElement
IF ( s.EndOf[] ) THEN { abort ¬ FALSE; RETURN }; -- "Services" feature workaround
abort ¬ CrRPC.ReadBulkDataStream[crH, s, checkAbort, EachElement];
}; -- Listing
{ -- for ENABLE --
DO
ENABLE UNWIND => { data.active ¬ FALSE; ReturnConnection[crH] };
ok: BOOL ¬ TRUE;
openL: LIST OF FilingP10V5.Handle;
listDir: LIST OF ROPE;
listBase: ROPE;
P: PROC = {
FilingP10V5.List[crH, root, types, scope, Listing, data.session­
! PFS.Error => {
IF error.code = $illegalScopeValue THEN ok ¬ FALSE; CONTINUE } ];
IF NOT ok THEN RETURN;
data.active ¬ FALSE; ReturnConnection[crH];
};
CallProtected[proc: P, h: h, filePath: NIL, uid: PFS.nullUniqueID];
IF ok THEN {
FOR l: LIST OF FilingP10V5.Handle ¬ openL, l.rest UNTIL l = NIL DO
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: l.first, session: data.session­];
ENDLOOP;
RETURN;
};
we are accessing a server that does not implement filters on pathnames, so we have to ramble down the directory path ourselves, and then do a depth 1 listing at the directory level, filtering on just the filename
BEGIN
fileH: FilingP10V5.Handle ¬ FilingP10V5.nullHandle;
[listDir, listBase] ¬ ParsePFSPath[pattern];
FOR l: LIST OF ROPE ¬ listDir, l.rest UNTIL l = NIL DO
directory: ROPE ~ l.first;
found: BOOL ¬ TRUE;
attributes: AttributeSequence ¬ BuildAttribute[directory, name];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Open[h: crH, attributes: attributes, directory: fileH,
controls: nullControls, session: data.session­
! FilingP10V5.AccessError => IF problem = fileNotFound THEN
{found ¬ FALSE; CONTINUE} ELSE REJECT];
IF NOT found THEN {
data.active ¬ FALSE; ReturnConnection[crH];
RETURN;
};
openL ¬ CONS[fileH, openL];
ENDLOOP;
END;
IF openL = NIL THEN RETURN;
root ¬ openL.first;
scope ¬ BuildScopeDepthAndFilter[1, matches, ConvertFSNameToXNS[listBase, enumerate], name];
ENDLOOP;
};
};
XNSFileInfo: PFSClass.FileInfoProc ~ {
[h: FSHandle, file: PATH, wantedUniqueID: UniqueID]
RETURNS [version: Version, attachedTo: PATH, bytes: INT, uniqueID: UniqueID, mutability: PFS.Mutability, fileType: PFS.FileType]
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data, h];
{ -- for ENABLE --
ENABLE UNWIND => {data.active ¬ FALSE; ReturnConnection[crH]};
P: PROC = {
fileH: FilingP10V5.Handle;
created: GMT;
[fileH, version, bytes, created, fileType] ¬
FindFile[h, crH, file, PFS.nullUniqueID, enumerate];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: fileH, session: data.session­];
data.active ¬ FALSE;
ReturnConnection[crH];
mutability ¬ immutable;
uniqueID ¬ MakeUID[created];
};
CallProtected[proc: P, h: h, filePath: file, uid: wantedUniqueID];
};
};
XNSLookup: PFSClass.LookupNameProc ~ {
[h: FSHandle, file: PATH] RETURNS[PATH]
vfs: VFS ~ NARROW[h.data]; -- sanity check
UnImplemented["XNSLookup"];
RETURN[file];
};
XNSRename: PFSClass.RenameProc ~ {
[h: FSHandle, fromFile: PATH, wantedUniqueID: UniqueID, toFile: PATH, createOptions: PFS.CreateOptions, proc: PFS.NameConfirmProc]
NameConfirmProc: TYPE ~ PROC [fullName: PATH, uniqueID: UniqueID] RETURNS [continue: BOOLFALSE];
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data, h];
fromDir, toDir, lor2: LIST OF ROPE; fromBase, toBase: ROPE;
different: BOOL ¬ FALSE;
openL: LIST OF FilingP10V5.Handle ¬ NIL;
[fromDir, fromBase -- version--] ¬ ParsePFSPath[fromFile];
[toDir, toBase -- version--] ¬ ParsePFSPath[toFile];
lor2 ¬ toDir; -- so we keep toDir intact
FOR lor1: LIST OF ROPE ¬ fromDir, lor1.rest UNTIL lor1 = NIL DO
IF lor2 = NIL THEN {different ¬ TRUE; EXIT};
IF lor2.first = NIL THEN {different ¬ TRUE; EXIT};
IF ~Rope.Equal[s1: lor1.first, s2: lor2.first, case: FALSE] THEN {different ¬ TRUE; EXIT};
lor2 ¬ lor2.rest;
ENDLOOP;
IF lor2 # NIL THEN different ¬ TRUE;
IF different
THEN {
ENABLE UNWIND => {
FOR l: LIST OF FilingP10V5.Handle ¬ openL, l.rest UNTIL l = NIL DO
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: l.first, session: data.session­
! FilingP10V5.AuthenticationError, FilingP10V5.HandleError, FilingP10V5.SessionError, FilingP10V5.UndefinedError, CrRPC.Error, IO.EndOfStream, IO.Error => CONTINUE
];
ENDLOOP;
data.active ¬ FALSE;
ReturnConnection[crH];
};
P: PROC = {
fileH, orig: FilingP10V5.Handle ¬ FilingP10V5.nullHandle;
version: Version;
[fileH: orig, version: version] ¬ FindFile[h, crH, fromFile, wantedUniqueID, rename]; -- may raise not found error
IF ( proc = NIL ) OR ( proc[fromFile, wantedUniqueID]) THEN { -- ???
attributes: AttributeSequence;
FOR l: LIST OF ROPE ¬ toDir, l.rest UNTIL l = NIL DO
directory: ROPE ~ l.first;
build: BOOL ¬ FALSE;
attributes ¬ BuildAttribute[directory, name];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Open[h: crH, attributes: attributes, directory: fileH,
controls: nullControls, session: data.session­
! FilingP10V5.AccessError => IF problem = fileNotFound THEN
{build ¬ TRUE; CONTINUE} ELSE REJECT];
IF build THEN { -- fileH is still the handle for the previous level directory
attributes ¬ BuildDirectoryAttribute[directory];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Create[h: crH, directory: fileH, attributes: attributes, controls: nullControls, session: data.session­];
};
openL ¬ CONS[fileH, openL];
ENDLOOP;
attributes ¬ BuildAttribute[toBase, name];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Move[h: crH, file: orig, destinationDirectory: fileH,
attributes: attributes, session: data.session­];
};
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: orig, session: data.session­];
FOR l: LIST OF FilingP10V5.Handle ¬ openL, l.rest UNTIL l = NIL DO
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: l.first, session: data.session­];
ENDLOOP;
data.active ¬ FALSE; ReturnConnection[crH];
};
CallProtected[proc: P, h: h, filePath: fromFile, uid: wantedUniqueID];
}
ELSE { -- the subdirectory is the same so just change the name
ENABLE UNWIND => {data.active ¬ FALSE; ReturnConnection[crH]};
P: PROC = {
fileH: FilingP10V5.Handle; version: Version;
[fileH: fileH, version: version] ¬ FindFile[h, crH, fromFile, wantedUniqueID, rename];
IF ( proc = NIL ) OR ( proc[fromFile, wantedUniqueID] ) THEN { -- ???
attributes: AttributeSequence ¬ BuildAttribute[toBase, name];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.ChangeAttributes[h: crH, file: fileH, attributes: attributes, session: data.session­];
};
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: fileH, session: data.session­];
data.active ¬ FALSE; ReturnConnection[crH];
};
CallProtected[proc: P, h: h, filePath: fromFile, uid: wantedUniqueID];
};
};
XNSCopy: PFSClass.CopyProc ~ {
[h: FSHandle, fromFile: PATH, wantedUniqueID: UniqueID, toFile: PATH, createOptions: PFS.CreateOptions, proc: PFS.NameConfirmProc] RETURNS [done: BOOLFALSE]
vfs: VFS ~ NARROW[h.data]; -- sanity check
UnImplemented["XNSCopy"];
};
XNSSetAttributes: PFSClass.SetAttributesProc = {
[h: FSHandle, file: OpenFile, attributes: PFS.CreateOptions]
This only does SetFileType
vfs: VFS ~ NARROW[h.data];
data: ServerData ~ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data, h];
xattributes: AttributeSequence ¬ NEW[AttributeSequenceObject[1]];
xattributes[0] ¬ [type: type, value: EncapsulateCard32[attributes.fileType]];
{ -- for ENABLE --
ENABLE UNWIND => { data.active ¬ FALSE; ReturnConnection[crH] };
P: PROC = {
xnsFile: XNSOpenFile ~ NARROW[file.data];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.ChangeAttributes[h: crH, file: xnsFile.fileH, attributes: xattributes, session: data.session­];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: xnsFile.fileH, session: data.session^];
data.active ¬ FALSE;
ReturnConnection[crH];
};
CallProtected[proc: P, h: h, filePath: NIL, uid: PFS.nullUniqueID];
};
};
XNSSetByteCountAndUniqueID: PFSClass.SetByteCountAndUniqueIDProc ~ {
[h: FSHandle, file: OpenFile, bytes: INT, uniqueID: PFS.UniqueID]
vfs: VFS ~ NARROW[h.data]; -- sanity check
UnImplemented["XNSSetByteCountAndUniqueID"];
};
ControlSeqFromRope: PROC [r: Rope.ROPE] RETURNS [cs: FilingP10V5.ControlSequence] = {
lock: FilingP10V5.Lock;
SELECT TRUE FROM
Rope.Equal[r, "lockNone"] => lock ¬ lockNone;
Rope.Equal[r, "share"] => lock ¬ share;
Rope.Equal[r, "exclusive"] => lock ¬ exclusive;
ENDCASE => ERROR; -- unknows ControlType
cs ¬ NEW [FilingP10V5.ControlSequenceObject[1]];
cs[0] ¬ NEW [FilingP10V5.ControlObject ¬ [lock[lock]]];
RETURN[cs];
};
XNSSetClientProperty: PFSClass.SetClientPropertyProc -- [h: FSHandle, file: OpenFile, propertyName: ROPE, propertyValue: ROPE] -- = {
vfs: VFS ~ NARROW[h.data]; -- sanity check
data: ServerData ~ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data, h];
SELECT TRUE FROM
Rope.Equal[propertyName, "ControlType"] => {
ENABLE UNWIND => { data.active ¬ FALSE; ReturnConnection[crH] };
P: PROC = {
controls: FilingP10V5.ControlSequence = ControlSeqFromRope[propertyValue];
xnsFile: XNSOpenFile ~ NARROW[file.data];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.ChangeControls[crH, xnsFile.fileH, controls, data.session­];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
};
CallProtected[proc: P, h: h, filePath: NIL, uid: PFS.nullUniqueID];
};
ENDCASE => PFSBackdoor.ProduceError[notImplemented, Rope.Concat[propertyName, " is not an implemented property in the XNS view"]];
};
XNSGetClientProperty: PFSClass.GetClientPropertyProc ~ {
[h: FSHandle, file: OpenFile, propertyName: ROPE] RETURNS [propertyValue: ROPE]
vfs: VFS ~ NARROW[h.data]; -- sanity check
UnImplemented["XNSGetClientProperty"];
RETURN[propertyValue];
};
XNSEnumerateClientProperties: PFSClass.EnumerateClientPropertiesProc ~ {
[h: FSHandle, file: OpenFile, proc: PFS.PropProc]
vfs: VFS ~ NARROW[h.data]; -- sanity check
UnImplemented["XNSEnumerateClientProperties"];
};
XNSRead: PFSClass.ReadProc ~ {
h: FSHandle, file: OpenFile, filePosition, nBytes: CARD, to: LONG POINTER, toStart: CARD] RETURNS [bytesRead: INT]
vfs: VFS ~ NARROW[h.data];
data: ServerData ¬ vfs.sD;
P: PROC ~ {
xnsFile: XNSOpenFile ~ NARROW[file.data];
range: FilingP10V5.ByteRange;
crH: CrRPC.Handle;
posAsInt: INT ¬ filePosition;
Sink: CrRPC.BulkDataSink ~ TRUSTED {
block: Basics.UnsafeBlock ~ [base: toPtr, startIndex: toStart, count: nBytes];
abort ¬ checkAbort[h];
IF ( NOT s.EndOf[] ) THEN bytesRead ¬ s.UnsafeGetBlock[block];
};
IF file.bytes <= posAsInt THEN RETURN; -- at end
range ¬ [filePosition, nBytes];
crH ¬ CrRPC.CreateClientHandle[crrpcClass, data.address];
FilingP10V5.RetrieveBytes[crH, xnsFile.fileH, range, Sink, data.session­];
CrRPC.DestroyClientHandle[crH];
};
bytesRead ¬ 0;
CallProtected[proc: P, h: h, filePath: NIL, uid: PFS.nullUniqueID];
};
XNSWrite: PFSClass.WriteProc ~ {
h: FSHandle, file: OpenFile, filePosition, nBytes: CARD, from: LONG POINTER, fromStart: CARD] RETURNS [bytesWritten: INT]
vfs: VFS ~ NARROW[h.data];
data: ServerData ¬ vfs.sD;
P: PROC ~ {
xnsFile: XNSOpenFile ~ NARROW[file.data];
range: FilingP10V5.ByteRange ~ [filePosition, nBytes];
Source: CrRPC.BulkDataSource ~ TRUSTED {
block: Basics.UnsafeBlock ~ [base: fromPtr, startIndex: fromStart, count: nBytes];
abort ¬ checkAbort[h];
s.UnsafePutBlock[block];
bytesWritten ¬ nBytes;
};
crH: CrRPC.Handle ~ CrRPC.CreateClientHandle[crrpcClass, data.address];
FilingP10V5.ReplaceBytes[crH, xnsFile.fileH, range, Source, data.session­];
CrRPC.DestroyClientHandle[crH];
};
bytesWritten ¬ 0;
CallProtected[proc: P, h: h, filePath: NIL, uid: PFS.nullUniqueID];
};
XNSOpen: PFSClass.OpenProc ~ {
h: FSHandle, file: PATH, wantedUniqueID: UniqueID, access: PFS.AccessOptions, checkFileType: BOOL, fileType: PFS.FileType, createOptions: PFS.CreateOptions] RETURNS [OpenFile
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
LegitimateMatch: PROC RETURNS [match: BOOL] ~ {
match ¬ TRUE;
};
version: Version;
attachedTo: PATH;
bytes: INT;
uniqueID: PFS.UniqueID;
mutability: PFS.Mutability;
zfileType: FileType;
of: PFSClass.OpenFile ¬ NIL;
OpenInner: PROC ~ {
ENABLE UNWIND => { NULL };
xnsFile: XNSOpenFile;
newFile: ROPE ~ RemoveLeadingSlash[PFS.RopeFromPath[file]];
attributes: AttributeSequence ~ BuildAttribute[newFile, pathname];
fileH: FilingP10V5.Handle;
crH: CrRPC.Handle ~ CrRPC.CreateClientHandle[crrpcClass, data.address];
fileH ¬ FilingP10V5.Open[h: crH, attributes: attributes, directory: data.root, controls: nullControls, session: data.session­];
CrRPC.DestroyClientHandle[crH];
xnsFile ¬ NEW[XNSOpenFileObject ¬ [fileH, BasicTime.Now[]]];
of ¬ NEW[PFSClass.OpenFileObject ¬ [fs: class, fullFName: file, attachedTo: attachedTo, uniqueID: uniqueID, bytes: bytes, mutability: mutability, fileType: zfileType, access: access, state: open, data: xnsFile]];
OpenInternal[data];
};
[version, attachedTo, bytes, uniqueID, mutability, zfileType] ¬
XNSFileInfo[h, file, wantedUniqueID];
IF ( NOT LegitimateMatch[] )
THEN {
UnknownFile[PFS.RopeFromPath[file], wantedUniqueID.egmt.gmt];
RETURN[NIL]; -- not reached!
};
CallProtected[proc: OpenInner, h: h, filePath: file, uid: wantedUniqueID];
RETURN[of];
};
XNSClose: PFSClass.CloseProc ~ {
h: FSHandle, file: OpenFile
vfs: VFS ~ NARROW[h.data];
data: ServerData ~ vfs.sD;
P: PROC ~ {
xnsFile: XNSOpenFile ~ NARROW[file.data];
IF (data.session # NIL ) THEN {
crH: CrRPC.Handle ~ CrRPC.CreateClientHandle[crrpcClass, data.address];
FilingP10V5.Close[h: crH, file: xnsFile.fileH, session: data.session­];
CrRPC.DestroyClientHandle[crH];
};
CloseInternal[data];
};
CallProtected[proc: P, h: h, filePath: NIL, uid: PFS.nullUniqueID];
};
XNSStore: PFSClass.StoreProc ~ {
[h: FSHandle, file: PATH, wantedUniqueID: UniqueID, str: IO.STREAM, proc: PFS.StoreConfirmProc, createOptions: PFS.CreateOptions]
StoreConfirmProc: TYPE ~ PROC [fullName: PATH] RETURNS [continue: BOOLEANTRUE];
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data, h];
Content: CrRPC.BulkDataSource = {
[h: CrRPC.Handle, s: STREAM, checkAbort: CrRPC.BulkDataCheckAbortProc] RETURNS [abort: BOOL]
buffer: REF TEXT = RefText.ObtainScratch[512];
abort ¬ FALSE;
DO
nBytes: NAT = IO.GetBlock[str, buffer, 0];
IF nBytes = 0 THEN EXIT;
IO.PutBlock[s, buffer, 0, nBytes];
ENDLOOP;
RefText.ReleaseScratch[buffer];
};
dir: LIST OF ROPE; fName, homeDir: ROPE;
fileH, newFileH: FilingP10V5.Handle ¬ FilingP10V5.nullHandle;
openL: LIST OF FilingP10V5.Handle ¬ NIL;
version: Version; -- aka 0
doIt: BOOL ¬ TRUE;
[dir, fName, version] ¬ ParsePFSPath[file];
homeDir ¬ dir.first; dir ¬ dir.rest; -- take off the main directory name
{ -- for ENABLE
ENABLE UNWIND => {
FOR l: LIST OF FilingP10V5.Handle ¬ openL, l.rest UNTIL l = NIL DO
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: l.first, session: data.session­
! FilingP10V5.AuthenticationError, FilingP10V5.HandleError, FilingP10V5.SessionError, FilingP10V5.UndefinedError, CrRPC.Error, IO.EndOfStream, IO.Error => CONTINUE
];
ENDLOOP;
data.active ¬ FALSE;
ReturnConnection[crH];
};
P: PROC = {
attributes: AttributeSequence ¬ BuildAttribute[homeDir, name];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Open[h: crH, attributes: attributes, directory: fileH, controls: nullControls, session: data.session­
! FilingP10V5.AccessError => IF problem = fileNotFound THEN CONTINUE ELSE REJECT];
IF fileH = FilingP10V5.nullHandle THEN { -- directory not found
data.active ¬ FALSE; ReturnConnection[crH];
ReportXNSError[unknownFile, h, file, nullGMT]; -- ???
};
openL ¬ CONS[fileH, openL];
FOR lor: LIST OF ROPE ¬ dir, lor.rest UNTIL lor = NIL DO
directory: ROPE ~ lor.first;
build: BOOL ¬ FALSE;
attributes ¬ BuildAttribute[directory, name];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Open[h: crH, attributes: attributes, directory: fileH,
controls: nullControls, session: data.session­
! FilingP10V5.AccessError => IF problem = fileNotFound THEN
{build ¬ TRUE; CONTINUE} ELSE REJECT];
IF build THEN { -- fileH is still the handle for the previous level directory
attributes ¬ BuildDirectoryAttribute[directory];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Create[h: crH, directory: fileH, attributes: attributes, controls: nullControls, session: data.session­];
};
openL ¬ CONS[fileH, openL];
ENDLOOP;
attributes ¬ BuildAttribute[fName, name];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
newFileH ¬ FilingP10V5.Open[h: crH, attributes: attributes, directory: fileH, controls: nullControls, session: data.session­
! FilingP10V5.AccessError => IF problem = fileNotFound THEN CONTINUE ELSE REJECT];
IF newFileH # FilingP10V5.nullHandle
THEN {
IF version.versionKind = none THEN {
version ¬ InfoFromOpenFile[data, crH, newFileH, store].version;
IF version.versionKind = numeric THEN
version.version ¬ version.version + 1;
};
}
ELSE { IF version.versionKind = none THEN version ¬ [numeric, 1] };
IF proc # NIL THEN {
doIt ¬ proc[file];
};
attributes ← BuildStoreAttributes[fName, version.version, created];
IF doIt THEN {
attributes ¬ BuildStoreAttributes[fName, version.version, wantedUniqueID.egmt.gmt];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Store[h: crH, directory: fileH, attributes: attributes, controls: nullControls, content: Content, session: data.session­];
};
openL ¬ CONS[fileH, openL];
FOR l: LIST OF FilingP10V5.Handle ¬ openL, l.rest UNTIL l = NIL DO
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: l.first, session: data.session­];
ENDLOOP;
data.active ¬ FALSE; ReturnConnection[crH];
};
CallProtected[proc: P, h: h, filePath: file, uid: wantedUniqueID];
};
};
XNSRetrieve: PFSClass.RetrieveProc ~ {
[h: FSHandle, file: PATH, wantedUniqueID: UniqueID, proc: PFS.RetrieveConfirmProc, checkFileType: BOOLFALSE, fileType: PFS.FileType]
RetrieveConfirmProc: TYPE ~ PROC [fullFName: PATH, bytes: INT, uniqueID: UniqueID]
RETURNS [STREAM];
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
localStream: STREAM ¬ NIL;
Content: CrRPC.BulkDataSink = {
[h: CrRPC.Handle, s: STREAM, checkAbort: CrRPC.BulkDataCheckAbortProc] RETURNS [abort: BOOL]
buffer: REF TEXT = RefText.ObtainScratch[512];
nBytes: NAT;
abort ¬ FALSE;
DO
IF s.EndOf[] THEN EXIT; -- seems to end this way sometimes
nBytes ¬ s.GetBlock[buffer, 0];
IF nBytes = 0 THEN EXIT;
localStream.PutBlock[buffer, 0, nBytes];
ENDLOOP;
RefText.ReleaseScratch[buffer];
};
InnerRetrieve: PROC = {
crH: CrRPC.Handle ¬ GetConnection[data, h];
{ -- for ENABLE --
ENABLE UNWIND => { data.active ¬ FALSE; ReturnConnection[crH] };
P: PROC = {
version: Version; bytes: INT; created: GMT;
fileH: FilingP10V5.Handle;
[fileH: fileH, version: version, bytes: bytes, created: created] ¬ FindFile[h, crH, file, wantedUniqueID, retrieve];
localStream ¬ proc[file, bytes, MakeUID[created]];
IF localStream # NIL THEN {
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Retrieve[h: crH, file: fileH, content: Content, session: data.session­];
};
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: fileH, session: data.session­];
data.active ¬ FALSE; ReturnConnection[crH];
};
CallProtected[proc: P, h: h, filePath: file, uid: wantedUniqueID];
};
};
InnerRetrieve[];
};
XNSGetInfo: PFSClass.GetInfoProc ~ {
h: FSHandle, file: OpenFile] RETURNS [fullFName, attachedTo: PATH, uniqueID: UniqueID, bytes: INT, mutability: PFS.Mutability, fileType: PFS.FileType
vfs: VFS ~ NARROW[h.data];
Generic Implementation
fullFName ¬ file.fullFName;
attachedTo ¬ file.attachedTo;
uniqueID ¬ file.uniqueID;
bytes ¬ file.bytes;
mutability ¬ file.mutability;
fileType ¬ file.fileType;
};
XNSSerialize: PFSClass.SerializeProc = {
[h: ServerHandle, directory: ROPE, str: STREAM, proc: ConfirmRetrieveProc]
};
XNSDeserialize: PUBLIC PROC[h: FSHandle, directory: PATH, str: STREAM,
proc: PFS.StoreConfirmProc] ~ {
StoreConfirmProc: TYPE ~ PROC [fullName: PATH] RETURNS [continue: BOOLTRUE];
vfs: VFS ~ NARROW[h.data];
data: ServerData ~ vfs.sD;
crH: CrRPC.Handle ¬ GetConnection[data, h];
Content: CrRPC.BulkDataSource = {
[h: CrRPC.Handle, s: STREAM, checkAbort: CrRPC.BulkDataCheckAbortProc] RETURNS [abort: BOOL]
buffer: REF TEXT = RefText.ObtainScratch[512];
abort ¬ FALSE;
DO
nBytes: NAT = IO.GetBlock[str, buffer, 0];
IF nBytes = 0 THEN EXIT;
IO.PutBlock[s, buffer, 0, nBytes];
ENDLOOP;
RefText.ReleaseScratch[buffer];
};
dir: LIST OF ROPE; fName, homeDir: ROPE;
fileH, newFileH: FilingP10V5.Handle ¬ FilingP10V5.nullHandle;
openL: LIST OF FilingP10V5.Handle ¬ NIL;
version: Version ¬ [lowest, 0]; -- aka 0
doIt: BOOL ¬ TRUE;
[dir, fName, version] ¬ ParsePFSPath[directory];
homeDir ¬ dir.first; dir ¬ dir.rest; -- take off the main directory name
{ -- for ENABLE
ENABLE UNWIND => {
FOR l: LIST OF FilingP10V5.Handle ¬ openL, l.rest UNTIL l = NIL DO
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: l.first, session: data.session­
! FilingP10V5.AuthenticationError, FilingP10V5.HandleError, FilingP10V5.SessionError, FilingP10V5.UndefinedError, CrRPC.Error, IO.EndOfStream, IO.Error => CONTINUE
];
ENDLOOP;
data.active ¬ FALSE;
ReturnConnection[crH];
};
P: PROC = {
attributes: AttributeSequence ¬ BuildAttribute[homeDir, name];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Open[h: crH, attributes: attributes, directory: fileH, controls: nullControls, session: data.session­
! FilingP10V5.AccessError => IF problem = fileNotFound THEN CONTINUE ELSE REJECT];
IF fileH = FilingP10V5.nullHandle THEN { -- directory not found
data.active ¬ FALSE; ReturnConnection[crH];
ReportXNSError[unknownFile, h, directory, nullGMT]; --???
};
openL ¬ CONS[fileH, openL];
FOR lor: LIST OF ROPE ¬ dir, lor.rest UNTIL lor = NIL DO
directory: ROPE ~ lor.first;
build: BOOL ¬ FALSE;
attributes ¬ BuildAttribute[directory, name];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Open[h: crH, attributes: attributes, directory: fileH,
controls: nullControls, session: data.session­
! FilingP10V5.AccessError => IF problem = fileNotFound THEN
{build ¬ TRUE; CONTINUE} ELSE REJECT];
IF build THEN { -- fileH is still the handle for the previous level directory
attributes ¬ BuildDirectoryAttribute[directory];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Create[h: crH, directory: fileH, attributes: attributes, controls: nullControls, session: data.session­];
};
openL ¬ CONS[fileH, openL];
ENDLOOP;
attributes ¬ BuildAttribute[fName, name];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
newFileH ¬ FilingP10V5.Open[h: crH, attributes: attributes, directory: fileH, controls: nullControls, session: data.session­
! FilingP10V5.AccessError => IF problem = fileNotFound THEN CONTINUE ELSE REJECT];
IF newFileH # FilingP10V5.nullHandle
THEN {
IF version.versionKind = none
THEN {
version ¬ InfoFromOpenFile[data, crH, newFileH, store].version;
IF version.versionKind = numeric THEN
version.version ¬ version.version + 1; -- not set ??
};
}
ELSE { IF version.versionKind = none THEN version ¬ [numeric, 1] };
IF proc # NIL THEN doIt ¬ proc[directory]; -- ???
attributes ← BuildStoreAttributes[fName, version, created];
attributes ¬ NEW [AttributeSequenceObject[0]];
IF doIt THEN {
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Deserialize[h: crH, directory: fileH, attributes: attributes, controls: nullControls, serializedFile: Content, session: data.session­];
};
openL ¬ CONS[fileH, openL];
FOR l: LIST OF FilingP10V5.Handle ¬ openL, l.rest UNTIL l = NIL DO
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
FilingP10V5.Close[h: crH, file: l.first, session: data.session­];
ENDLOOP;
data.active ¬ FALSE; ReturnConnection[crH];
};
CallProtected[proc: P, h: h, filePath: directory, uid: PFS.nullUniqueID];
};
};
Filing defaults
nullAttributes: AttributeSequence ~ NEW[AttributeSequenceObject[0]];
nullControls: FilingP10V5.ControlSequence ~ NEW [FilingP10V5.ControlSequenceObject[0]];
Registered with PFSClass
XNSGetServer: PFSClass.GetHandleProc ~ {
[fs: ROPE, flavorSpecified: BOOL] RETURNS [h: FSHandle, downMsg: ROPE]
Returns [NIL, NIL] if server doesn't exist.
Returns [NIL, msg] if server exists but is down.
The flavorSpecified parameter is ignored.
vfs: VFS ~ NEW[VFSInstance ¬ [fs: fs, flavorSpecified: flavorSpecified, sD: NIL] ];
data: ServerData;
session: REF Session;
qualified: XNSCHName.Name; address: REF XNS.Address; name: ROPE;
crH: CrRPC.Handle;
conversation: XNSAuth.Conversation; credentials: XNSAuth.Credentials;
verifier: XNSAuth.Verifier; realId: XNSAuth.Identity; ttl: CARD ¬ initialConnectionTTL;
root: FilingP10V5.Handle;
opened: BOOL ¬ FALSE;
downMsg ¬ "can\'t connect";
{
ENABLE {
CrRPC.Error =>
{ downMsg ¬ GetCrRPCErrorMsg[errorReason, text]; CONTINUE };
FilingP10V5.AccessError =>
{ downMsg ¬ GetAccessErrorMsg[problem]; CONTINUE };
FilingP10V5.AuthenticationError =>
{ downMsg ¬ GetAuthErrorMsg[problem]; CONTINUE };
FilingP10V5.ServiceError =>
{ downMsg ¬ GetServiceErrorMsg[problem]; CONTINUE };
FilingP10V5.SessionError =>
{ downMsg ¬ GetSessionErrorMsg[problem]; CONTINUE };
FilingP10V5.UndefinedError =>
{ downMsg ¬ "Undefined error from server"; CONTINUE };
XNSAuth.AuthenticationError =>
{ downMsg ¬ GetAuthErrorMsg[problem]; CONTINUE };
XNSAuth.CallError =>
{ downMsg ¬ GetAuthCallErrorMsg[problem]; CONTINUE };
};
add: XNS.Address;
[qualified, add] ¬ XNSCH.LookupAddressFromRope[fs];
IF add = XNS.unknownAddress THEN -- host not known -- RETURN[NIL, NIL];
name ¬ XNSCHName.RopeFromName[qualified];
address ¬ NEW[XNS.Address ¬ add];
crH ¬ CrRPC.CreateClientHandle[crrpcClass, address];
realId ¬ XNSCredentials.GetIdentity[];
conversation ¬ XNSAuth.Initiate[realId, qualified];
credentials ¬ XNSAuth.GetCredentials[conversation];
XNSAuth.SetRecipientHostNumber[conversation, address.host];
verifier ¬ XNSAuth.GetNextVerifier[conversation];
session ¬ NEW[Session ¬ FilingP10V5.Logon[crH, qualified, credentials, verifier]];
session.verifier ¬ XNSAuth.GetNextVerifier[conversation];
ttl ¬ FilingP10V5.Continue[crH, session­];
XNSAuth.Refresh[conversation, ttl];
session.verifier ¬ XNSAuth.GetNextVerifier[conversation];
root ¬ FilingP10V5.Open[crH, nullAttributes, FilingP10V5.nullHandle, nullControls, session­];
CrRPC.DestroyClientHandle[crH];
opened ¬ TRUE;
};
IF NOT opened THEN RETURN [NIL, downMsg];
data ¬ NEW[ServerDataObject ¬
[downMsg: NIL, connectionTTL: ttl, continuance: ttl, session: session, conversation: conversation, conversationTTL: 10000, qualified: qualified, serverName: name, address: address, root: root]];
[] ¬ FinalizeOps.EnableFinalization[data, dfq];
vfs.sD ¬ data;
h ¬ NEW[PFSClass.FSObject ¬ vfsClass­];
h.name ¬ fs;
h.data ¬ vfs;
RETURN [h, NIL];
};
XNSValidate: PFSClass.ValidateProc ~ {
[h: FSHandle] RETURNS [obsolete: BOOL, downMsg: ROPE]
ENABLE UNWIND => NULL;
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
IF data.connectionTTL > 0 THEN [] ¬ Revive[data]; -- returns 0 on errors
RETURN [(data.connectionTTL <= 0), NIL];
};
dfq: FinalizeOps.CallQueue ~ FinalizeOps.CreateCallQueue[Finalize];
XNSSweep: PFSClass.SweepProc -- [h: FSHandle, seconds: CARD] -- ~ {
vfs: VFS ~ NARROW[h.data];
data: ServerData ~ vfs.sD;
session: REF Session ¬ NIL; address: REF XNS.Address;
XNSSweepInner: ENTRY PROC ~ {
ENABLE UNWIND => NULL;
IF data.connectionTTL > seconds
THEN data.connectionTTL ¬ data.connectionTTL - seconds
ELSE {
IF NOT data.active THEN {-- there is no time left on the connection so kill it.
session ¬ data.session;
address ¬ data.address;
data.session ¬ NIL;
data.connectionTTL ¬ 0;
};
};
};
XNSSweepInner[];
IF session # NIL THEN {
crH: CrRPC.Handle ~ CrRPC.CreateClientHandle[crrpcClass, address];
FilingP10V5.Logoff[h: crH, session: session­ ! FilingP10V5.SessionError, FilingP10V5.AuthenticationError, CrRPC.Error => CONTINUE];
CrRPC.DestroyClientHandle[crH];
};
};
Finalize: FinalizeOps.FinalizeProc = {
droppedData: ServerData ~ NARROW[object];
session: REF Session ~ droppedData.session;
IF session # NIL THEN {
address: REF XNS.Address ~ droppedData.address;
crH: CrRPC.Handle ¬ CrRPC.CreateClientHandle[crrpcClass, address];
FilingP10V5.Logoff[h: crH, session: session­ ! FilingP10V5.SessionError, FilingP10V5.AuthenticationError, CrRPC.Error => CONTINUE];
CrRPC.DestroyClientHandle[crH];
droppedData.session ¬ NIL;
};
};
Utilities
startO, endO: CARD;
openTime: CARD;
UnImplemented: PROC [msg: ROPE] ~
{ PFSBackdoor.ProduceError[notImplemented, msg] };
RemoveLeadingSlash: PROC [maybeWithSlash: ROPE]
RETURNS [noSlash: ROPE] ~ {
IF Rope.IsEmpty[maybeWithSlash] THEN RETURN[maybeWithSlash];
noSlash ¬ IF ( maybeWithSlash.Fetch[0] = '/ ) THEN maybeWithSlash.Substr[1] ELSE maybeWithSlash;
};
FindFile: PROC [h: FSHandle, crH: CrRPC.Handle, fromPath: PATH, wantedUniqueID: PFS.UniqueID, op: Op]
RETURNS [fileH: FilingP10V5.Handle ¬ FilingP10V5.nullHandle, version: Version, bytes: INT, created: GMT, fileType: FileType, pathName: REF TEXT, fID: FileID,
isDir: BOOL, withKids: BOOL] = {
wantedTime: GMT ~ wantedUniqueID.egmt.gmt;
file: ROPE ¬ RemoveLeadingSlash[PFS.RopeFromPath[fromPath]];
class: PFSClass.FSHandle ~ h;
vfs: VFS ~ NARROW[class.data];
data: ServerData ~ vfs.sD;
timeSearch: BOOL ~ wantedTime # nullGMT;
attributes: AttributeSequence;
newFile: ROPE ¬ ConvertFSNameToXNS[file, op];
IF NOT Revive[data, crH] THEN ReportXNSError[resourceLimitExceeded, h, NIL, nullGMT];
attributes ¬ BuildAttribute[newFile, pathname];
startO ¬ BasicTime.GetClockPulses[];
fileH ¬ FilingP10V5.Open[h: crH, attributes: attributes, directory: data.root,
controls: nullControls, session: data.session­
! FilingP10V5.AccessError => {IF problem = fileNotFound THEN CONTINUE ELSE REJECT};
];
IF fileH # FilingP10V5.nullHandle THEN { -- found
endO ¬ BasicTime.GetClockPulses[];
[version, bytes, created, fileType, pathName, fID, isDir, withKids] ¬ InfoFromOpenFile[data, crH, fileH, op];
IF (NOT timeSearch) OR (wantedTime = created) THEN { -- this one is ok
openTime ¬ BasicTime.PulsesToMicroseconds[endO] - BasicTime.PulsesToMicroseconds[startO];
RETURN[fileH, version, bytes, created, fileType, pathName, fID, isDir, withKids];
};
};
at this point a file may or may not have been found. If it was found it didn't meet the create time requested so we need to do an enumerate to find a file matching it
IF timeSearch THEN { -- since it is a timeSearch we need to do an enumerate
Note: PROC [eFile: PATH, eBytes: INT, eCreated: GMT, type: PFS.FileType, eVersion: Version, eFID: FileID, dir: BOOL, kids: BOOL, path: REF TEXT]
RETURNS [continue: BOOL] ~ {
IF wantedTime = eCreated THEN { -- ask if this is the one
as: AttributeSequence ¬ NEW[AttributeSequenceObject[1]];
as[0] ¬ [type: fileID, value: EncapsulateFileID[eFID]];
data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation];
fileH ¬ FilingP10V5.Open[h: crH, attributes: as,
directory: FilingP10V5.nullHandle, controls: nullControls,
session: data.session­];
version ¬ eVersion; bytes ¬ eBytes; created ¬ eCreated; fileType ¬ type;
pathName ¬ path; isDir ¬ dir; withKids ¬ kids;
RETURN[FALSE];
};
RETURN[TRUE]; -- not the right one
};
fileH ¬ FilingP10V5.nullHandle; -- because there may be a non-time-matching file left from the first call to open
file ¬ PFS.RopeFromPath[PFSNames.StripVersionNumber[PFS.PathFromRope[file]]]; --UGH!
file ← FileNames.StripVersionNumber[file];
file ¬ file.Concat["!*"];  -- ??
newFile ¬ ConvertFSNameToXNS[file, op];
InnerEnumerate[h: h, pattern: PFS.PathFromRope[newFile], namesOnly: FALSE, proc: Note, op: op];
endO ¬ BasicTime.GetClockPulses[];
openTime ¬ BasicTime.PulsesToMicroseconds[endO] - BasicTime.PulsesToMicroseconds[startO];
IF fileH # FilingP10V5.nullHandle THEN RETURN[fileH, version, bytes, created, fileType, pathName, fID, isDir, withKids];
};
UnknownFile[name: Rope.Cat[r1: "[", r2: data.serverName, r3: "]", r4: file],
createdTime: wantedTime];
};
MakeUID: PROC[gmt: GMT] RETURNS[uid: PFS.UniqueID] =
{ uid.egmt.gmt ¬ gmt };
Reference Counting Files Open/Close
OpenInternal: ENTRY PROC [data: ServerData] ~ {
ENABLE UNWIND => { NULL };
data.files ¬ data.files.SUCC;
};
CloseInternal: ENTRY PROC [data: ServerData] ~ {
ENABLE UNWIND => { NULL };
data.files ¬ data.files.PRED;
};
Debugging
RPath: PROC [path: PATH] RETURNS [rope: ROPE] ~
{ RETURN[PFS.RopeFromPath[path]] };
Initialization
Init: PROC = {
PFSClass.Register[myFlavor, XNSGetServer];
};
Init[];
END.