-- Copyright (C) 1985 by Xerox Corporation. All rights reserved.
-- FloppyImplPublicD.mesa (last edited by: EKN on: 1-Feb-85 8:02:07)
DIRECTORY
Environment USING [wordsPerPage],
File USING [nullFile, Type],
FileTypes USING [tUnassigned],
Floppy USING [
Density, Error, ErrorType, FileID, maxCharactersInLabel, nullFileID, Sides],
FloppyChannel USING [Attributes, GetDeviceAttributes, GetHandle, SetContext],
FloppyExtras USING [ExtrasError, ExtrasErrorType],
FloppyFormat USING [
badSpotSector, BadSpotSectors, ConvertPageCount, dataContext, FileListEntry,
FileListSeal, FileListVersion, FloppySeal, FloppyVersion, ImplementedFileSize,
MarkerPage, MarkerPageEntryType, MarkerPageVersion, MarkerSeal, maxBadSectors,
minTrackZeroSectors, nullSector, Sector, SectorNine, TrackZero,
trackZeroAddress, TrackZeroSector],
FloppyImplInterface USING [
AccessFloppy, Allocation, allocationsPerPage, CreateBuffer, DiskChanged,
FileListType, FirstDataSector, IOError, IsDriveWriteProtected, ReadFloppy,
ValidDrive, VolumeDesc, VolumeChanging, VolumeStable, volumeTable,
WordsToPages],
Runtime USING [CallDebugger],
Space USING [Error, Interval, Map, nullInterval, PageCount, Unmap];
FloppyImplPublicD: MONITOR
LOCKS volumeDesc USING volumeDesc: FloppyImplInterface.VolumeDesc
IMPORTS Floppy, FloppyChannel, FloppyExtras, FloppyFormat, FloppyImplInterface,
Runtime, Space
EXPORTS FloppyExtras
SHARES FloppyImplInterface =
BEGIN
-- PUBLIC ERRORs, SIGNALs and TYPEs
-- Private constants, types, variables
BugType: TYPE = {insufficientVM};
Bug: PROCEDURE [bug: BugType] = {Runtime.CallDebugger["Insufficient VM"L]};
-- When Bug moves from RuntimeInternal to SpecialRuntime, use the following
--Bug: PROCEDURE [bug: BugType] = {RuntimeInternal.Bug[bug]};
ScavengeProblemType: TYPE = {allocMapInconsistent, badPageTable, bootFile,
duplicateFileID, duplicateFileList, fileList, fileListEntry,
freeSpaceConflict, ioError, none, sectorNine};
problem: ScavengeProblemType ← none;
-- PUBLIC operations
--FloppyExtras.--
NewScavenge: PUBLIC PROCEDURE [drive: CARDINAL] RETURNS [okay: BOOLEAN] =
BEGIN
buffer: Space.Interval ← Space.nullInterval;
type: Floppy.ErrorType;
ScavengeInternal: ENTRY PROCEDURE [
volumeDesc: FloppyImplInterface.VolumeDesc] =
BEGIN
Shutdown: PROCEDURE =
BEGIN
buffer.pointer ← Space.Unmap[buffer.pointer ! Space.Error => CONTINUE];
IF volumeDesc.fileList ~= NIL THEN volumeDesc.fileList ←
Space.Unmap[volumeDesc.fileList ! Space.Error => CONTINUE];
IF volumeDesc.allocationMap ~= NIL THEN volumeDesc.allocationMap ←
Space.Unmap[volumeDesc.allocationMap ! Space.Error => CONTINUE];
END;
BEGIN ENABLE
BEGIN
FloppyImplInterface.IOError => {problem ← ioError; GOTO notOK};
Floppy.Error => {type ← error; GOTO floppyError}; --?????necessary????
UNWIND => Shutdown[];
END;
trackZero: FloppyFormat.TrackZero;
markerPage: LONG POINTER TO FloppyFormat.MarkerPage;
requestedSize: Space.PageCount;
sectorNine: LONG POINTER TO FloppyFormat.SectorNine;
ValidMarkers: PROCEDURE [fileAddress: FloppyFormat.Sector,
enclosee: FloppyFormat.MarkerPageEntryType,
size: FloppyFormat.ImplementedFileSize,
id: Floppy.FileID ← Floppy.nullFileID,
type: File.Type ← FileTypes.tUnassigned]
RETURNS [valid: BOOLEAN] =
BEGIN
FloppyImplInterface.ReadFloppy[
volumeDesc: volumeDesc, buffer: markerPage, count: 1,
address: fileAddress - 1];
IF NOT ((markerPage.seal = FloppyFormat.MarkerSeal)
AND (markerPage.version = FloppyFormat.MarkerPageVersion)
AND (markerPage.next.length = size)) THEN GOTO failed;
WITH mp: markerPage.next SELECT FROM
free => IF enclosee ~= free THEN GOTO failed;
file =>
IF NOT ((enclosee = file) AND (mp.file = id) AND (mp.type = type))
THEN GOTO failed;
fileList =>
IF NOT
((enclosee = fileList) AND (mp.file = id) AND (mp.type = type))
THEN GOTO failed;
badSectors => IF enclosee ~= badSectors THEN GOTO failed;
ENDCASE => GOTO failed;
FloppyImplInterface.ReadFloppy[
volumeDesc: volumeDesc, buffer: markerPage, count: 1,
address: fileAddress + size];
IF NOT ((markerPage.seal = FloppyFormat.MarkerSeal)
AND (markerPage.version = FloppyFormat.MarkerPageVersion)
AND (markerPage.previous.length = size)) THEN GOTO failed;
WITH mp: markerPage.previous SELECT FROM
free => IF enclosee ~= free THEN GOTO failed;
file =>
IF ((enclosee ~= file) AND (mp.file = id) AND (mp.type = type))
THEN GOTO failed;
fileList =>
IF ((enclosee ~= fileList) AND (mp.file = id) AND (mp.type = type))
THEN GOTO failed;
badSectors => IF enclosee ~= badSectors THEN GOTO failed;
ENDCASE => GOTO failed;
valid ← TRUE;
EXITS failed => valid ← FALSE;
END; -- ValidMarkers
-- Begin ScavengeInternal main code
IF volumeDesc.open THEN
RETURN WITH ERROR FloppyExtras.ExtrasError[volumeOpen];
-- Get buffers and handle to access the floppy
requestedSize ←
FloppyImplInterface.WordsToPages[(SIZE[FloppyFormat.TrackZeroSector] *
FloppyFormat.minTrackZeroSectors)] +
FloppyImplInterface.WordsToPages[SIZE[FloppyFormat.MarkerPage]];
buffer ← FloppyImplInterface.CreateBuffer[requestedSize];
IF requestedSize ~= buffer.count THEN Bug[insufficientVM];
trackZero.BASE ← LOOPHOLE[buffer.pointer];
markerPage ← LOOPHOLE[
BASE[trackZero] +
Environment.wordsPerPage *
(FloppyImplInterface.WordsToPages[FloppyFormat.minTrackZeroSectors *
SIZE[FloppyFormat.TrackZeroSector]])];
volumeDesc.fileList ← NIL;
volumeDesc.allocationMap ← NIL;
volumeDesc.handle ← FloppyChannel.GetHandle[drive];
IF (volumeDesc.writeProtected ←
FloppyImplInterface.IsDriveWriteProtected[volumeDesc])
THEN RETURN WITH ERROR Floppy.Error[writeInhibited];
-- Read track zero. Validate contents, fill in our local database.
-- (We can access fixed-format track0 before volumeDesc is complete.)
FloppyImplInterface.AccessFloppy[
volumeDesc, BASE[trackZero], FloppyFormat.trackZeroAddress,
FloppyFormat.minTrackZeroSectors, read];
-- Check SectorNine, continue filling in volumeDesc
volumeDesc.sectorNine↑ ← LOOPHOLE[trackZero[9]];
sectorNine ← volumeDesc.sectorNine;
IF FloppyChannel.SetContext[volumeDesc.handle, FloppyFormat.dataContext[single]] AND
FloppyChannel.GetDeviceAttributes[volumeDesc.handle].maxSectorsPerTrack = sectorNine.sectorsPerTrack
THEN volumeDesc.density ← single
ELSE IF FloppyChannel.SetContext[volumeDesc.handle, FloppyFormat.dataContext[double]] AND
FloppyChannel.GetDeviceAttributes[volumeDesc.handle].maxSectorsPerTrack = sectorNine.sectorsPerTrack
THEN volumeDesc.density ← double
ELSE {problem ← sectorNine; GOTO notOK};
SELECT sectorNine.tracksPerCylinder FROM
1 => volumeDesc.sides ← one;
2 => volumeDesc.sides ← two;
ENDCASE => {problem ← sectorNine; GOTO notOK};
BEGIN
attrib: FloppyChannel.Attributes ←
FloppyChannel.GetDeviceAttributes[volumeDesc.handle];
IF NOT ((sectorNine.seal = FloppyFormat.FloppySeal)
AND (sectorNine.version = FloppyFormat.FloppyVersion)
AND (sectorNine.cylinders > 0)
AND (sectorNine.cylinders = attrib.numberOfCylinders)
AND (sectorNine.tracksPerCylinder = attrib.numberOfHeads)
AND (sectorNine.sectorsPerTrack = attrib.maxSectorsPerTrack))
THEN {problem ← sectorNine; GOTO notOK};
volumeDesc.numPages ← sectorNine.sectorsPerTrack*
sectorNine.tracksPerCylinder * sectorNine.cylinders;
END;
-- Check sector nine file list entries.
IF NOT ((sectorNine.fileList ~= FloppyFormat.nullSector)
AND (sectorNine.fileList < volumeDesc.numPages)
AND (sectorNine.fileListID ~= Floppy.nullFileID)
AND (sectorNine.fileListSize > 0))
THEN {problem ← sectorNine; GOTO notOK};
IF ~ValidMarkers[fileAddress: sectorNine.fileList, enclosee: fileList,
size: sectorNine.fileListSize, id: sectorNine.fileListID,
type: FloppyImplInterface.FileListType]
THEN {problem ← fileList; GOTO notOK};
-- Get file list itself, check
volumeDesc.fileListSpace ←
FloppyImplInterface.CreateBuffer[sectorNine.fileListSize];
volumeDesc.fileList ← volumeDesc.fileListSpace.pointer;
FloppyImplInterface.ReadFloppy[
volumeDesc, volumeDesc.fileList, sectorNine.fileList,
sectorNine.fileListSize];
IF NOT ((volumeDesc.fileList.seal = FloppyFormat.FileListSeal)
AND (volumeDesc.fileList.version = FloppyFormat.FileListVersion)
AND (volumeDesc.fileList.maxEntries > 0)
AND (volumeDesc.fileList.maxEntries >= volumeDesc.fileList.count)
AND (volumeDesc.fileList.count > 0))
THEN {problem ← fileList; GOTO notOK};
BEGIN
fileListFound: BOOLEAN ← FALSE;
highestID: Floppy.FileID ← Floppy.nullFileID;
FOR i: CARDINAL IN [0..volumeDesc.fileList.count) DO
file: FloppyFormat.FileListEntry = volumeDesc.fileList.files[i];
IF NOT ((file.file ~= Floppy.nullFileID)
AND (file.location ~= FloppyFormat.nullSector)
AND (file.size > 0) AND (file.size <= volumeDesc.numPages))
THEN {problem ← fileListEntry; GOTO notOK};
IF file.type = FloppyImplInterface.FileListType
THEN
{IF ~fileListFound THEN fileListFound ← TRUE
ELSE {problem ← duplicateFileList; GOTO notOK}}
ELSE IF ~ ValidMarkers[fileAddress: file.location, enclosee: file,
size: file.size, id: file.file, type: file.type]
THEN {problem ← fileListEntry; GOTO notOK};
IF SmallerIDLargerID[highestID, file.file] THEN highestID ← file.file;
FOR j: CARDINAL IN [0..volumeDesc.fileList.count - 1) DO
IF i = j THEN LOOP;
IF file.file = volumeDesc.fileList.files[j].file
THEN {problem ← duplicateFileID; GOTO notOK};
ENDLOOP;
ENDLOOP;
IF ~fileListFound THEN {problem ← fileList; GOTO notOK};
IF sectorNine.nextUnusedFileID <= LOOPHOLE[highestID, LONG CARDINAL]
THEN {problem ← sectorNine; GOTO notOK};
-- For level 1 scavenger, nextUnusedFileID is guaranteed to be > last
-- actual ID in file list, but it may be > last ID + 1.
-- Level 2: reset nextUnusedFileID if ~= highestID + 1
END; -- check file list and related
-- Level 2: if file list not sorted correctly (see PPM), rewrite sorted
-- Check root file
IF (~NullID[sectorNine.rootFile]
AND (sectorNine.nextUnusedFileID <=
LOOPHOLE[sectorNine.rootFile, LONG CARDINAL]))
THEN {problem ← fileList; GOTO notOK};
-- ?? check at advertised loc only if ID > lastIDAllocated
-- Check boot files: ensure advertised sector is in a file in the file list.
-- Not necessary to check file itself because check file list did that.
IF sectorNine.pilotMicrocode ~= FloppyFormat.nullSector THEN
{IF ~BootFileInFileList[volumeDesc, sectorNine.pilotMicrocode]
THEN {problem ← bootFile; GOTO notOK}};
IF sectorNine.diagnosticMicrocode ~= FloppyFormat.nullSector
THEN {IF ~BootFileInFileList[
volumeDesc, sectorNine.diagnosticMicrocode]
THEN {problem ← bootFile; GOTO notOK}};
IF sectorNine.germ ~= FloppyFormat.nullSector THEN
{IF ~BootFileInFileList[volumeDesc, sectorNine.germ]
THEN {problem ← bootFile; GOTO notOK}};
IF sectorNine.pilotBootFile ~= FloppyFormat.nullSector THEN
{IF ~BootFileInFileList[volumeDesc, sectorNine.pilotBootFile]
THEN {problem ← bootFile; GOTO notOK}};
IF NOT ((sectorNine.firstAlternateSector < volumeDesc.numPages)
AND (sectorNine.countBadSectors < FloppyFormat.maxBadSectors)
AND (sectorNine.labelSize <= Floppy.maxCharactersInLabel))
THEN {problem ← sectorNine; GOTO notOK};
-- End check sector nine
-- Check bad spot table
BEGIN
track0Map: LONG POINTER TO FloppyFormat.BadSpotSectors ←
LOOPHOLE[BASE[trackZero] +
(FloppyFormat.badSpotSector-1)*SIZE[FloppyFormat.TrackZeroSector]];
FOR i: CARDINAL IN [0..FloppyFormat.maxBadSectors) DO
volumeDesc.badPageMap[i] ← track0Map[i];
ENDLOOP;
END;
FOR i: CARDINAL IN [0..sectorNine.countBadSectors) DO
IF NOT ((volumeDesc.badPageMap[i].bad ~= FloppyFormat.nullSector)
AND (volumeDesc.badPageMap[i].bad < volumeDesc.numPages)
AND (volumeDesc.badPageMap[i].bad ~= volumeDesc.badPageMap[i].alternate)
AND (volumeDesc.badPageMap[i].alternate ~= FloppyFormat.nullSector)
AND (volumeDesc.badPageMap[i].alternate < volumeDesc.numPages))
THEN {problem ← badPageTable; GOTO notOK};
-- check page has valid markers or other bad page adjacent
-- check sorted?
ENDLOOP;
-- End check bad spot table
-- Cross check file list, bad spot table, and standard overhead sectors
-- for overlap by building an allocation map.
BEGIN
-- This calculation is flakey so allocate an extra page for safety
size: CARDINAL ← 1 + FloppyImplInterface.WordsToPages[
(volumeDesc.numPages + FloppyImplInterface.allocationsPerPage - 1)/
FloppyImplInterface.allocationsPerPage];
volumeDesc.allocationMapSpace ← Space.Map[[File.nullFile, 0, size]];
-- We should really catch the signals and convert them into something else
volumeDesc.allocationMap ← volumeDesc.allocationMapSpace.pointer;
IF ~BuildAllocationMap[
volumeDesc: volumeDesc,
map: volumeDesc.allocationMapSpace.pointer].consistent
THEN {problem ← allocMapInconsistent; GOTO notOK};
END; -- build and check allocation map
-- Check that chunks the allocation map thinks are free, i.e., not used by
-- files, bad pages, or file system structures, are well formed free spaces
-- on the disk.
BEGIN
first: FloppyFormat.Sector;
next: FloppyFormat.Sector ←
FloppyImplInterface.FirstDataSector[volumeDesc];
map: LONG POINTER TO PACKED ARRAY [0..0) OF FloppyImplInterface.Allocation
← volumeDesc.allocationMapSpace.pointer;
DO
FOR first ← next, first + 1 UNTIL first > volumeDesc.numPages DO
IF map[first] = free THEN EXIT;
REPEAT
FINISHED => GOTO allDone;
ENDLOOP;
FOR next ← first + 1, next + 1 UNTIL next > volumeDesc.numPages DO
IF map[next] ~= free THEN EXIT;
REPEAT
FINISHED => {problem ← freeSpaceConflict; GOTO notOK};
ENDLOOP;
IF ~ValidMarkers[fileAddress: first, enclosee: free, size: next-first]
THEN {problem ← freeSpaceConflict; GOTO notOK};
REPEAT
allDone => NULL;
ENDLOOP;
END; -- check free spaces
-- Check, correct if wrong once file system verified (but not error)
-- check track 0 sectors contents
-- 0..8, 10 specified in FloppyFormat
-- 11..26 are like 10
-- check track 1 sectors contents
FloppyImplInterface.VolumeStable[volumeDesc];
Shutdown[];
EXITS
floppyError => {Shutdown[]; RETURN WITH ERROR Floppy.Error[type]};
notOK =>
BEGIN
FloppyImplInterface.VolumeChanging[volumeDesc];
Shutdown[];
okay ← FALSE;
END;
END; -- scope of ENABLE
END; -- ScavengerInternal
-- BEGIN Scavenge main code
BEGIN ENABLE UNWIND => NULL;
IF ~FloppyImplInterface.ValidDrive[drive] THEN
ERROR Floppy.Error[noSuchDrive];
okay ← TRUE;
ScavengeInternal[FloppyImplInterface.volumeTable[drive]
! FloppyImplInterface.DiskChanged => RETRY];
END; -- scope of ENABLE
END; -- Scavenge
BootFileInFileList: PROCEDURE [
volumeDesc: FloppyImplInterface.VolumeDesc, page: FloppyFormat.Sector]
RETURNS [BOOLEAN] =
BEGIN
FOR i: CARDINAL IN [0..volumeDesc.fileList.count) DO
IF page IN [volumeDesc.fileList.files[i].location..
volumeDesc.fileList.files[i].location+volumeDesc.fileList.files[i].size)
THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
END;
BuildAllocationMap: PROCEDURE [
volumeDesc: FloppyImplInterface.VolumeDesc,
map: LONG POINTER TO PACKED ARRAY [0..0) OF FloppyImplInterface.Allocation]
RETURNS [consistent: BOOLEAN] =
BEGIN
-- This is almost exactly the same as
-- FloppyImplPrivate.InitializeAllocationMap. Volume is not open (thus not
-- closed) and it doesn't quit as soon as it finds an inconsistency.
-- InitializeEtc. could be modified to use this for stuff they have in common.
-- Start clean
FOR i: CARDINAL IN [0..FloppyFormat.ConvertPageCount[volumeDesc.numPages]] DO
map[i] ← free;
ENDLOOP;
consistent ← TRUE;
-- Mark first and last pages of disk as marker pages. If they are actually
-- bad, loop over the bad page list below will take care of it.
map[FloppyImplInterface.FirstDataSector[volumeDesc]] ← markerPage;
map[FloppyFormat.ConvertPageCount[volumeDesc.numPages]] ← markerPage;
-- Mark bad page list: bad pages and markers around them
FOR i: CARDINAL IN [0..volumeDesc.sectorNine.countBadSectors) DO
map[volumeDesc.badPageMap[i].bad] ← badPage;
IF map[volumeDesc.badPageMap[i].bad-1] = free THEN
map[volumeDesc.badPageMap[i].bad-1] ← markerPage;
IF map[volumeDesc.badPageMap[i].bad+1] = free THEN
map[volumeDesc.badPageMap[i].bad+1] ← markerPage;
ENDLOOP;
-- Mark Sector 0 and cylinder 0 allocated
FOR i: CARDINAL IN [0..FloppyImplInterface.FirstDataSector[volumeDesc]) DO
map[i] ← allocated;
ENDLOOP;
-- Mark marker pages and contents pages for all files in the File list
FOR i: CARDINAL IN [0..volumeDesc.fileList.count) DO
file: FloppyFormat.FileListEntry = volumeDesc.fileList.files[i];
IF ~(map[file.location-1] = free OR map[file.location-1] = markerPage)
THEN consistent ← FALSE ELSE map[file.location-1] ← markerPage;
IF ~(map[file.location + file.size] = free OR
map[file.location + file.size] = markerPage)
THEN consistent ← FALSE ELSE map[file.location+file.size] ← markerPage;
FOR j: CARDINAL IN [file.location..file.location + file.size) DO
IF map[j] ~= free THEN consistent ← FALSE ELSE map[j] ← allocated;
ENDLOOP;
ENDLOOP;
END; -- BuildAllocationMap
NullID: PROCEDURE [file: Floppy.FileID] RETURNS [BOOLEAN] = INLINE
{RETURN[LOOPHOLE[file, LONG CARDINAL]
= LOOPHOLE[Floppy.nullFileID, LONG CARDINAL]]};
SmallerIDLargerID: PROCEDURE [idA, idB: Floppy.FileID] RETURNS [BOOLEAN] =
INLINE
{RETURN[LOOPHOLE[idA, LONG CARDINAL] < LOOPHOLE[idB, LONG CARDINAL]]};
END.
LOG
13-Nov-84 15:42:58 CAJ Created file.
1-Feb-85 8:02:15 EKN Removed uses of FloppyFormat.dataSectorsPerTrack. Fixed up uses of FloppyFormat.TrackZero. Fixed arrows.