<> <> <> <> <<>> DIRECTORY AuthenticationP14V2 USING [CallProblem, Problem, Verifier], Basics USING [LongNumber, Word16], BasicTime USING [FromNSTime, GetClockPulses, OutOfRange, PulsesToMicroseconds, ToNSTime], CrRPC USING [CreateClientHandle, DestroyClientHandle, Error, ErrorReason, GetCard16, GetCard32, Handle], FilingAttributesP10V5, FilingP10V5, IO, PFS USING [Error, ErrorGroup, FileType, RopeFromPath, tDirectory, tUnspecified, UniqueID], PFSBackdoor USING [ErrorCode, ProduceError], PFSClass USING [FSHandle], PFSNames USING [Component, ComponentCount, ComponentRope, Directory, Fetch, PATH, ShortName, Version, VersionToRope], QuickFilingP10V5 USING [QuickGetAttributes], RefText, Rope, XNSAuth USING [AuthenticationError, CallError, Conversation, GetNextVerifier, Refresh], XNSRemoteFilePrivate, XNSRemoteFileTypes, XNSStream USING [ConnectionClosed, Timeout]; XNSRemoteFileUtilsImpl: CEDAR MONITOR IMPORTS BasicTime, CrRPC, FilingP10V5, IO, PFS, PFSBackdoor, PFSNames, RefText, Rope, QuickFilingP10V5, XNSAuth, XNSStream EXPORTS XNSRemoteFilePrivate ~ BEGIN OPEN XNSRemoteFileTypes; <> PATH: TYPE = PFSNames.PATH; FSHandle: TYPE = PFSClass.FSHandle; FileType: TYPE ~ PFS.FileType; Version: TYPE = PFSNames.Version; <> OpaqueByteObject: TYPE ~ MACHINE DEPENDENT RECORD [ length: CARDINAL, <> body: PACKED ARRAY [0..1000) OF CHAR ]; <> <> <> <> <<];>> <> startE, endE: CARD; InfoFromOpenFile: PUBLIC PROC [data: ServerData, crH: CrRPC.Handle, fileH: FilingP10V5.Handle, op: Op] RETURNS [version: Version, bytes: INT, created: GMT, fileType: FileType, pathName: REF TEXT, fID: FileID, isDir: BOOL, withKids: BOOL] = { types: AttributeTypeSequence ~ GetCedarAttributes[op]; <> numKids: CARD ¬ 0; plainFileType: CARD; data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation]; startE ¬ BasicTime.GetClockPulses[]; <> <<[version, bytes, created, fileType, pathName, fID, isDir, numKids] _ InfoFromAttributeSequence[attributes]; -- initializes the variables before the process>> version.versionKind ¬ numeric; [version.version, bytes, created, plainFileType, pathName, fID, isDir, numKids] ¬ QuickFilingP10V5.QuickGetAttributes[h: crH, file: fileH, types: types, session: data.session­]; -- initializes the variables before the process fileType ¬ [plainFileType]; endE ¬ BasicTime.GetClockPulses[]; withKids ¬ isDir AND (numKids # 0); infoTime ¬ BasicTime.PulsesToMicroseconds[endE] - BasicTime.PulsesToMicroseconds[startE]; }; infoTime: CARD; <> GetConnection: PUBLIC PROC [data: ServerData, h: FSHandle] RETURNS [crH: CrRPC.Handle] = { < ReportXNSError[resourceLimitExceeded, h, NIL, nullGMT] ]; -- ???>> crH ¬ CrRPC.CreateClientHandle[$CMUX, data.address ! CrRPC.Error => CONTINUE]; IF crH = NIL THEN ReportXNSError[resourceLimitExceeded, h, NIL, nullGMT]; -- ??? IF NOT Revive[data, crH] THEN { IF crH # NIL THEN CrRPC.DestroyClientHandle[crH]; ReportXNSError[resourceLimitExceeded, h, NIL, nullGMT]; -- ??? }; data.active ¬ TRUE; }; <> <> <> <<};>> <<>> <> <> <<};>> ReturnConnection: PUBLIC PROC [crH: CrRPC.Handle] = { IF crH # NIL THEN CrRPC.DestroyClientHandle[crH ! CrRPC.Error => CONTINUE]; }; minStart: CARD ¬ 10; -- seconds to allow credentials to get down to Revive: PUBLIC ENTRY PROC [data: ServerData, crH: CrRPC.Handle ¬ NIL] RETURNS [ok: BOOL ¬ FALSE] ~ { ENABLE UNWIND => NULL; IF data.connectionTTL < minStart THEN RETURN[ReviveInternal[data, crH]]; data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation]; data.connectionTTL ¬ data.continuance; RETURN[TRUE]; }; ReviveInternal: INTERNAL PROC [data: ServerData, crH: CrRPC.Handle ¬ NIL] RETURNS [ok: BOOL ¬ FALSE] ~ { supplied: BOOL ~ crH # NIL; { -- for supplied ENABLE { UNWIND => {IF NOT supplied AND crH # NIL THEN CrRPC.DestroyClientHandle[crH]}; CrRPC.Error => GOTO Out; FilingP10V5.AuthenticationError, FilingP10V5.SessionError, FilingP10V5.ServiceError => GOTO Out; XNSAuth.AuthenticationError, XNSAuth.CallError => GOTO Out; }; IF NOT supplied THEN crH ¬ CrRPC.CreateClientHandle[$CMUX, data.address]; data.session.verifier ¬ XNSAuth.GetNextVerifier[data.conversation]; data.connectionTTL ¬ data.continuance ¬ FilingP10V5.Continue[crH, data.session­]; XNSAuth.Refresh[conversation: data.conversation, seconds: data.connectionTTL]; IF NOT supplied THEN CrRPC.DestroyClientHandle[crH]; RETURN[TRUE]; EXITS Out => { IF NOT supplied AND crH # NIL THEN CrRPC.DestroyClientHandle[crH]; data.connectionTTL ¬ 0; RETURN[FALSE]; }; }; }; <> CallProtected: PUBLIC PROC[proc: PROC, h: FSHandle, filePath: PATH, uid: PFS.UniqueID] ~ { time: GMT ~ uid.egmt.gmt; file: ROPE ~ PFS.RopeFromPath[filePath]; gName: ROPE ~ Rope.Cat["[", h.name, "]", file]; quotedGName: ROPE ~ Rope.Cat["\"", gName, "\""]; { -- for ENABLE ENABLE { XNSStream.ConnectionClosed => PFSBackdoor.ProduceError[resourceLimitExceeded, IO.PutFR1["Server for %g closed the connection.", [rope[quotedGName]]]]; --??? CrRPC.Error => MakeCrRPCError[errorReason, text]; IO.EndOfStream => ERROR PFS.Error[[environment, $unexpectedClose, IO.PutFR1["Server for %g closed the connection.", [rope[quotedGName]]]]]; IO.Error => ERROR PFS.Error[[bug, $unexpectedError, IO.PutFR1["File: %g unexpected IO.Error.", [rope[quotedGName]]]]]; FilingP10V5.AccessError => { SELECT problem FROM accessRightsInsufficient, accessRightsIndeterminate => PFSBackdoor.ProduceError[accessDenied, IO.PutFR1["Can't access %g", [rope[file]]]]; fileChanged, fileDamaged => PFSBackdoor.ProduceError[inconsistent, IO.PutFR1["%g is damaged", [rope[file]]]]; < >> <> fileInUse, fileOpen => PFSBackdoor.ProduceError[accessDenied, IO.PutFR1["%g is in use", [rope[file]]]]; fileNotFound => UnknownFile[gName, time]; ENDCASE => ERROR; }; FilingP10V5.AttributeTypeError => ERROR PFS.Error[[bug, $illegalAttributeType, IO.PutFR["%g attribute (%g)", [rope[argumentErrorRopes[problem]]], [cardinal[type]]]]]; FilingP10V5.AttributeValueError => ERROR PFS.Error[[bug, $illegalAttributeValue, IO.PutFR["%g attribute value (%g)", [rope[argumentErrorRopes[problem]]], [cardinal[type]]]]]; FilingP10V5.ControlTypeError => ERROR PFS.Error[[bug, $illegalControlType, IO.PutFR["%g control type (%g)", [rope[argumentErrorRopes[problem]]], [rope[controlErrorRopes[type]]]]]]; FilingP10V5.ControlValueError => ERROR PFS.Error[[bug, $illegalControlValue, IO.PutFR["%g control value (%g)", [rope[argumentErrorRopes[problem]]], [rope[controlErrorRopes[type]]]]]]; FilingP10V5.ScopeTypeError => ERROR PFS.Error[[bug, $illegalScope, IO.PutFR["%g scope (%g)", [rope[argumentErrorRopes[problem]]], [rope[scopeErrorRopes[type]]]]]]; FilingP10V5.ScopeValueError => ERROR PFS.Error[[bug, $illegalScopeValue, IO.PutFR["%g scope value (%g)", [rope[argumentErrorRopes[problem]]], [rope[scopeErrorRopes[type]]]]]]; FilingP10V5.AuthenticationError => MakeAuthError[problem, gName]; FilingP10V5.ConnectionError => { r: ROPE ~ SELECT problem FROM noRoute => "noRoute", noResponse => "noResponse", transmissionHardware => "transmissionHardware", transportTimeout => "transportTimeout", tooManyLocalConnections => "tooManyLocalConnections", tooManyRemoteConnections => "tooManyRemoteConnections", missingCourier => "missingCourier", missingProgram => "missingProgram", missingProcedure => "missingProcedure", protocolMismatch => "protocolMismatch", parameterInconsistency => "parameterInconsistency", invalidMessage => "invalidMessage", returnTimedOut => "returnTimedOut", otherCallProblem => "otherCallProblem", ENDCASE => ERROR; ERROR PFS.Error[[client, $connectionTrouble, IO.PutFR["Server for %g reported a connection problem: %g", [rope[quotedGName]], [rope[r]]]]]; }; FilingP10V5.HandleError => { r: ROPE ~ SELECT problem FROM invalid => "invalid", nullDisallowed => "nullDisallowed", directoryRequired => "directoryRequired", ENDCASE => ERROR; ERROR PFS.Error[[client, $fileHandleError, IO.PutFR["File handle error for %g: %g", [rope[file]], [rope[r]]]]]; }; FilingP10V5.InsertionError => { r: ROPE ~ SELECT problem FROM positionUnavailable => "positionUnavailable", fileNotUnique => "fileNotUnique", loopInHierarchy => "loopInHierarchy", ENDCASE => ERROR; ERROR PFS.Error[[client, $insertionError, IO.PutFR["Server for %g reported insertion error (%g)", [rope[quotedGName]], [rope[r]]]]]; }; FilingP10V5.RangeError => ERROR PFS.Error[[client, $rangeError, IO.PutFR["%g Range error: (%g)", [rope[quotedGName]], [rope[argumentErrorRopes[problem]]]]]]; FilingP10V5.ServiceError => ERROR PFS.Error[[client, $serviceProblem, IO.PutFR["Server for %g reports a service error: %g", [rope[quotedGName]], [rope[serviceErrorRopes[problem]]]]]]; FilingP10V5.SessionError => MakeSessionError[problem]; FilingP10V5.SpaceError => MakeSpaceError[problem]; FilingP10V5.TransferError => ERROR PFS.Error[[client, $transferError, IO.PutFR["Server for %g has a transfer problem %g", [rope[quotedGName]], [rope[transferErrorRopes[problem]]]]]]; FilingP10V5.UndefinedError => ERROR PFS.Error[[bug, $undefinedError, IO.PutFR["Server for %g has an undefined error (%g)", [rope[quotedGName]], [cardinal[problem]]]]]; }; proc[ ! XNSStream.Timeout => RESUME]; -- since the timeout is retryable }; }; UnknownFile: PUBLIC PROC[name: ROPE, createdTime: GMT] = { IF createdTime = nullGMT THEN PFSBackdoor.ProduceError[unknownFile, IO.PutFR1["Could not find %g", [rope[name]]] ] ELSE PFSBackdoor.ProduceError[unknownUniqueID, IO.PutFR["Could not find \"%g\" created on %t", [rope[name]], [time[createdTime]]]]; }; ReportXNSError: PUBLIC PROC [code: PFSBackdoor.ErrorCode, h: FSHandle, filePath: PATH, time: GMT] ~ { file: ROPE ~ PFS.RopeFromPath[filePath]; gName: ROPE ¬ Rope.Cat["[", h.name, "]", file]; quotedGName: ROPE ¬ Rope.Cat["\"", gName, "\""]; OldError: PROC [code: PFSBackdoor.ErrorCode, e1, e2: ROPE, downTime: CARD ¬ 0] ~ { < 0 THEN SetServerDown[h, downTime];>> PFSBackdoor.ProduceError[code, Rope.Cat[(IF e1 # NIL THEN e1 ELSE "Server for "), quotedGName, e2]]; }; NewError: PROC [group: PFS.ErrorGroup, code: ATOM, e1, e2: ROPE, downTime: CARD ¬ 0] ~ { < 0 THEN SetServerDown[h, downTime];>> ERROR PFS.Error[[group, code, Rope.Cat[(IF e1 # NIL THEN e1 ELSE "Server for "), quotedGName, e2]]]; }; SELECT code FROM unknownFile => UnknownFile[gName, time]; -- raises PFS.Error invalidNameSyntax => OldError[invalidNameSyntax, NIL, " says that the file name is illegal", 0]; accessDenied => PFSBackdoor.ProduceError[code, "Server timed out the connection or File is busy"]; ENDCASE => ERROR; }; REQUIREDROPE: TYPE ~ ROPE ¬; accessErrorRopes: REF ARRAY FilingP10V5.AccessProblem OF REQUIREDROPE ~ NEW[ARRAY FilingP10V5.AccessProblem OF REQUIREDROPE ¬ [ accessRightsInsufficient: "Access Rights Insufficient", accessRightsIndeterminate: "Access Rights Indeterminate", fileChanged: "File Changed", fileDamaged: "File Damaged", fileInUse: "File In Use", fileNotFound: "File Not Found", fileOpen: "File Open" ]]; MakeAccessError: PROC [problem: FilingP10V5.AccessProblem, h: FSHandle, file: ROPE, time: GMT] = { gName: ROPE ¬ Rope.Cat["[", h.name, "]", file]; SELECT problem FROM accessRightsInsufficient, accessRightsIndeterminate => PFSBackdoor.ProduceError[accessDenied, IO.PutFR1["Can't access %g", [rope[file]]]]; fileChanged, fileDamaged => PFSBackdoor.ProduceError[inconsistent, IO.PutFR1["%g is damaged", [rope[file]]]]; < >> <> fileInUse, fileOpen => PFSBackdoor.ProduceError[accessDenied, IO.PutFR1["%g is in use", [rope[file]]]]; fileNotFound => UnknownFile[gName, time]; ENDCASE => ERROR; }; GetAccessErrorMsg: PUBLIC PROC[problem: FilingP10V5.AccessProblem] RETURNS[msg: ROPE] = { RETURN[accessErrorRopes[problem]] }; argumentErrorRopes: REF ARRAY FilingP10V5.ArgumentProblem OF REQUIREDROPE ~ NEW[ARRAY FilingP10V5.ArgumentProblem OF REQUIREDROPE ¬ [ illegal: "Illegal", disallowed: "Disallowed", unreasonable: "Unreasonable", unimplemented: "Unimplemented", duplicated: "Duplicated", missing: "Missing" ]]; authErrorRopes: REF ARRAY AuthenticationP14V2.Problem OF REQUIREDROPE ~ NEW[ARRAY AuthenticationP14V2.Problem OF REQUIREDROPE ¬ [ credentialsInvalid: "Credentials Invalid", verifierInvalid: "Verifier Invalid", verifierExpired: "Verifier Expired", verifierReused: "Verifier Reused", credentialsExpired: "Credentials Expired", inappropriateCredentials: "Inappropriate Credentials" ]]; MakeAuthError: PROC [problem: AuthenticationP14V2.Problem, file: ROPE] = { r: ROPE ~ authErrorRopes[problem]; <> PFSBackdoor.ProduceError[accessDenied, IO.PutFR["%g for %g", [rope[r]], [rope[file]]]]; }; GetAuthErrorMsg: PUBLIC PROC[problem: AuthenticationP14V2.Problem] RETURNS[msg: ROPE] = { RETURN[authErrorRopes[problem]] }; controlErrorRopes: REF ARRAY FilingP10V5.ControlType OF REQUIREDROPE ~ NEW[ARRAY FilingP10V5.ControlType OF REQUIREDROPE ¬ [ lock: "lock", timeout: "timeout", access: "access" ]]; scopeErrorRopes: REF ARRAY FilingP10V5.ScopeType OF REQUIREDROPE ~ NEW[ARRAY FilingP10V5.ScopeType OF REQUIREDROPE ¬ [ count: "count", direction: "direction", filter: "filter", depth: "depth" ]]; serviceErrorRopes: REF ARRAY FilingP10V5.ServiceProblem OF REQUIREDROPE ~ NEW[ARRAY FilingP10V5.ServiceProblem OF REQUIREDROPE ¬ [ cannotAuthenticate: "Cannot Authenticate", serviceFull: "Service Full", serviceUnavailable: "Service Unavailable", sessionInUse: "Session In Use", serviceUnknown: "Service Unknown" ]]; GetServiceErrorMsg: PUBLIC PROC[problem: FilingP10V5.ServiceProblem] RETURNS[msg: ROPE] = { RETURN[serviceErrorRopes[problem]] }; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<]];>> transferErrorRopes: REF ARRAY FilingP10V5.TransferProblem OF REQUIREDROPE ~ NEW[ARRAY FilingP10V5.TransferProblem OF REQUIREDROPE ¬ [ aborted: "aborted", checksumIncorrect: "checksumIncorrect", formatIncorrect: "formatIncorrect", noRendevous: "noRendevous", wrongDirection: "wrongDirection" ]]; crRPCErrorRopes: REF ARRAY CrRPC.ErrorReason OF REQUIREDROPE ~ NEW[ARRAY CrRPC.ErrorReason OF REQUIREDROPE ¬ [ unknown: "Unknown", unknownClass: "unknownClass", courierVersionMismatch: "courierVersionMismatch", rejectedNoSuchProgram: "rejectedNoSuchProgram", rejectedNoSuchVersion: "rejectedNoSuchVersion", rejectedNoSuchProcedure: "rejectedNoSuchProcedure", rejectedInvalidArgument: "rejectedInvalidArgument", rejectedUnspecified: "rejectedUnspecified", remoteError: "remoteError", cantConnectToRemote: "Can't Connect To Remote", argsError: "Args Error", resultsError: "Results Error", bulkDataError: "Bulk DataError", protocolError: "Protocol Error", remoteClose: "Remote Close", communicationFailure: "Communication Failure", notImplemented: "notImplemented", unknownOperation: "unknownOperation", notServerHandle: "notServerHandle", notClientHandle: "notClientHandle", addressInappropriateForClass: "addressInappropriateForClass", other: "Other" ]]; MakeCrRPCError: PROC [errorReason: CrRPC.ErrorReason, text: ROPE] = { ERROR PFS.Error[[$environment, $CrRPCError, GetCrRPCErrorMsg[errorReason, text]]]; }; GetCrRPCErrorMsg: PUBLIC PROC[errorReason: CrRPC.ErrorReason, text: ROPE] RETURNS[msg: ROPE] = { RETURN[IO.PutFR["CrRPC Error: %g, %g", [rope[crRPCErrorRopes[errorReason]]], [rope[text]] ]]; }; sessionErrorRopes: REF ARRAY FilingP10V5.SessionProblem OF REQUIREDROPE ~ NEW[ARRAY FilingP10V5.SessionProblem OF REQUIREDROPE ¬ [ tokenInvalid: "tokenInvalid", serviceAlreadySet: "serviceAlreadySet" ]]; MakeSessionError: PROC [problem: FilingP10V5.SessionProblem] = { ERROR PFS.Error[[client, $IllegalSession, sessionErrorRopes[problem]]]; }; GetSessionErrorMsg: PUBLIC PROC[problem: FilingP10V5.SessionProblem] RETURNS[msg: ROPE] = { RETURN[sessionErrorRopes[problem]] }; authCallErrorRopes: REF ARRAY AuthenticationP14V2.CallProblem OF REQUIREDROPE ~ NEW[ARRAY AuthenticationP14V2.CallProblem OF REQUIREDROPE ¬ [ tooBusy: "Too Busy", accessRightsInsufficient: "Access Rights Insufficient", keysUnavailable: "Keys Unavailable", strongKeyDoesNotExist: "Strong Key Does Not Exist", simpleKeyDoesNotExist: "Simple Key Does Not Exist", strongKeyAlreadyRegistered: "Strong Key Already Registered", simpleKeyAlreadyRegistered: "Simple Key Already Registered", domainForNewKeyUnavailable: "Domain For New Key Unavailable", domainForNewKeyUnknown: "Domain For New Key Unknown", badKey: "Bad Key", badName: "Bad Name", databaseFull: "Database Full", other: "Other" ]]; GetAuthCallErrorMsg: PUBLIC PROC[problem: AuthenticationP14V2.CallProblem] RETURNS[msg: ROPE] = { RETURN[authCallErrorRopes[problem]] }; MakeSpaceError: PROC [problem: FilingP10V5.SpaceProblem] = { SELECT problem FROM allocationExceeded => PFSBackdoor.ProduceError[quotaExceeded, "No more room for store"]; attributeAreaFull => PFSBackdoor.ProduceError[outOfPropertySpace, "Out of attribute space"]; mediumFull => PFSBackdoor.ProduceError[volumeFull, "Remote volume is full"]; ENDCASE => ERROR; }; <> BuildScopeFilter: PUBLIC PROC [filterType: FilingP10V5.FilterType, rope: ROPE, type: CARD32] RETURNS [scope: ScopeSequence] = { value: AttributeValue ¬ EncapsulateRope[rope]; filter: Filter; scope ¬ NEW[ScopeSequenceObject[4]]; scope[0] ¬ NEW[ScopeObject.count ¬ [count[FilingP10V5.unlimitedCount]]]; scope[1] ¬ NEW[ScopeObject.depth ¬ [depth[FilingP10V5.allDescendants]]]; scope[2] ¬ NEW[ScopeObject.direction ¬ [direction[forward]]]; SELECT filterType FROM matches => filter ¬ NEW [FilterObject.matches ¬ [matches[matches: [ attribute: [type: type, value: value] ]] ]]; equal => filter ¬ NEW [FilterObject.equal ¬ [equal[equal: [ attribute: [type: type, value: value], interpretation: string ]] ]]; ENDCASE => filter ¬ NEW [FilterObject.none ¬ [none[none: [null: 0]]]]; scope[3] ¬ NEW [ScopeObject.filter ¬ [filter[filter: filter]]]; }; BuildScopeDepthAndFilter: PUBLIC PROC [depth: CARD32, filterType: FilingP10V5.FilterType, rope: ROPE, type: CARD32] RETURNS [scope: ScopeSequence] = { value: AttributeValue ¬ EncapsulateRope[rope]; filter: Filter; scope ¬ NEW[ScopeSequenceObject[4]]; scope[0] ¬ NEW[ScopeObject.count ¬ [count[FilingP10V5.unlimitedCount]]]; scope[1] ¬ NEW[ScopeObject.depth ¬ [depth[depth]]]; scope[2] ¬ NEW[ScopeObject.direction ¬ [direction[forward]]]; SELECT filterType FROM matches => filter ¬ NEW [FilterObject.matches ¬ [matches[matches: [ attribute: [type: type, value: value] ]] ]]; equal => filter ¬ NEW [FilterObject.equal ¬ [equal[equal: [ attribute: [type: type, value: value], interpretation: string ]] ]]; ENDCASE => filter ¬ NEW [FilterObject.none ¬ [none[none: [null: 0]]]]; scope[3] ¬ NEW [ScopeObject.filter ¬ [filter[filter: filter]]]; }; BuildScopeDepth: PUBLIC PROC [depth: CARD32] RETURNS [scope: ScopeSequence] = { scope ¬ NEW[ScopeSequenceObject[3]]; scope[0] ¬ NEW[ScopeObject.count ¬ [count[FilingP10V5.unlimitedCount]]]; scope[1] ¬ NEW[ScopeObject.depth ¬ [depth[depth]]]; scope[2] ¬ NEW[ScopeObject.direction ¬ [direction[forward]]]; }; BuildAttribute: PUBLIC PROC [name: ROPE, type: CARD32, time: GMT ¬ nullGMT, timeType: CARD32 ¬ 0] RETURNS [attributes: AttributeSequence] = { IF time = nullGMT THEN { attributes ¬ NEW [AttributeSequenceObject[1]]; attributes[0] ¬ [type: type, value: EncapsulateRope[name]]; RETURN[attributes]; }; attributes ¬ NEW [AttributeSequenceObject[2]]; attributes[0] ¬ [type: timeType, value: EncapsulateCard32[BasicTime.ToNSTime[time]]]; attributes[1] ¬ [type: type, value: EncapsulateRope[name]]; RETURN[attributes]; }; BuildDirectoryAttribute: PUBLIC PROC [nameR: ROPE] RETURNS [attributes: AttributeSequence] = { attributes ¬ NEW [AttributeSequenceObject[3]]; attributes[0] ¬ [type: isDirectory, value: EncapsulateBoolean[TRUE]]; attributes[1] ¬ [type: name, value: EncapsulateRope[nameR]]; attributes[2] ¬ [type: type, value: EncapsulateCard32[1 -- tDirectory --]]; RETURN[attributes]; }; BuildStoreAttributes: PUBLIC PROC [file: ROPE, vers: CARDINAL, created: GMT] RETURNS [attributes: AttributeSequence] ~ { num: CARDINAL ¬ IF created = nullGMT THEN 2 ELSE 3; attributes ¬ NEW [AttributeSequenceObject[num]]; attributes[0] ¬ [type: name, value: EncapsulateRope[file]]; attributes[1] ¬ [type: version, value: EncapsulateCard16[vers]]; IF created # nullGMT THEN { time: CARD32 ¬ BasicTime.ToNSTime[created]; attributes[2] ¬ [type: createdOn, value: EncapsulateCard32[time]]; }; }; GetCedarAttributes: PUBLIC PROC [op: Op] RETURNS [attributes: AttributeTypeSequence] = { RETURN[SELECT op FROM delete => deleteAttributes, enumerate => enumerateAttributes, enumerateNames => enumerateNamesAttributes, rename => enumerateAttributes, retrieve => retrieveAttributes, store => storeAttributes, ENDCASE => ERROR]; }; deleteAttributes: AttributeTypeSequence; enumerateAttributes: AttributeTypeSequence; enumerateNamesAttributes: AttributeTypeSequence; retrieveAttributes: AttributeTypeSequence; storeAttributes: AttributeTypeSequence; MakeCedarAttributes: PROC [] = { deleteAttributes ¬ NEW[AttributeTypeSequenceObject[6]]; deleteAttributes[0] ¬ createdOn; deleteAttributes[1] ¬ fileID; deleteAttributes[2] ¬ isDirectory; deleteAttributes[3] ¬ numberOfChildren; deleteAttributes[4] ¬ version; deleteAttributes[5] ¬ pathname; enumerateAttributes ¬ NEW[AttributeTypeSequenceObject[8]]; enumerateAttributes[0] ¬ createdOn; enumerateAttributes[1] ¬ dataSize; enumerateAttributes[2] ¬ name; enumerateAttributes[3] ¬ pathname; enumerateAttributes[4] ¬ type; enumerateAttributes[5] ¬ isDirectory; enumerateAttributes[6] ¬ version; enumerateAttributes[7] ¬ fileID; enumerateNamesAttributes ¬ NEW[AttributeTypeSequenceObject[2]]; enumerateNamesAttributes[0] ¬ pathname; enumerateNamesAttributes[1] ¬ isDirectory; retrieveAttributes ¬ NEW[AttributeTypeSequenceObject[5]]; retrieveAttributes[0] ¬ createdOn; retrieveAttributes[1] ¬ dataSize; retrieveAttributes[2] ¬ version; retrieveAttributes[3] ¬ pathname; retrieveAttributes[4] ¬ fileID; storeAttributes ¬ NEW[AttributeTypeSequenceObject[1]]; storeAttributes[0] ¬ version; }; GetAttributeSequence: PROC [in: STREAM] RETURNS [attributeSequence: AttributeSequence] ~ { GetAttribute: PROC [in: STREAM] RETURNS [attribute: Attribute] ~ { GetAttributeValue: PROC [in: STREAM] RETURNS [attributeValue: AttributeValue] ~ { len: CARDINAL ~ CrRPC.GetCard16[in]; attributeValue ¬ NEW [AttributeValueObject[len]]; FOR j: CARDINAL IN [0..len) DO attributeValue.body[j] ¬ CrRPC.GetCard16[in]; ENDLOOP; }; attribute.type ¬ CrRPC.GetCard32[in]; attribute.value ¬ GetAttributeValue[in]; }; -- GetAttribute length: CARDINAL ~ CrRPC.GetCard16[in]; attributeSequence ¬ NEW [AttributeSequenceObject[length]]; FOR i: CARDINAL IN [0..length) DO attributeSequence.body[i] ¬ GetAttribute[in]; ENDLOOP; }; ConvertFSNameToXNS: PUBLIC PROC [file: ROPE, op: Op] RETURNS [ROPE] = { AddChar: PROC [c: CHAR] = {text[text.length] ¬ c; text.length ¬ text.length + 1}; name, versionR: ROPE ¬ NIL; text: REF TEXT ¬ RefText.ObtainScratch[nChars: 200]; lastSlash: INT ¬ LAST[INT]; inDirVersion: BOOL ¬ FALSE; text.length ¬ 0; FOR i: INT IN [0 .. Rope.Length[file]) DO c: CHAR ~ Rope.Fetch[base: file, index: i]; SELECT c FROM '< => LOOP; -- throw the < away. '> => IF NOT inDirVersion THEN { AddChar['/]; -- the XNS server uses / as a file separator lastSlash ¬ i; }; '/ => { lastSlash ¬ i; IF inDirVersion THEN inDirVersion ¬ FALSE ELSE AddChar[c] }; '* => IF NOT inDirVersion THEN { AddChar[c]; AddChar[c] }; -- kludge to handle * '! => { -- the version part is next IF lastSlash = i-1 THEN { -- directory version, not file version; get rid of it inDirVersion ¬ TRUE; LOOP; }; versionR ¬ Rope.Substr[base: file, start: i]; -- the whole version including the ! EXIT; }; ENDCASE => IF NOT inDirVersion THEN AddChar[c]; ENDLOOP; IF versionR # NIL THEN { SELECT Rope.Length[versionR] FROM 0 => versionR ¬ NIL; -- no version specified 1 => -- the version rope is just a ! so do the 'default' -- { SELECT op FROM delete => versionR ¬ "!-"; -- lowest enumerate, enumerateNames => versionR ¬ NIL; rename, retrieve => versionR ¬ "!+"; -- highest store => versionR ¬ "!+"; -- is this right? ENDCASE => ERROR; -- can't happen }; 2 => { -- is it H, L or *? SELECT Rope.Fetch[versionR, 1] FROM 'h, 'H => versionR ¬ "!+"; 'l, 'L => versionR ¬ "!-"; '* => versionR ¬ NIL; ENDCASE => NULL; }; ENDCASE => NULL; }; name ¬ Rope.Concat[base: Rope.FromRefText[s: text], rest: versionR]; RefText.ReleaseScratch[t: text]; RETURN[name]; }; ConvertPathToFile: PUBLIC PROC [path: REF TEXT, isDir: BOOL ¬ FALSE] RETURNS [file: ROPE] = { state: {name, version} ¬ name; AddChar: PROC [t: REF TEXT, c: CHAR] = {t[t.length] ¬ c; t.length ¬ t.length + 1}; name: REF TEXT ¬ RefText.ObtainScratch[nChars: 200]; ver: REF TEXT ¬ RefText.ObtainScratch[nChars: 200]; slashSeen: BOOL ¬ FALSE; name.length ¬ 0; ver.length ¬ 0; AddChar[name, '/]; -- since the path doesn't have this (pfs syntax) FOR i: INT IN [0 .. RefText.Length[path]) DO c: CHAR ~ RefText.Fetch[path, i]; SELECT c FROM '! => { ver.length ¬ 0; -- discard the version information since it is still a subdir state ¬ version; }; '/ => { slashSeen ¬ TRUE; ver.length ¬ 0; -- discard the version information since it is still a subdir state ¬ name; -- go back to collection characters AddChar[name, '/]; -- insert PFS's notion of what a delineator is }; '> => -- an illegal character -- { RefText.ReleaseScratch[t: name]; RefText.ReleaseScratch[t: ver]; RETURN [NIL]; }; ENDCASE => { SELECT state FROM name => AddChar[name, c]; version => AddChar[ver, c]; ENDCASE => ERROR; }; ENDLOOP; IF state = version THEN { -- this is the version from the item last on the path IF (NOT slashSeen) OR isDir THEN AddChar[name, '/]; AddChar[name, '!]; -- indicate the version name ¬ RefText.Append[to: name, from: ver]; -- and glue it on }; file ¬ Rope.FromRefText[s: name]; RefText.ReleaseScratch[t: name]; RefText.ReleaseScratch[t: ver]; }; <> <> <> <> <