def deSetData(self, data): """Set contents of data blocks. Length of data must be deLength * rt11util.rtBlockSize.""" rt11util.assertError( len(data) == (self.deLength * rt11util.rtBlockSize), "deSetData() received invalid data length." ) self.deData = data
def rad50ToFilename(inRad50): """Returns ASCII string of filename represented by list of Radix-50 words (inRad50). inRad50 must be exactly 3 words long, with the first two words representing the filename and the final word representing the extension. Filename and extension will be trimmed of trailing spaces.""" rt11util.assertError(len(inRad50) == 3, "rad50ToFilename() requires 3-word list.") fname = rad50ListToAsc(inRad50[0:2]).rstrip(' ') fext = rad50ValToAsc(inRad50[2]).rstrip(' ') return fname + '.' + fext
def __init__(self, port, speed=9600, blocks=512): """Initialize the class and connect to the drive. Caller must specify path to serial port device. Optionally specify baud rate. Optionally specify length of tape in blocks if connecting to an emulated drive with a nonstandard tape length.""" rt11util.assertError(speed > 0, "Baud rate must be positive.") rt11util.assertError(blocks > 0, "Block count must be positive.") rt11util.assertError(blocks <= 65536, "Block must not exceed 65536.") self._blocks = blocks try: self._port = serial.Serial(port, speed, timeout=0) except: rt11util.assertError(False, 'Could not open serial port "{:s}".'.format(port)) self.init()
def readblock(self, unit, block): rt11util.assertError(block >= 0, "Block number must not be negative.") rt11util.assertError(block < self._blocks, "Block number is too large.") pass
def fsAddFile(self, hostFilename, rtFilename="", dateWord=None, statusWord=deEPERM): """Read file and add it to the RT-11 filesystem. hostFilename refers to a local file on the host's filesystem. If rtFilename is not specified, filename on RT-11 filesystem will be derived from hostFilename. If host file size is not a multiple of rtBlockSize, it will be padded with zeroes. Negative dateWord will be replaced with today's date.""" # Use host filename if RT-11 name not specified if rtFilename == "": rtFilename = hostFilename rad50Filename = rad50.filenameToRad50(rtFilename) rtFilename = rad50.rad50ToFilename(rad50Filename) if dateWord is None: dateWord = rt11util.todayWord(self.fsThisYear) # Read host file, pad if necessary f = open(hostFilename, "rb") buf = f.read() f.close pad = rt11util.rtBlockSize - (len(buf) % rt11util.rtBlockSize) if pad != 512: buf += "\000" * pad fileLen = len(buf) / rt11util.rtBlockSize # Delete existing file with same name self.fsDeleteFile(rtFilename) # Search for first empty block large enough to hold file addedFile = False for idx, de in enumerate(self.fsFileList): if (de.deStatus & deEMPTY) and (de.deLength >= fileLen): addedFile = True # If empty block is larger than new file, split it. if de.deLength > fileLen: ede = dirEntry( name=rad50.filenameToRad50(" EMPTY.FIL"), length=de.deLength - fileLen, start=de.deStart + fileLen, status=deEMPTY, date=rt11util.todayWord(self.fsThisYear), ) ede.deEmptyData() self.fsFileList.insert(idx + 1, ede) de.deStatus = statusWord de.deName = rad50Filename de.deLength = fileLen de.deJobChan = 0 de.deDate = dateWord de.deSetData(buf) self.fsFileList[idx] = de break rt11util.assertError(addedFile, "Could not find space for " + rtFilename)
def _fsWriteDirSegs(self, f): """Write the directory segments.""" # How many of the dir segments will we use? Need to # hold files in file list plus end of segment marker per dir seg dsNeeded = math.ceil((len(self.fsFileList) + self.fsNumDirSegs) / self.fsDirEntPerSeg) # Sanity check: Will the entries fit? rt11util.assertError(dsNeeded <= self.fsNumDirSegs, "Not enough directory segments to hold file list.") # Start constructing dir segs in memory. Represent as lists of 16-bit # word values for now. dirSegs = [None] * self.fsNumDirSegs for dsNum in range(self.fsNumDirSegs): dirSegs[dsNum] = [0] * rt11util.rtBlockSize # Fill in header info that we already know for dsNum in range(len(dirSegs)): dirSegs[dsNum][:rtDirSegHdrSize] = [self.fsNumDirSegs, 0, 0, self.fsExtraWords * 2, 0] # Fill in file information dsNum = 1 deNum = 0 highDs = 0 blkNum = self.fsDataStart for de in self.fsFileList: # Fill in more directory entry header information if deNum == 0: dirSegs[dsNum - 1][1] = dsNum + 1 dirSegs[dsNum - 1][4] = blkNum # Fill in one directory entry deOffset = rtDirSegHdrSize + (deNum * self.fsDirEntSize) dirSegs[dsNum - 1][deOffset : deOffset + rtDirEntSize] = [ de.deStatus, de.deName[0], de.deName[1], de.deName[2], de.deLength, de.deJobChan, de.deDate, ] rt11util.assertError(blkNum == de.deStart, "Block number calculation error.") blkNum = blkNum + de.deLength if self.fsDirEntSize > rtDirEntSize: # Fill in extra bytes dirsegs[dsNum - 1][deOffset + rtDirEntSize : deOffset + self.fsDirEntSize] = de.deExtra deNum = deNum + 1 if deNum == (self.fsDirEntPerSeg - 1): # Add end of segment marker deOffset = rtDirSegHdrSize + (deNum * self.fsDirEntSize) dirSegs[dsNum - 1][deOffset] = deEEOS deNum = 0 dsNum = dsNum + 1 if deNum != 0: # If deNum == 0, we should have just written an end of segment # marker. If not, add one now. deOffset = rtDirSegHdrSize + (deNum * self.fsDirEntSize) dirSegs[dsNum - 1][deOffset] = deEEOS # Next dir seg pointer = 0 dirSegs[dsNum - 1][1] = 0 # Now plug in high dir seg number highDs = dsNum for dsNum in range(len(dirSegs)): dirSegs[dsNum][2] = highDs # Write the constructed dir segs to disk blks = bytearray(0) for ds in dirSegs: blks += rt11util.wordsToBytes(ds) rt11util.writeBlocks(f, blks, self.fsFirstDirSeg, self.fsNumDirSegs * 2)
def _fsReadDirSegs(self, f): """Parse the directory segments. Directory segments are 2 blocks long, start at block number fsFirstDirSeg, and are numbered 1 through 31. We ignore garbage in unused directory segments.""" if self.fsVerbose: print "_fsReadDirSegs():" dsNum = 1 while dsNum > 0: # Read directory segment, convert to 16-bit words bnum = self.fsFirstDirSeg + ((dsNum - 1) * 2) if self.fsVerbose: print " blocks {:d},{:d}".format(bnum, bnum + 1) blk = rt11util.readBlocks(f, bnum, 2) wblk = rt11util.bytesToWords(blk) # Extract fields from directory segment header numDs, nextDs, highDs, xBytes, dStart = wblk[:rtDirSegHdrSize] if self.fsVerbose: print " numDs =", numDs print " nextDs =", nextDs print " highDs =", highDs print " xBytes =", xBytes print " dStart =", dStart rt11util.assertError((xBytes % 2) == 0, "Extra bytes count must be even.") if dsNum == 1: # Initialize stuff from first dir segment header self.fsNumDirSegs = numDs self.fsExtraWords = xBytes / 2 self.fsDirEntSize = rtDirEntSize + self.fsExtraWords self.fsDirEntPerSeg = (rtDirSegSize - rtDirSegHdrSize) / self.fsDirEntSize self.fsDataStart = dStart else: # Sanity checks # Each segment should have matching number of dir segs count in header rt11util.assertWarn( numDs == self.fsNumDirSegs, "Number of directory segments changed in segment " + str(dsNum) ) # Each segment should have matching extra byte count in header rt11util.assertWarn( xBytes / 2 == self.fsExtraWords, "Number of extra entry bytes changed in segment " + str(dsNum) ) # Dir segments should point to contiguous sets of data blocks rt11util.assertWarn(dStart == dsDataBlock, "Non-contiguous data in directory segment " + str(dsNum)) # Unpack remainder of directory segment into list of # directory entry tuples. # This line is so ugly, it ought to be in Perl. # Don't ask me how it works; I barely understood it when I wrote it. dsEntries = zip(*[iter(wblk[rtDirSegHdrSize:])] * self.fsDirEntSize) dsDataBlock = dStart for dsEntry in dsEntries: statWord, fn1, fn2, ext, fileLen, jobChan, fileDate = dsEntry[:7] if statWord & deEEOS: # End of this directory segment break entry = dirEntry( name=[fn1, fn2, ext], length=fileLen, start=dsDataBlock, status=statWord, jobchan=jobChan, date=fileDate, extra=rt11util.wordsToBytes(dsEntry[7:]), ) self.fsFileList.append(entry) self.fsDataBlocks += fileLen dsDataBlock = dsDataBlock + fileLen if nextDs > 0: rt11util.assertWarn(nextDs == dsNum + 1, "Non-sequential directory segments.") dsNum = nextDs
def fsValidate(self): """Sanity-check filesystem parameters.""" rt11util.assertError(self.fsSize > 9, "Filesystem is too small.") # ? rt11util.assertError(self.fsSize < 65536, "Filesystem is too large.") rt11util.assertError(self.fsNumDirSegs > 0, "No directory segments.") rt11util.assertError(self.fsNumDirSegs < 32, "Too many directory segments.") rt11util.assertError(self.fsExtraWords >= 0, "Negative extra dir entry byte count.") rt11util.assertWarn(self.fsFirstDirSeg == 6, "Unexpected first directory segment block number.") rt11util.assertWarn( self.fsDataStart == (self.fsFirstDirSeg + (self.fsNumDirSegs * 2)), "Data blocks do not start immediately after directory segments.", ) rt11util.assertError( self.fsDataStart + self.fsDataBlocks + self.fsResBlocks == self.fsSize, "Invalid block counts." ) rt11util.assertError(self.fsResBlocks >= 0, "Negative reserved block count.")