//-*-c++-*---------------------------------------------------------------------
// moDevSim.cpp
//
// Source file for the moDeviceSimulator class.
//
// CLASSES:
//
//      moDeviceSimulator
//-----------------------------------------------------------------------------
// $Source: /projects/HMS/nsr/udfCore/devs/moDevSim.cpp,v $
// $Revision: 1.2 $     $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/05/09 15:41:01 $
//-----------------------------------------------------------------------------
//      Unpublished Confidential Information of Hewlett-Packard
//      Company. Do not disclose.  Copyright (c) Hewlett-Packard
//      Company 1995. All rights reserved
// 
// The contents of this software are proprietary and confidential to
// the Hewlett- Packard Company, and are limited in distribution to
// those with a direct need to know.  Individuals having access to
// this software are responsible for maintaining the confidentiality
// of the content and for keeping the software secure when not in use.
// Transfer to any party is strictly forbidden other than as expressly
// permitted in writing by Hewlett-Packard Company.  Unauthorized
// transfer to or possession by any unauthorized party may be a
// criminal offense.
//
//                  RESTRICTED RIGHTS LEGEND
//
//      Use,  duplication,  or disclosure by the U. S. Government is
//      subject to restrictions as set forth in subparagraph (c) (1)
//      (ii) in the Rights in Technical Data and Computer Software
//      clause in DFARS 252.227-7013 or any other successor clause.
//
//                      HEWLETT-PACKARD COMPANY
//                      3000 Hanover St.
//                      Palo Alto, CA  94304 U.S.A.
//
//      Rights for non-DOD U.S. Government Departments and Agencies
//      are as set forth in FAR 52.227-19 (c) (1,2) or any other
//      successor clause.
//-----------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <rw/cstring.h>
#include "nsrLib.h"
#include "moDevSim.h"

#define MIN_SECTOR_SIZE         512

//-----------------------------------------------------------------------------
// METHOD:  MODeviceSimulator::MODeviceSimulator()
//
// Constructor for the moDeviceSimulator class.  Takes a filename, sector size,
// and number of sectors, and initializes the object to simulate a disk device.
//
// ARGUMENTS:  
//
// const char * fileNamee   IN  Name of file to use for the simulated disk.
//
//      OR
//
// const char * fileName    IN  Name of file to use for the simulated disk.
// NSR::UINT32       sectorSize  IN  Sector size, in bytes.
// NSR::UINT32  numSectors  IN  Number of sectors in disk to be simulated.
//
// RETURNS:  None.
//
// PRECONDITIONS:
//
// --Argument fileName is non-NULL and represents a read-write accessible
//   path.
// --sectorSize parameter is a multiple of 2 of 512 (i.e., 512, 1024, 2048,
//   4096,...).
// --numSectors is greater than 0.
//
// POSTCONDITIONS:
//
// NOTES:
// 
//-----------------------------------------------------------------------------

MODeviceSimulator::MODeviceSimulator
(
    const char *fileName
) : fd_(-1),sectorSize_(0),numSectors_(0),dderror_(NSR::error_None)
{
    if(fileName == NULL)
    {
        return;
    }

    // In this version of the constructor, the file must already exist, since
    // we depend on reading a valid header to get the sector size and number
    // of sectors in the simulated disk.

    fd_ = open(fileName,O_RDWR);
    if(fd_ < 0)         // Open failed!
    {
        dderror_ = NSR::error_Cant_make_disk_device_file;
        return;
    }
    fileName_ = fileName;

    // If the file already exists, attempt to read the header.  If the header
    // exists and is valid, we override the sectorSize/numSectors parameters
    // that the user passed in.

    if(readHeader())            // No header
    {
        dderror_ = NSR::error_Cant_make_disk_device_file;
        return;
    }
}

MODeviceSimulator::MODeviceSimulator
(
    const char *fileName,
    NSR::UINT32 sectorSize,
    NSR::UINT32 numSectors
) : fd_(-1),sectorSize_(0),numSectors_(0),dderror_(NSR::error_None)
{
    // Check for invalid arguments.  The first rather mysterious check is
    // simply to see if the sectorSize is 512, 1024, 2048, 4096, etc.

    NSR::UINT32 ss = sectorSize;
    NSR::UINT32 count = 0;

    if((sectorSize < MIN_SECTOR_SIZE) || (numSectors == 0) || 
       (fileName == NULL))
    {
        return;
    }
    ss >>= 9;
    while(ss)
    {
        if(ss & 0x01)           // Count # of 1 bits, should be only 1!
        {
            count++;
        }
        ss >>= 1;
    }
    if(count != 1)      // Invalid sector size
    {   
        return;
    }

    // Open the file, creating it if necessary.

    NSR::INT32 existed = 1;

    if(access(fileName,F_OK))
    {
        existed = 0;

        FILE *fp = fopen(fileName,"w");
        if(fp == NULL)
        {
            perror("fopen");
            dderror_ = NSR::error_Cant_make_disk_device_file;
            return;
        }
        fclose(fp);
    }

    fd_ = open(fileName,O_RDWR);
    if(fd_ < 0)         // Open failed!
    {
        dderror_ = NSR::error_Cant_make_disk_device_file;
        return;
    }
    fileName_ = fileName;

    // If the file already exists, attempt to read the header.  If the header
    // exists and is valid, we override the sectorSize/numSectors parameters
    // that the user passed in.

    if(readHeader())            // No header
    {
        if(existed)
        {
            // This error should be different, something to indicate that
            // the construction failed because there was an invalid header
            // in the argument file.

            dderror_ = NSR::error_Cant_make_disk_device_file;
            close(fd_);
            fd_ = -1;
            return;
        }
        if(writeHeader(sectorSize,numSectors))
        {
            close(fd_);
            fd_ = -1;
        }
    }
}

//-----------------------------------------------------------------------------
// METHOD:  MODeviceSimulator::~MODeviceSimulator
//
// ARGUMENTS:  None.
//
// RETURNS:  None.
//
// PRECONDITIONS:
//
// POSTCONDITIONS:
//
// NOTES:
// 
//-----------------------------------------------------------------------------

MODeviceSimulator::~MODeviceSimulator
(
    void
)
{
    if(fd_ >= 0)
    {
        close(fd_);
    }
    fd_ = -1;
}

//-----------------------------------------------------------------------------
// METHOD:  MODeviceSimulator::read()                           --JKH
//
// This method reads the specified sectors from disk into the given buffer.
//
// ARGS:
//
//  NSR::UINT32 start             IN       Starting sector use for read
//  NSR::UINT32 nsectors             IN       Maximum number of sectors to read
//  const NSR::byteArrayRef& buf  IN/OUT   The buffer to store the data.
//  NSR::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 MODeviceSimulator 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
//   NSR::byteArrayRef buf will be read from disk and stored in the memory referred 
//   to by buf
//
// ERRORS:
//
// NSR::error_None:  Function completed successfully.
// NSR::error_No_free_store:  Could not allocate memory for buffering.
// NSR::error_Empty_path_element:  Argument buffer is invalid.
// NSR::error_Drive_blank_check:  A blank sector was encountered during the read.
// NSR::error_Read_EOM:  Attempted to read past physical EOM
// NSR::error_Read_EOLM:  Attempted to read past logical EOM
// NSR::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.
//-----------------------------------------------------------------------------

NSR::INT32
MODeviceSimulator::read
(
    NSR::UINT32 start,
    NSR::UINT32 nsectors,
    const NSR::ByteArrayRef& buf,
    NSR::Error& err
)
{
    NSR::INT32    rv;           // Return value from system call
    NSR::INT32    offset;       // For lseek(); offset in bytes
    NSR::INT32    extsize;
    NSR::INT32    residue;
    NSR::INT32    request_len;
    NSR::BYTE_PTR bptr;
    
    // Verify that the MODeviceSimulator object has been successfully 
    // constructed.

    if(fd_ < 0) {
        err = dderror_;
        return -1;
    }

    if((start >= numSectors_) || (start < 0))
    {
        err = dderror_ = NSR::error_Read_EOLM;
        return -1;
    }
    err = dderror_ = NSR::error_None;

    if(buf.isValid() == NSR::False) {
        err = NSR::error_Empty_path_element;
        return -1;
    }

    // Set up read values.  

    extsize = nsectors * sectorSize_;
    request_len = ((extsize < buf.length()) ? extsize : buf.length());
    residue = request_len % sectorSize_;
    offset = (start+1) * sectorSize_;
    bptr = (NSR::BYTE_PTR) buf;

    // Verify that the number of bytes to be read is a sector multiple.  If
    // it is not, then we split the read into two parts:  the first part
    // will be the first n-1 sectors of the request; the second part will
    // be the last sector, which must be buffered to prevent overrunning the
    // user's buffer.  Both of these reads will be done recursively.

    if(residue != 0) {
        NSR::ByteArrayRef       newbuf1(buf.theArray(),buf.arrayLen(),
                                        buf.offset(),
                                        buf.length() - residue);
        rv = 0;
        if(((NSR::INT32) buf.length() - (NSR::INT32) residue) > 0) {
            
            rv = read(start,nsectors,newbuf1,err);

            // If the read doesn't fully succeed, don't even try to do the last
            // sector's worth.  Makes error handling much easier to deal with.

            if(rv < (NSR::INT32) newbuf1.length()) {
                return rv;
            }
        }

        // Now set up to do the last sector.

        NSR::ByteArray  newbarr(sectorSize_);
        NSR::ByteArrayRef       newbuf2(newbarr);
        NSR::INT32      rv2;
        NSR::INT32      sector_offset = (buf.length() - residue) / 
                                                 sectorSize_;

        if(((NSR::BYTE_PTR) newbarr) == NULL) { // Can't buffer: we're hosed
            err = dderror_ =  NSR::error_No_free_store;
            return rv;
        }

        rv2 = read(start+sector_offset,1,newbuf2,err);
        if(rv2 < (NSR::INT32) sectorSize_) {
            return rv2;                 // A short count!
        }

        newbuf1.offset(buf.offset() + buf.length() - residue);
        newbuf1.length(residue);
        newbuf1.fromMem((NSR::BYTE_PTR) newbuf2);
        return (rv + residue);
    }

    // Seek into the disk

    rv = lseek(fd_,offset,SEEK_SET);
    if(rv < 0)  {                       // The lseek() failed!
        err = dderror_ =  NSR::error_Drive_read;
        return -1;
    }

    // Perform the read

    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 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 MODeviceSimulator::read().
//-----------------------------------------------------------------------------

NSR::INT32
MODeviceSimulator::read
(
    NSR::UINT32 start,
    NSR::UINT32 nbytes,
    NSR::BYTE_PTR buf,
    NSR::Error& err
)
{
    NSR::INT32  rv;             // Return value from system call
    NSR::INT32  offset;         // For lseek(); offset in bytes
    NSR::INT32  residue;
    
    // Verify that the MODeviceSimulator object has been successfully 
    // constructed.

    if(fd_ < 0) {
        err = dderror_;
        return -1;
    }
    if((start >= numSectors_) || (start < 0))
    {
        err = dderror_ =  NSR::error_Read_EOLM;
        return -1;
    }

    err = dderror_ =  NSR::error_None;

    if(buf == NULL) {
        err =  NSR::error_Null_pointer;
        return -1;
    }

    err =  NSR::error_None;

    // Set up read values.  

    residue = nbytes % sectorSize_;
    offset = (start + 1) * sectorSize_;

    // Verify that the number of bytes to be read is a sector multiple.  If
    // it is not, then we split the read into two parts:  the first part
    // will be the first n-1 sectors of the request; the second part will
    // be the last sector, which must be buffered to prevent overrunning the
    // user's buffer.  Both of these reads will be done recursively.

    if(residue != 0) {
        
        rv = 0;
        if(((NSR::INT32) nbytes - (NSR::INT32) residue) > 0) {
            rv = read(start,nbytes - residue,buf,err);

            // If the read doesn't fully succeed, don't even try to do the last
            // sector's worth.  Makes error handling much easier to deal with.

            if(rv < (NSR::INT32) (nbytes - residue)) {
                return rv;
            }
        }

        // Now set up to do the last sector.

        NSR::BYTE_PTR           newbuf = new NSR::BYTE[sectorSize_];
        NSR::INT32              rv2;
        NSR::INT32              sector_offset = (nbytes - residue) / 
                                                sectorSize_;

        if(newbuf == NULL) {                    // Can't buffer: we're hosed
            err = dderror_ =  NSR::error_No_free_store;
            return rv;
        }

        rv2 = read(start + sector_offset,sectorSize_,newbuf,err);
        if(rv2 < (NSR::INT32) sectorSize_) {
            delete newbuf;
            return rv;                  // A short count!
        }

        memcpy((NSR::VOID_PTR) (buf + nbytes - residue),newbuf,residue);
        delete newbuf;
        return (rv + residue);
    }

    // Seek into the disk

    rv = lseek(fd_,offset,SEEK_SET);
    if(rv < 0)  {                       // The lseek() failed!
        err = dderror_ =  NSR::error_Drive_read;
        return -1;
    }

    // Perform the read

    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 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:  MODeviceSimulator::write()                                  --JKH
//
// This method writes the specified sectors to disk from the given buffer.
//
// ARGS:
//
// NSR::UINT32 start            IN      Starting sector use for write
// NSR::UINT32 nsectors    IN      Maximum number of sectors to write
// const NSR::ByteArrayRef& buf IN/OUT  The buffer to supply the data.
// NSR::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.  
//
// PRE-CONDS:
//
// --The MODeviceSimulator 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
//   NSR::byteArrayRef buf will be written to disk and taken from the memory 
//   referred to by buf
//
// ERRORS:
//
// NSR::error_None:  Function completed successfully.
// NSR::error_No_free_store:  Could not allocate memory for buffering.
// NSR::error_Empty_path_element:  Argument buffer is invalid.
// NSR::error_Write_EOM:  Attempted to write past physical EOM
// NSR::error_Write_EOLM:  Attempted to write past logical EOM
// NSR::error_Drive_write:  Generic I/O failure on write
// NSR::error_Set_verify_failed:  Cannot set verify mode.
//-----------------------------------------------------------------------------

NSR::INT32
MODeviceSimulator::write
( 
    NSR::UINT32 start,
    NSR::UINT32 nsectors,
    const  NSR::ByteArrayRef& buf,
    NSR::Error& err
)
{
    NSR::INT32     rv;          // Return value from system call
    NSR::INT32     offset;              // For lseek(); offset in bytes
    NSR::INT32     extsize;
    NSR::INT32     residue;
    NSR::INT32     request_len;
    NSR::CONST_BYTE_PTR bptr;
    
    // Verify that the MODeviceSimulator object has been successfully 
    // constructed.

    if(fd_ < 0) {
        err = dderror_;
        return -1;
    }
    if((start >= numSectors_) || (start < 0))
    {
        err = dderror_ =  NSR::error_Read_EOLM;
        return -1;
    }
    err = dderror_ =  NSR::error_None;

    if(buf.isValid() ==  NSR::False) {
        err =  NSR::error_Empty_path_element;
        return -1;
    }

    // Set up write values.

    extsize = nsectors * sectorSize_;
    request_len = ((extsize < buf.length()) ? extsize : buf.length());
    residue = request_len % sectorSize_;
    offset = (start + 1) * sectorSize_;
    bptr = (NSR::CONST_BYTE_PTR) buf;

    // Verify that the number of bytes to be written is a sector multiple.  If
    // it is not, then we split the write into two parts:  the first part
    // will be the first n-1 sectors of the request; the second part will
    // be the last sector, which must be padded so that one whole sector
    // may be written.  This padding will be performed by zero-filling
    // the last sector.

    if(residue != 0) {
        rv = 0;
        if(((NSR::INT32) buf.length() - (NSR::INT32) residue) > 0) {
             NSR::ByteArrayRef  newbuf1(buf.theArray(),buf.arrayLen(),
                                        buf.offset(),
                                        buf.length() - residue);

            rv = write(start,nsectors,newbuf1,err);


            // If the write doesn't fully succeed, don't even try to do the last
            // sector's worth.  Makes error handling much easier to deal with.

            if(rv < (NSR::INT32) newbuf1.length()) {
                return rv;
            }
        }

        // Now set up to do the last sector.  We must allocate a sector-sized
        // buffer, copy the remaining user data into it, and zero-fill it.

         NSR::ByteArray newbarr(sectorSize_);
         NSR::ByteArrayRef      newbuf2(newbarr);
        NSR::INT32      rv2;
        NSR::INT32      sector_offset = (buf.length() - residue) / sectorSize_;

        if(((NSR::BYTE_PTR) newbarr) == NULL) { // Can't buffer: we're hosed
            err = dderror_ =  NSR::error_No_free_store;
            return rv;
        }

        memset((NSR::BYTE_PTR) newbuf2,0,sectorSize_); // zero-fill
        newbuf2.length(residue);
        newbuf2.fromMem(((NSR::BYTE_PTR) buf) + (buf.length() - residue));
        newbuf2.length(sectorSize_);
        
        rv2 = write(start+sector_offset,1,newbuf2,err);
        if(rv2 < (NSR::INT32) sectorSize_) {
            return rv;                  // A short count!
        }
        return rv + residue;
    }

    // Seek into the disk

    rv = lseek(fd_,offset,SEEK_SET);
    if(rv < 0)  {       // The lseek() failed!
        err = dderror_ =  NSR::error_Drive_write;
        return -1;
    }

    // Perform the write.  

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

    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 MODeviceSimulator::write().
//-----------------------------------------------------------------------------

NSR::INT32
MODeviceSimulator::write
(
    NSR::UINT32 start,
    NSR::UINT32 nbytes,
    NSR::CONST_BYTE_PTR buf,
    NSR::Error& err
)
{
    NSR::INT32  rv;             // Return value from system call
    NSR::INT32  offset;         // For lseek(); offset in bytes
    NSR::INT32  residue;
    
    // Verify that the MODeviceSimulator object has been successfully 
    // constructed.

    if(fd_ < 0) {
        err = dderror_;
        return -1;
    }
     
    if((start >= numSectors_) || (start < 0))
    {
        err = dderror_ =  NSR::error_Read_EOLM;
        return -1;
    }
    err = dderror_ =  NSR::error_None;

    if(buf == NULL) {
        err =  NSR::error_Null_pointer;
        return -1;
    }

    // Set up write values.

    residue =  nbytes % sectorSize_;
    offset = (start + 1) * sectorSize_;

    // Verify that the number of bytes to be written is a sector multiple.  If
    // it is not, then we split the write into two parts:  the first part
    // will be the first n-1 sectors of the request; the second part will
    // be the last sector, which must be padded so that one whole sector
    // may be written.  This padding will be performed by zero-filling
    // the last sector.

    if(residue != 0) {

        rv = 0;
        if(((NSR::INT32) nbytes - (NSR::INT32) residue) > 0) {
            rv = write(start,nbytes - residue,buf,err);


            // If the write doesn't fully succeed, don't even try to do the last
            // sector's worth.  Makes error handling much easier to deal with.

            if(rv < (NSR::INT32) (nbytes - residue)) {
                return rv;
            }
        }

        // Now set up to do the last sector.  We must allocate a sector-sized
        // buffer, copy the remaining user data into it, and zero-fill it.

        NSR::BYTE_PTR   newbuf = new NSR::BYTE[sectorSize_];
        NSR::INT32         rv2;
        NSR::INT32         sector_offset = (nbytes - residue) / sectorSize_;

        if(newbuf == NULL) {                    // Can't buffer: we're hosed
            err = dderror_ =  NSR::error_No_free_store;
            return rv;
        }

        memset(newbuf,0,sectorSize_);           // zero-fill
        memcpy(newbuf,buf + (nbytes - residue),residue);
        
        rv2 = write(start + sector_offset,sectorSize_,newbuf,err);
        if(rv2 < (NSR::INT32) sectorSize_) {
            delete newbuf;
            return rv;                  // A short count!
        }
        delete newbuf;
        return rv + residue;
    }

    // Seek into the disk

    rv = lseek(fd_,offset,SEEK_SET);
    if(rv < 0)  {       // The lseek() failed!
        err = dderror_ =  NSR::error_Drive_write;
        return -1;
    }

    // Perform the write.

    rv = ::write(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_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:  MODeviceSimulator::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:
//
// NSR::UINT32 start             IN      Starting sector use for write
// NSR::UINT32 nsectors          IN      Maximum number of sectors to write
// const NSR::byteArrayRef& buf  IN/OUT  The buffer to supply the data.
// NSR::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 MODeviceSimulator 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 MODeviceSimulator 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
//   NSR::byteArrayRef buf will be written to disk and taken from the memory 
//   referred to by buf
//
// ERRORS:
//
// NSR::error_None:  Function completed successfully.
// NSR::error_No_free_store:  Could not allocate memory for buffering.
// NSR::error_Empty_path_element:  Argument buffer is invalid.
// NSR::error_Write_EOM:  Attempted to write past physical EOM
// NSR::error_Write_EOLM:  Attempted to write past logical EOM
// NSR::error_Drive_write:  Generic I/O failure on write
//-----------------------------------------------------------------------------

NSR::INT32
MODeviceSimulator::writeWOE
( 
    NSR::UINT32 start,
    NSR::UINT32 nsectors,
    const  NSR::ByteArrayRef& arr,
    NSR::Error& err
)
{
    NSR::INT32 rval;

    rval = write(start,nsectors,arr,err);
    return rval;
}

//-----------------------------------------------------------------------------
// Alternate interface for MODeviceSimulator::writeWOE().
//-----------------------------------------------------------------------------

NSR::INT32
MODeviceSimulator::writeWOE
(
    NSR::UINT32 start,
    NSR::UINT32 nbytes,
    NSR::CONST_BYTE_PTR buf,
    NSR::Error& err
)
{
    NSR::INT32 rval;

    rval = write(start,nbytes,buf,err);
    return rval;
}

//-----------------------------------------------------------------------------
// METHOD:  MODeviceSimulator::erase()                                  --JKH
//
// Erases the sectors in the argument PhysExtent.
//
// ARGS:
//
// NSR::UINT32 start           IN  Starting sector to erase
// NSR::UINT32 nsectors     IN  Number of sectors to erase
// NSR::Error& err             OUT Error value to hold result of operation
//
// RETURNS:  Returns 0 on success, -1 on failure.
//
// PRE-CONDS:
//
// --The MODeviceSimulator 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:
//
// NSR::error_None            Function completed successfully.
// NSR::error_Bad_erase_size  Extent size is not a multiple of the sector size.
// NSR::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.
//-----------------------------------------------------------------------------

NSR::INT32
MODeviceSimulator::erase
(
     NSR::UINT32 start,
     NSR::UINT32 nsectors,
     NSR::Error& err
)
{
    err =  NSR::error_None;

    // Check to ensure that this object has been successfully created

    if(fd_ < 0) {
        err = dderror_;
        return -1;
    }

    return 0;
}

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

NSR::Error
MODeviceSimulator::checkValidity(void) const
{
    if(fd_ < 0) {
        return  NSR::Error(dderror_);
    }
    return  NSR::Error( NSR::error_None);
}

NSR::Boolean     
MODeviceSimulator::verifyBlank
(
    NSR::UINT32 start,
    NSR::UINT32 nsectors,
    NSR::UINT32 *which
)
{
    return NSR::False;
}

//-----------------------------------------------------------------------------
// METHOD:  MODeviceSimulator::readHeader()
//
// Attempts to read the header from the file.
//
// ARGUMENTS:  None.
//
// RETURNS:  Returns 0 on success, -1 on failure.
//
// PRECONDITIONS:  File has a valid descriptor and is accessible.
//
// POSTCONDITIONS:  If a valid header is found, the numSectors_ and
//                  sectorSize_ variables will be set to the values read in
//                  the header.
//
// NOTES:
//
// Header file format:
//
// A header is one logical sector.  Since readHeader() may be called without
// knowing the sector size, we read the minimum sector size (512 bytes), and
// read the magic numbers from the first 512 bytes.
//
// FORMAT:
//
// Offset       Field           Format                          Length
// ------------ --------------- ------------------------------- -------------
// 0            Magic number    %8c (6a6f6e68)                  8
// 8            sectorSize      %08x\000\000                    10
// 18           numSectors      %08x\000\000                    10
// 
//-----------------------------------------------------------------------------

NSR::INT32
MODeviceSimulator::readHeader
(
   void
)
{
    char        buffer[MIN_SECTOR_SIZE];
    int         rval;

    if(fd_ < 0)
    {
        return -1;
    }
    if(::lseek(fd_,0,SEEK_SET))
    {
        return -1;
    }
    rval = ::read(fd_,buffer,MIN_SECTOR_SIZE);
    if(rval != MIN_SECTOR_SIZE)
    {
        return -1;
    }

    // Look for the magic number

    if(strncmp(buffer,MODevSim_Magic,MODevSim_MagicLength))
    {
        return -1;
    }

    // We got a good magic number.  Read sector size and number of sectors.

    if(sscanf(buffer + MODevSim_MagicLength,"%08x",&sectorSize_) != 1)
    {
        sectorSize_ = 0;
        return -1;
    }
    if(sscanf(buffer + MODevSim_MagicLength + MODevSim_FieldLength,"%08x",
              &numSectors_) != 1)
    {
        numSectors_ = 0;
        return -1;
    }

    return 0;
}


//-----------------------------------------------------------------------------
// METHOD:  MODeviceSimulator::writeHeader()
//
// Attempts to write the header to the file.
//
// ARGUMENTS:
//
// NSR::UINT32  sectorSize  IN  Size of a logical sector, in bytes
// NSR::UINT32  numSectors  IN  Number of sectors in logical device
//
// RETURNS:  Returns 0 on success, -1 on failure.
//
// PRECONDITIONS:  File has a valid descriptor and is accessible.
//
// POSTCONDITIONS:  A header will be written to the file.
//
// NOTES:
//
// Header file format:
//
// A header is one logical sector.  Since readHeader() may be called without
// knowing the sector size, we read the minimum sector size (512 bytes), and
// read the magic numbers from the first 512 bytes.
//
// FORMAT:
//
// Offset       Field           Format                          Length
// ------------ --------------- ------------------------------- -------------
// 0            Magic number    %8c (6a6f6e68)                  8
// 8            sectorSize      %08x\000\000                    10
// 18           numSectors      %08x\000\000                    10
// 
//-----------------------------------------------------------------------------

NSR::INT32
MODeviceSimulator::writeHeader
(
    NSR::UINT32 sectorSize,
    NSR::UINT32 numSectors
)
{
    char        *buffer;
    int         rval;

    if(fd_ < 0)
    {
        return -1;
    }
    if(::lseek(fd_,0,SEEK_SET))
    {
        return -1;
    }
    buffer = new char[sectorSize];
    if(buffer == NULL)
    {
        return -1;
    }
    memset(buffer,0,sectorSize);

    // Write the magic number and fields

    sprintf(buffer,"%8c%08x%c%c%08x%c%c",MODevSim_Magic,sectorSize,0,0,
            numSectors,0,0);
    
    rval = ::write(fd_,buffer,sectorSize);
    delete [] buffer;
    if(rval != sectorSize)
    {
        return -1;
    }

    // Set internal state variables

    sectorSize_ = sectorSize;
    numSectors_ = numSectors;

    return 0;
}


