<> <> <> <> DIRECTORY Ascii, Basics USING [BytePair], Disk USING [Channel, DoIO, DriveAttributes, Label, PageCount, PageNumber, Request, Status], DiskFace USING [DeviceHandle, DiskAddress], ExtraIagoOps USING [GetAddress], ExtraIagoUtils, File USING [Error, FindVolumeFromID, FindVolumeFromName, FP, Handle, Info, nullVolumeID, Open, PageCount, PageNumber, Read, Volume, VolumeFile, VolumeID, wordsPerPage], FileInternal USING [Handle, FindRun, TranslateLogicalRun], FS USING [Error], FSBackdoor USING [Version], FSFileOps USING [GetNameBodyAndVersion], IagoOps USING [Confirm, FileError], IO, PhysicalVolume USING [GetSubVolumes, Physical, SubVolumeDetailsObject, SubVolumes], Rope, VM USING [AddressForPageNumber, Allocate, Free, Interval, nullInterval, PagesForWords, SwapIn, Unpin], VolumeFormat USING [Attributes, LogicalPage, LogicalRun, RelID]; ExtraIagoUtilsImpl: CEDAR PROGRAM IMPORTS Basics, Disk, ExtraIagoOps, File, FileInternal, FS, FSFileOps, IagoOps, IO, PhysicalVolume, VM EXPORTS ExtraIagoUtils = BEGIN OPEN IO, ExtraIagoUtils; STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; <<* * * * * * * * * * * * * * * * * Utility Procedures * * * * * * * * * * * * * * * >> StatusToRope: PUBLIC PROC[status: Disk.Status] RETURNS[r: ROPE, wasOK: BOOL] = { wasOK _ FALSE; WITH s: status SELECT FROM changed => r _ "drive's ChangeCount changed; no IO performed"; unchanged => SELECT s.status FROM inProgress => r _ "inProgress"; goodCompletion => {r _ "good Completion"; wasOK _ TRUE}; notReady => r _ "notReady"; recalibrateError => r _ "recalibrateError"; seekTimeout => r _ "seekTimeout"; headerCRCError => r _ "headerCRCError"; labelCRCError => r _ "labelCRCError"; dataCRCError => r _ "dataCRCError"; headerNotFound => r _ "headerNotFound"; labelVerifyError => r _ "labelVerifyError"; dataVerifyError => r _ "dataVerifyError"; overrunError => r _ "overrunError"; writeFault => r _ "writeFault"; memoryError => r _ "memoryError"; memoryFault => r _ "memoryFault"; clientError => r _ "clientError"; operationReset => r _ "operationReset"; otherError => r _ "otherError"; ENDCASE; ENDCASE; }; ReportStatus: PUBLIC PROC[out: STREAM, status: Disk.Status, dontReportOK: BOOL _ TRUE] = { r: ROPE; wasOK: BOOL; [r, wasOK]_ StatusToRope[status]; IF dontReportOK AND wasOK THEN RETURN; out.PutF["\n\n **********Status reported as: %g **********", rope[r]]; }; OKStatusForWrite: PUBLIC PROC[status: Disk.Status, in, out: STREAM] RETURNS[ok: BOOL] = { WITH s: status SELECT FROM changed => RETURN[FALSE]; unchanged => SELECT s.status FROM labelCRCError => NULL; goodCompletion, dataCRCError, dataVerifyError => RETURN[TRUE]; inProgress, notReady, recalibrateError, seekTimeout, headerCRCError, headerNotFound, labelVerifyError, overrunError, writeFault, memoryError, memoryFault, clientError, operationReset, otherError => RETURN[FALSE]; ENDCASE; ENDCASE; out.PutRope["\n LabelCRCError reported; attempt the write anyway?"]; ok _ IagoOps.Confirm[in, out]; }; ChannelToDeviceHandle: PUBLIC PROC[c: Disk.Channel] RETURNS[d: DiskFace.DeviceHandle] = { ordinal: CARDINAL _ Disk.DriveAttributes[c].ordinal; d _ LOOPHOLE[ordinal, DiskFace.DeviceHandle]; }; VolumeFileToRope: PUBLIC PROC[which: File.VolumeFile] RETURNS[ROPE] = { RETURN[SELECT which FROM checkpoint => "Checkpoint", microcode => "Microcode", germ => "Germ", bootFile => "BootFile", debugger => "Debugger", debuggee => "Debuggee", VM => "VM", VAM => "VAM", client => "FS-root", alpine => "Alpine-root", ENDCASE => "Other-root"]; }; AttributesToRope: PUBLIC PROC[attributes: VolumeFormat.Attributes] RETURNS[ROPE] = { RETURN[SELECT attributes FROM physicalRoot => "PhysicalRoot", badPageList => "BadPageList", badPage => "BadPage", subVolumeMarker => "SubVolumeMarker", logicalRoot => "LogicalRoot", freePage => "FreePage", header => "HeaderPage", data => "DataPage", lastCedar => "LastCedarAttribute", ENDCASE => "UnknownType"]; }; <<* * * * * * * * commands implementation * * * * * * * *>> DescribeOnePage: PUBLIC PROC[ logicalPage: VolumeFormat.LogicalPage, vol: File.Volume, in, out: STREAM] = TRUSTED { pageSpace: VM.Interval _ VM.nullInterval; { ENABLE UNWIND => FreeSpace[pageSpace]; initLabel: Disk.Label; data: LONG POINTER; label: Disk.Label _ initLabel; request: Disk.Request; status: Disk.Status; attr: VolumeFormat.Attributes; realPage: Disk.PageNumber; val: WORD; run: VolumeFormat.LogicalRun; channel: Disk.Channel; [pageSpace, data]_ GetSpace[1]; run _ [[logicalPage], 1]; [channel, realPage]_ FileInternal.TranslateLogicalRun[run, vol ! File.Error => { out.PutF["\nFile.Error (%g) during TranslateLogicalRun of logical page %g\n", IO.rope[IagoOps.FileError[why]], IO.int[logicalPage]]; GOTO doQuit; }; ]; BEGIN ENABLE File.Error => { out.PutF["\n File.Error (%g) reading page %g (physical page %g)\n", rope[IagoOps.FileError[why]], int[logicalPage], int[realPage]]; GOTO keepGoing; }; request _ [diskPage: [realPage], data: data, incrementDataPtr: FALSE, command: [verify, read, read], count: 1]; [status, ]_ Disk.DoIO[channel: channel, label: @label, request: @request]; attr _ LOOPHOLE[label.attributes]; IF attr = freePage THEN { out.PutF["\n Page %g (physical page %g) is free, with status %g", int[logicalPage], int[realPage], rope[StatusToRope[status].r]]; RETURN }; IF attr = data OR attr = header THEN { fp: File.FP _ LOOPHOLE[label.fileID.relID]; handle: File.Handle _ File.Open[vol, fp]; nameBody: Rope.Text; version: FSBackdoor.Version; [nameBody, version]_ FSFileOps.GetNameBodyAndVersion[f: handle ! FS.Error => { out.PutF["\n Page %g (physical page %g),\n\t***FS error: \"%g\"\n", int[logicalPage], int[realPage], rope[error.explanation] ]; out.PutF["\tFile.FP is: [id: %g (%bB), da: %g (%bB)]\n", IO.card[LOOPHOLE[fp.id]], IO.card[LOOPHOLE[fp.id]], IO.card[LOOPHOLE[fp.da]], IO.card[LOOPHOLE[fp.da]]]; out.PutF["\tIs %g page %g of some file", IF attr = data THEN rope["data"] ELSE rope["header"], IO.int[label.filePage] ]; CONTINUE}]; IF nameBody # NIL THEN { out.PutF["\n Page %g (physical page %g), with status %g\n", int[logicalPage], int[realPage], rope[StatusToRope[status].r]]; out.PutF[" *** is %g page %g of file %g, version %g", IF attr = data THEN rope["data"] ELSE rope["header"], int[label.filePage], rope[nameBody], int[version]]; }; RETURN; }; val _ LOOPHOLE[label.attributes]; out.PutF["\nOther type of page: logical: %g (physical %g), attributes: %g (%06b)\n", IO.int[logicalPage], int[realPage], IO.rope[AttributesToRope[attr]], IO.card[val]]; EXITS keepGoing => NULL; END; FreeSpace[pageSpace]; EXITS doQuit => FreeSpace[pageSpace]; }; }; CheckPages: PUBLIC PROC[handle: File.Handle, name: ROPE, out: STREAM] = { ENABLE FS.Error => { out.PutRope[" ... "]; IF error.code = $invalidPropertyPage THEN out.PutRope["not an FS file"] ELSE out.PutRope[error.explanation]; CONTINUE }; size: File.PageCount _ File.Info[handle].size; didStop: BOOL _ FALSE; diskPage: Disk.PageNumber; channel: Disk.Channel; pageSpace: VM.Interval _ VM.nullInterval; numPages: INT = 25; { ENABLE UNWIND => FreeSpace[pageSpace]; data: LONG POINTER; [pageSpace, data]_ GetSpace[numPages]; out.PutF["\nReading pages from %g file (%g pages)\n", IO.rope[name], IO.int[size]]; TRUSTED { i: File.PageCount _ 0; WHILE i < size DO IF i # 0 THEN IF i MOD 100 = 0 THEN IF i MOD 1000 = 0 THEN out.PutF["(%g)", IO.int[i]] ELSE out.PutChar['~]; File.Read[handle, [i], numPages, data ! File.Error => {IF why # unknownPage THEN didStop _ TRUE; EXIT}]; i _ i + numPages; ENDLOOP; IF didStop THEN { ff: FileInternal.Handle; badPage: INT _ -1; FOR j: File.PageCount IN [i..i+numPages) DO File.Read[handle, [j], 1, data ! File.Error => { out.PutF["\nFile error reading page %g\n", IO.int[j]]; out.PutRope[" ... "]; out.PutRope[IagoOps.FileError[why]]; badPage _ j; EXIT}]; ENDLOOP; ff _ LOOPHOLE[handle]; [diskPage, , channel]_ FileInternal.FindRun[[badPage], 1, ff.runTable]; out.PutChar['\n]; PrintPageAndDiskAddr[diskPage, ChannelToDeviceHandle[channel], out]; ReadAndReport[NIL, out, diskPage, 1, channel, none]; }; }; FreeSpace[pageSpace]; }; }; LogicalToPhysicalPage: PUBLIC PROC[logicalPage: VolumeFormat.LogicalPage, volName: ROPE] RETURNS[page: Disk.PageNumber] = { vol: File.Volume = File.FindVolumeFromName[volName]; IF vol = NIL THEN RETURN[[-1]]; [ , page] _ FileInternal.TranslateLogicalRun[[logicalPage, 1], vol]; }; PhysicalToLogicalPage: PUBLIC PROC[p: PhysicalVolume.Physical, page: Disk.PageNumber] RETURNS[logicalPage: VolumeFormat.LogicalPage, vol: File.Volume] = { sv: PhysicalVolume.SubVolumes = PhysicalVolume.GetSubVolumes[p]; FOR i: CARDINAL IN [0..sv.count) DO addr: INT = sv[i].address; IF page >= addr AND page < addr+sv[i].size THEN { logicalPage _ [page-addr]; vol _ File.FindVolumeFromID[sv[i].id]; RETURN }; ENDLOOP; RETURN[[0], File.FindVolumeFromID[File.nullVolumeID]]; }; PrintPageAndDiskAddr: PUBLIC PROC[ page: Disk.PageNumber, d: DiskFace.DeviceHandle, out: STREAM] = { addr: DiskFace.DiskAddress _ ExtraIagoOps.GetAddress[d, page]; IF addr.cylinder = LAST[CARDINAL] THEN { out.PutF[" Drive %g doesn't exist", int[LOOPHOLE[d, INTEGER]]]; RETURN}; out.PutF[" Page: %g (%bB),", int[page], card[page]]; out.PutF[" [cyl: %g (%bB), head: %g, sector: %g]", card[addr.cylinder], card[addr.cylinder], card[addr.head], card[addr.sector]]; }; ReadAndReport: PUBLIC PROC[in, out: STREAM, first, count: INT, d: Disk.Channel, howToShowData: HowToShowData] = TRUSTED { pageSpace: VM.Interval _ VM.nullInterval; { ENABLE UNWIND => FreeSpace[pageSpace]; origin: Disk.PageNumber; initLabel: Disk.Label; data: LONG POINTER; [pageSpace, data]_ GetSpace[1]; FOR i: INT IN [0 .. count) DO label: Disk.Label _ initLabel; val: WORD; request: Disk.Request; status: Disk.Status; countDone: INT; attr: VolumeFormat.Attributes; origin _ [first + i]; request _ [diskPage: origin, data: data, incrementDataPtr: FALSE, command: [verify, read, read], count: 1]; [status, countDone]_ Disk.DoIO[channel: d, label: @label, request: @request]; ReportStatus[out, status]; out.PutF["\n\n *******Label for disk page %g is:\nFileID: ", int[first+i]]; FOR i: INT IN [0..5) DO val _ LOOPHOLE[@label + i, LONG POINTER TO CARDINAL]^; out.PutF[" %06b", IO.card[val]]; ENDLOOP; attr _ LOOPHOLE[label.attributes]; IF attr = data OR attr = header THEN { relID: VolumeFormat.RelID _ LOOPHOLE[label.fileID.relID]; da: VolumeFormat.LogicalPage _ LOOPHOLE[relID.da]; out.PutF["\n Logical Address of Header: %g", int[da]]; }; val _ LOOPHOLE[label.attributes]; out.PutF["\nfilePage: %g, attributes: %g (%06b)\n", IO.int[label.filePage], IO.rope[AttributesToRope[attr]], IO.card[val]]; IF howToShowData # none THEN { ptr: LONG POINTER TO CARDINAL _ LOOPHOLE[data]; valsPerLine: INT = 8; -- so its easy to change numLines: INT _ 256/valsPerLine; out.PutF["\n *****Data for that page is:\n"]; IF howToShowData = all THEN { FOR i: INT IN [0..numLines) DO out.PutF["%03b/ ", IO.int[i*valsPerLine]]; FOR j: INT IN [0..valsPerLine) DO val _ (ptr+ j)^; out.PutF[" %06b", IO.card[val]]; ENDLOOP; out.PutRope[" "]; -- some white space FOR j: INT IN [0..valsPerLine) DO bytes: Basics.BytePair; val _ (ptr+ j)^; bytes _ LOOPHOLE[val, Basics.BytePair]; out.PutChar[CheckChar[LOOPHOLE[bytes.high, CHAR]]]; out.PutChar[CheckChar[LOOPHOLE[bytes.low, CHAR]]]; ENDLOOP; out.PutChar['\n]; ptr _ ptr + valsPerLine; ENDLOOP; } ELSE FOR i: INT IN [0 .. 256) DO bytes: Basics.BytePair; val _ (ptr+ i)^; bytes _ LOOPHOLE[val, Basics.BytePair]; out.PutChar[CheckChar[LOOPHOLE[bytes.high, CHAR]]]; out.PutChar[CheckChar[LOOPHOLE[bytes.low, CHAR]]]; ENDLOOP; }; ENDLOOP; FreeSpace[pageSpace]; }; }; CheckChar: PUBLIC PROC[ch: CHAR] RETURNS[CHAR] = { OPEN Ascii; xx: CARDINAL [0 .. 377B]; IF ch = NUL THEN RETURN[' ]; IF ch IN [ControlA .. ControlZ] THEN RETURN['*]; IF (xx _ LOOPHOLE[ch]) IN [200B .. 377B] THEN RETURN['!]; RETURN[ch] }; GetSpace: PROC[num: INT] RETURNS[space: VM.Interval, data: LONG POINTER] = TRUSTED { space _ VM.Allocate[VM.PagesForWords[num*File.wordsPerPage]]; data _ VM.AddressForPageNumber[space.page]; VM.SwapIn[interval: space, kill: TRUE, pin: TRUE]; }; FreeSpace: PROC[space: VM.Interval] = TRUSTED { IF space = VM.nullInterval THEN RETURN; VM.Unpin[space]; VM.Free[space]; space _ VM.nullInterval; }; END.