<> <> <> <> <> <> <> <> <<>> DIRECTORY BasicTime USING [ GMT, nullGMT ], Booting USING [ Boot, Switches ], DefaultRemoteNames USING [Get], Disk USING [ok], File USING [Delete, Error, FindVM, FP, GetVolumeName, Handle, Info, LogicalInfo, nullFP, Open, RC, SystemVolume, Volume, VolumeFile], FileBackdoor USING [GetRoot, IsDebugger, SetRoot], FileInternal USING [EnumeratePages, EnumeratePagesProc, FreeRun, GetBadPages, IsUsed, SetPageUsed, VolumeObject], FS USING [BytesForPages, Close, ComponentPositions, Copy, EnumerateForInfo, EnumerateForNames, Error, ExpandName, FileInfo, InfoProc, Lock, NameProc, Open, OpenFile, Rename, SetByteCountAndCreatedTime, SetKeep, SetDefaultWDir ], FSBackdoor USING [CreateFileProcs, CreateProcsOpenFile, EnumerateCacheForNames, Flush, GetFileHandle, NameProc, ScavengeDirectoryAndCache ], FSRemoteFile USING [ Retrieve ], GermSwap USING [ Switch ], IagoCommands, IagoOps --USING everything--, IO USING [ Close, Error, PutChar, PutF, PutF1, PutRope, RIS, RopeFromROS, ROS, SetIndex, STREAM, UnsafeGetBlock ], Loader USING [ Error, IRItem, Instantiate, Start], PhysicalVolume USING [ SetPhysicalRoot, SubVolumeDetails ], PrincOps USING [ ControlModule, NullControl], Rope USING [ Cat, Equal, Fetch, Find, Index, Length, ROPE, Run, Substr ], SimpleTerminal USING [TurnOff, TurnOn], UserCredentials USING [ChangeState, Login, LoginOptions], VM USING [PagesForBytes], VolumeFormat USING [Attributes, LogicalPage]; IagoCommands2Impl: CEDAR PROGRAM IMPORTS Booting, DefaultRemoteNames, File, FileBackdoor, FileInternal, FS, FSBackdoor, FSRemoteFile, IagoOps, IO, Loader, PhysicalVolume, Rope, SimpleTerminal, UserCredentials, VM EXPORTS IagoCommands, File SHARES File = { ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; myOpenFileRec: TYPE = RECORD [ strm: STREAM, created: BasicTime.GMT, bytes: INT, pages: INT ]; myOpenFile: TYPE = REF myOpenFileRec; Volume: TYPE = REF VolumeObject; -- File -- VolumeObject: PUBLIC TYPE = FileInternal.VolumeObject; <<******** The command subroutines (alphabetic order) ********>> FlushCache: PUBLIC PROC [in, out: STREAM] = { count: INT _ 0; DoName: FSBackdoor.NameProc = { count _ count+1; IO.PutF1[out, "\nFlushing %g ... ", [rope[fullGName]]]; { ENABLE FS.Error => { IO.PutRope[out, error.explanation]; CONTINUE }; FSBackdoor.Flush[fullGName: fullGName, volName: volName]; IO.PutRope[out, "done"]; }; RETURN[TRUE]; }; v: File.Volume = IagoOps.GetLogical[in, out, "On "]; volName: ROPE = File.GetVolumeName[v]; pattern: ROPE _ IagoOps.GetFile[in: in, out: out, pattern: TRUE]; IO.PutRope[out, " ... "]; FSBackdoor.EnumerateCacheForNames[DoName, volName, pattern]; IF count = 0 THEN IO.PutRope[out, "not found"] ELSE IO.PutF1[out, "\n%g files", [integer[count]] ]; }; InstallCredentials: PUBLIC PROC [in, out: STREAM] = { IO.PutRope[out, "\nDo you want to password-protect this disk? "]; [] _ UserCredentials.ChangeState[IF IagoOps.Confirm[in, out] THEN name ELSE nameHint]; }; InstallLogicalFile: PUBLIC PROC [in, out: STREAM, which: File.VolumeFile[checkpoint..bootFile]] = { v: File.Volume = IagoOps.GetLogical[in, out, "For "]; localVolume: ROPE = Rope.Cat["[]<", File.GetVolumeName[v], ">"]; createTime: BasicTime.GMT; name: ROPE = IagoOps.GetFile[ in: in, out: out, extension: IagoOps.ext[which], default: IagoOps.RemoteRootFileName[which], wDir: localVolume, check: TRUE ]; localName: ROPE _ name; fsFile: FS.OpenFile; file: File.Handle; PhysicalToo: PROC RETURNS[BOOL] = { IF which # bootFile THEN { IO.PutRope[out, "\nInstalling on the physical volume"]; RETURN[TRUE]; }; IF FileBackdoor.IsDebugger[v] THEN RETURN[FALSE]; IO.PutRope[out, "\nDo you want to use this file when you boot the physical volume? "]; RETURN[IagoOps.Confirm[in, out]] }; <<"name" is a canonicalized FS name.>> IO.PutRope[out, " ... "]; IF name.Length[] # 0 AND name.Fetch[0] = '[ AND Rope.Run[s1: name, s2: localVolume, case: FALSE] # localVolume.Length[] AND ( v # File.SystemVolume[] OR Rope.Run[s1: name, s2: "[]<>"] # 4 ) THEN { Help: PROC = { IO.PutRope[out, "? Please type the name of an FS local file such as \"a.b\""]; }; localName _ IagoOps.LocalRootFileName[which]; IO.PutRope[out, IF name.Length[] >=1 AND name.Fetch[1] = '] THEN "that file is on another volume ..." ELSE "that file is on a server ..."]; <> DO localName _ IagoOps.GetArg[ in: in, out: out, prompt: "\nCopy to local file name (please confirm or alter): ", default: localName, help: Help]; IF localName.Length[] = 0 OR localName.Fetch[0] = '[ THEN Help[] ELSE EXIT; ENDLOOP; IO.PutRope[out, " ... copying ... "]; [] _ FS.Copy[from: name, to: localName, wDir: localVolume]; }; createTime _ FS.FileInfo[name: localName, wDir: localVolume].created; fsFile _ FS.Open[name: localName, lock: write, wDir: localVolume]; file _ FSBackdoor.GetFileHandle[fsFile]; IF File.Info[file].volume # v THEN IO.PutRope[out, "I'm confused: the file is on the wrong volume"] ELSE { IO.PutRope[out, "installing ... "]; FileBackdoor.SetRoot[which, file]; FS.SetByteCountAndCreatedTime[file: fsFile, created: createTime]; IO.PutRope[out, "done"]; }; FS.Close[fsFile]; IF PhysicalToo[] THEN { PhysicalVolume.SetPhysicalRoot[v, which]; IO.PutRope[out, " ... done"] }; }; ListCache: PUBLIC PROC [in, out: STREAM] = { count: INT _ 0; DoName: FSBackdoor.NameProc = { count _ count+1; IO.PutF1[out, "\n %g", [rope[fullGName]]]; RETURN[TRUE]; }; v: File.Volume = IagoOps.GetLogical[in, out, "On "]; pattern: ROPE _ IagoOps.GetFile[in: in, out: out, pattern: TRUE]; IO.PutRope[out, " ... "]; FSBackdoor.EnumerateCacheForNames[DoName, File.GetVolumeName[v], pattern]; IF count = 0 THEN IO.PutRope[out, "not found"] ELSE IO.PutF1[out, "\n%g files", [integer[count]] ]; }; ListFileInfo: PUBLIC PROC [in, out: STREAM] = { count: INT _ 0; DoName: FS.InfoProc = { count _ count+1; IO.PutF[out, "\n %g, keep: %g, bytes: %g, created: %g", [rope[fullFName]], [cardinal[keep]], [integer[bytes]], [time[created]] ]; IF attachedTo # NIL THEN IO.PutF1[out, ", attached to %g", [rope[attachedTo]] ]; RETURN[TRUE]; }; pattern: ROPE _ IagoOps.GetFile[in: in, out: out, pattern: TRUE]; IO.PutRope[out, " ... "]; FS.EnumerateForInfo[pattern, DoName]; IF count = 0 THEN IO.PutRope[out, "not found"] ELSE IO.PutF1[out, "\n%g files", [integer[count]] ]; }; ListNames: PUBLIC PROC [in, out: STREAM] = { count: INT _ 0; prevFile: ROPE _ NIL; DoName: FS.NameProc = { newFile, newPrefix: ROPE; newPos: FS.ComponentPositions; count _ count+1; [fullFName: newFile, cp: newPos] _ FS.ExpandName[fullFName]; newPrefix _ fullFName.Substr[len: newPos.ver.start]; IF prevFile # NIL AND newPos.ver.length # 0 AND prevFile.Equal[newPrefix, FALSE] THEN IO.PutF1[out, ", %g", [rope[fullFName.Substr[start: newPos.ver.start, len: newPos.ver.length]]] ] ELSE { prevFile _ newPrefix; IO.PutF1[out, "\n%g", [rope[fullFName]]]; }; RETURN[TRUE]; }; pattern: ROPE _ IagoOps.GetFile[in: in, out: out, pattern: TRUE]; IO.PutRope[out, " ... "]; FS.EnumerateForNames[pattern, DoName]; IF count = 0 THEN IO.PutRope[out, "not found"] ELSE IO.PutF1[out, "\n%g files", [integer[count]] ]; }; Login: PUBLIC PROC [options: UserCredentials.LoginOptions] = { TurnOnProc: PROC RETURNS [in, out: STREAM] = { [in: in, out: out] _ SimpleTerminal.TurnOn[]; IO.PutRope[out, "\nPlease login ...\n"]; }; TurnOffProc: PROC [in, out: STREAM] = { SimpleTerminal.TurnOff[] }; UserCredentials.Login[startInteraction: TurnOnProc, endInteraction: TurnOffProc, options: options]; }; RecomputeVAM: PUBLIC PROC[in, out: STREAM] = { NowPage: FileInternal.EnumeratePagesProc = TRUSTED { <> attr: VolumeFormat.Attributes = LOOPHOLE[label.attributes]; SELECT TRUE FROM status # Disk.ok => { badStatus _ badStatus + 1; }; attr = header => { IF FileInternal.IsUsed[volume: volume, page: da] THEN headers _ headers + 1 ELSE { freeHeaders _ freeHeaders + 1; [] _ FileInternal.SetPageUsed[volume: volume, page: da, inUse: TRUE]; }; }; attr = data => { IF FileInternal.IsUsed[volume: volume, page: da] THEN datas _ datas + 1 ELSE { freeDatas_ freeDatas + 1; [] _ FileInternal.SetPageUsed[volume: volume, page: da, inUse: TRUE]; }; }; attr = freePage => { diskaddr: INT = LOOPHOLE[da]; IF label.filePage # diskaddr THEN { badFreeLabels _ badFreeLabels+1; IF lastBadFreeLabel+1 # diskaddr THEN { badFreeLabelRuns _ badFreeLabelRuns+1; IF listBadFreeLabels THEN out.PutRope["--"]; }; lastBadFreeLabel _ diskaddr; IF listBadFreeLabels THEN out.PutF[ "(%g)", [integer[diskaddr]] ]; IF fixBadFreeLabels THEN FileInternal.FreeRun[[da,1], volume, label] ELSE [] _ FileInternal.SetPageUsed[volume: volume, page: da, inUse: TRUE]; } ELSE IF FileInternal.IsUsed[volume: volume, page: da] THEN { freeMissings _ freeMissings + 1; [] _ FileInternal.SetPageUsed[volume: volume, page: da, inUse: FALSE]; } ELSE frees _ frees + 1; }; ENDCASE => { IF FileInternal.IsUsed[volume: volume, page: da] THEN others _ others + 1 ELSE { freeOthers_ freeOthers + 1; [] _ FileInternal.SetPageUsed[volume: volume, page: da, inUse: TRUE]; }; }; deltaPages _ deltaPages + 1; IF deltaPages >= 1000 THEN { thousandsCount _ thousandsCount + 1; IF thousandsCount >= 10 THEN { IO.PutRope[out, "!"]; thousandsCount _ 0; } ELSE IO.PutRope[out, "."]; deltaPages _ 0; }; exit _ FALSE; }; countBadPages: CARDINAL _ 0; badPageWorkProc: PROC [page: VolumeFormat.LogicalPage] = TRUSTED { IF NOT FileInternal.SetPageUsed[volume: volume, page: page, inUse: TRUE].wasInUse THEN countBadPages _ countBadPages+1; }; volume: File.Volume = IagoOps.GetLogical[in, out]; volumeFree: INT; volumeSize: INT; badStatus: INT _ 0; thousandsCount: INT _ 0; deltaPages: INT _ 0; headers, freeHeaders, datas, freeDatas, freeMissings, frees, others, freeOthers: INT _ 0; badFreeLabels, lastBadFreeLabel, badFreeLabelRuns: INT _ 0; fixBadFreeLabels, listBadFreeLabels: BOOL _ FALSE; [size: volumeSize, free: volumeFree] _ File.LogicalInfo[volume ! File.Error => { IO.PutRope[out, "LogicalInfo for volume error  "]; IO.PutRope[out, IagoOps.FileError[why]]; }; ]; IO.PutRope[out, "\nRepair bad labels on free pages? "]; fixBadFreeLabels _ IagoOps.Confirm[in,out]; IO.PutRope[out, "\nList pages with bad labels as they are encountered? "]; listBadFreeLabels _ IagoOps.Confirm[in,out]; IO.PutRope[out, "\n Scan started "]; TRUSTED { FileInternal.EnumeratePages[volume: volume, start: [0], skipBadPages: FALSE, work: NowPage]; }; <> TRUSTED { volumeAlias: Volume; volumeAlias _ volume; FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ volumeAlias.subVolumes, sv.rest UNTIL sv = NIL DO FileInternal.GetBadPages [subVolume: sv.first, work: badPageWorkProc] ; ENDLOOP; }; out.PutF["\n Volume has %s pages with %g pages free in the VAM\n", [integer[volumeSize]], [integer[volumeFree-countBadPages]] ]; IF badStatus > 0 THEN out.PutF[" %g pages had read errors\n", [integer[badStatus]]]; out.PutF["Statisitics on mismatches from scan:\n"]; out.PutF[" Header pages %g (%g free in VAM), Data pages %g (%g free in VAM) \n", [integer[headers]], [integer[freeHeaders]], [integer[datas]], [integer[freeDatas]] ]; out.PutF[" Other pages %g (%g free in VAM), Free pages %g (%g not free in VAM) \n", [integer[others]], [integer[freeOthers]], [integer[frees]], [integer[freeMissings]] ]; out.PutF[" Free pages with bad labels %g (in %g runs) which were %gfixed\n", [integer[badFreeLabels]], [integer[badFreeLabelRuns]], [rope[IF fixBadFreeLabels THEN "" ELSE "not "]] ]; }; Rename: PUBLIC PROC [in, out: STREAM] = { from: ROPE = IagoOps.GetFile[in: in, out: out, prompt: "From ", check: TRUE]; to: ROPE = IagoOps.GetFile[in: in, out: out, prompt: "To "]; IO.PutRope[out, " ... "]; FS.Rename[from: from, to: to]; IO.PutRope[out, "done"]; }; Rollback: PUBLIC PROC [in, out: STREAM] = { v: File.Volume = IagoOps.GetLogical[in, out]; reject: ROPE; mySwitches: Booting.Switches _ ALL[FALSE]; mySwitches[r] _ TRUE; reject _ Booting.Boot[[logical[v]], mySwitches]; IF reject # NIL THEN { IO.PutRope[out, " ... couldn't: "]; IO.PutRope[out, reject] }; }; RunDiagnosticBCD: PUBLIC PROC [in, out: STREAM] = { loadedOK: BOOL _ TRUE; fName: ROPE; cp: FS.ComponentPositions; fsFile: FS.OpenFile; strm: STREAM _ NIL; tryNoCache: BOOL _ FALSE; remoteOnly: BOOL _ FALSE; cm: PrincOps.ControlModule; unboundImports: LIST OF Loader.IRItem; default: ROPE _ Rope.Cat[DefaultRemoteNames.Get[].current, "Iago>ExtraIago.bcd"]; IO.PutRope[out, "\nAvoid using the local disk for caching the bcd file? "]; remoteOnly _ IagoOps.Confirm[in, out]; fName_ IagoOps.GetFile[in: in, out: out, prompt: "FileName ", default: default, check: ~remoteOnly]; IF ~remoteOnly THEN fsFile_ FS.Open[fName ! FS.Error => { IF error.code = $noCache OR error.code = $badBTree THEN { tryNoCache _ TRUE; CONTINUE; }; IO.PutRope[out, error.explanation]; GOTO noFile; }; ]; IF tryNoCache OR remoteOnly THEN { fullFName: ROPE; bytesOnServer: INT; pagesOnServer: INT _ 0; createdTime: BasicTime.GMT; myfsFile: myOpenFile; ProduceStream: PROC[fullGName: ROPE, bytes: INT, created: BasicTime.GMT] RETURNS [STREAM] = { pagesOnServer_ VM.PagesForBytes[bytes]; bytesOnServer _ bytes; createdTime _ created; RETURN[strm_ IO.ROS[]]; }; [fullFName, cp, ]_ FS.ExpandName[fName]; FSRemoteFile.Retrieve[fullFName.Substr[cp.server.start, cp.server.length], fullFName.Substr[cp.dir.start-1], BasicTime.nullGMT, ProduceStream ! FS.Error => { out.PutChar['\n]; out.PutRope[error.explanation]; GO TO noFile}]; IF strm = NIL THEN {out.PutRope["\n Can't retreive file"]; GO TO noFile}; strm_ IO.RIS[IO.RopeFromROS[strm], strm]; myfsFile _ NEW [myOpenFileRec _ [strm, createdTime, bytesOnServer, pagesOnServer]]; fsFile _ FSBackdoor.CreateProcsOpenFile[clientFile: myfsFile, fileProcs: FSBackdoor.CreateFileProcs[GetInfo: streamInfo, Read: streamReader] ]; }; out.PutRope[" ... "]; TRUSTED { [cm, unboundImports]_ Loader.Instantiate[fsFile ! Loader.Error => { IO.PutRope[out, message]; loadedOK _ FALSE; cm_ PrincOps.NullControl; CONTINUE; }; ]; }; IF unboundImports#NIL THEN { IO.PutRope[out, "\nThere are unbound imports; confirm to start the code anyway? "]; IF ~IagoOps.Confirm[in, out] THEN RETURN; }; TRUSTED { IF cm # PrincOps.NullControl THEN Loader.Start[cm ! ABORTED => { loadedOK _ FALSE; IO.PutRope[out, "Loader.Start was ABORTED"]; CONTINUE; }; ]; }; IF loadedOK THEN IO.PutRope[out, "Loaded and Started\n"]; FS.Close[fsFile ! FS.Error => CONTINUE]; IF strm # NIL THEN strm.Close[ ! IO.Error => CONTINUE]; EXITS noFile => NULL; }; Scavenge: PUBLIC PROC [in, out: STREAM] = { v: File.Volume = IagoOps.GetLogical[in, out]; volumeFreePages: INT; rootStatus: File.RC; fsRootFP: File.FP; fsRootSize: INT _ 900; [rootStatus: rootStatus, free: volumeFreePages] _ File.LogicalInfo[v]; SELECT rootStatus FROM ok => NULL; nonCedarVolume => { IO.PutRope[out, "\nVolume is not a Cedar Volume"]; RETURN; }; wentOffline => { IO.PutRope[out, "\nVolume went offline"]; RETURN; }; inconsistent => { IO.PutRope[out, "\nRoot page is inconsistent (wrong version?)"]; RETURN; }; software => { IO.PutRope[out, "\nCan't find the volume root page (label-check)"]; RETURN; }; hardware => { IO.PutRope[out, "\nHard disk error reading the volume root page"]; RETURN; }; ENDCASE => ERROR File.Error[rootStatus]; fsRootFP _ FileBackdoor.GetRoot[v, client].fp; -- client is the client file system: FS IF fsRootFP # File.nullFP THEN { file: File.Handle _ File.Open[v, fsRootFP ! File.Error => GO TO nope]; fsRootSize _ File.Info[file ! File.Error => CONTINUE].size; EXITS nope => {}; }; <> IF (fsRootSize + 200) > volumeFreePages THEN { <> checkpointFP: File.FP; checkpointFile: File.Handle _ NIL; checkpointFP _ FileBackdoor.GetRoot[v, checkpoint].fp; checkpointFile _ File.Open[v, checkpointFP ! File.Error => CONTINUE]; IF checkpointFile = NIL THEN { IO.PutRope[out, "\nThere appears not to be enough room to scavenge and there is not a checkpoint to delete. Continue? "]; IF NOT IagoOps.Confirm[in, out] THEN RETURN; } ELSE { IO.PutRope[out, "\nThere appears not to be enough room to scavenge. OK to delete checkpoint? "]; IF IagoOps.Confirm[in, out] THEN { status: File.RC _ ok; File.Delete[checkpointFile ! File.Error => { status _ why; CONTINUE;}]; IO.PutRope[out, IF status = ok THEN "\nCheckpoint file has been deleted" ELSE "\nError in delete. Checkpoint file has NOT been deleted."]; }; }; checkpointFile _ NIL; }; IO.PutRope[out, " Scavenging ... "]; [] _ File.FindVM[]; FSBackdoor.ScavengeDirectoryAndCache[File.GetVolumeName[v]]; IO.PutRope[out, "done"]; }; SetKeep: PUBLIC PROC [in, out: STREAM] = { count: INT _ 0; DoName: FS.NameProc = { count _ count+1; fullFName _ fullFName.Substr[start: 0, len: fullFName.Index[0, "!"]]; IO.PutF1[out, "\nSetting %g ... ", [rope[fullFName]]]; { ENABLE FS.Error => { IO.PutRope[out, error.explanation]; CONTINUE }; FS.SetKeep[fullFName, k]; IO.PutRope[out, "done"]; }; RETURN[TRUE]; }; pattern: ROPE _ IagoOps.GetFile[ in: in, out: out, pattern: TRUE, prompt: "For "]; k: INT = IagoOps.GetNumber[ in, out, 1, LAST[CARDINAL]-1, "\nNumber of versions to keep: ", "? Type how many versions of each file should be kept (excess versions will be deleted)"]; IF Rope.Find[pattern, "!"] < 0 THEN pattern _ pattern.Cat["!H"]; IO.PutRope[out, " ... "]; FS.EnumerateForNames[pattern, DoName]; IF count = 0 THEN IO.PutRope[out, "not found"] ELSE IO.PutF1[out, "\n%g files", [integer[count]] ]; }; SetPhysicalFile: PUBLIC PROC [in, out: STREAM, which: File.VolumeFile[checkpoint..bootFile]] = { v: File.Volume = IagoOps.GetLogical[in, out, "From "]; IO.PutRope[out, " ... "]; PhysicalVolume.SetPhysicalRoot[v, which]; IO.PutRope[out, "done"]; }; SetWDir: PUBLIC PROC [in, out: STREAM] = { Help: PROC = { IO.PutRope[out, "? Type the prefix of a file name, such as \"[]\""]; }; name: ROPE = IagoOps.GetArg[ in: in, out: out, prompt: "\nWorking directory: ", default: IF IagoOps.clientVolName.Length[] # 0 THEN Rope.Cat["[]<", IagoOps.clientVolName, ">"] ELSE NIL, help: Help]; FS.SetDefaultWDir[name]; }; <> streamInfo: PROC [clientFile: REF] RETURNS [keep: CARDINAL, pages, bytes: INT, created: BasicTime.GMT, lock: FS.Lock] = { myfsFile: myOpenFile = NARROW[clientFile, myOpenFile]; keep _ 1; pages _ myfsFile.pages; bytes _ myfsFile.bytes; created _ myfsFile.created; lock _ read; }; streamReader: UNSAFE PROC [clientFile: REF, from, nPages: INT, to: LONG POINTER] = TRUSTED { strm: STREAM = NARROW[clientFile, myOpenFile].strm; strm.SetIndex[FS.BytesForPages[from]]; [] _ strm.UnsafeGetBlock[[base: LOOPHOLE[to], startIndex: 0, count: FS.BytesForPages[nPages]]]; }; }. <<>> <<>> <> <> <> <> <> <<>> <<>> <> <> <<>>