<> <> <> <> DIRECTORY Ascii, Basics USING [LongDivMod, DivMod], Convert USING [Error, IntFromRope], Disk USING [Channel, DoIO, DriveAttributes, GetDeviceFromChannel, Label, PageCount, PageNumber, Request, Status], DiskFace USING [DeviceHandle, DiskAddress, GetDeviceAttributes, GetTrueDeviceAttributes], ExtraIagoOps, ExtraIagoUtils, File USING [Delete, Error, FindVolumeFromID, FP, GetVolumeName, Handle, nullFP, Open, PageCount, PageNumber, Volume, VolumeFile, VolumeID, wordsPerPage], FileBackdoor USING [GetRoot, CreatePhysicalVolume, PhysicalPageBad], FileInternal USING [Handle, Alloc, Commit, FindRun, Free, GetBadPages, TranslateLogicalRun], FS USING [Close, Error, FileInfo, nullOpenFile, Open, OpenFile], FSBackdoor USING [GetFileHandle], FormatDisk USING [altoRegionJargon, altoRegionsSize, hardUCodeSize, BadPage, Sweep], IagoOps USING [Confirm, FileError, GetArg, GetDrive, GetFile, GetLogical, GetNumber, GetPhysical, NewID, NextRun, PutID], IO, PhysicalVolume USING [GetSubVolumes, NextPhysical, Physical, PhysicalInfo, PhysicalRC, SubVolumeDetails, SubVolumeDetailsObject, SubVolumes], Rope, SystemVersion USING [machineType], VM USING [AddressForPageNumber, Allocate, Free, Interval, nullInterval, PagesForBytes, PagesForWords, SwapIn, Unpin], VolumeFormat USING [BadPageList, LogicalPage, LogicalRun, PhysicalRoot]; ExtraIagoOpsImpl: CEDAR PROGRAM IMPORTS Basics, Convert, Disk, DiskFace, ExtraIagoUtils, File, FileBackdoor, FileInternal, FormatDisk, FS, FSBackdoor, IagoOps, IO, PhysicalVolume, Rope, SystemVersion, VM EXPORTS ExtraIagoOps = BEGIN OPEN IO, ExtraIagoUtils; STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; HowToShowData: TYPE = ExtraIagoUtils.HowToShowData; pagePrompt: ROPE = "\nPage number: "; logicalPagePrompt: ROPE = "\nLogicalPage number: "; pageHelp: ROPE = "? type the page number (in decimal)"; numPagesPrompt: ROPE = "\nNumber of pages: "; numPagesHelp: ROPE = "? type the number of pages (in decimal)"; howToPrompt: ROPE = "\nHow to show the data: "; howToHelp: ROPE = "? type one of {n (none), a (all), t (text), s (stop)}"; cylinderPrompt: ROPE = "\nCylinder number: "; startingCylinderPrompt: ROPE = "\nStarting Cylinder number: "; cylinderHelp: ROPE = "? type the cylinder number (in decimal)"; headPrompt: ROPE = "\nHead number: "; startingHeadPrompt: ROPE = "\nStarting Head number: "; headHelp: ROPE = "? type the head number (in decimal)"; sectorPrompt: ROPE = "\nSector number: "; sectorHelp: ROPE = "? type the sector number (in decimal)"; <<* * * * * * * * * * * * * * * * * Utility Procedures * * * * * * * * * * * * * * * >> GetCylinder: PROC[in, out: STREAM, max: INT] RETURNS[cyl: INT] = { cyl _ 0; cyl _ IagoOps.GetNumber[ in: in, out: out, default: cyl, max: max, prompt: cylinderPrompt, help: cylinderHelp]; }; GetStartingCylinder: PROC[in, out: STREAM, max: INT] RETURNS[cyl: INT] = { cyl _ 0; cyl _ IagoOps.GetNumber[ in: in, out: out, default: cyl, max: max, prompt: startingCylinderPrompt, help: cylinderHelp]; }; GetDrive: PROC[in, out: STREAM] RETURNS[d: Disk.Channel, drive: INT] = { d _ IagoOps.GetDrive[in, out]; drive _ IF d # NIL THEN Disk.DriveAttributes[d].ordinal ELSE -1; }; GetHeadNumber: PROC[in, out: STREAM, max: INT] RETURNS[head: INT] = { head _ 0; head _ IagoOps.GetNumber[ in: in, out: out, default: head, max: max, prompt: headPrompt, help: headHelp]; }; GetStartingHead: PROC[in, out: STREAM, max: INT] RETURNS[head: INT] = { head _ 0; head _ IagoOps.GetNumber[ in: in, out: out, default: head, max: max, prompt: startingHeadPrompt, help: headHelp]; }; GetPageNumber: PROC[in, out: STREAM, max: INT] RETURNS[page: INT] = { page _ 0; page _ IagoOps.GetNumber[ in: in, out: out, default: page, max: max, prompt: pagePrompt, help: pageHelp]; }; GetLogicalPageNumber: PROC[in, out: STREAM, max: INT] RETURNS[logicalPage: VolumeFormat.LogicalPage] = { page: INT _ 0; page _ IagoOps.GetNumber[ in: in, out: out, default: page, max: max, prompt: logicalPagePrompt, help: pageHelp]; RETURN[[page]]; }; GetNumberOfPages: PROC[in, out: STREAM, max: INT _ 20] RETURNS[count: INT] = { count _ 1; count _ IagoOps.GetNumber[ in: in, out: out, default: count, max: max, prompt: numPagesPrompt, help: numPagesHelp]; }; GetSector: PROC[in, out: STREAM, max: INT] RETURNS[sector: INT] = { sector _ 0; sector _ IagoOps.GetNumber[ in: in, out: out, default: sector, max: max, prompt: sectorPrompt, help: sectorHelp]; }; GetHowToShow: PROC[in, out: STREAM] RETURNS[howToShowData: HowToShowData] = { howTo: ROPE _ "n (none)"; HowToHelp: PROC = {out.PutRope[howToHelp]}; DO howTo _ IagoOps.GetArg[ in: in, out: out, default: howTo, prompt: howToPrompt, help: HowToHelp]; SELECT howTo.Fetch[0] FROM 'n, 'N => RETURN[none]; 'a, 'A => RETURN[all]; 't, 'T => RETURN[text]; 's, 'S => RETURN[stop]; ENDCASE => out.PutF["%g is not a legal value; try again", rope[howTo]]; ENDLOOP; }; IConvert: PUBLIC PROC[in, out: IO.STREAM] = { Help: PROC = { IO.PutRope[out, "type a number, ending in B or H"] }; ok: BOOL _ TRUE; sizeRope: ROPE _ IagoOps.GetArg[ in: in, out: out, prompt: "(xxxB) ", default: "0", help: Help]; size: INT; size _ Convert.IntFromRope[sizeRope ! Convert.Error => { ok _ FALSE; CONTINUE } ]; IF ok THEN out.PutF[" in decimal is %g", IO.int[size]] ELSE out.PutRope["\nNot a number"]; }; GetAddress: PUBLIC PROC[d: DiskFace.DeviceHandle, page: INT] RETURNS[addr: DiskFace.DiskAddress] = { ENABLE UNWIND => NULL; thisCylinder: CARDINAL; temp: CARDINAL; cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL; [cylinders, movingHeads, fixedHeads, sectorsPerTrack]_ DiskFace.GetDeviceAttributes[d]; IF cylinders = 0 THEN RETURN[[LAST[CARDINAL], 0, 0]]; [quotient: thisCylinder, remainder: temp] _ Basics.LongDivMod[ num: page, den: sectorsPerTrack * movingHeads]; IF (LOOPHOLE[d, CARDINAL] = 0) AND (SystemVersion.machineType = dorado) THEN <> RETURN[[cylinder: thisCylinder MOD 815, sector: temp MOD sectorsPerTrack, head: thisCylinder/815 ] ] ELSE RETURN[ [cylinder: thisCylinder, sector: temp MOD sectorsPerTrack, head: temp/sectorsPerTrack ] ] }; <<* * * * * * * * commands implementation * * * * * * * *>> AddPageToBadPageTable: PUBLIC PROC[in, out: STREAM] = { p: PhysicalVolume.Physical _ IagoOps.GetPhysical[in, out]; page: INT = GetPageNumber[in, out, LAST[INT]]; name: ROPE = PhysicalVolume.PhysicalInfo[p].name; out.PutF["\nAdding physical page %g to bad page table for %g", int[page], rope[name]]; []_ FileBackdoor.PhysicalPageBad[p, [page]]; }; DescribeAllocated: PUBLIC PROC[in, out: STREAM] = { FOR p: PhysicalVolume.Physical _ PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] UNTIL p = NIL DO channel: Disk.Channel; rootStatus: PhysicalVolume.PhysicalRC; id: File.VolumeID; name: Rope.ROPE; size: Disk.PageCount; free: Disk.PageCount; inUse: INT _ 0; reserved: INT; [channel: channel, rootStatus: rootStatus, id: id, name: name, size: size, free: free] _ PhysicalVolume.PhysicalInfo[p]; out.PutF["\n\nID: "]; IagoOps.PutID[out, id]; out.PutF["\nOn drive RD%g", [integer[Disk.DriveAttributes[channel].ordinal]] ]; SELECT rootStatus FROM ok => NULL--drop through to after ENDCASE--; wentOffline => { out.PutRope["\nVolume went offline"]; LOOP }; inconsistent => { out.PutRope["\nRoot page is inconsistent (wrong version?)"]; LOOP }; software => { out.PutRope["\nCan't find the volume root page (label-check)"]; LOOP }; hardware => { out.PutRope["\nHard disk error reading the volume root page"]; LOOP }; ENDCASE => ERROR File.Error[rootStatus]; out.PutF["\nName: %g\nSize: %g\n Logical sub-volumes:", [rope[name]], [integer[size]] ]; { sv: PhysicalVolume.SubVolumes = PhysicalVolume.GetSubVolumes[p]; FOR i: CARDINAL IN [0..sv.count) DO out.PutF["\n At physical page %g, logical id: ", [integer[sv[i].address]] ]; IagoOps.PutID[out, sv[i].id]; out.PutF[", pages [%g..%g)", [integer[sv[i].start]], [integer[sv[i].start+sv[i].size]] ]; inUse _ inUse + sv[i].size; ENDLOOP; reserved _ size - inUse - free; IF reserved # 0 THEN { num: INT _ reserved - FormatDisk.hardUCodeSize; IF num >= FormatDisk.altoRegionsSize THEN out.PutF["\n%g pages (%g %g) reserved for alto allocations", [integer[num]], [integer[num/FormatDisk.altoRegionsSize]], [rope[FormatDisk.altoRegionJargon]] ]; }; IF free # 0 THEN out.PutF["\n%g pages (%g %g) for potential alto allocations", [integer[free]], [integer[(free+1)/FormatDisk.altoRegionsSize]], [rope[FormatDisk.altoRegionJargon]] ]; }; ENDLOOP; }; BadPagesRec: TYPE = RECORD[ ls: LIST OF VolumeFormat.LogicalPage, subV: PhysicalVolume.SubVolumeDetails, vol: File.Volume, drive: DiskFace.DeviceHandle ]; BadPagesListProc: TYPE = PROC[ls: LIST OF VolumeFormat.LogicalPage, subV: PhysicalVolume.SubVolumeDetails, vol: File.Volume, drive: DiskFace.DeviceHandle]; DescribeBadPages: PUBLIC PROC[in, out: IO.STREAM] = TRUSTED { badPagesInfo: LIST OF BadPagesRec _ NIL; count: INT _ 0; Dbp: BadPagesListProc = TRUSTED { badPagesInfo _ CONS[ [ls, subV, vol, drive], badPagesInfo] }; DoBadPages[in, out, Dbp, FALSE]; IF badPagesInfo = NIL THEN RETURN; FOR bp: LIST OF BadPagesRec _ badPagesInfo, bp.rest UNTIL bp = NIL DO out.PutF["\n\nBad Pages for %g\n", rope[File.GetVolumeName[bp.first.vol]]]; FOR ls: LIST OF VolumeFormat.LogicalPage _ bp.first.ls, ls.rest UNTIL ls=NIL DO DescribeOnePage[ls.first, bp.first.vol, in, out]; IF (count _ count + 1) MOD 10 = 0 THEN { out.PutRope["\nContinue?"]; IF ~IagoOps.Confirm[in, out] THEN RETURN; }; ENDLOOP; ENDLOOP; }; DescribeVolumePage: PUBLIC PROC[in, out: IO.STREAM] = { vol: File.Volume = IagoOps.GetLogical[in, out]; logicalPage: VolumeFormat.LogicalPage = GetLogicalPageNumber[in, out, LAST[INT]]; DescribeOnePage[logicalPage, vol, in, out]; }; DescribeDiskPage: PUBLIC PROC[in, out: IO.STREAM] = { p: PhysicalVolume.Physical _ IagoOps.GetPhysical[in, out]; page: INT = GetPageNumber[in, out, LAST[INT]]; name: ROPE = PhysicalVolume.PhysicalInfo[p].name; logicalPage: VolumeFormat.LogicalPage; vol: File.Volume; [logicalPage, vol] _ PhysicalToLogicalPage[p, [page]]; IF vol = NIL THEN { out.PutF["\nPage %g not valid for %g", IO.int[page], IO.rope[name]]; RETURN; }; DescribeOnePage[logicalPage, vol, in, out]; }; EnsureBadPagesInVAM: PUBLIC PROC[in, out: IO.STREAM] = { Ensure: BadPagesListProc = TRUSTED { FOR vls: LIST OF VolumeFormat.LogicalPage _ ls, vls.rest UNTIL vls=NIL DO AllocatePageInVAM[out, vol, vls.first]; ENDLOOP; }; DoBadPages[in, out, Ensure, TRUE]; }; ListBadPages: PUBLIC PROC[in, out: IO.STREAM] = { Print: BadPagesListProc = TRUSTED { FOR vls: LIST OF VolumeFormat.LogicalPage _ ls, vls.rest UNTIL vls=NIL DO PrintBadPageInfo[out, subV, vls.first, drive]; ENDLOOP; }; DoBadPages[in, out, Print, TRUE]; }; MakeDiskAddress: PUBLIC PROC[in, out: STREAM] = { drive, cyl, head, sector: INT; page: INT _ 0; cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL; d: Disk.Channel; device: DiskFace.DeviceHandle; [d, drive] _ GetDrive[in, out]; IF d = NIL THEN RETURN; device _ Disk.GetDeviceFromChannel[d]; [cylinders, movingHeads, fixedHeads, sectorsPerTrack] _ DiskFace.GetTrueDeviceAttributes[device]; cyl _ GetCylinder[in, out, cylinders-1]; head _ GetHeadNumber[in, out, movingHeads-1]; sector _ GetSector[in, out, sectorsPerTrack-1]; IF cylinders # 0 THEN { IF drive = 0 AND (SystemVersion.machineType = dorado) THEN -- funny drive 0 addressing page _ head*cylinders*sectorsPerTrack + cyl*sectorsPerTrack + sector ELSE page _ head*sectorsPerTrack + cyl*movingHeads*sectorsPerTrack + sector; }; out.PutF["\nPage number %g", IO.int[page]]; }; MarkPageAllocated: PUBLIC PROC[in, out: STREAM] = { vol: File.Volume = IagoOps.GetLogical[in, out]; logicalPage: VolumeFormat.LogicalPage = GetLogicalPageNumber[in, out, LAST[INT]]; AllocatePageInVAM[out, vol, logicalPage]; }; MiniDiskDiagnostic: PUBLIC PROC[in, out: IO.STREAM] = { firstPage, drive, count: INT; cylinders, movingHeads, fixedHeads, sectorsPerTrack: CARDINAL; d: Disk.Channel; [d, drive] _ GetDrive[in, out]; IF d = NIL THEN RETURN; [cylinders, movingHeads, fixedHeads, sectorsPerTrack] _ DiskFace.GetTrueDeviceAttributes[Disk.GetDeviceFromChannel[d]]; IF drive = 0 AND (SystemVersion.machineType = dorado) THEN { ch: CHAR; isP, isH: BOOL _ FALSE; DO ph: ROPE = "\nType p for specific pages, h for specific head: "; Hph: PROC = { out.PutRope[ph] }; resp: ROPE _ IagoOps.GetArg[in, out, ph, "p", Hph]; ch _ resp.Fetch[0]; IF (isP _ (ch = 'p OR ch = 'P)) OR (isH _ (ch = 'h OR ch = 'H)) THEN EXIT; ENDLOOP; IF isP THEN { firstPage _ GetPageNumber[in, out, LAST[INT]]; count _ GetNumberOfPages[in, out, LAST[INT]]; } ELSE { head: INT _ GetHeadNumber[in, out, movingHeads-1]; startingCyl: INT _ GetStartingCylinder[in, out, cylinders-1]; count _ GetNumberOfPages[in, out, (cylinders-startingCyl)*sectorsPerTrack]; firstPage _ head*cylinders*sectorsPerTrack + startingCyl*sectorsPerTrack; }; } ELSE { -- not drive 0 or not Dorado ch: CHAR; isP, isC: BOOL _ FALSE; DO pc: ROPE = "\nType p for specific pages, c for specific cylinder: "; Hpc: PROC = { out.PutRope[pc] }; resp: ROPE _ IagoOps.GetArg[in, out, pc, "p", Hpc]; ch _ resp.Fetch[0]; IF (isP _ (ch = 'p OR ch = 'P)) OR (isC _ (ch = 'c OR ch = 'C)) THEN EXIT; ENDLOOP; IF isP THEN { firstPage _ GetPageNumber[in, out, LAST[INT]]; count _ GetNumberOfPages[in, out, LAST[INT]]; } ELSE { cyl: INT _ GetCylinder[in, out, cylinders-1]; startingHead: INT _ GetStartingHead[in, out, movingHeads-1]; count _ GetNumberOfPages[in, out, LAST[INT]]; firstPage _ startingHead*sectorsPerTrack + cyl*sectorsPerTrack; }; }; <> TRUSTED {ScanThesePages[in, out, d, firstPage, count]}; }; ReadDiskPages: PUBLIC PROC[in, out: STREAM] = { page, drive, count: INT; d: Disk.Channel; howToShowData: HowToShowData; [d, drive] _ GetDrive[in, out]; IF d = NIL THEN RETURN; page _ GetPageNumber[in, out, LAST[INT]]; count _ GetNumberOfPages[in, out]; IF count = 0 THEN RETURN; howToShowData _ GetHowToShow[in, out]; IF howToShowData = stop THEN RETURN; out.PutF["\nReading %g pages starting at %g on RD%g ...", int[count], int[page], int[drive]]; ReadAndReport[in, out, page, count, d, howToShowData]; }; ReadFilePages: PUBLIC PROC[in, out: STREAM] = { name: ROPE _ IagoOps.GetFile[in: in, out: out]; openFile: FS.OpenFile; handle: File.Handle; fiHandle: FileInternal.Handle; fullName: ROPE _ NIL; diskPage: Disk.PageNumber; channel: Disk.Channel; bytes, pagesInFile, startingPage, count: INT; howToShowData: HowToShowData _ none; [fullName, , , bytes, ]_ FS.FileInfo[name: name ! FS.Error => SELECT error.code FROM $unknownServer, $unknownVolume, $unknownFile, $illegalName, $patternNotAllowed => { out.PutRope[" ... "]; out.PutRope[error.explanation]; CONTINUE }; ENDCASE => NULL ]; IF fullName.Length[] = 0 THEN RETURN; pagesInFile _ VM.PagesForBytes[bytes]; startingPage _ GetPageNumber[in, out, pagesInFile]; count _ GetNumberOfPages[in, out]; IF count = 0 THEN RETURN; IF (count + startingPage) > pagesInFile THEN count _ pagesInFile - startingPage - 1; howToShowData _ GetHowToShow[in, out]; IF howToShowData = stop THEN RETURN; handle _ FSBackdoor.GetFileHandle[openFile _ FS.Open[fullName]]; TRUSTED {fiHandle _ LOOPHOLE[handle]}; FOR i: INT IN [0 .. count) DO [diskPage, , channel]_ FileInternal.FindRun[[startingPage+i], 1, fiHandle.runTable]; ReadAndReport[in, out, diskPage, count, channel, howToShowData]; ENDLOOP; IF openFile # FS.nullOpenFile THEN FS.Close[openFile]; }; ReadLogicalPages: PUBLIC PROC[in, out: IO.STREAM] = { count: INT; d: Disk.Channel; diskPage: Disk.PageNumber; howToShowData: HowToShowData; run: VolumeFormat.LogicalRun; volume: File.Volume = IagoOps.GetLogical[in, out]; logicalPage: VolumeFormat.LogicalPage; IF volume = NIL THEN RETURN; logicalPage _ GetLogicalPageNumber[in, out, LAST[INT]]; count _ GetNumberOfPages[in, out]; IF count = 0 THEN RETURN; howToShowData _ GetHowToShow[in, out]; IF howToShowData = stop THEN RETURN; run _ [logicalPage, 1]; [d, diskPage]_ FileInternal.TranslateLogicalRun[run, volume]; out.PutF["\nReading %g pages starting at (logical) %g on volume: %g ...", int[count], int[logicalPage], rope[File.GetVolumeName[volume]]]; ReadAndReport[in, out, diskPage, count, d, howToShowData]; }; ReadNamedFile: PUBLIC PROC[in, out: IO.STREAM] = { name: ROPE = IagoOps.GetFile[in: in, out: out]; openFile: FS.OpenFile; handle: File.Handle = FSBackdoor.GetFileHandle[openFile _ FS.Open[name]]; IF handle # NIL THEN CheckPages[handle, name, out]; IF openFile # FS.nullOpenFile THEN FS.Close[openFile]; }; ShowDiskAddress: PUBLIC PROC[in, out: IO.STREAM] = { d: Disk.Channel = GetDrive[in: in, out: out].d; page: INT = GetPageNumber[in, out, LAST[INT]]; out.PutChar['\n]; PrintPageAndDiskAddr[[page], ChannelToDeviceHandle[d], out]; }; WriteDiskPage: PUBLIC PROC[in, out: STREAM] = TRUSTED { diskPage, drive: INT; d: Disk.Channel; useOldData: BOOL; pageSpace: VM.Interval; [d, drive] _ GetDrive[in, out]; IF d = NIL THEN RETURN; diskPage _ GetPageNumber[in, out, LAST[INT]]; out.PutRope["\nDo you want to try to rewrite the old data? "]; useOldData _ IagoOps.Confirm[in, out]; { ENABLE UNWIND => FreeSpace[pageSpace]; data: LONG POINTER; initLabel: Disk.Label; label: Disk.Label _ initLabel; request: Disk.Request; status: Disk.Status; [pageSpace, data]_ GetSpace[1]; request _ [diskPage: [diskPage], data: data, incrementDataPtr: FALSE, command: [verify, read, read], count: 1]; [status, ]_ Disk.DoIO[channel: d, label: @label, request: @request]; IF OKStatusForWrite[status, in, out] THEN { IF ~useOldData THEN FOR i: INT IN [0 .. File.wordsPerPage) DO (data+i)^_ 0; ENDLOOP; request _ [diskPage: [diskPage], data: data, incrementDataPtr: FALSE, command: [verify, verify, write], count: 1]; [status, ]_ Disk.DoIO[channel: d, label: @label, request: @request]; ReportStatus[out, status, FALSE]; } ELSE { ReportStatus[out, status]; out.PutRope["\n Write NOT done\n"]; }; FreeSpace[pageSpace]; }; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> DeleteSystemFile: PUBLIC PROC[in, out: IO.STREAM, which: File.VolumeFile] = { volume: File.Volume _ IagoOps.GetLogical[in, out]; fp: File.FP; file: File.Handle; name: ROPE _ VolumeFileToRope[which]; fp _ FileBackdoor.GetRoot[volume, which].fp; IF fp = File.nullFP THEN { out.PutF["\n%g file doesn't exist", rope[name]]; RETURN}; file _ File.Open[volume, fp ! File.Error => { out.PutRope[" ... "]; out.PutRope[IagoOps.FileError[why]]; file _ NIL; CONTINUE } ]; IF file # NIL THEN { out.PutF["Confirm deletion of %g file: ", rope[name]]; IF IagoOps.Confirm[in, out] THEN { out.PutF["\nDeleting %g file", rope[name]]; File.Delete[file]; out.PutF["\n%g file has been deleted", rope[name]] }; }; }; ReadSystemFile: PUBLIC PROC[in, out: STREAM, which: File.VolumeFile] = { volume: File.Volume _ IagoOps.GetLogical[in, out]; fp: File.FP; page: File.PageNumber; handle: File.Handle; name: ROPE _ VolumeFileToRope[which]; [fp, page]_ FileBackdoor.GetRoot[volume, which]; IF fp = File.nullFP THEN RETURN; handle _ File.Open[volume, fp ! File.Error => { out.PutRope[" ... "]; out.PutRope[IagoOps.FileError[why]]; handle _ NIL; CONTINUE } ]; IF handle # NIL THEN CheckPages[handle, name, out]; }; AllocatePageInVAM: PROC[ out: STREAM, vol: File.Volume, logicalPage: VolumeFormat.LogicalPage] = { given: VolumeFormat.LogicalRun _ FileInternal.Alloc[volume: vol, first: logicalPage, size: 1, min: 1]; IF given.first = logicalPage THEN { FileInternal.Commit[vol]; out.PutF["\n Logical page %g has been added to the VAM for volume: %g", IO.int[logicalPage], IO.rope[File.GetVolumeName[vol]]] } ELSE { FileInternal.Free[vol, given]; out.PutF["\n Logical page %g was already in the VAM", IO.int[logicalPage]]; }; }; ReservedList: TYPE = LIST OF RECORD[start: Disk.PageNumber, size: Disk.PageCount]; P2: TYPE = REF PhysicalObject; PhysicalObject: TYPE = RECORD[ channel: Disk.Channel, rootStatus: PhysicalVolume.PhysicalRC _ ok, root: LONG POINTER TO VolumeFormat.PhysicalRoot _ NIL, bad: LONG POINTER TO VolumeFormat.BadPageList _ NIL, name: Rope.ROPE _ NIL, reserved: ReservedList _ NIL, rest: REF PhysicalObject _ NIL ]; DoBadPages: PROC[in, out: IO.STREAM, proc: BadPagesListProc, doOut: BOOL] = { p: PhysicalVolume.Physical _ IagoOps.GetPhysical[in, out]; anyBad: BOOL _ FALSE; sv: PhysicalVolume.SubVolumes; drive: DiskFace.DeviceHandle; subV: PhysicalVolume.SubVolumeDetails; vol: File.Volume; badList: LIST OF VolumeFormat.LogicalPage; IF p = NIL THEN RETURN; TRUSTED { p2: P2 _ LOOPHOLE[p]; p2.bad _ NIL; <> }; drive _ ChannelToDeviceHandle[PhysicalVolume.PhysicalInfo[p].channel]; sv _ PhysicalVolume.GetSubVolumes[p]; FOR i: CARDINAL IN [0..sv.count) DO KeepList: PROC[logicalPage: VolumeFormat.LogicalPage] = { badList _ CONS[logicalPage, badList]; }; badList _ NIL; subV _ NEW[PhysicalVolume.SubVolumeDetailsObject _ sv[i]]; FileInternal.GetBadPages[subVolume: subV, work: KeepList]; IF badList = NIL THEN LOOP; anyBad _ TRUE; vol _ File.FindVolumeFromID[subV.id]; IF doOut THEN out.PutF["\n\nBad Pages for %g {subvolume %g}:", rope[File.GetVolumeName[vol]], card[i]]; IF proc # NIL THEN proc[badList, subV, vol, drive]; ENDLOOP; IF ~anyBad THEN IF doOut THEN out.PutRope["\nNo bad pages listed"]; }; PrintBadPageInfo: PROC[out: STREAM, subV: PhysicalVolume.SubVolumeDetails, logicalPage: VolumeFormat.LogicalPage, drive: DiskFace.DeviceHandle] = { bad: Disk.PageNumber _ [logicalPage + subV.address + subV.start]; out.PutF["\n Logical page: %g (%bB)", int[logicalPage], int[logicalPage]]; PrintPageAndDiskAddr[bad, drive, out]; }; ScanThesePages: UNSAFE PROC [in, out: STREAM, d: Disk.Channel, firstPage, count: INT] = UNCHECKED { pageSpace: VM.Interval; data: LONG POINTER; soft, hard: INT _ 0; leftToScan: Disk.PageCount _ count; BadList: TYPE = LIST OF RECORD[ page: Disk.PageNumber, correctable: BOOL, dontReport: BOOL _ FALSE]; badPages: BadList _ NIL; errorsThisPass: BOOL; ScanPages: UNSAFE PROC[out: STREAM, d: Disk.Channel] = UNCHECKED { origin: Disk.PageNumber _ [firstPage]; initLabel: Disk.Label; label: Disk.Label _ initLabel; tildasPrinted: INT _ 0; pagesPerTilda: INT = 1000; errorsThisPass _ FALSE; DO thisFirstPage: Disk.PageNumber; pageCount: Disk.PageCount; IF leftToScan <= 0 THEN EXIT; [thisFirstPage, pageCount] _ IagoOps.NextRun[d, origin]; IF pageCount <= 0 THEN EXIT; IF pageCount > leftToScan THEN pageCount _ leftToScan; WHILE pageCount > pagesPerTilda DO FormatDisk.Sweep[d, read, thisFirstPage, pagesPerTilda, @label, data]; IF (tildasPrinted _ tildasPrinted + 1) MOD 10 = 0 THEN out.PutChar['!] ELSE out.PutChar['~]; pageCount _ pageCount - pagesPerTilda; thisFirstPage _ [thisFirstPage + pagesPerTilda]; leftToScan _ leftToScan - pagesPerTilda; ENDLOOP; <> FormatDisk.Sweep[d, read, thisFirstPage, pageCount, @label, data]; IF (tildasPrinted _ tildasPrinted + 1) MOD 10 = 0 THEN out.PutChar['!] ELSE out.PutChar['~]; origin _ [thisFirstPage+pageCount]; leftToScan _ leftToScan - pageCount; ENDLOOP; IF NOT errorsThisPass THEN out.PutRope[" .. no errors"]; }; { ENABLE { UNWIND => VM.Free[pageSpace]; FormatDisk.BadPage => { <<[page: Disk.PageNumber, correctable: BOOL]>> b: BadList; FOR b _ badPages, b.rest UNTIL b = NIL DO IF b.first.page = page THEN { IF b.first.correctable THEN { IF NOT correctable THEN { b.first.correctable _ FALSE; soft _ soft-1; hard _ hard+1 }; }; EXIT }; REPEAT FINISHED => { badPages _ b _ CONS[first: [page: page, correctable: correctable], rest: badPages]; IF correctable THEN soft _ soft+1 ELSE hard _ hard+1; } ENDLOOP; IF hard > 100 THEN { out.PutRope["\nThere are over 100 hard disk errors. Continue? "]; IF NOT IagoOps.Confirm[in, out] THEN CONTINUE; hard _ 0; }; IF NOT b.first.dontReport THEN { logicalDiskCyl: CARDINAL; physicalDiskCyl, physicalDiskTrack, physicalDiskSector: CARDINAL; cylinder, track, sector, temp: CARDINAL; device: DiskFace.DeviceHandle _ Disk.GetDeviceFromChannel[d]; IO.PutRope[out, IF errorsThisPass THEN ", " ELSE "\nDisk error(s): "]; errorsThisPass _ TRUE; [cylinders: physicalDiskCyl, movingHeads: physicalDiskTrack, sectorsPerTrack: physicalDiskSector] _ DiskFace.GetTrueDeviceAttributes[device]; [cylinders: logicalDiskCyl] _ DiskFace.GetDeviceAttributes[device]; IF logicalDiskCyl # physicalDiskCyl THEN { <> [quotient: temp, remainder: sector] _ Basics.LongDivMod[ num: page, den: physicalDiskSector ]; [quotient: track, remainder: cylinder] _ Basics.DivMod[ num: temp, den: physicalDiskCyl]; } ELSE { <> [quotient: cylinder, remainder: temp] _ Basics.LongDivMod[ num: page, den: physicalDiskSector * physicalDiskTrack]; [quotient: track, remainder: sector] _ Basics.DivMod[ num: temp, den: physicalDiskSector]; }; IO.PutF[out, "%bB(%g)", [integer[page]], [rope[IF correctable THEN "soft" ELSE "hard"]] ]; b.first.dontReport _ NOT correctable; -- suppress messages on subsequent passes }; RESUME }; }; [pageSpace, data] _ GetSpace[1]; out.PutChar['\n]; ScanPages[out, d]; <> { p: PhysicalVolume.Physical _ NIL; rc: PhysicalVolume.PhysicalRC _ ok; IF badPages # NIL THEN { out.PutRope["Do you want the bad pages recorded in the Bad Page Table?"]; IF IagoOps.Confirm[in, out] THEN { FOR p _ PhysicalVolume.NextPhysical[NIL], PhysicalVolume.NextPhysical[p] DO IF p = NIL OR PhysicalVolume.PhysicalInfo[p].channel = d THEN EXIT; ENDLOOP; IF p = NIL THEN { IO.PutRope[out, "\nCreating a physical volume to hold the bad page table ..."]; p _ FileBackdoor.CreatePhysicalVolume[ where: d, name: "JustForTheBadPageTable", id: IagoOps.NewID[]]; IO.PutRope[out, "done"]; }; IO.PutRope[out, "\nRecording bad pages in physical bad page table ... "]; FOR b: BadList _ badPages, b.rest UNTIL b = NIL OR rc # ok DO full: BOOL _ FALSE; IF NOT b.first.correctable THEN { [full, rc] _ FileBackdoor.PhysicalPageBad[p, b.first.page]; IF full THEN { IO.PutRope[out, "\nOops, bad page table full. Aborting."]; GO TO tableFull; }; }; ENDLOOP; IF rc = ok THEN IO.PutRope[out, " done" ] ELSE ERROR File.Error[rc]; EXITS tableFull => {}; }; }; }; FreeSpace[pageSpace]; }; }; 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.