//-*-c++-*--------------------------------------------------------------
//  nsrSSVol.cpp
//   Methods for Single Surface Volumes and their related classes.
//
//      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.
//
//  CLASSES:
//   Partition
//   LogicalVolume
//   PhysicalVolume
//   SingleSurfaceVolume
//  
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfCore/ss/nsrSSVol.cpp,v $
// $Revision: 1.48 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/06/27 02:54:50 $

#include "nsrLib.h"

//----------------------------------------------------------------------
// METHOD: Integrity::toDisk()  --DVM
//      
//  This writes out the current integrity's state to the disk at the
//  proper place.
//
// ARGS:
//  Error &  err OUT  Error condition associated with the operation.
//
// RETURNS:
//  void
// 
// PRE-CONDITIONS:
//  The entire state of the Integrity object must be set properly
//  before this is called.
// 
// POST-CONDITIONS:
//  The LogicalVolumeIntegrityDesc has been written to disk.
// 
// NOTES:
//  <Notes_go_here>
//
//----------------------------------------------------------------------
// Private comments go here - will not be extracted into HTML.
//----------------------------------------------------------------------
void
NSR::Integrity::toDisk( Error & err )
{
    ByteArray buf(logVolumep_->blockSize());
    if (err = buf.error()) {
        return;
    }
    PhysExtent & iext = logVolumep_->integSeqExtent(err);
    if (err) {
        return;
    }
    // Fill in the size and free space tables from the LV and SR.
    integrityDesc_.sizeTable[0] = (
        logVolumep_->blockSize() * logVolumep_-> numBlocks());
    // Since there is only one partition, use partition[1]
    integrityDesc_.freeSpaceTable[0] =
        logVolumep_->partitionp(1)->spaceResourcep()->availBytes();
    // Translate to ISO
    integrityDesc_.toISO((BYTE_PTR)buf,
                         iext.startSector());
    if (err = integrityDesc_.error()) {
        return;
    }
    iext.write(buf,err);
    if (err) {
        return;
    }
}

//----------------------------------------------------------------------
// METHOD: Integrity::fromDisk()  --DVM
//      
//  This reads in the Integrity's state from its image on disk.
//
// ARGS:
//  Error &  err OUT  Error condition associated with the operation.
//
// RETURNS:
//  void
// 
// PRE-CONDITIONS:
//  The LogicalVolumeDesc pointed to by the Integrity object must have
//  a valid integrityExt().
// 
// POST-CONDITIONS:
//  The Integrity's state matches that of the one on the disk.
// 
// NOTES:
//  <Notes_go_here>
//
//----------------------------------------------------------------------
// Private comments go here - will not be extracted into HTML.
//----------------------------------------------------------------------
void
NSR::Integrity::fromDisk( Error & err )
{
    ByteArray buf(logVolumep_->blockSize());
    if (err = buf.error()) {
        return;
    }
    PhysExtent & iext = logVolumep_->integSeqExtent(err);
    if (err) {
        return;
    }
    iext.read(buf,err);
    if (err) {
        return;
    }
    integrityDesc_.fromISO(buf,0,iext.startSector());
    if (err = integrityDesc_.error()) {
        return;
    }
}

//----------------------------------------------------------------------
// METHOD:  Partition::Partition() --DVM
//      
//  Constructor for Partition Object
//
// ARGS:
//  physExt   IN  Specifies area on disk to be associated with the
//                Partition.
//
// PRECONDITIONS:
//  physExtent must be Valid, so that it has access to the disk?
// 
// POSTCONDITIONS:
//  A valid Partition object is created. The partition pointer of the
//  PhysVolume is changed to point to this partition.
// 
// NOTES:
//  <Notes_go_here>
//
//----------------------------------------------------------------------

NSR::Partition::Partition( const PhysExtent & physExt ) :
        partitionExt_        (physExt),
        erasedSpaceTblExt_   (this,0),
        erasedSpaceBMapExt_  (this,0),
        unerasedSpaceTblExt_ (this,0),
        unerasedSpaceBMapExt_(this,0),
        error_               (error_None), // Redundant
        logVolumep_          (NULL),
        spaceResourcep_      (NULL)
{
    // Now, point PhysVolume to this Partition.
    partitionExt_.physVolumep()->partitionp(this);
}

//----------------------------------------------------------------------
// METHOD: Partition::validateSpaceExtents           --DVM
//      
//  Returns an error if combinations of Space Tables, Bitmaps, etc are
//  not allowed.
//
// ARGS:
//  none.
//
// RETURNS:
//  See errors section.
// 
// PRE-CONDITIONS:
//  none.
// 
// POST-CONDITIONS:
//  none.
// 
// ERRORS:
//  error_PART_Both_space_bitmap_and_table
//  error_PART_Mixed_space_tables_and_bitmaps
// 
// NOTES:
//  Does NOT currently check for WORM issues.
//
//----------------------------------------------------------------------
// Private comments go here - will not be extracted into HTML.
//----------------------------------------------------------------------
NSR::Error
NSR::Partition::validateSpaceExtents(void) const
{
    Error error(error_None);
    
    INT16 est = ( erasedSpaceTblExt_   .infoLen() != 0 );
    INT16 esb = ( erasedSpaceBMapExt_  .infoLen() != 0 );
    INT16 ust = ( unerasedSpaceTblExt_ .infoLen() != 0 );
    INT16 usb = ( unerasedSpaceBMapExt_.infoLen() != 0 );

    if ( est && esb || ust && usb ) // ISO rule
        error = error_PART_Both_space_bitmap_and_table;
    else if ( est && usb || ust && esb ) // OSTA 2.3.3
        error = error_PART_Mixed_space_tables_and_bitmaps;
    // !!! Should check for WORM issues - No bitmaps allowed.
    return error;
}

//----------------------------------------------------------------------
// METHOD:  Partition::fromISO() --DVM
//      
//  Fills in all data members based on ISO representation
//
//  This translates the ISO representation of a PartitionDesc from the
//  supplied buffer into the in-memory Partition form.
//
// ARGS:
//  buf     IN  ByteArray containing ISO representation of Partition.
//  pext    IN  PhysExtent where this PD was located.
//  error   OUT Set if any errors occurred.
//
// RETURNS: none.
// 
// PRECONDITIONS:
//  List_preconds
// 
// POSTCONDITIONS:
//  List_postconds
// 
// ERRORS:
//  error_
//  error_
//  error_
// 
// NOTES:
//  <Notes_go_here>
//
//----------------------------------------------------------------------

void
NSR::Partition::fromISO( const ByteArray &buf,
                         const PhysExtent & pext,
                         Error & error )
{
    // Read the PD into the BA from the PhysExtent.
    partitionDesc_.fromISO( buf,0,pext.startSector() );
    if (error = partitionDesc_.error())
        return;

    // Convert the appropriate fields into the PhysExtent which
    // comprises the partition.
    partitionExt_ = PhysExtent( pext.physVolumep(),
                                partitionDesc_.partStartLoc,
                                partitionDesc_.partLength );
    // Must check error right after construction.
    if (error = partitionExt_.error())
        return;
    
    // Now set all of the Space Extents based on the fields in the
    // PHD.
    PartitionHdrDesc & phd = partitionDesc_.partitionHdrDesc;

    phd.unallocSpaceTable.toLogExtent(this,erasedSpaceTblExt_,error);
    if ( error ) return;
    phd.unallocSpaceBitmap.toLogExtent(this,erasedSpaceBMapExt_,error);
    if ( error ) return;
    phd.freedSpaceTable.toLogExtent(this,unerasedSpaceTblExt_,error);
    if ( error ) return;
    phd.freedSpaceBitmap.toLogExtent(this,unerasedSpaceBMapExt_,error);
    if ( error ) return;

    // Finally, check to see that type and combination of Space
    // Extents is legal.
    error = validateSpaceExtents();
}

//----------------------------------------------------------------------
// METHOD:  Partition::toISO() --DVM
//      
//  Translates Partition information into ISO representation.
//
//  This translates the the in-memory Partition information into an
//  ISO representation in the supplied buffer.
//
// ARGS:
//  ByteArray &buf IN  ByteArray to contain ISO representation of
//                     Partition. 
//  PhysExtent &pe IN  PhysExtent where this PD will be located.
//
// RETURNS: none.
// 
// PRECONDITIONS:
//  List_preconds
// 
// POSTCONDITIONS:
//  List_postconds
// 
// ERRORS:
//  error_
//  error_
//  error_
// 
// NOTES:
//  Just about the exact complement of fromISO().
//
//  This is SOLELY concerned with the PD structures, and the SRs must
//  be separately written to the disk.
//
//----------------------------------------------------------------------

void
NSR::Partition::toISO( ByteArray &buf,
                       const PhysExtent & pext,
                       Error & error )
{
    // First, make sure that combination of Space Extents is legal.
    if (error = validateSpaceExtents())
        return;

    // Next, fill in the PHD with the Space Extents.
    PartitionHdrDesc & phd = partitionDesc_.partitionHdrDesc;
    
    phd.unallocSpaceTable.fromLogExtent(erasedSpaceTblExt_,error);
    if ( error ) return;
    phd.unallocSpaceBitmap.fromLogExtent(erasedSpaceBMapExt_,error);
    if ( error ) return;
    phd.freedSpaceTable.fromLogExtent(unerasedSpaceTblExt_,error);
    if ( error ) return;
    phd.freedSpaceBitmap.fromLogExtent(unerasedSpaceBMapExt_,error);
    if ( error ) return;

    // Check to make sure that we have a valid Partition extent.
    if (error = partitionExt_.error())
        return;
    // Fill in Other PD fields.
    partitionDesc_.partStartLoc = partitionExt_.startSector();
    partitionDesc_.partLength   = partitionExt_.numSectors();

    // Now translate the PD to ISO, and set the error if any.
    partitionDesc_.toISO( (BYTE_PTR)buf,pext.startSector() );
    error = partitionDesc_.error();
    return;
}

//----------------------------------------------------------------------
// METHOD:  LogicalVolume::blockSize() --DVM
// METHOD:  LogicalVolume::numBlocks() --DVM
//      
//  Returns the block size or number of Blocks for the LogicalVolume
//
// PRECONDITIONS:
//  Partition pointer is non-null.
// 
// NOTES:
//  If partition pointer is not valid, returns ???
//
//----------------------------------------------------------------------

NSR::UINT32
NSR::LogicalVolume::blockSize() const
{
    return partitionp_->blockSize();
}

NSR::UINT32
NSR::LogicalVolume::numBlocks() const
{
    return partitionp_->numBlocks();
}

//----------------------------------------------------------------------
// METHOD:  PhysicalVolume::PhysicalVolume()
//
// Constructor for the PhysicalVolume object.
//
// ARGS:
//
// DriveDevice *drive  IN  Pointer to a fully constructed DriveDevice
//                         object. The DriveDevice object must have
//                         been successfully constructed.
// Error& err          OUT Error to hold results of operation
//
// RETURNS:  none
//
// PRECONDITIONS:
//
// --Argument DriveDevice * must point to a successfully constructed
//   object 
//
// POSTCONDITIONS:
//
// --The PhysicalVolume's driveDevp_ will be initialized to the specified
//   value; the physVolSetp_ and partitionp_ pointers will be initialized to NULL.
//
// ERRORS:
//
// error_None:  Function completed successfully.
// error_DriveDevice_invalid:  The DriveDevice pointer argument was
// invalid.
//----------------------------------------------------------------------

NSR::PhysicalVolume::PhysicalVolume( SSDevice *drivep ) :
        priVolDesc_  (),
        physVolSetp_ (NULL),
        partitionp_  (NULL),
        driveDevp_   (NULL),
        error_       (error_None)
{
    if(drivep == NULL) {
        error_ = error_DriveDevice_invalid;
        return;
    }

    // Verify that DriveDevice object was successfully created.

    if((ErrorCodeType) drivep->checkValidity() != error_None) {
        error_ = error_DriveDevice_invalid;
        return;
    }
    driveDevp_ = drivep;
}

//----------------------------------------------------------------------
// METHOD:  PhysicalVolume::~PhysicalVolume()
//
// Destructor for the PhysicalVolume object.
//
// ARGS:           none
// RETURNS:        none
// PRECONDITIONS:  none
// POSTCONDITIONS: none
//
// NOTES:  This is currently a no-op.
//----------------------------------------------------------------------

NSR::PhysicalVolume::~PhysicalVolume()
{
    ;
}

//----------------------------------------------------------------------
// METHOD:  PhysicalVolume::physVolSetp_()
//
// Sets the PhysicalVolumeSet pointer to the supplied value.
//
// ARGS:
//
// PhysicalVolumeSet *newpvsp  IN  New physVolSetp_ value.
//
// RETURNS:  
//
// Returns the old PhysicalVolumeSet pointer.
//
// PRECONDITIONS:   none
// POSTCONDITIONS:
// 
// --The physVolSetp_ value will be changed to the specified value, even if the 
//   specified value is invalid.
//
// ISSUE:  PV::physVolSetp(): Verify that new physVolSetp_ pointer is valid?
//----------------------------------------------------------------------

NSR::PhysicalVolumeSet * 
NSR::PhysicalVolume::physVolSetp( PhysicalVolumeSet *newpvsp )
{
    PhysicalVolumeSet *oldpvsp;

    oldpvsp      = physVolSetp_;
    physVolSetp_ = newpvsp;
    error_       = error_None;
    return oldpvsp;
}

//----------------------------------------------------------------------
// METHOD:  PhysicalVolume::partitionp()
//
//  Sets the partition pointer to the supplied value.
//
// ARGS:
//
//  Partition *newpartp   IN  Pointer to use as new value for
//                           partitionp_ field. 
//
// RETURNS:  
//  Returns the old Partition pointer.
//
// PRECONDITIONS:
//
//  none.
//
// POSTCONDITIONS:
//
//  The partitionp_ value will be changed to the specified value,
//  even if the specified value is invalid.
//
// ISSUE:  PV::partitionp(): Verify that new partitionp_ pointer is valid?
//----------------------------------------------------------------------

NSR::Partition * 
NSR::PhysicalVolume::partitionp( Partition *newpartp )
{
    Partition   *oldpartp;

    oldpartp    = partitionp_;
    partitionp_ = newpartp;
    error_      = error_None;
    return oldpartp;
}

//----------------------------------------------------------------------
// METHOD:  PhysicalVolume::driveDevp()
//
//  Sets the ddevp pointer to the supplied value.
//
// ARGS:
//
//  Partition *newpartp   IN  Pointer to use as new value for partitionp_ field.
//
// RETURNS:  
//
//  Returns the old DriveDevice pointer.
//
// PRECONDITIONS:
//
//  none.
//
// POSTCONDITIONS:
//
//  The ddevp value will be changed to the specified value, even if
//  the specified value is invalid.
//
// ISSUE:  PV::driveDevp(): Verify that new driveDevp pointer is valid?
//-----------------------------------------------------------------------

NSR::SSDevice * 
NSR::PhysicalVolume::driveDevp( SSDevice *newddevp )
{
    SSDevice    *old_ddevp;

    if(newddevp == NULL) {
        error_ = error_DriveDevice_invalid;
        return driveDevp_;
    }
    if((ErrorCodeType) newddevp->checkValidity() != error_None) {
        error_ = error_DriveDevice_invalid;
        return driveDevp_;
    }

    old_ddevp  = driveDevp_;
    driveDevp_ = newddevp;
    error_     = error_None;
    return old_ddevp;
}

//----------------------------------------------------------------------
// METHOD:  PhysicalVolume::read()              --JKH
//
// This method passes a read request down to the DriveDevice object.
//
// ARGS:
//
// const PhysExtent& extent IN     The extent to read from disk.
// const ByteArrayRef&  buf IN/OUT The buffer to hold the data.
// Error&  err              OUT    Error to store result 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.
//
// PRECONDITIONS:
//
//  The DriveDevice object was successfully constructed and contains
//  media 
//  The extent parameter specifies a range of sectors physically
//  accessible on the media
//
// POSTCONDITIONS:
//
//  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_DriveDevice_invalid:  Invalid DriveDevice bound to PhysicalVolume.
//  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.
//----------------------------------------------------------------------

NSR::INT32
NSR::PhysicalVolume::read( const PhysExtent& extent,
                           const ByteArrayRef& buf,
                           Error& err)
{
    INT32       rval;

    err = error_None;
    if(driveDevp_ == NULL) {    // This object wasn't successfully constructed!
        err = error_ = error_DriveDevice_invalid;
        return -1;
    }

    rval = driveDevp_->read(extent.startSector(),
                            extent.numSectors(),
                            buf,
                            error_);
    err  = error_;
    return rval;
}

//----------------------------------------------------------------------
// METHOD:  PhysicalVolume::write()                             --JKH
//
//  This method passes a write request down to the DriveDevice object.
//
// ARGS:
//
//  const PhysExtent& extent  IN  The extent to write to disk.
//  const ByteArrayRef& buf   IN  The buffer to supply the data.
//  Error& err               OUT Error to hold result 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-CONDITIONS:
//
//  The DriveDevice object was successfully constructed and contains
//  media 
//  The start parameter is in the range [0,capacity)
//  The nsectors parameter satisfies the following:
//         start + nsectors < capacity
//  The device is writable (e.g., not write-protected and, if WORM,
//  the sectors are unwritten).
//
// POST-CONDITIONS:
//
//  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_DriveDevice_invalid: Invalid DriveDevice bound to PV.
//  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.
//
//----------------------------------------------------------------------
// Private comments go here - will not be extracted into HTML.
//----------------------------------------------------------------------

NSR::INT32
NSR::PhysicalVolume::write( const PhysExtent& extent,
                            const ByteArrayRef& buf,
                            Error& err)
{
    INT32 rval;

    err = error_None;
    if(driveDevp_ == NULL) { // This object wasn't successfuly constructed!
        err = error_ = error_DriveDevice_invalid;
        return -1;
    }

    rval = driveDevp_->write(extent.startSector(),extent.numSectors(),buf,
                             error_);
    err  = error_;
    return rval;
}

//----------------------------------------------------------------------
// METHOD:  PhysicalVolume::writeWOE()                          --JKH
//
//  This method passes a writeWOE request down to the DriveDevice
//  object. 
//
// ARGS:
//
//  const PhysExtent& extent  IN  The extent to write to disk.
//  const ByteArrayRef& buf   IN  The buffer to supply the data.
//  Error& err               OUT Error to hold result 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.
//
// PRECONDITIONS:
//
//  The DriveDevice object was successfully constructed and contains
//  media 
//  The start parameter is in the range [0,capacity)
//  The nsectors parameter satisfies the following:
//             start + nsectors < capacity
//  The device is writable (e.g., not write-protected and, if WORM,
//  the sectors are unwritten).
//
// POSTCONDITIONS:
//
//  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_DriveDevice_invalid: Invalid DriveDevice bound to PV.
//  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.
//----------------------------------------------------------------------
// Private comments go here - will not be extracted into HTML.
//----------------------------------------------------------------------

NSR::INT32
NSR::PhysicalVolume::writeWOE(const PhysExtent& extent,
                              const ByteArrayRef& buf,
                              Error& err)
{
    INT32 rval;

    err = error_None;
    if(driveDevp_ == NULL) { // This object wasn't successfuly constructed!
        err = error_ = error_DriveDevice_invalid;
        return -1;
    }

    rval = driveDevp_->writeWOE(extent.startSector(),extent.numSectors(),buf,
                                error_);
    err  = error_;
    return rval;
}

//----------------------------------------------------------------------
// METHOD:  PhysicalVolume::erase()                             --JKH
//
// This method passes an erase request to the DriveDevice object.
//
// ARGS:
//
// PhysExtent& extent  IN  Physical extent specifying start sector and
//                         number of sectors to erase
// Error& err          OUT Error to hold results of operation
//
// RETURNS:  Returns 0 on success, -1 on failure.
//
// PRECONDITIONS:
//
//  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).
//
// POSTCONDITIONS:
//
// --The sectors specified by the argument PhysExtent will be erased.
//
// ERRORS:
//
//  error_None            Function completed successfully.
//  error_DriveDevice_invalid  Invalid DriveDevice bound to PV.
//  error_Bad_erase_size  Extent size is not a multiple of 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. 
//----------------------------------------------------------------------

NSR::INT32
NSR::PhysicalVolume::erase( const PhysExtent& extent, Error& err )
{
    INT32 rval;

    err = error_None;
    if(driveDevp_ == NULL) {            // This object wasn't successfuly constructed!
        err = error_ = error_DriveDevice_invalid;
        return -1;
    }

    rval = driveDevp_->erase(extent.startSector(),extent.numSectors(),error_);
    err  = error_;
    return rval;
}

//----------------------------------------------------------------------
// METHOD:  PhysicalVolume::checkValidity()             --DVM
//
//  This method checks whether the PhysicalVolume in question is
//  valid--i.e., it was successfully constructed.
// 
//  The PhysicalVolume is valid if and only if the DriveDevice to
//  which it is bound is valid.
//
// ARGS:  none
//
// RETURNS:  
// 
// Returns an error with one of the following values:
//
//  error_None:                  object is valid.
//  error_DriveDevice_invalid:   DriveDevice pointer is NULL.
//  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
//                               capacity. 
//
// PRECONDITIONS:   none
// POSTCONDITIONS:  see returns
// ERRORS:          see returns
//----------------------------------------------------------------------

NSR::Error
NSR::PhysicalVolume::checkValidity(void)
{
    if(driveDevp_ == NULL) {
        return Error(error_DriveDevice_invalid);
    }
    return Error(driveDevp_->checkValidity());
}

//----------------------------------------------------------------------
//                     Class SingleSurfaceVolume
//----------------------------------------------------------------------

// Supporting Volume Recognition Sequence
void
NSR::SingleSurfaceVolume::writeVRS(
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

// Supporting Allocation of volume space
void
NSR::SingleSurfaceVolume::allocVolSpace(
    PhysExtent& privdse,
    PhysExtent& rsvvdse,
    PhysExtent& lvidse,
    PhysExtent& parte,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

void
NSR::SingleSurfaceVolume::allocPartSpace(
    LogExtent& fsdse,
    LogExtent& ricbe,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

// Supporting Volume Descriptor Sequence
void
NSR::SingleSurfaceVolume::writeAVDPs(
    const PhysExtent& privdse,
    const PhysExtent& rsvvdse,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

void
NSR::SingleSurfaceVolume::writePVDs(
    const String & volID,
    const String & volSetID,
    PhysExtent & privdse,
    PhysExtent & rsvvdse,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

void
NSR::SingleSurfaceVolume::writePDs(
    Partition * partp,
    PhysExtent & privdse,
    PhysExtent & rsvvdse,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

void
NSR::SingleSurfaceVolume::writeLVDs(
    const String & logVolID,
    const PhysExtent & lvidse,
    const LogExtent & fsdse,
    PhysExtent & privdse,
    PhysExtent & rsvvdse,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

void
NSR::SingleSurfaceVolume::writeOVDs(
    const String & logVolID,
    PhysExtent & privdse,
    PhysExtent & rsvvdse,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

void
NSR::SingleSurfaceVolume::writeUSDs(
    PhysExtent & privdse,
    PhysExtent & rsvvdse,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

void
NSR::SingleSurfaceVolume::writeTDs(
    PhysExtent & privdse,
    PhysExtent & rsvvdse,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

// Supporting File Set Descriptor
void
NSR::SingleSurfaceVolume::writeFSD(
    const String & fileSetID,
    const String & logVolID,
    const LogExtent & rootNodeAddr,
    const LogExtent & fsdse,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

// Supporting the root directory
void
NSR::SingleSurfaceVolume::writeEmptyRootDir(
    LogExtent & rootNodeAddr,
    Error & err )
{
    err = error_SSVOL_No_disk_initialization_yet;
}

//----------------------------------------------------------------------
// METHOD: SingleSurfaceVolume::fromDisk()              --DVM
//
//  Get ALL NSR information from the disk.
//
// ARGS:  none. It uses the device it already has.
//
// NOTES:
//
// This assumes only that a valid drive device is available. First, it
// frees all other objects, and then re-instantiates them as it finds
// descriptors on the disk.
//----------------------------------------------------------------------

void
NSR::SingleSurfaceVolume::fromDisk(Error & err)
{
    // Free all objects allocated by this object.
    free();

#ifdef OLD_CODE_DVM
    // Bail out if drive not valid.
    if ( err = checkDriveValidity() ) return;
#endif
    
    // Local temporary variables:
    ByteArray buf(driveDevp_->sectorSize()); // For all
    const ByteArrayRef baref(buf); // could let it build a temp each
                                   // call to read(). 
    UINT32 validBytes;          // read() methods will return values
    // to here.
    SearchDescriptor sd;        // For reading the sequences
    AnchorVolumeDesc avd;       // For finding the VDS location

    // Try the three AVDP locations and obtain an AVDP.

    const UINT16 AVDLen  = 1;   // number of sectors
    const UINT32 AVDLoc1 = 256;
    const UINT32 AVDLoc2 = driveDevp_->numSectors() - 1;
    const UINT32 AVDLoc3 = AVDLoc2 - 256;

    // AVD code stolen by DVM from nsrArc - 1/19/95
    // Note that it aborts if a drive error occurs, rather than trying
    // another location.

    driveDevp_->read(AVDLoc1,AVDLen,baref,err);
    if ( err ) return;
    avd.fromISO(buf,0,AVDLoc1);
    if ( err = avd.error() ) {
        driveDevp_->read( AVDLoc2,AVDLen,baref,err);
        if ( err ) return;
        avd.fromISO( buf,0,AVDLoc2 );
        if ( err = avd.error() ) {
            driveDevp_->read( AVDLoc3,AVDLen,baref,err);
            if ( err ) return;
            avd.fromISO( buf,0,AVDLoc3 );
            if ( err = avd.error() ) return;
        }
    }

    // Has a valid AVD! 

    const UINT32 MaxReasonableDescLen = 0xffff; // 64KBytes

    // Given that, we can make a real PhysicalVolume.
    // This means that a non-NULL PhysicalVolume pointer can be
    // missing its normal information. Do we REALLY want to do this?

    physVolumep_ = new PhysicalVolume(driveDevp_);
    if ( physVolumep_ == NULL ) {
        err = error_No_free_store;
        return;
    }
    if ( err = physVolumep_->checkValidity() )
        return;

    // Create a PhysExtent holding where the VDS is
    PhysExtent vdsExtent( physVolumep_,0,0 );
    avd.mainVolDescSeqExt.toPhysExtent(physVolumep_,vdsExtent,err );
    if (err)
        return;

    // NOTE that it does NOT read the reserveVDSExtent.

    // Sequence through entire extent, one sector at a time, by
    // shrinking the vdsExtent by a sector each time through.

    for ( ; vdsExtent.numSectors(); vdsExtent.removeStartSector() ) {

        // read next Volume Descriptor
        vdsExtent.read(buf,err);
        if ( err ) return;

        // Use a SearchDescriptor to find out what type of descriptor
        // we have found. Make sure buffer can hold entire descriptor.
        // Note that the buffer can only grow, so the minimum (chosen
        // above) of one sectorSize will be maintained.

        sd.fromISOBlind( (BYTE_PTR) buf );
        if ( sd.descLen() > MaxReasonableDescLen ) {
            err = error_SSVOL_Desc_too_long;
            return;
        } else if ( sd.descLen() > buf.length() ) {
            buf.setLen(sd.descLen());
            vdsExtent.read(buf,err);
            if ( err )
                return;
            sd.fromISOBlind( (BYTE_PTR) buf ); // not REALLY needed, but
            // better safe than
            // sorry...-DVM 
        }

        switch( sd.type() ) {
        case tag_PVD :

            physVolumep_->fromISO(buf,vdsExtent,err); // gets all info from PVD
            if ( err ) return;
            break;

        case tag_PD :
        {                       // New scope defined for Borland compiler...
            // Make up an extent on this physicalVolume.
            PhysExtent partExt(physVolumep_,0,0);
            if ( err = partExt.error() )
                return; // construction errors not strictly needed

            // Now, construct Partition, tying it to the extent invented above.
            partitionp_ = new Partition(partExt);
            if ( partitionp_ == NULL ) {
                err = error_No_free_store;
                return;
            }
            if ( err = partitionp_->error() )
                return; // construction errors

            // Then, reinitialize it from the disk.
            partitionp_->fromISO(buf,vdsExtent,err); // gets size, etc. from PD
            if ( err )
                return;  // fromISO errors

            // NOTE that we are NOT reading in the SpaceResource at
            // this point, because there may be multiple PDs on a
            // disk, each of which may point to a different
            // SpaceResource.

            break;
        }
            
        case tag_LVD :

            // Assumes valid partitionp_, which means PD precedes LVD. May
            // be BIG PROBLEM, since ISO says nothing of the
            // sort...(that I can find...)
            // In order to fix this, we may need to DELAY binding.
            // Real binding should occur THROUGH the
            // PhysicalVolumeSet! 
            // This was our original plan. Hopefully, this works for
            // now...-DVM

            if ( partitionp_ == NULL ) {
                err = error_SSVOL_No_LVD_before_PD_yet;
                return;
            }
            logVolumep_ = new LogicalVolume(partitionp_);
            if ( logVolumep_ == NULL ) {
                err = error_No_free_store;
                return;
            }
            logVolumep_->fromISO(buf,vdsExtent,err); // gets all info from LVD
            if ( err ) return;
            break;

        case tag_IUVD :
        case tag_USD:
            // IGNORE these
            break;
        case tag_VDP:
            // Implement LATER
            err = error_SSVOL_No_multiple_VDS_extents_yet;
            return;
        case tag_TD :
            // This is the normal termination mechanism...
            break;

            // ISO Violations below
        case tag_Unknown:
            err = error_SSVOL_Unknown_desc_in_VDS;
            return;
        default:
            err = error_SSVOL_Illegal_desc_in_VDS;
            return;
        }
        if ( sd.type() == tag_TD )
            break;
    }

    if ( err )                  // ISSUE: Do we want to give up on ALL ISO errors???
        return;
    
    // Now instantiate a SpaceResource object
    spaceResourcep_ = new MasterSpaceResource(*partitionp_);
    if (spaceResourcep_ == NULL) {
        err = error_No_free_store;
        return;
    }
    if (err = spaceResourcep_->error()) {
        return;
    }

    // Now instantiate an Integrity object
    integrityp_ = new Integrity(logVolumep_);
    if (integrityp_ == NULL) {
        err = error_No_free_store;
        return;
    }
    if (err = integrityp_->error()) {
        return;
    }
    // Do a fromDisk() on the Integrity object
    integrityp_->fromDisk(err);
    if (err) {
        return;
    }

    // LATER - We need to set this up, and then use IT to create the
    // TRUE LogicalVolume.
    // 
    // physVolSetp_ = new PhysicalVolumeSet(physVolumep_);
    // 

    // Now get the extent holding the FileSetDescSequence
    LogExtent fsdsExtent = logVolumep_->fsdsExtent(err);
    if (err)
        return;
    
    // Does NOT currently handle chained FSDSes or even multiple-entry
    // FSDSes.

    buf.setLen(partitionp_->blockSize());
    fsdsExtent.read(buf,err);
    if ( err ) return;

    fileSetp_ = new FileSet(logVolumep_);
    if ( fileSetp_ == NULL ) {
        err = error_No_free_store;
        return;
    }
    fileSetp_->fromISO(buf,fsdsExtent,err); // gets all info from FSD
    if (err) {
        return;
    }
    
    // DONE!
    return;

}

//----------------------------------------------------------------------
// METHOD: SingleSurfaceVolume::initDisk  --DVM
//      
//  Initializes a volume with new UDF structures
//
//  This uses the default locations, etc., and lays down a
//  UDF-compliant set of volume structures which describe a file set
//  consisting of a root directory with a single entry (parent, which
//  points to itself). Required arguments are all of the volume
//  labeling information (names).
//
// ARGS:
//  String    volID      IN   The name for the volume.
//  String    volSetID   IN   The name for the volume set.
//  String    logVolID   IN   The name for the logical volume.
//  String    fileSetID  IN   The name for the file set.
//  Error &   error      OUT  Any errors encountered in performing the
//                            operation.
//
// RETURNS:
//  nothing.
// 
// PRE-CONDITIONS:
//  This does NOT destroy any other UDF structures on the volume, so
//  that must be done ahead of time to prevent potentially confusing
//  volume structures.
// 
// POST-CONDITIONS:
//  The disk has been initialized, and the SSVolume object is
//  initialized as well from what was written to disk.
// 
// ERRORS:
//  error_
//  error_
//  error_
// 
// NOTES:
//  This (currently) lays down the format, and then calls the
//  fromDisk() method to initialize its state properly.
//
//----------------------------------------------------------------------
// Private comments go here - will not be extracted into HTML.
//----------------------------------------------------------------------
void
NSR::SingleSurfaceVolume::initDisk( const String & volID,
                                    const String & volSetID,
                                    const String & logVolID,
                                    const String & fileSetID,
                                    Error & err )
{
#ifndef INIT_DISK_SUPPORTED
    err = error_SSVOL_No_disk_initialization_yet;
    return;
#else
    physVolumep_ = new PhysicalVolume(driveDevp_);
    if (physVolumep_ == NULL) {
        err = error_No_free_store;
        return;
    }
    
    // Write a volume recognition sequence
    writeVRS(err);
    if (err) {
        return;
    }
    
    // Allocate Volume Space for
    //   - Primary and Reserve VDS
    //   - LVIDS
    //   - Partition
    PhysExtent primaryVDSExt(physVolumep_,0,0);
    PhysExtent reserveVDSExt(physVolumep_,0,0);
    PhysExtent LVIDSeqExt(physVolumep_,0,0);
    PhysExtent partitionExt(physVolumep_,0,0);
    
    allocVolSpace( primaryVDSExt,
                   reserveVDSExt,
                   LVIDSeqExt,
                   partitionExt,
                   err);
    if (err) {
        return;
    }
    
    // Write AVDPs
    writeAVDPs(primaryVDSExt,
               reserveVDSExt,
               err);
    if (err) {
        return;
    }
    
    // *** Lay out Partition Space ***
    // Initialize SR
    //   - instantiate Partition before SR.
    partitionp_ = new Partition(partitionExt);
    if (partitionp_ == NULL) {
        err = error_No_free_store;
        return;
    }
    if (err = partitionp_->error()) {
        return;
    }
    // Make a LogExtent to hold entire partition.
    LogExtent entirePartition(partitionp_,
                              0,
                              partitionp_->numBlocks());
    if (err = entirePartition.error()) {
        return;
    }
    // Now make a LogExtentList to hold the entirePartition
    LogExtentList allSpace(*logVolumep_);
    if (err = allSpace.error()) {
        return;
    }
    allSpace.append(entirePartition,err);
    if (err) {
        return;
    }
    // Now create the SpaceResource starting with all of the space in
    // the partition.
    spaceResourcep_ = new MasterSpaceResource(*partitionp_,
                                              srDesc_Bitmap,
                                              srSpace_UnerasedOnly,
                                              allSpace);
    if (spaceResourcep_ == NULL) {
        err = error_No_free_store;
        return;
    }
    if (err = spaceResourcep_->error()) {
        return;
    }

    // Allocate Partition Space for
    //   - FSDS
    //   - Root Node
    
    LogExtent FSDSExt;          // null extent
    LogExtent rootICBExt;       // null extent
    
    allocPartSpace( FSDSExt,
                    rootICBExt,
                    err );
    if (err) {
        return;
    }

    // *** Write Both VDSes ***
    // Write PVDs
    writePVDs(volID,
              volSetID,
              primaryVDSExt,
              reserveVDSExt,
              err);
    if (err) {
        return;
    }
    
    // Write PDs (initialized from SR)
    writePDs(partitionp_,
             primaryVDSExt,
             reserveVDSExt,
             err);
    if (err) {
        return;
    }
    
    // Write LVDs with FSDS, LVIDS allocated earlier
    writeLVDs(logVolID,
              LVIDSeqExt,
              FSDSExt,
              primaryVDSExt,
              reserveVDSExt,
              err);
    if (err) {
        return;
    }
              
    // Write OVDs
    writeOVDs(logVolID,
              primaryVDSExt,
              reserveVDSExt,
              err);
    if (err) {
        return;
    }

    // Write Primary USDs (initialized with no space)
    writeUSDs(primaryVDSExt,
              reserveVDSExt,
              err);
    if (err) {
        return;
    }
    
    // Write TDs
    writeTDs(primaryVDSExt,
              reserveVDSExt,
              err);
    if (err) {
        return;
    }


    // Write an empty root directory
    //  Create Root Dir Node
    //  Create DirList w/ single parent entry
    //  Write DirList
    //  Write Root Node
    writeEmptyRootDir(rootICBExt,
                      err);
    if (err) {
        return;
    }

    // *** Write Key updatable structures ***
    // Write the FSD with appropriate info.
    writeFSD(fileSetID,
             logVolID,
             rootICBExt,
             FSDSExt,
             err);
    if (err) {
        return;
    }

    // Write SR
    spaceResourcep_->syncToDisk(err);
    if (err) {
        return;
    }

    // Write the LVIDS (closed)
    integrityp_->uniqueID(16);
    integrityp_->numFiles(0);   // or does it count the directories, too?
    integrityp_->numDirectories(1);
    integrityp_->markClosed();
    integrityp_->toDisk(err);
    if (err) {
        return;
    }
    // Then, to make sure the WEB is woven back the way it should be,
    // read it all in from disk...
    free();
    fromDisk(err);
    if (err) {
        return;
    }
    
    return;
#endif // INIT_DISK_SUPPORTED
}

void
NSR::SingleSurfaceVolume::free()
{
    if ( fileSetp_ != NULL ) {
        delete fileSetp_;
        fileSetp_ = NULL;
    }
    if ( spaceResourcep_ != NULL ) {
        delete spaceResourcep_;
        spaceResourcep_ = NULL;
    }
    if ( integrityp_ != NULL ) {
        delete integrityp_;
        integrityp_ = NULL;
    }
    if ( logVolumep_ != NULL ) {
        delete logVolumep_;
        logVolumep_ = NULL;
    }
    if ( partitionp_ != NULL ) {
        delete partitionp_;
        partitionp_ = NULL;
    }
    if ( physVolSetp_ != NULL ) {
        delete physVolSetp_;
        physVolSetp_ = NULL;
    }
    if ( physVolumep_ != NULL ) {
        delete physVolumep_;
        physVolumep_ = NULL;
    }
}

#ifdef OLD_CODE_DVM
//----------------------------------------------------------------------
// METHOD: SingleSurfaceVolume::error()                 --DVM
//
//  This method returns an Error value corresponding to the results of
//  the last call to the fromDisk() or toDisk() methods.
//
// NOTES:
//
// !!! SSVol::error() - This should be reviewed.
//
// Problems doing ISOSpaceResource::fromISO() is held in the err
// state, while SOME errors MAY BE HELD inside the other objects.
// !!! Potential help would be for the SSVolume object to NOT do the
// fromDisk() on it, but rather allow the user to do that. This is
// closer to viewing the SR as a peer of the SSVolume object, which
// could have some merit. Even the RootICBAddr() model could be
// followed here, or it could be changed to be more like what this
// ends up being. Other options include allowing an ISOSpaceResource
// to be constructed from a reference to an SSVolume, or a Partition,
// which would need to be available from the SSVolume. There may be
// some benefit beyond elegance to allowing the libUser to instantiate
// the SpaceResource. In that case, the methods from the SSVol would
// be to obtain (and set) the LogExtents at which the SpaceResources
// will be located, and to provide these to the SR::to/fromDisk()
// methods. 
//
// Instead of an error() methods, perhaps the SSVol should hold its
// STATE, indicating whether it is ready to be toISO()ed, or ready to
// return a pointer to a valid SpaceResource, etc.
//
// This should NOT be called before a fromISO(), or before all of the
// objects pointed to by the SSVol are initialized, since it will
// simply return one of the "invalid" errors.
//
// ARGUMENTS:  none
//---------------------------------------------------------------------

NSR::Error
NSR::SingleSurfaceVolume::error(void)
{
    if ( err )
                 return err;
    if ( err = checkPhysVolumeValidity() ) return err;
    if ( err = physVolumep_->error() )     return err;
    if ( err = checkPartitionValidity() )  return err;
    if ( err = partitionp_->error() )      return err;
    if ( err = checkLogVolumeValidity() )  return err;
    if ( err = logVolumep_->error() )      return err;
    if ( err = checkFileSetValidity() )    return err;
    if ( err = fileSetp_->error() )        return err;
    return err;
}
#endif
