def ds_open(self,extent): assert isinstance(extent,Extent),\ "%s 'extent' argument must be a fbadscb.Extent object: %s" \ % (eloc(self,"ds_open",module=this_module),extent) if self.extent: raise NotImplementedError("%s extent already open: %s" \ % (eloc(self,"ds_open",module=this_module),self.extent)) self._ck_extent(extent) self.extent=extent # Remember the physical extent self.lower=extent.lower # The first physical sector of the extent self.upper=extent.upper # The last physical sector of the extent self.ds_sector=0 # The next logical sector to be accessed self.ds_sectors=extent.sectors() # Calculate the number of sectors self.ds_last=self.ds_sectors-1 # The last logical sector in the extent # Trace the data set open if tracing is enabled if __debug__: if self._trace: print("%s DS_OPEN: extent:%s sectors:%s logical sectors:0-%s" \ % (eloc(self,"ds_open",module=this_module),\ extent,self.ds_sectors,self.ds_last)) self.seek(self.lower) # Position at start of the extent
def seek(self,sector): # Determine the position in the file for a physical sector access if sector>self.last: raise ValueError("%s FBA sector %s is beyond last sector %s" \ % (eloc(self,"seek",module=this_module),sector,self.last)) sector_loc=sector*512 if sector_loc>self.filesize: raise IOError("%s FBA sector %s file position %s is beyond EOF: %s" \ % (eloc(self,"seek",module=this_module),sector,sector_loc,\ self.filesize)) # Perform the positioning by physical sector number in this object and # position the file object accordingly try: self.fo.seek(sector_loc) except IOError: raise IOError("%s IOError while positioning to FBA sector: %s" \ % (eloc(self,"seek",module=this_module),self.sector)) self.sector=sector # Trace the seek if physical tracing is enabled if __debug__: if self._trace: print("%s SEEK: sector: %s file pos: %s" \ % (eloc(self,"seek",module=this_module),sector,self.fo.tell()))
def ds_tell(self): if not self.extent: raise NotImplementedError("%s no open extent for logical sector position" \ % (eloc(self,"ds_tell",module=this_module))) if __debug__: if self._trace: print("%s returning: %s" \ % (eloc(self,"ds_tell",module=this_module),self.ds_sector)) return self.ds_sector
def _to_physical(self,sector,sectors=1): assert sector >=0,"%s 'sector' argument must be >= 0: %s" \ % (eloc(self,"_to_physical",module=this_module),sector) assert sectors >=1,"%s 'sectors' argument must be >= 1: %s" \ % (eloc(self,"_to_physical",module=this_module),sectors) l_last=sector+sectors-1 if l_last > self.upper: raise ValueError(\ "%s end of operation beyond end of extent (%s): %s" \ % (eloc(self,"_to_physical",module=this_module),\ self.upper,l_last)) return (self.lower+sector,self.lower+l_last,l_last)
def ds_erase(self,sector=0,sectors=1,fill=0): assert isinstance(sector,int) and sector>=0,\ "%s 'sector' argument must be an integer >= 0: %s"\ % (eloc(self,"ds_erase",module=this_module),sector) if self.ro: raise NotImplementedError("%s can not erase a read-only image file: %s"\ % (eloc(self,"ds_erase",module=this_module),self.filename)) if not self.extent: raise NotImplementedError("%s no open extent for erasing" \ % (eloc(self,"ds_erase",module=this_module))) # Create the content for an erased sector if isinstance(fill,str): if len(fill)>0: f=ord(fill[0]) else: raise ValueError("%s 'fill' argument must not be an empty string"\ % eloc(self,"ds_erase",module=this_module)) elif isinstance(fill,int): if fill<0 or fill >255: raise ValueError("%s 'fill' argument out of range (0-255): %s" \ % eloc(self,"ds_erase",module=this_module),fill) f=fill byts=bytes([f,]*512) # Determine the physical sectors of erased area if sectors == True: secs=self.ds_sectors else: assert isinstance(sectors,int) and sectors>=1,\ "%s 'sectors' argument must be True or an integer >= 1: %s" \ % (eloc(self,"ds_erase",module=this_module),sectors) secs=sectors p_first,p_last,l_last = self._to_physical(sector,sectors=secs) # Erase the requested sectors' content self.seek(p_first) sec=sector if __debug__: if self._trace: if secs == 1: s="%s DS_ERASING: logical sector:%s physical sector: %s"\ "file pos:%s with:%02X" \ % (eloc(self,"ds_read",module=this_module),\ sector,p_first,self.fo.tell(),f) else: s="%s DS_ERASING: logical sectors:%s-%s physical sectors: %s-%s" \ " sectors:%s file pos:%s with:0x%02X" \ % (eloc(self,"ds_read",module=this_module),\ sector,l_last,p_first,p_last,secs,self.fo.tell(),\ f) print(s) # Erase the requested sectors' content for n in range(secs): self._write(byts) self.sector+=1 self.ds_sector+=1
def _ck_extent(self,extent): if extent.notused: raise ValueError("%s Extent object must be used: %s" \ % (eloc(self,"_ck_extent",module=this_module),extent)) lower=extent.lower if lower>self.last: raise ValueError(\ "%s extent lower boundary is not within the FBA image (0-%s): %s"\ % (eloc(self,"_ck_extent",module=this_module),self.last,lower)) upper=extent.upper if upper>self.last: raise ValueError(\ "%s extent upper boundary is not within the FBA image (0-%s): %s"\ % (eloc(self,"_ck_extent",module=this_module),self.last,upper))
def read(self,sector=None,array=False): # Position the image file if requested to do so. if sector is not None: self.seek(sector) # Trace the read if physical tracing is enabled if __debug__: if self._trace: sec=self.sector fpos=self.fo.tell() # Read the physical sector using the low-level routine data=self._read(512) self.sector+=1 # Trace the read if physical tracing is enabled if __debug__: if self._trace: print("%s READ: sector: %s file pos: %s" \ % (eloc(self,"read",module=this_module),sec,fpos)) if self._tdump: dump(data,indent=" ") # Return the information is the requested sequence type if array: return bytearray(data) return data
def ds_write(self,byts,sector=None): if self.ro: raise NotImplementedError("%s can not write to a read-only image file: %s"\ % (eloc(self,"ds_write",module=this_module),self.filename)) if not self.extent: raise NotImplementedError("%s no open extent for writing" \ % (eloc(self,"ds_write",module=this_module))) # Validate there is enough data to write entire sectors try: sectors=bytes2sectors(byts) except ValueError: raise ValueError("%s 'byts' argument not full sectors, length: %s" \ % (eloc(self,"ds_write",module=this_module),len(byts))) from None # Locate where the write operation begins if sector is None: sec=self.ds_sector else: sec=sector p_first,p_last,l_last=self._to_physical(sec,sectors=sectors) # Position to the starting sector self.seek(p_first) self.ds_sector=sec # Perform tracing if requested. if __debug__: if self._trace: if sectors == 1: s="%s DS_WRITE: logical sector:%s physical sector: %s"\ "file pos: %s" % (eloc(self,"ds_write",module=this_module),\ sector,p_first,self.fo.tell()) else: s="%s DS_WRITE: logical sectors:%s-%s physical sectors: %s-%s" \ "sectors:%s file pos:%s" \ % (eloc(self,"ds_write",module=this_module),sector,\ l_last,p_first,p_last,sectors,self.fo.tell()) print(s) if self._tdump: dump(byts,indent=" ") # Write the sector' or sectors' content self._write(byts) self.sector+=sectors self.ds_sector+=sectors
def trace(self,state,dump=False): if state: self._trace=True self._tdump=dump if __debug__: print("%s set tracing:%s, dump:%s" \ % (eloc(self,"trace",module=this_module),self._trace,self._tdump)) else: if __debug__: was_tracing=self._trace self._trace=False self._tdump=False if __debug__: if was_tracing: print("%s set tracing:%s, dump:%s" \ % (eloc(self,"trace",module=this_module),\ self._trace,self.dump))
def detach(self): if __debug__: if self._trace: print("%s detaching image file: %s" \ % (eloc(self,"detach",module=this_module),self.filename)) if self.extent: self.ds_close() try: self.fo.flush() self.fo.close() except IOError: raise IOError(\ "%s IOError detaching %s FBA image: %s" \ % (eloc(self,"detach",module=this_module),self.filename)) self.pending=False
def ds_read(self,sector=None,sectors=1,array=False): if not self.extent: raise NotImplementedError("%s no open extent for reading" \ % eloc(self,"ds_read",module=this_module)) if sector is None: sec=self.ds_sector else: sec=sector p_first,p_last,l_last=self._to_physical(sec,sectors=sectors) # Position to the starting sector self.seek(p_first) self.ds_sector=sec # Perform tracing if requested if __debug__: if self._trace: if sectors == 1: s="%s DS_READ: logical sector:%s physical sector: %s"\ " file pos:%s" % (eloc(self,"ds_read",module=this_module),\ sec,p_first,self.fo.tell()) else: s="%s DS_READ: logical sectors:%s-%s physical sectors: %s-%s" \ " sectors:%s file pos: %s"\ % (eloc(self,"ds_read",module=this_module),\ sec,l_last,p_first,p_last,sectors,self.fo.tell()) print(s) # Read the sector' or sectors' content byts=self._read(sectors*512) self.sector+=sectors self.ds_sector+=sectors # Dump the content read from the file image if content tracing enabled if __debug__: if self._tdump: dump(byts,indent=" ") # Return the content read from the sector or sectors as bytes or bytearray # as requested. if array: return bytearray(byts) return byts
def _read(self,size): # Force writing any pending writes before attempting to read the file # otherwise, the image file may have stale sector data. if self.pending: # By using this method we get to trace the flush operation self.flush() # Read the requested bytes try: byts=self.fo.read(size) except IOError: raise IOError("%s IOError while reading FBA physical sector: %s" \ % (eloc(self,"_read",module=this_module),self.sector)) # Ensure we actually read the number of expected bytes. if len(byts)!=size: raise ValueError(\ "%s did not read requested bytes (%s) from image file: %s" % (eloc(self,"_read",module=this_module),size,len(byts))) return byts
def _write(self,data): # Ensure bytes sequence is being written not bytearray. Python requires # a bytes sequence. Sequence can not be bytearray. This give the using # software the freedom to use either sequence. if isinstance(data,bytearray): byts=bytes(data) else: byts=data assert isinstance(byts,bytes),\ "%s 'data' argument must be a bytes/bytearray sequence for sector %s: %s" \ % (eloc(self,"_write",module=this_module),byts,self.sector) # Write the bytes try: self.fo.write(byts) except IOError: raise IOError(\ "%s IOError while writing FBA physical sector: %s" \ % (eloc(self,"write",module=this_module),self.sector)) self.pending=True # Indicate the write might be pending
def ds_extent(self,lower,upper): if upper is True: up=self.last else: up=upper ext=Extent(lower=lower,upper=up) if __debug__: if self._trace: print("%s returned: %s" % (eloc(self,"ds_extent"),ext)) return ext
def ds_close(self): # Trace the data set close if logical sector tracing is enabled if __debug__: if self._trace: print("%s DS_CLOSE: closing extent: %s" \ % (eloc(self,"ds_close",module=this_module),self.extent)) if self.extent: self.flush() # Write any pending sectors to the image file self.ds_sector=self.ds_last=self.extent=self.lower=self.upper=None self.ds_sectors=0
def write(self,byts,sector=None,pad=False): if self.ro: raise NotImplementedError(\ "%s can not write to read-only FBA image: %s" \ % (eloc(self,"write",module=this_module),self.filename)) # Position the image file if requested to do so. if sector is not None: self.seek(sector) # Pad or detect truncated sector content data=byts if len(data)!=512: if pad: data=data+fba.pad data=data[:512] else: raise ValueError("%s FBA image sector must be 512 bytes: %s"\ % (eloc(self,"write",module=this_module),len(data))) # Trace the write operation if physical sector tracing is enabled if __debug__: if self._trace: if len(data)>len(byts): padded=" pad: %s" % len(data) - len(byts) else: padded="" print("%s WRITE: sector: %s file pos: %s%s" \ % (eloc(self,"write",module=this_module),\ sector,self.fo.tell(),padded)) if self._tdump: dump(data,indent=" ") # Write to the sector using the low-level routine self._write(data) self.sector+=1
def tell(self): if __debug__: if self._trace: print("%s returning: %s" % (eloc(self,"tell",module=this_module),\ self.sector)) return self.sector
def flush(self): if __debug__: if self._trace: print("%s forcing pending image file writes" \ % eloc(self,"flush",module=this_module)) self._flush() # Use the low-level method to actually force pending writes