---------------------------------------------------------------------
  CLASS: DriveDevice     --JKH
  PUBLIC METHODS:
 
     DriveDevice()    - Constructor.  Takes device file pathname as
                      argument. 
    ~DriveDevice()  - Destructor.  Closes device.
     numSectors()     - Returns the number of sectors on the device.
     sectorSize()     - Returns the size, in bytes, of a sector on the
                      device.
     read()           - Read bytes into argument ByteArray from the
                      given ExtentList. 
     write()          - Write bytes from argument ByteArray into the
                      given ExtentList. 
     writeWOE()       - As write, but does a write-without-erase.
     verifyMode()     - Setter: puts DriveDevice in write-with-verify
                      mode. 
     verifyMode()     - Getter: returns write-with-verify mode flag.
     erase()          - Erases the sectors specified by the given
                      PhysExtent. 
     checkValidity()  - Returns whether the DriveDevice is in a valid
                      state. 
     error()          - Returns the error state of the DriveDevice
                      object. 
 
  USAGE NOTES:
   none yet.
  
 ----------------------------------------------------------------------

---------------------------------------------------------------------
  METHOD:  DriveDevice::DriveDevice                            --JKH
  
  Constructor for the DriveDevice object.
 
  ARGUMENTS:  
 
  devfile      IN      String reference containing pathname of the device 
                       file to which the DriveDevice object should be bound.  
 
  RETURNS:  None.
 
  PRECONDITIONS:
      
  --The pathname contained in the argument String devfile corresponds to a
    valid (i.e., existing) drive device.  
  --The executing process must have read/write access to this device. 
  --No other process must have the device opened in EXCLUSIVE mode.
 
  POSTCONDITIONS:
 
  --The device will be opened (and held open until the DriveDevice object
    is destructed) for read and write.  
  --Capacity and sectorSize information will be stored in the object's
    data members.
 
  ERRORS:
 
  error_None:  Function completed successfully.
  error_Empty_path_element:  NULL devfile argument.
  error_File_not_found:  Device file passed as argument cannot be opened.
  error_Capacity_failed:  Cannot get numSectors_ information.
 
  ISSUE:  DriveDevice::DriveDevice():  Should no media in drive be an error?
          Currently, this condition will not impede successful construction,
          though obviously no read/write/erase operation will subsequently
          be successful.
 -----------------------------------------------------------------------------

DriveDevice::DriveDevice( const NSR::String& devfile ) : verifyMode_(0)


---------------------------------------------------------------------
  METHOD:  DriveDevice::~DriveDevice()                         --JKH
 
  Destructor for the DriveDevice object.
 
  ARGS:  none
  RETURNS:  none
  PRE-CONDS:  none
  POST-CONDS:  
 
  --The file descriptor corresponding to the drive device bound to this 
    object, if open, will be closed.
 -----------------------------------------------------------------------------

DriveDevice::~DriveDevice()


---------------------------------------------------------------------
  METHOD:  DriveDevice::read()                                 --JKH
 
  This method reads the specified sectors from disk into the given buffer.
 
  ARGS:
 
   UINT32 start             IN       Starting sector use for read
   UINT32 nsectors          IN       Maximum number of sectors to read
   const ByteArrayRef& buf  IN/OUT   The buffer to store the data.
   Error& err               OUT      Error value to hold results of operation
 
  RETURNS:
 
  The function returns the number of bytes read.  If -1 is returned, then
  an actual I/O error (not a blank check) occurred.  If a blank check or
  read past EOM/EOLM condition occurred, a "short count" will be returned.
 
  PRE-CONDS:
 
  --The DriveDevice object was successfully constructed and contains media
  --The start parameter is in the range [0,numSectors_)
  --The nsectors parameter satisfies the following:
               start + nsectors < numSectors_
 
  POST-CONDS:
 
  --The number of bytes specified in the length field of the argument
    ByteArrayRef buf will be read from disk and stored in the memory referred 
    to by buf
 
  ERRORS:
 
  error_None:  Function completed successfully.
  error_No_free_store:  Could not allocate memory for buffering.
  error_Empty_path_element:  Argument buffer is invalid.
  error_Drive_blank_check:  A blank sector was encountered during the read.
  error_Read_EOM:  Attempted to read past physical EOM
  error_Read_EOLM:  Attempted to read past logical EOM
  error_Drive_read:  Generic I/O failure on read
 
  NOTES:
 
  buf.length() indicates how many bytes to read; the sectors specified by
  start and nsectors only indicate which sectors may be used to satisfy
  the request.  nsectors may be a greater number of bytes than specified
  by buf; this is not an error condition.
 
  If the request is not a sector-multiple, the last sector of the read must
  be buffered.  This buffering requires dynamic allocation for the new
  buffer, and so may cause the entire read to fail due to no free store.
 -----------------------------------------------------------------------------

    rv = ::read(fd_,bptr,((extsize < buf.length()) ? extsize : buf.length()));
    if(rv < (NSR::INT32 ) buf.length()) {                 Got a short count
        perror("read");
        if(rv == extsize) {                       Extent too small
            err = dderror_ = NSR::error_Read_EOLM;
            return rv;
        }

          Extent was big enough, presumably, so we need to check for physical
          EOM

        if((offset + buf.length()) > numSectors_*sectorSize_) {
            err = dderror_ = NSR::error_Read_EOM;
            return rv;
        }

          We didn't fall off the edge of the physical media, so our last 
          chance is a blank check.

        if(isBlankCheck()) {
            err = dderror_ = NSR::error_Drive_blank_check;
            return rv;
        }

          We didn't read past physical EOM, we didn't get a blank check,
          but we received a short count.  According to the read(2) manpage,
          this condition is undefined, so we report a generic I/O error.

        err = dderror_ = NSR::error_Drive_read;
        return rv;
    }

      If we got here, then everything went smoothly:  success!

    err = dderror_ = NSR::error_None;
    return rv;
}


 -----------------------------------------------------------------------------
  Alternate interface for DriveDevice::read().
 -----------------------------------------------------------------------------

NSR::INT32
DriveDevice::read
(
    NSR::UINT32 start,
    NSR::UINT32 nbytes,
    NSR::BYTE_PTR buf,
    NSR::Error& err
)

    rv = ::read(fd_,buf,nbytes);
    if(rv < (NSR::INT32) nbytes) {                        Got a short count

          We need to check for physical EOM

        if((offset + nbytes) > numSectors_*sectorSize_) {
            err = dderror_ = NSR::error_Read_EOM;
            return rv;
        }

          We didn't fall off the edge of the physical media, so our last 
          chance is a blank check.

        if(isBlankCheck()) {
            err = dderror_ = NSR::error_Drive_blank_check;
            return rv;
        }

          We didn't read past physical EOM, we didn't get a blank check,
          but we received a short count.  According to the read(2) manpage,
          this condition is undefined, so we report a generic I/O error.

        err = dderror_ = NSR::error_Drive_read;
        return rv;
    }

      If we got here, then everything went smoothly:  success!

    err = dderror_ = NSR::error_None;
    return rv;

}

 -----------------------------------------------------------------------------
  METHOD:  DriveDevice::write()                                --JKH
 
  This method writes the specified sectors to disk from the given buffer.
 
  ARGS:
 
  UINT32 start            IN      Starting sector use for write
  UINT32 nsectors         IN      Maximum number of sectors to write
  const ByteArrayRef& buf IN/OUT  The buffer to supply the data.
  Error& err              OUT     Error value to hold results of operation
 
  RETURNS:
 
  The function returns the number of bytes written.  If -1 is returned, then
  an actual I/O error occurred.  If a write past EOM/EOLM condition occurred, 
  a "short count" will be returned.  NOTE:  if the DriveDevice is in verify
  mode, and the DriveDevice cannot successfully put itself in verify mode
  for the write, the write call will fail, returning -1 and setting error
  to error_Set_verify_failed.
 
  PRE-CONDS:
 
  --The DriveDevice object was successfully constructed and contains media
  --The start parameter is in the range [0,numSectors_)
  --The nsectors parameter satisfies the following:
               start + nsectors < numSectors_
  --The device is writable (e.g., not write-protected and, if WORM, the
    sectors are unwritten).
 
  POST-CONDS:
 
  --The number of bytes specified in the length field of the argument
    ByteArrayRef buf will be written to disk and taken from the memory 
    referred to by buf
 
  ERRORS:
 
  error_None:  Function completed successfully.
  error_No_free_store:  Could not allocate memory for buffering.
  error_Empty_path_element:  Argument buffer is invalid.
  error_Write_EOM:  Attempted to write past physical EOM
  error_Write_EOLM:  Attempted to write past logical EOM
  error_Drive_write:  Generic I/O failure on write
  error_Set_verify_failed:  Cannot set verify mode.
 -----------------------------------------------------------------------------

NSR::INT32
DriveDevice::write
( 
    NSR::UINT32 start,
    NSR::UINT32 nsectors,
    const NSR::ByteArrayRef& buf,
    NSR::Error& err
)


---------------------------------------------------------------------
  METHOD:  DriveDevice::write()                                --JKH
 
  This method writes the specified sectors to disk from the given buffer.
 
  ARGS:
 
  UINT32 start            IN      Starting sector use for write
  UINT32 nsectors         IN      Maximum number of sectors to write
  const ByteArrayRef& buf IN/OUT  The buffer to supply the data.
  Error& err              OUT     Error value to hold results of operation
 
  RETURNS:
 
  The function returns the number of bytes written.  If -1 is returned, then
  an actual I/O error occurred.  If a write past EOM/EOLM condition occurred, 
  a "short count" will be returned.  NOTE:  if the DriveDevice is in verify
  mode, and the DriveDevice cannot successfully put itself in verify mode
  for the write, the write call will fail, returning -1 and setting error
  to error_Set_verify_failed.
 
  PRE-CONDS:
 
  --The DriveDevice object was successfully constructed and contains media
  --The start parameter is in the range [0,numSectors_)
  --The nsectors parameter satisfies the following:
               start + nsectors < numSectors_
  --The device is writable (e.g., not write-protected and, if WORM, the
    sectors are unwritten).
 
  POST-CONDS:
 
  --The number of bytes specified in the length field of the argument
    ByteArrayRef buf will be written to disk and taken from the memory 
    referred to by buf
 
  ERRORS:
 
  error_None:  Function completed successfully.
  error_No_free_store:  Could not allocate memory for buffering.
  error_Empty_path_element:  Argument buffer is invalid.
  error_Write_EOM:  Attempted to write past physical EOM
  error_Write_EOLM:  Attempted to write past logical EOM
  error_Drive_write:  Generic I/O failure on write
  error_Set_verify_failed:  Cannot set verify mode.
 -----------------------------------------------------------------------------

    rv = ::write(fd_,bptr,((extsize < buf.length()) ? extsize : buf.length()));

    if(verifyMode_) {                     Turn off verify mode
        (void) setVMode(VERIFY_OFF);
    }

    if(rv < (NSR::INT32) buf.length()) {                  Got a short count
        if(rv == extsize) {                       Extent too small
            err = dderror_ = NSR::error_Write_EOLM;
            return rv;
        }
          Extent was big enough, presumably, so we need to check for physical
          EOM

        if((offset + buf.length()) > numSectors_*sectorSize_) {
            err = dderror_ = NSR::error_Write_EOM;
            return rv;
        }

          We didn't fall off the edge of the physical media, so we got
          a mysterious short count.  This condition is undefined by the
          write(2) man page, so we just report a generic I/O problem.

        err = dderror_ = NSR::error_Drive_write;
        return rv;
    }

      If we got here, then everything went smoothly:  success!

    err = dderror_ = NSR::error_None;
    return rv;
}

 -----------------------------------------------------------------------------
  Alternate interface for DriveDevice::write().
 -----------------------------------------------------------------------------

NSR::INT32
DriveDevice::write
(
    NSR::UINT32 start,
    NSR::UINT32 nbytes,
    NSR::CONST_BYTE_PTR buf,
    NSR::Error& err
)

    rv = ::write(fd_,buf,nbytes);

    if(verifyMode_) {                     Turn off verify mode
        (void) setVMode(VERIFY_OFF);
    }

    if(rv < (NSR::INT32) nbytes) {                        Got a short count
        
          We need to check for physical EOM

        if((offset + nbytes) > numSectors_*sectorSize_) {
            err = dderror_ = NSR::error_Write_EOM;
            return rv;
        }

          We didn't fall off the edge of the physical media, so we got
          a mysterious short count.  This condition is undefined by the
          write(2) man page, so we just report a generic I/O problem.

        err = dderror_ = NSR::error_Drive_write;
        return rv;
    }

      If we got here, then everything went smoothly:  success!

    err = dderror_ = NSR::error_None;
    return rv;
}

 -----------------------------------------------------------------------------
  METHOD:  DriveDevice::writeWOE()                             --JKH
 
  This method writes the specified sectors to disk from the given buffer 
  using write-without-erase.  
 
  NOTE:  if the sectors to be written without erase are NOT in fact already 
  erased, they will be garbaged and henceforth unreadable, and no error will 
  be detected during the write!  It is very important that users using this 
  method are sure that the sectors in question are actually erased.
 
  ARGS:
 
  UINT32 start             IN      Starting sector use for write
  UINT32 nsectors          IN      Maximum number of sectors to write
  const ByteArrayRef& buf  IN/OUT  The buffer to supply the data.
  Error& err               OUT     Error value to hold results of operation
 
  RETURNS:
 
  The function returns the number of bytes written.  If -1 is returned, then
  an actual I/O error occurred.  If a write past EOM/EOLM condition occurred, 
  a "short count" will be returned.  If the DriveDevice cannot be put into
  write-without-erase mode successfully, the call will proceed in normal
  write-with-erase mode, and will not flag an error.
 
  PRE-CONDS:
 
  --The DriveDevice object was successfully constructed and contains media
  --The PhysExtent is valid (i.e., valid values)
  --The device is writable (e.g., not write-protected and, if WORM, the
    sectors are unwritten).
  --The sectors specified by the PhysExtent are already erased.
 
  POST-CONDS:
 
  --The number of bytes specified in the length field of the argument
    ByteArrayRef buf will be written to disk and taken from the memory 
    referred to by buf
 
  ERRORS:
 
  error_None:  Function completed successfully.
  error_No_free_store:  Could not allocate memory for buffering.
  error_Empty_path_element:  Argument buffer is invalid.
  error_Write_EOM:  Attempted to write past physical EOM
  error_Write_EOLM:  Attempted to write past logical EOM
  error_Drive_write:  Generic I/O failure on write
 -----------------------------------------------------------------------------

NSR::INT32
DriveDevice::writeWOE
( 
    NSR::UINT32 start,
    NSR::UINT32 nsectors,
    const NSR::ByteArrayRef& arr,
    NSR::Error& err
)


---------------------------------------------------------------------
  METHOD:  DriveDevice::writeWOE()                             --JKH
 
  This method writes the specified sectors to disk from the given buffer 
  using write-without-erase.  
 
  NOTE:  if the sectors to be written without erase are NOT in fact already 
  erased, they will be garbaged and henceforth unreadable, and no error will 
  be detected during the write!  It is very important that users using this 
  method are sure that the sectors in question are actually erased.
 
  ARGS:
 
  UINT32 start             IN      Starting sector use for write
  UINT32 nsectors          IN      Maximum number of sectors to write
  const ByteArrayRef& buf  IN/OUT  The buffer to supply the data.
  Error& err               OUT     Error value to hold results of operation
 
  RETURNS:
 
  The function returns the number of bytes written.  If -1 is returned, then
  an actual I/O error occurred.  If a write past EOM/EOLM condition occurred, 
  a "short count" will be returned.  If the DriveDevice cannot be put into
  write-without-erase mode successfully, the call will proceed in normal
  write-with-erase mode, and will not flag an error.
 
  PRE-CONDS:
 
  --The DriveDevice object was successfully constructed and contains media
  --The PhysExtent is valid (i.e., valid values)
  --The device is writable (e.g., not write-protected and, if WORM, the
    sectors are unwritten).
  --The sectors specified by the PhysExtent are already erased.
 
  POST-CONDS:
 
  --The number of bytes specified in the length field of the argument
    ByteArrayRef buf will be written to disk and taken from the memory 
    referred to by buf
 
  ERRORS:
 
  error_None:  Function completed successfully.
  error_No_free_store:  Could not allocate memory for buffering.
  error_Empty_path_element:  Argument buffer is invalid.
  error_Write_EOM:  Attempted to write past physical EOM
  error_Write_EOLM:  Attempted to write past logical EOM
  error_Drive_write:  Generic I/O failure on write
 -----------------------------------------------------------------------------


---------------------------------------------------------------------
  METHOD:  DriveDevice::erase()                                --JKH
 
  Erases the sectors in the argument PhysExtent.
 
  ARGS:
 
  UINT32 start        IN  Starting sector to erase
  UINT32 nsectors     IN  Number of sectors to erase
  Error& err          OUT Error value to hold result of operation
 
  RETURNS:  Returns 0 on success, -1 on failure.
 
  PRE-CONDS:
 
  --The DriveDevice object was successfully created and contains media
  --The executing process is the only process that has the drive open
  --The PhysExtent specifies a range of sectors physically accessible on
    the media
  --The media is erasable (e.g., not a hard disk, not WORM)
 
  POST-CONDS:
 
  --The sectors specified by the argument PhysExtent will be erased.
  
  ERRORS:
 
  error_None            Function completed successfully.
  error_Bad_erase_size  Extent size is not a multiple of the sector size.
  error_Erase_failed    The erase failed due to an I/O error, or inability
                        to put the drive in exclusive mode.
 
  NOTES:  The erase capability provided by the SCSI-2 spec for MO drives
          only allows for erasing whole sectors.  Therefore, the argument
          PhysExtent MUST be a multiple of sector size in length.  If this
          condition is not met, the erase call will fail.
 -----------------------------------------------------------------------------


---------------------------------------------------------------------
  METHOD:  DriveDevice::checkValidity()                        --JKH
   
  This method checks whether the DriveDevice object in in question is 
  valid--i.e, it was successfully constructed.
 
  ARGS:  none
 
  RETURN VALUES:  
 
  Returns an Error with one of the following values:
 
  error_None:                  object is valid.
  error_Empty_path_element:    Construction failed because the argument
                               String to the constructor was invalid.
  error_File_not_found:        Construction failed; could not find specified
                               device file.
  error_Capacity_found:        Construction failed; could not get numSectors_.
 
  PRE-CONDS:   none
  POST-CONDS:  see return values
 -----------------------------------------------------------------------------
NSR::Error
DriveDevice::checkValidity(void) const


---------------------------------------------------------------------
  METHOD:  DriveDevice::error()                                --JKH
 
  This method returns an Error value corresponding to the results of the last 
  call to the read(), write(), or erase() methods, or construction.
 
  ARGS:  none
 
  RETURNS:
 
  Returns an Error object corresponding to the results of the last call to
  the read(), write(), or erase() methods, or construction.
 
  PRE-CONDS:   none
  POST_CONDS:  see return values
 -----------------------------------------------------------------------------
NSR::Error
DriveDevice::error(void) const