// BFSNewDisk.Bcpl -- Procedures to create a virgin Alto File System // on a Diablo disk, and extend an existing file system. // Copyright Xerox Corporation 1979, 1980, 1982 // Last modified April 27, 1982 11:00 PM by Boggs get "Streams.d" get "AltoFileSys.d" get "Disks.d" get "Bfs.d" get "SysDefs.d" external [ // outgoing procedures BFSNewDisk; BFSExtendDisk // incoming procedures OpenFile PositionPage; GetCurrentFa; TruncateDiskStream CreateDiskStream; WriteBlock; SetFilePos; FilePos ReadLeaderPage; WriteLeaderPage Closes; Resets; Puts; Gets; Allocate; Free Zero; MoveBlock; SysErr; DefaultArgs; Usc; Max CreateDiskFile; AssignDiskPage; ReleaseDiskPage; ActOnDiskPages InitializeDiskCBZ; GetDiskCb; DoDiskCommand; CloseDisk BFSInit; BFSAssignDiskPage; BFSWriteDiskDescriptor ReadDDPage // incoming statics freePageFp; oneBits ] manifest [ defaultDirLen = 5000 //default size of SysDir in words bDir = 100000b rshift offset SN.directory ] //---------------------------------------------------------------------------- let BFSNewDisk(diskZone, driveNum, nDisks, nTracks, dirLen, nSectors; numargs na) = valof //---------------------------------------------------------------------------- // Creates a virgin Alto file system on the specified disk. // nDisks is 2 to make a file system that occupies two packs. // nTracks may be anything up to the physical size of the disk. // nSectors may be 14 on D0s and Dorados. [ DefaultArgs(lv na, -1, 0, 0, 0, defaultDirLen, 0) // Init the disk object let disk = BFSInit(diskZone, true, driveNum, 0, true) if disk eq 0 resultis false // BFSInit fills in DSK with the physical shape of the disk. // The user may want some other logical shape, such as two packs. if nDisks ne 0 then disk>>BFSDSK.nDisks = nDisks if nTracks ne 0 then disk>>BFSDSK.nTracks = nTracks if nSectors ne 0 then disk>>BFSDSK.nSectors = nSectors // # of pages in this file system let diskSize = disk>>BFSDSK.nDisks * disk>>BFSDSK.nTracks * disk>>BFSDSK.nHeads * disk>>BFSDSK.nSectors // Allocate a page sized temporary bit table and // plug in our own AssignDiskPage procedure for initial allocations. // This procedure will put assigned addresses in tempBT. // We must not allocate more than 255 pages here, or we // will have to figure out a new way around this Catch-22. let buf = Allocate(diskZone, BFSwordsPerPage) Zero(buf, BFSwordsPerPage) disk>>BFSDSK.initmode = buf disk>>DSK.AssignDiskPage = NewDiskAssignPage // It's too late to back out now: ClearDisk(disk, 0, diskSize-1) let fpSysDir = disk>>DSK.fpSysDir let fpDiskDescriptor = disk>>DSK.fpDiskDescriptor // Create SysDir. Its SN must be 100 with the directory bit set. // Its leader page must be at virtual address 1. (lv disk>>BFSDSK.lastSn)!1 = 99 //init last SN used disk>>BFSDSK.lastPageAlloc = 0 //skip page 0 let dir = NewDiskCreateFile(disk, "SysDir.", fpSysDir, diskZone, 0, bDir) let pos = FilePos(dir) SetFilePos(dir, 0, dirLen lshift 1) //make nice contiguous dir SetFilePos(dir, 0, pos) // Create DiskDescriptor and make sure it is consecutively allocated. let ddStream = NewDiskCreateFile(disk, "DiskDescriptor.", fpDiskDescriptor, diskZone, dir, 0) let diskBTsize = (diskSize-1) rshift 4 +1 //total words in BT disk>>BFSDSK.diskBTsize = diskBTsize // Extend disk descriptor and fill it with zeros. // DD file is made big enough to handle a Double 44 filesystem without // having to extend it. KDH.diskBTsize tells how many words are // really part of the bit table; the remaining ones should be ignored. PositionPage(ddStream, 5) let q, r = diskSize rshift 4, diskSize & 17B SetFilePos(ddStream, 0, (lKDHeader+q) lshift 1) // Last word of BT needs special treatment since number of pages // on disk may not be a mulitple of 16. if r ne 0 then Puts(ddStream, -1 rshift r) // DDMgr needs VDAs of disk descriptor pages to function. // This is normally done by BFSInit, but DiskDescriptor didn't exist then. for i = 1 to (lKDHeader+diskBTsize-1) rshift BFSlnWordsPerPage +1 do [ PositionPage(ddStream, i) let fa = vec lFA GetCurrentFa(ddStream, fa) disk>>BFSDSK.VDAdiskDD^i = fa>>FA.da ] Closes(ddStream) // BFSNewDisk (cont'd) // Fill out rest of directory with empties of small enough size. [ let len = dirLen - FilePos(dir) rshift 1 if len le 0 then break if len gr 100 then len = 100 let a = vec 1 a>>DV.type = dvTypeFree a>>DV.length = len WriteBlock(dir, a, len) ] repeat TruncateDiskStream(dir) // Now put back the normal AssignDiskPage procedure and mark the pages // we have allocated in the bit table in the normal way. disk>>DSK.AssignDiskPage = BFSAssignDiskPage for i = 1 to buf!0 do if AssignDiskPage(disk, buf!i-1) ne buf!i then SysErr(disk, ecBadAssignPage) // Assign page zero. This is slightly tricky since zero = eofDA+1 disk>>BFSDSK.lastPageAlloc = 0 if AssignDiskPage(disk, eofDA) ne 0 then SysErr(disk, ecBadAssignPage) // Install the disk shape parameters as a file property in the leader // page of SysDir. This is mostly for Mesa's benefit. ReadLeaderPage(dir, buf) let fProp = buf + buf>>LD.propertyBegin fProp>>FPROP.type = fpropTypeDShape fProp>>FPROP.length = lDSHAPE+1 let dShape = fProp+1 dShape>>DSHAPE.nDisks = disk>>BFSDSK.nDisks dShape>>DSHAPE.nTracks = disk>>BFSDSK.nTracks dShape>>DSHAPE.nHeads = disk>>BFSDSK.nHeads dShape>>DSHAPE.nSectors = disk>>BFSDSK.nSectors WriteLeaderPage(dir, buf) Closes(dir) Free(diskZone, buf) // Flush DiskDescriptor out to the disk CloseDisk(disk) resultis true ] //---------------------------------------------------------------------------- and BFSExtendDisk(zone, disk, nDisks, nTracks) be //---------------------------------------------------------------------------- // 'disk' is the file system to extend. Presumably either 'nTracks' or // 'nDisks' or both is bigger than the corresponding current parameter. [ let pgsPerCyl = disk>>BFSDSK.nSectors * disk>>BFSDSK.nHeads let firstVDA = pgsPerCyl * disk>>BFSDSK.nTracks * disk>>BFSDSK.nDisks let lastVDA = pgsPerCyl * nTracks * nDisks -1 if Usc(firstVDA, lastVDA) ge 0 return // nothing to do let buf = Allocate(zone, BFSwordsPerPage); Zero(buf, BFSwordsPerPage) disk>>BFSDSK.initmode = buf disk>>DSK.AssignDiskPage = NewDiskAssignPage disk>>BFSDSK.nTracks = nTracks disk>>BFSDSK.nDisks = nDisks ClearDisk(disk, firstVDA, lastVDA) disk>>DSK.AssignDiskPage = ExtendDiskAssignPage let ddStream = OpenFile("DiskDescriptor.", ksTypeReadWrite, wordItem, 0, disk>>BFSDSK.fpDiskDescriptor, 0, zone, 0, disk) let q, r = firstVDA rshift 4, firstVDA & 17B SetFilePos(ddStream, 0, (lKDHeader+q) lshift 1) if r ne 0 then [ let bits = Gets(ddStream) SetFilePos(ddStream, 0, (lKDHeader+q) lshift 1) Puts(ddStream, bits & not -1 rshift r) ] q, r = (lastVDA+1) rshift 4, (lastVDA+1) & 17B for i = FilePos(ddStream) rshift 1 to lKDHeader+q do Puts(ddStream, 0) //fill with zeros if r ne 0 then Puts(ddStream, -1 rshift r) let diskBTsize = lastVDA rshift 4 +1 //total words in BT for i = 1 to (lKDHeader+diskBTsize-1) rshift BFSlnWordsPerPage +1 do [ PositionPage(ddStream, i) let fa = vec lFA; GetCurrentFa(ddStream, fa) disk>>BFSDSK.VDAdiskDD^i = fa>>FA.da ] Closes(ddStream) disk>>BFSDSK.diskBTsize = diskBTsize disk>>DSK.AssignDiskPage = BFSAssignDiskPage for i = 1 to buf!0 do [ let vda = AssignDiskPage(disk, buf!i-1) if vda ne buf!i then ReleaseDiskPage(disk, vda) ] disk>>BFSDSK.freePages = lastVDA - firstVDA + disk>>BFSDSK.freePages +1 let DAs = vec 2; DAs!1 = 1 ActOnDiskPages(disk, 0, DAs+1, disk>>DSK.fpSysDir, 0, 0, DCreadD, 0, 0, buf) let fProp = buf + buf>>LD.propertyBegin fProp>>FPROP.type = fpropTypeDShape fProp>>FPROP.length = lDSHAPE+1 let dShape = fProp+1 dShape>>DSHAPE.nDisks = disk>>BFSDSK.nDisks dShape>>DSHAPE.nTracks = disk>>BFSDSK.nTracks dShape>>DSHAPE.nHeads = disk>>BFSDSK.nHeads dShape>>DSHAPE.nSectors = disk>>BFSDSK.nSectors ActOnDiskPages(disk, 0, DAs+1, disk>>DSK.fpSysDir, 0, 0, DCwriteD, 0, 0, buf) Free(zone, buf) BFSWriteDiskDescriptor(disk) ] //---------------------------------------------------------------------------- and NewDiskCreateFile(disk, name, fp, zone, dirStream, word1) = valof //---------------------------------------------------------------------------- //Creates the file on the disk and appends a directory entry to dirStream. //If dirStream is zero, the new file itself is assumed to be the directory // and the entry is appended to it. Returns an open stream. [ CreateDiskFile(disk, name, fp, 0, word1) // Make stream let s = CreateDiskStream(fp, 0, 0, 0, 0, zone, 0, disk) if s eq 0 then SysErr(disk, ecEssentialFile) // Now make directory entry if dirStream eq 0 then dirStream = s //SysDir is first let dv = vec lDV dv>>DV.type = dvTypeFile let lName = (name>>STRING.length+2) rshift 1 dv>>DV.length = lDV+lName MoveBlock(lv dv>>DV.fp, fp, lFP) WriteBlock(dirStream, dv, lDV) WriteBlock(dirStream, name, lName) // return stream resultis s ] //---------------------------------------------------------------------------- and NewDiskAssignPage(disk, vda, nil) = valof //---------------------------------------------------------------------------- // The AssignDiskPage procedure used while creating the initial files // (SysDir and DiskDescriptor). // Keeps a list of assigned pages in the buffer pointed to by the // initmode word in the disk structure. // Number of allocated pages is in buf!0, followed by that many VDAs. [ vda = vda eq eofDA? disk>>BFSDSK.lastPageAlloc+1, vda+1 disk>>BFSDSK.lastPageAlloc = vda let tempBT = disk>>BFSDSK.initmode // search for first unused slot, and make sure page not already assigned let assigned = false for i = 1 to tempBT!0 if vda eq tempBT!i then [ assigned = true; break ] unless assigned do //assign the page [ if tempBT!0 eq 255 then SysErr(disk, ecBadAssignPage) //full! tempBT!0 = tempBT!0 +1 //bump count of pages allocated tempBT!(tempBT!0) = vda //remember vda of allocated page resultis vda ] ] repeat //---------------------------------------------------------------------------- and ExtendDiskAssignPage(disk, vda, nil) = valof //---------------------------------------------------------------------------- // During extension, no new files are created, so vda can't be eofDA. [ if vda ls disk>>BFSDSK.diskBTsize lshift 4 then [ vda = BFSAssignDiskPage(disk, vda) ReadDDPage(disk>>BFSDSK.ddMgr, disk, 1) //flush it out vda = vda eq -1? disk>>BFSDSK.diskBTsize lshift 4, vda-1 ] resultis NewDiskAssignPage(disk, vda) ] //---------------------------------------------------------------------------- and ClearDisk(disk, firstVDA, lastVDA) be //---------------------------------------------------------------------------- // The idea here is to initialize every page in the file system. // The simple way is to write 'free' in every page, but this will make // incorrigable pages free again, causing future problems. // It is the job of a disk certification program such as DiEx to test // each page and mark it incorrigable, and we shouldn't disturb them. // If the disk has never been formatted before, we should assume that // all pages are good and set them all to free. [ let tempBT = disk>>BFSDSK.initmode //temp bit table let cbz = vec CBzoneLength // Read each page to see if it is incorrigable or unreadable. // If there are more than 20 of them then assume the disk is // unformatted and abandon the read pass. // Pages 0 and 1 are not checked and are unconditionally marked free. // They are critical, and must be good or else chuck the disk! let buf1 = vec 256 //I wish I could disable data record transfers InitializeDiskCBZ(disk, cbz, Max(2, firstVDA), CBzoneLength, RetryRead, lv ClearDiskError) cbz>>CBZ.cleanupRoutine = ClearDiskCleanup RetryRead: test tempBT!0 le 20 ifso [ for page = cbz>>CBZ.currentPage to lastVDA do DoDiskCommand(disk, GetDiskCb(disk, cbz), buf1, page, freePageFp, page, DCreadLD) while cbz>>CBZ.head ne 0 do GetDiskCb(disk, cbz) ] ifnot Zero(tempBT, 256) //abandon read pass, assume all pages are good Zero(buf1, 256) //write zeros in data records // Write each page, marking it free or incorrigable. let incorrigableFp = table [ -2; -2; -2 ] InitializeDiskCBZ(disk, cbz, firstVDA, CBzoneLength, RetryWrite) RetryWrite: [ for page = cbz>>CBZ.currentPage to lastVDA do [ let bad = false for i = 1 to tempBT!0 if page eq tempBT!i then [ bad = true; break ] DoDiskCommand(disk, GetDiskCb(disk, cbz), buf1, page, (bad? incorrigableFp, freePageFp), page, DCwriteHLD) ] while cbz>>CBZ.head ne 0 do GetDiskCb(disk, cbz) ] ] //---------------------------------------------------------------------------- and ClearDiskCleanup(nil, cb, cbz) be //---------------------------------------------------------------------------- // Cleanup routine called during the read pass. // If the label says incorrigable, mark the page used in the bit table. [ let label = lv cb>>CB.labelAddress>>DL.fileId for i = 0 to 2 do if label!i ne -2 return // The page is bad and should be marked used in the bit table. ClearDiskError(nil, cb) ] //---------------------------------------------------------------------------- and ClearDiskError(nil, cb, nil) be //---------------------------------------------------------------------------- // Error routine called for a hard error during the read pass. // Also called from ClearDiskCleanup for a sector labelled incorrigable. [ let vda = cb>>CB.truePageNumber let disk = cb>>CB.cbz>>CBZ.disk if AssignDiskPage(disk, vda-1) ne vda then SysErr(disk, ecBadAssignPage) // Zero the label so ClearDiskCleanup doesn't think it incorrigable // and try to mark it in use again. Zero(lv cb>>CB.labelAddress>>DL.fileId, lFID) ]