---------------------------------------------------------------------
  CLASS: Archive     --DVM
  PUBLIC METHODS:
    _GENERAL_
        error()         - return error state
        setDevice()     - supply a device
        openArchive()   - open, supplying volset info
        closeArchive()  - close
    _LABELING_
        getVolumeInfo()       - return info about a vol
        addLabeledVolume()    - add a labeled vol
        isCompleteVolSet()    - True if no missing vols
        getVolSetInfo()       - return info about a volume set
        getRootVolume()       - identify which vol in set is root
        checkOpenIntegrity()  - is logicalVol open?
        markOpenIntegrity()   - mark logicalVol as open
        destroyFileSet()      - render fileset inaccessible
        destroyVolumeLabel()  - make a vol unlabeled
        getWriteProtect()     - return logicalVol status
        setWriteProtect()     - set logicalVol writeprot status
    _GROWING_
        beginGrow()           - start volume set growth process
        addUnlabeledVolume()  - label and add an unlabeled vol
        completeGrow()        - end volume set growth process
    _EXPORT_
        overflowSectors()     - return extra space needed for ICB
        sizeICBTable()        - return space needed for ICBs
        sizeDirectory()       - return space needed for a directory
        setAvailSpace()       - supply free space for a volume
        allocateSeqSectors()  - alloc space for USE,FSD sequences
        allocateICBTable()    - alloc space for ICBTable
        beginExport()         - start export process
        createDirectory()     - create a directory
        addDirEntry()         - add a file or subdir to current dir
        writeDirectory()      - write current dir
        closeDirectory()      - go back to parent dir
        writeRootICB()        - write ICB for root dir
        writeNextICB()        - write next ICB in ICBTable
        completeExport()      - end export process
    _IMPORT_
        getImplementInfo()  - return info on who wrote fileset
        beginImport()       - start import process
        readRootICB()       - read ICB for root dir
        readNextICB()       - read next ICB in table
        endOfICBTable()     - at end of ICB table
        readPath()          - read path for symlink
        locateDirectory()   - seek to and open a dir
        numDirEntries()     - return # entries in current dir
        getFirstDirEntry()  - get first entry in current dir
        getNextDirEntry()   - get next entry in current dir
        getDirEntry()       - get specified entry in current dir
        endOfDirectory()    - at end of current dir
        completeImport()    - end import process
    _DEBUGGING_
        logFile()        - sets file to log to.
        logErrors()      - enables/disables logging of error state.
        logAlgorithms() - enables/disables miscellaneous printf() s
        logCalls()       - enables/disables logging of entries/exits
 
  USAGE NOTES:
   User notes go here.
  
 ----------------------------------------------------------------------

---------------------------------------------------------------------
  METHOD:  Archive::setDevice
       
  ARGS:
       MSDevice dev IN multi-surface device object
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS: 
       The private data member "currentDevice" is set.
  
  ERRORS:
  
  NOTES: import & export
 ----------------------------------------------------------------------
void
NSR::Archive::setDevice( MSDevice *msDev )
{


---------------------------------------------------------------------
  METHOD:  Archive::openArchive 
       
  ARGS:
     UINT16 maxNumLabeledVols IN   maximum number of labeled
                                   volumes in set 
     const char *volSetName   IN   volume set name
     UINT32 sectorSize        IN   logical block size
     MediaType mt             IN   Rewritable or WORM
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS: 
  -  volumeTable is populated with empty volumes. It will be filled
     in later with real volume information via the function
     addVolume(). Note that this implementation will likely change in
     order to allow the 
  -  various other private member variables are set.
 
  ERRORS:
  
  openArchive replaces the pre-alpha createArchive() and
  openArchive().  It requires a vsname, lvname, and a sectorSize, as
  well as the number of volumes in the volume set. The other two
  parameters from createArchive() are moved as follows:
 
  - fsname - beginExport(fsname)
  - fileDataStartSector - addUnlabeledVol(labelLimit)
 
  openArchive now also requires the media type, replacing
  setMediaType().  NOTE that the mediaType and sectorSize are then
  checked against the properties of the media in addVolume().
 
  NOTE that the vsname is simply stored off in a data member, and
  addVolume does not allow any volumes to be added whose vsname does
  not match it.
 
  NOTE ALSO that the sectorSize is used in the calculation of the
  overflow numbers below, inside of openArchive.
 
  - extentsPerICB
  - extentsPerBlock
  - extentsPerUSE
  - extentsPerAED
 
  CONCERNS:
 
  - How will other methods cope without the default openArchive()? Do
    we need to have a private openArchive() with no parms? Surely it
    should be named something else...?
 
 ----------------------------------------------------------------------
void
NSR::Archive::openArchive( UINT16 maxNumLabeledVols,
                           const char *volSetName,
                           UINT32 sectorSize,
                           MediaType mt )
{


---------------------------------------------------------------------
  METHOD:  Archive::closeArchive
       
   Performs clean up operations on archive object.
 
  ARGS:
   NONE.
 
  RETURNS:
  
  PRE-CONDS:  
  
  POST-CONDS: 
     - The icb table is freed.
     - mode = Closed.
 
  ERRORS:
   This clears out any errors.
  
  NOTES:
 ----------------------------------------------------------------------
void
NSR::Archive::closeArchive()
{


---------------------------------------------------------------------
  METHOD:  Archive::getVolumeInfo
       
     Return volume information to the archive user.
 
  ARGS:
     sid_t sid,            IN    surface id of volume
     UINT16 *nvols         OUT   # volumes. > 0 if is or has been root.
     char *vsname          OUT   volume set name (132 bytes)
     char *vname           OUT   volume name (32 bytes)
     UINT16 *vid           OUT   ordinal volume number - 0,1,2,...
     ExtentList *unerased  OUT   unerased space information
     ExtentList *erased    OUT   erased space information
     UINT32 *dataStart     OUT   where file data starts (end of fsd seq + 1)
 
  RETURNS:
     Returns True if volume has a UDF label; otherwise, returns False.
  
  PRE-CONDS:
  
  POST-CONDS:
  
  ERRORS:
  
  NOTES:
   This temporarily changes the mediaType and sectorSize states of
   the Archive object, but then changes them back to what they were.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::getVolumeInfo( sid_t sid,
                             UINT16 *nvols,
                             char *vsname,
                             char *vname,
                             UINT16 *vid,
                             ExtentList *unerasedSpace,
                             ExtentList *erasedSpace,
                             UINT32 *dataStart )
{


---------------------------------------------------------------------
  METHOD: Archive::addLabeledVolume
 
     Adds a volume to the Archive if, after checking its blockSize
     and mediaType, as well as its vsname and its volume number, it
     finds that it fits in the open VolumeSet.
 
     Its mediaType, sectorSize, and volume set name must exactly
     match that of the current volume set, and the volume number must
     be less than or equal to the maxNumLabeledVols parameter
     supplied in the openArchive() method.
 
  ARGS:
     sid   IN  surface identifier
 
  RETURNS:
     void
  
  PRE-CONDS:  
  
  POST-CONDS:
  
  ERRORS:
   This clears out any existent error before doing anything.
  
  NOTES:
  
 ----------------------------------------------------------------------
void
NSR::Archive::addLabeledVolume( sid_t sid )
{


---------------------------------------------------------------------
  METHOD: Archive::isCompleteVolSet  --DVM
       
   This returns True if all labeled volumes in the set have been
   added. This means that there are no missing volumes (i.e., all
   volumes up through the highest-numbered volume have been added),
   and that the current highest-numbered volume is a valid root.
 
  ARGS:
   NONE.
 
  RETURNS:
   See above.
  
  PRE-CONDS:
   openArchive() must have been called.
  
  POST-CONDS:
   May set the error state.
  
  ERRORS:
   error_Empty_volume_set - Not complete if empty
   error_Not_root_volume  - Highest-numbered vol is not a root
   error_Missing_volume   - Holes in set.
  
  NOTES:
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::isCompleteVolSet( void )
{


---------------------------------------------------------------------
  METHOD:  Archive::getVolSetInfo    --AP
       
   This returns a summary of the information currently held by the
   archive object about the current volume set.
 
   Note that if the isComplete field is False, the rest of the
   structure is simply zeroed out. Thus:
 
   Always filled in appropriately:
       expectedVols
       sectorSize
       mediaType
       volSetName
       volList
       isComplete
   Zeroed if volume set incomplete:
       rootSid
       integrityType
       writeProtectType
       fileSetList
   
   
  ARGS:
   VolumeSetInfo *vsip   OUT   all info about volume set
 
  RETURNS: void
  
  PRE-CONDS:
   NONE
  
  POST-CONDS:
   Error may be set, based upon whether or not a valid root volume
   exists. It should return the same errors as isCompleteVolSet().
  
  ERRORS:
   error_
  
  NOTES:
   volInfoList will be fill in only for those volumes which have been
   added to the Archive. Thus, volInfoList.entries() returns how many
   volumes have been added so far.
 
 ----------------------------------------------------------------------
void
NSR::Archive::getVolSetInfo( VolumeSetInfo *vsip ) 
{


---------------------------------------------------------------------
  METHOD: Archive::getRootVolume  --DVM
       
   Returns sid of root volume in Volume Set.
 
  ARGS:
   NONE.
 
  RETURNS:
   The Surface ID of the root volume in the volume table. If an error
   occurs, it returns zero, and sets the Archive object's error.
  
  PRE-CONDS: A complete volume set must be loaded.
  
  POST-CONDS: The error may be set.
  
  ERRORS:
  
  NOTES:
   getRootVolume() now is only informational, and need not be called
   during each export (or import).
 
   The root volume is BY DEFINITION the volume in the set with the
   largest vid or volume number. This method may be used on import,
   labeling, and/or export and simply returns the sid for that
   volume.
 
 ----------------------------------------------------------------------
NSR::sid_t
NSR::Archive::getRootVolume( void )
{


---------------------------------------------------------------------
  METHOD:  Archive::checkOpenIntegrity  --DVM
       
   This is returns True if the currently-loaded volume set is OPEN.
 
   This may be used with a partially-loaded volume set (i.e., with
   only the root volume loaded. To do this, use the REAL volume set
   size for the maxLabeledVols argument in openArchive(), and then
   load it. 
 
  ARGS: none.
 
  RETURNS:
   True if OPEN, False if CLOSED.
  
  PRE-CONDS:
   The root volume must be loaded.
  
  POST-CONDS:
   This sets an error if the highest-numbered volume is not a root
   volume.
  
  ERRORS:
   error_Not_root_volume (returns False)
  
  NOTES:
   This simply returns the value in the volumeTable for the root
   volume.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::checkOpenIntegrity( void )
{


---------------------------------------------------------------------
  METHOD: Archive::destroyFileSet  --DVM
       
   Overwrites the FileSetDescriptor.
 
   This wipes out the file set descriptor on the supplied root volume
   sid. It is used on rewritable when metadata space is re-used, in
   addition to marking a logical volume open.
 
   This is called in addition to markOpenIntegrity() on Rewritable.
 
  ARGS:
   NONE
 
  RETURNS:
   NONE.
  
  PRE-CONDS:
   - The volume set is complete.
   - markOpenIntegrity() has been called.
   - The Media type is NOT Worm.
   - The Logical Volume must NOT be write protected (soft OR hard).
   - The number of file sets found must exactly equal one.
    
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
   This should be called after markOpenIntegrity() on rewritable
   media. It overwrites the FSD with a TD, so that all knowledge of a
   fileset is destroyed on the supplied volume.
 
   Note that it, like markOpenIntegrity(), requires that the archive
   object be open, with a complete volume set.
 
   This returns an error if the media is WORM, and if there is no
   file set descriptor?.
 
 ----------------------------------------------------------------------
void
NSR::Archive::destroyFileSet( void )
{


---------------------------------------------------------------------
  METHOD: Archive::destroyVolumeLabel  --DVM
       
   Nukes NSR information from supplied surface.
 
   This overwrites the AVDPs on the supplied surface, if any
   exist. This essentially makes the disk non-NSR, removing enough
   information that it is no longer recognizable by
   getVolumeInfo().
 
  ARGS:
   sid   IN  surface identifier
 
  RETURNS:
   NONE
  
  PRE-CONDS:
   The archive object must be OPEN, and no exports or imports may be
   in process. Also, the media must not be WORM.
  
  POST-CONDS:
   Volume can now only be added to a volume set via
   addUnlabeledVolume().
  
  ERRORS:
   error_Invalid_function
   error_Wrong_media_type
   error_Wrong_sector_size
   error_Invalid_function_for_media
 
  NOTES:
   This overwrites the AVDPs on the supplied surface, if any
   exist. This essentially makes the disk non-NSR.
  
   This returns an error if the media is WORM.
  
   This must be used with extreme care, in order to prevent
   destroying existing volumeSets. Also note that this is required
   since the addUnlabeledVolume() function requires that a disk be
   non-NSR if it is to label it.
 
 ----------------------------------------------------------------------
void
NSR::Archive::destroyVolumeLabel ( sid_t sid )
{


---------------------------------------------------------------------
  METHOD: Archive::getWriteProtect  --DVM
       
   Returns the write protect status from the current volume.
 
  ARGS: none.
 
  RETURNS:
   Write protect status of current LogicalVolume. Returns Hard
   protect if there are any errors. Thus, should case on error if
   this is the case.
  
  PRE-CONDS: archive must be open.
  
  POST-CONDS: error state may be set.
  
  ERRORS:
   error_Not_root_volume - either unlabelled or non-root volume.
   error_Volume_set_not_supported - Can't handle non-HP??
  
  NOTES:
   Note that this will only work on the currently loaded volume set.
   HOWEVER, this may be called on an incomplete volume set.
 
 ----------------------------------------------------------------------
NSR::WriteProtectType
NSR::Archive::getWriteProtect(void)
{


---------------------------------------------------------------------
  METHOD: Archive::setWriteProtect  --DVM
       
   This sets the LogicalVolume's write protect to the supplied value.
 
   setWriteProtect() enforces the limitations OSTA places on
   hardWriteProtect never being altered, returning an appropriate
   error if requests cannot be satisfied.
 
  ARGS:
   wp       IN  desired write protect status
 
  RETURNS:
   NONE.
  
  PRE-CONDS: archive object must be open.
  
  POST-CONDS: error state may be set.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
   Note that this operates upon the current volume set, which must
   have its root volume loaded. It may or may not work unless all
   volumes are loaded into the volume set.
 
 ----------------------------------------------------------------------
void
NSR::Archive::setWriteProtect( WriteProtectType wp )
{


---------------------------------------------------------------------
  METHOD: Archive::beginGrow  --DVM
       
   This initiates the volume set growth process.
 
   Complete VolSets and empty VolSets may be grown, by adding
   unlabeled volumes to them. This must be called beforehand. See
   completeGrow() also.
 
  ARGS: none.
 
  RETURNS: none.
  
  PRE-CONDITIONS:
   A complete volume set must be loaded, and the Archive object must
   not be importing or exporting. The current volume set may be
   either open or closed, as completeGrow() will propagate that state
   onto the grown volume set. See markOpenIntegrity() and
   checkOpenIntegrity().
  
  POST-CONDITIONS:
   The volume table is cleaned out of any empty entries and the
   volume set is in groth mode, or an error state may be set.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
   This holds the current root information, so that the copying of
   the fileset information may be performed by completeGrow().
 
 ----------------------------------------------------------------------
void
NSR::Archive::beginGrow( void ) 
{


---------------------------------------------------------------------
  METHOD: Archive::addUnlabeledVolume
 
     Labels a volume and adds it to the volume set.
  
     Labels the supplied volume with the supplied name, and adds it
     to the current volume set. The next available vid is assigned to
     it, as is the current volume set name. Note that it must have
     the same sectorSize and media type as the current volume set in
     order to be added. Thus, it grows the volume set by one volume.
 
     The volume name must be unique within the volume set, so if it
     is not unique, an error will be returned.
 
     The label contains free space information also, so the initial
     free space must be supplied to this method as well. Note that
     the free space information is resupplied to the Archive object
     on every export, but also note that this is the free space
     information which will be returned on getVolumeInfo() and
     getVolSetInfo() until an export is performed.
 
     The limit for how many sectors may be used by the label
     information at the beginning of media must also be
     supplied. The labeling process itself will not necessarily use
     all of the available space, but rather round this limit down to
     where the difference between it and 257 is a multiple of
     four. This number must be greater than or equal to 297, or an
     error will be returned.
 
     For example:
 
         labelLimit = 296 => ERROR
         labelLimit = 297 => used = 297
         labelLimit = 298 => used = 297
         labelLimit = 299 => used = 297
         labelLimit = 300 => used = 297
         labelLimit = 301 => used = 301
 
  ARGS:
     sid_t      sid           IN  surface identifier
     char      *volName       IN  volume name
     UINT32     labelLimit    IN  Maximum number of sectors allowed
                                  for label information at beginning
                                  of media.
     ExtentList unerasedSpace IN  Free space after numLabeledSectors
     ExtentList erasedSpace   IN  Free space after numLabeledSectors
 
  RETURNS:
     void
  
  PRE-CONDS:
     beginGrow() must have been called before this is called.
  
  POST-CONDS:
     The volume set is grown by one, but the root information has not
     yet been copied to it.
  
  ERRORS:
     Any of several device errors may be set, plus the following:
 
       error_Invalid_function
       error_Wrong_media_type
       error_Wrong_sector_size
       error_Volume_already_labeled
  
  NOTES:
     
 ----------------------------------------------------------------------
void
NSR::Archive::addUnlabeledVolume ( sid_t sid,
                                   const char *volName,
                                   UINT32 labelLimit,
                                   const ExtentList & unerasedSpace,
                                   const ExtentList & erasedSpace )
{


---------------------------------------------------------------------
  METHOD: Archive::completeGrow  --DVM
       
   This completes the volume set growth process.
 
   This writes root volume information on the current
   highest-numbered volume, including copying any fileset history
   from the prior root to the new root. This must be called before
   performing import or export, once beginGrow() has been
   called. The grown volume set is marked the same as the volume set
   was marked during beginGrow().
 
  ARGS: none.
 
  RETURNS: none.
  
  PRE-CONDITIONS:
   beginGrow() must have been called.
  
  POST-CONDITIONS:
   The Volume Set how has a valid root volume, although its integrity
   is still OPEN.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
void
NSR::Archive::completeGrow( void ) 
{


---------------------------------------------------------------------
  METHOD:  Archive::overflowSectors(noExtents)
       
   Calculates the number of sectors required in addition to a file
   ICB in order to record all its extents, or its path information.
 
   Called during export to determine the number of overflow sectors
   required when writing an FE. It is a f(number of extents) used in
   storing the file data. The alternate form is an f(pathlist) used
   in storing a symbolic link.
 
  ARGS:
    UINT32           noExtents IN  The number of extents associated with
                                   this file.
    PathElementList  pList     IN  The path for the symbolic link. 
 
  RETURNS:
   The number of additional sectors required for the file/symlink.
  
  PRE-CONDS:
  
  POST-CONDS:
  
  ERRORS:
   error_Invalid_function
   error_No_free_store
  
  NOTES:
   
 ----------------------------------------------------------------------
NSR::UINT32
NSR::Archive::overflowSectors(UINT32 noExtents)
{
NSR::UINT32 
NSR::Archive::overflowSectors( const PathElementList &pList )
{


---------------------------------------------------------------------
  METHOD:  Archive::sizeICBTable
       
  ARGS:
     UINT32 nDirs    IN    number of directories, including root
     UINT32 nFiles   IN    number of files
     UINT32 ovf      IN    number of overflow sectors
 
  RETURNS:
     number of sectors required to write the icb's (fileEntries).
  
  PRE-CONDS:  
  
  POST-CONDS: 
  
  ERRORS:
  
  NOTES:
   Used during export.
 
  Calculations are valid, regardless of mode.
  ISSUE: should figure in some amount for the icb table ea in the
         root ICB. It is a non-embedded EA, so for now, assume it is
         2 sectors. That gives 1 sector for the EAFE and  1 sector for
         the EA. Worst case for 512 byte media:
             512
            - 24     ea header
            - 48     impl use ea fields
            ====     
             440     bytes for icb table 
 
         Each extent (long_ad) is 16 bytes, so there is room for
            440/16 = 27 extents. This should be enough.
 
 ----------------------------------------------------------------------
NSR::UINT32 
NSR::Archive::sizeICBTable( UINT32 nDirs, UINT32 nFiles, UINT32 nOvfl )
{


---------------------------------------------------------------------
  METHOD:  Archive::sizeDirectory
       
     Calculated sectors required to write a directory.
 
  ARGS:
     UINT32 nentries,         IN    number of files and dirs in this directory
     UINT32 sum_name_len      IN    sum of strlen() over all entries
 
  RETURNS:
     number of sectors required to write the directory
  
  PRE-CONDS:  
  
  POST-CONDS: 
  
  ERRORS:
  
  NOTES:  export
 ----------------------------------------------------------------------
NSR::UINT32 
NSR::Archive::sizeDirectory( UINT32 nEntries, UINT32 sumNameLen ) 
{


---------------------------------------------------------------------
  METHOD: Archive::setAvailSpace  --DVM
       
   Supplies partition parameters to the Archive object (for Export)
 
   This must be called for all volumes in the set, before
   allocateICBTable() are called.  may be called immediately after a
   labeled or unlabeled volume is added, if desired.
 
  ARGS:
   sid            IN  Surface Identifier
   unerased       IN  list of unerased space (after labelLimit)
   erased         IN  list of erased space (after labelLimit)
 
  RETURNS:
   
  
  PRE-CONDS:  (E.g., assumes)
   List_preconds
  
  POST-CONDS: (E.g., changes to class data members)
   List_postconds
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
   The PDs/USEs are not written until each volume is visited later in
   the export process. This is because the USEs cannot be written
   until after space used by NSR has been removed first.
 
 ----------------------------------------------------------------------
void
NSR::Archive::setAvailSpace ( sid_t sid,
                              ExtentList &unerased,
                              ExtentList &erased )
{


---------------------------------------------------------------------
  METHOD:  Archive::allocateSeqSectors
       
   Allocates extents to be used during the export process for
   extending sequences into the partition space. 
 
   This may be used for extending the USE sequence or the file set
   descriptor sequence. Space is allocated here and used later, as
   needed, when writing the USE or FSD sequence.
 
  ARGS:
 
  RETURNS:
      The total amount of free space left on all the volumes.
 
  PRE-CONDS:  
  
  POST-CONDS: 
  
  ERRORS:
     error_End_of_Sequence_space
  
  NOTES: export
 ----------------------------------------------------------------------
NSR::UINT32
NSR::Archive::allocateSeqSectors()
{


---------------------------------------------------------------------
  METHOD:  Archive::allocateICBTable
       
   Allocate space from the volume for use in writing the icb information.
 
 
  ARGS:
     UINT32 nodirs  IN    number of directories to export (incl root)
     UINT32 files   IN    number of files to export
     UINT32 ovf     IN    number of overflow sectors calculated
 
  RETURNS: none.
  
  PRE-CONDS:  
  
  POST-CONDS: error may be set.
  
  ERRORS:
  
  NOTES:
     Also allocates space for use in writing an "icb extent" table.
     If there is any netware information associated with the root
     icb, space for it is also calculated.
 
     Allocates space for the ICB table.  The total amount of space
     equals the number directories + files + overflow sectors + one
     for each extent allocated for the ICB table.  For WORM, a
     verification is done on the extents allocated to ensure that the
     icb's will be located properly.
 ----------------------------------------------------------------------
void
NSR::Archive::allocateICBTable( UINT32 nodirs, UINT32 files, UINT32 ovf)
{


---------------------------------------------------------------------
  METHOD: Archive::beginExport  --DVM
       
   This begins the export process, naming the fileset.
 
  ARGS:
   fsname   IN  File Set Name
 
  RETURNS:
   NONE.
  
  PRE-CONDS:
   - A complete volume set must be loaded.
   - The Logical Volume must not be write-protected (soft OR hard).
   - markOpenIntegrity() must have been called.
   - setAvailSpace() must have been called for all vols with space.
   - WORM - All file sets are hard write-protected.
   - else - No file sets exist.
  
  POST-CONDS:
   Either the operation is successful, placing the Archive object
   into export mode, or an error state will be set.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
void
NSR::Archive::beginExport( const char *fsname )
{


---------------------------------------------------------------------
  METHOD:  Archive::createDirectory
       
   Creates a directory. 
 
   Creates the subdirectory specified by dname for the current
   directory.  The entry for the subdirectory must already exist by
   using addDirectory().  Also, the current directory must already
   have been written to disk.
 
   If no current Directory exists, and no value is given for dname,
   the root directory is created.
 
  ARGS:
     char *dname    IN    The name of the directory.
 
  RETURNS:
  
  PRE-CONDS:  
  
  POST-CONDS: 
 
     currentDirectory: newly created directory.
 
     directoryStack:   | currentDirectory (prev)   |<-- top of stack
                       |---------------------------|
                       | currentDirectory (prev-1) |<-- top of stack - 1
  
  ERRORS:
     error_Directory_not_found: Directory to be created not found in 
         currentDirectory.
  
  NOTES: export
 ----------------------------------------------------------------------
void
NSR::Archive::createDirectory( char *dname )
{


---------------------------------------------------------------------
  METHOD:  Archive::addDirEntry
       
   Add a file or subdirectory to the contents of currentDirectory.
 
  ARGS:
     Boolean  isSubDir IN  Whether is a subdirectory or a file.
     char    *name     IN  name of file or subdirectory
     UINT32   ovf      IN  overflow sectors for file or subdirectory
 
  RETURNS:
  
  PRE-CONDS:  
  
  POST-CONDS: 
  
  ERRORS:
     error_Name_not_unique
  
  NOTES:
   Overflow sectors are normally only for files, but if
   extendedAttributes are attached to a directory, ovfl will be
   non-zero. 
 ----------------------------------------------------------------------
void
NSR::Archive::addDirEntry( Boolean isSubDir, char *name, UINT32 ovf )
{


---------------------------------------------------------------------
  METHOD:  Archive::writeDirectory
       
   Write currentDirectory to disk.
 
   Dumps info about current directory, the sets current directory to
   parent.
 
  ARGS:
     Extent *ext    OUT    Location where currentDirectory was written.
                           Used in writing the ICB.
 
  RETURNS:
  
  PRE-CONDS:
  
  POST-CONDS:
     currentDirectory: marked clean
  
  ERRORS:
  
  NOTES: export
 ----------------------------------------------------------------------
void
NSR::Archive::writeDirectory( Extent *ext )
{


---------------------------------------------------------------------
  METHOD:  Archive::closeDirectory
       
   User is done with this directory. Pop the directory stack.
 
  ARGS:
 
  RETURNS:
  
  PRE-CONDS:  
  
  POST-CONDS: 
     currentDirectory: directoryStack.pop();
     
     directoryStack:   | currentDirectory (prev-1) |<-- top of stack
                       |---------------------------|
                       | currentDirectory (prev-2) |<-- top of stack - 1
  
  ERRORS:
     error_Directory_dirty
  
  NOTES: export
 ----------------------------------------------------------------------
void
NSR::Archive::closeDirectory()
{


---------------------------------------------------------------------
  METHOD:  Archive::writeRootICB
       
   Write the ICB for the root directory.
 
   This will also write the ICB table - the list of extents used
   in writing the meta-data information for this volume set. This
   set of locations is later read during import of the volume set.
 
   Writes the ICB for the root directory at the first
   location on the ICB table.  It then sets the ICB pointer
   to the next ICB.
 
  ARGS:
     Attributes rootAttr  IN    Attributes for the root directory.
     Extent rootExt       IN    Where the root directory is located.
 
  RETURNS: none.
  
  PRE-CONDS:  
  
  POST-CONDS: 
   icbTable is bumped by 1 + #sectors used in writing the EA for the
   Root ICB.
 
  ERRORS:
   error_Invalid_function
   error_No_free_store
  
  NOTES: export
 ----------------------------------------------------------------------
void
NSR::Archive::writeRootICB( Attributes &rootAttr,
                            const Extent &rootExt )
{


---------------------------------------------------------------------
  METHOD:  Archive::writeNextICB
       
   Write a non-root ICB.
 
   Verifies that the file id matches next ICB to be written.  Writes
   the next ICB and any required overflow sectors at the ICB pointer
   location on the ICB table.  It then increments the ICB pointer to
   the next available ICB.
 
  ARGS:
     Attributes &attr            IN    Attributes for this file or dir.
     const ExtentList &extList   IN    Locations of file data or directory.
     PathElementList pList       IN    The symbolic link information.
     Extent EAICBAddr            IN    Location of ICB for EAs associated
                                       with file or directory.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
   The icb table address is bumped the appropriate amount.
   endICBTable: may be set.
  
  ERRORS:
  
  NOTES: export
 ----------------------------------------------------------------------
void
NSR::Archive::writeNextICB( Attributes &attr,
                            const ExtentList &extList )
{
void
NSR::Archive::writeNextICB( Attributes attr,
                            PathElementList pList ) 
{
void
NSR::Archive::writeNextICB( Attributes &attr,
                            const ExtentList &extList,
                            const Extent &EAICBAddr )
{
void
NSR::Archive::writeNextICB( const Attributes &attr,
                            const PathElementList &pList,
                            const Extent &EAICBAddr )
{


---------------------------------------------------------------------
  METHOD:  Archive::completeExport
       
   Performs all actions appropriate for end of export.
 
   Writes nsr volume information on all volumes. Writes a closed
   integrity entry.
 
  ARGS: none.
 
  RETURNS: none.
  
  PRE-CONDS: 
  
  POST-CONDS: 
  
  ERRORS:
  
  NOTES:
 
   Updates each volume still marked dirty with space
   information. Then, writes FSD, LVID on root, and closes the
   logical volume. 
 
 ----------------------------------------------------------------------
void
NSR::Archive::completeExport()
{


---------------------------------------------------------------------
  METHOD: Archive::getImplementInfo  --DVM
       
   This reads the ImplementationRegID from the root ICB, and returns
   that information, so that the user may know for sure which user of
   this code wrote the file set.
 
   If fsNum is not supplied, the default file set information is
   provided. 
 
  ARGS:
   fsNum                IN   Which file set being checked.
   implementationInfo   OUT  Information about implementation
 
  RETURNS:
   void
  
  PRE-CONDS:
   Must have a complete volume set
  
  POST-CONDS:
   NONE
  
  ERRORS:
   error_Invalid_function - not in proper mode.
   various device errors are possible as well.
  
  NOTES:
   
 
 ----------------------------------------------------------------------
void
NSR::Archive::getImplementInfo( ImplementationInfo * implInfo ) 
void
NSR::Archive::getImplementInfo( INT32 fsnum, ImplementationInfo * implInfo ) 
{


---------------------------------------------------------------------
  METHOD:  Archive::beginImport
     
   Begin the import process.
 
  ARGS:
 
   UINT16      fsnum    IN    Import this file set number. If this is
                              not supplied, the last fileSet found
                              will be imported. Note that File sets
                              are numbered  0,1,2,...
   ExtentList *icbSpace OUT   The space occupied by the fileset's
                              ICBs. This may be spread over many
                              volumes. This is made available, so
                              that in the case of Rewritable, this
                              space may be reclaimed. This does NOT
                              include extents used in writing the
                              directories.
 
  RETURNS: none
  
  PRE-CONDS: A valid complete volume set is loaded.
  
  POST-CONDS: The error state may be set.
  
  ERRORS:
   error_
  
  NOTES: import 
 ----------------------------------------------------------------------
void
NSR::Archive::beginImport( ExtentList *icbSpace )
{
void
NSR::Archive::beginImport( INT32 fsnum )
{
void
NSR::Archive::beginImport( void )


---------------------------------------------------------------------
  METHOD:  Archive::readRootICB
       
   Read the root ICB.
 
   This must be the first ICB read, as the icb table is located here.
 
  ARGS:
     Attributes *rootDirAttr      OUT  Attributes for root directory.
     Extent *rootDirExt           OUT  Location of root directory.
     ImplementationInfo *implInfo OUT  Implementation Information.
     ExtentList *EAExtList        OUT  Locations holding EAs.
 
  RETURNS:
  
  PRE-CONDS: 
     rootICB must be set to the location of the root ICB. This is done in
     beginImport().
 
  POST-CONDS:
   Sets the ICB pointer to the next ICB.
  
  ERRORS:
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
void
NSR::Archive::readRootICB( Attributes *rootDirAttr,
                           Extent *rootDirExt )
{


---------------------------------------------------------------------
  METHOD:  Archive::readNextICB
       
   Read the next ICB.
 
   Reads the next ICB from the ICB table. Also increments the 
   ICB pointer to the next ICB.  
 
   If extList returns NULL, the next ICB was not on the current
   volume.  The next volume is then specified by addr.
 
  ARGS:
     Attributes *attr           OUT    Attributes for next directory.
     ExtentList *extList        OUT    Location of next directory.
     Extent     *EAICBAddr      OUT    Location of ICB for EAs.
 
  RETURNS:
       -1    The end of the ICB table has been reached.
        0    The end of the ICB table has not been reached.
 
  PRE-CONDS: 
 
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 
 ----------------------------------------------------------------------
int
NSR::Archive::readNextICB( Address    *addr,
                           Attributes *attr,
                           ExtentList *extList )
{
int
NSR::Archive::readNextICB( Address    *addr,
                           Attributes *attr,
                           ExtentList *extList,
                           Extent     *EAICBAddr )
{


---------------------------------------------------------------------
  METHOD: Archive::endOfICBTable  --AP
       
   Returns True if at end of ICB Table.
 
  ARGS:
   NONE
 
  RETURNS:
  
  PRE-CONDS:
  
  POST-CONDS:
  
  ERRORS:
  
  NOTES:
 
 ----------------------------------------------------------------------
NSR::Boolean 
NSR::Archive::endOfICBTable() 
{


---------------------------------------------------------------------
  METHOD:  Archive::readPath
 
     Read the path information for a symbolic link.
 
  ARGS:
     ExtentList &elist,       IN    Locations for path information
     PathElementList *pList   OUT   The path information.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import 
 ----------------------------------------------------------------------
void
NSR::Archive::readPath( ExtentList &elist,
                        PathElementList *pList ) 
{


---------------------------------------------------------------------
  METHOD:  Archive::locateDirectory
 
   Attempts to find the specified directory on the current volume in
   the device.
 
   If found, sets the current directory to that specified 
 
  ARGS:
     Extent ext    IN    Location of the directory.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 
   Locates a directory and populates currentDirectory with its
   contents. 
 ----------------------------------------------------------------------
void
NSR::Archive::locateDirectory( Extent ext )
{


---------------------------------------------------------------------
  METHOD: Archive::numDirEntries  --AP
       
   Returns the number of entries in the current directory.
 
  ARGS:
   NONE
 
  RETURNS:
   See above.
  
  PRE-CONDS:
   Assumes that a directory has been properly found using
   locateDirectory(). 
  
  POST-CONDS:
   NONE.
  
  ERRORS:
   error_
   error_
  
  NOTES:
 ----------------------------------------------------------------------
NSR::UINT32
NSR::Archive::numDirEntries()
{


---------------------------------------------------------------------
  METHOD: Archive::getFirstDirEntry
       
   Reads the first entry in the current directory. Sets the entry
   pointer to the next directory entry.
 
  ARGS:
   Boolean *dir   OUT  Entry is a subdirectory
   char    *name  OUT  name of the dir entry.
   Address *addr  OUT  ICB addr of the entry.
 
  RETURNS:
   void
  
  PRE-CONDS:
   Assumes that a directory has been properly found using
   locateDirectory(). 
  
  POST-CONDS:
   Pointer set to second entry, ready for getNextDirEntry().
  
  ERRORS:
   error_
   error_
  
  NOTES:
   
 
 ----------------------------------------------------------------------
void
NSR::Archive::getFirstDirEntry( Boolean *dir,
                                char *name,
                                Address *addr )
{


---------------------------------------------------------------------
  METHOD: Archive::getNextDirEntry
       
   Reads the next entry in the current directory. Sets the entry
   pointer to the next directory entry.
 
  ARGS:
   Boolean *dir   OUT  Entry is a subdirectory
   char    *name  OUT  name of the dir entry.
   Address *addr  OUT  ICB addr of the entry.
 
  RETURNS:
    The return value should probably be ignored. Look instead at the
    error() return value after calling this.
  
  PRE-CONDS:
   Assumes that a directory has been properly found using
   locateDirectory(). 
  
  POST-CONDS:
   Pointer set to next entry.
  
  ERRORS:
   error_
   error_
  
  NOTES:
   
 
 ----------------------------------------------------------------------
int
NSR::Archive::getNextDirEntry( Boolean *dir,
                               char *name,
                               Address *addr )
{


---------------------------------------------------------------------
  METHOD: Archive::getDirEntry
       
   Reads the specified entry in the current directory. Sets the entry
   pointer to the next directory entry.
 
  ARGS:
   Boolean *dir   OUT  Entry is a subdirectory
   char    *name  OUT  name of the dir entry.
   Address *addr  OUT  ICB addr of the entry.
 
  RETURNS:
   void
  
  PRE-CONDS:
   Assumes that a directory has been properly found using
   locateDirectory(). 
  
  POST-CONDS:
   Pointer set to next entry.???
  
  ERRORS:
   error_
   error_
  
  NOTES:
   
 
 ----------------------------------------------------------------------
void
NSR::Archive::getDirEntry( unsigned i,
                           Boolean *dir,
                           char *name,
                           Address *addr )
{


---------------------------------------------------------------------
  METHOD: Archive::endOfDirectory -- AP
       
   Returns True if all entries in the current directory are
   exhausted. 
 
  ARGS:
 
  RETURNS:
   See above.
  
  PRE-CONDS:
   Assumes that a directory has been properly found using
   locateDirectory(). 
  
  POST-CONDS:
   NONE.
  
  ERRORS:
   error_
   error_
  
  NOTES:
   
 
 ----------------------------------------------------------------------
NSR::Boolean 
NSR::Archive::endOfDirectory() 
{


---------------------------------------------------------------------
  METHOD:  Archive::completeImport
   Cleans up the state of the archive object.
 
  ARGS:
   NONE.
 
  RETURNS:
   void
  
  PRE-CONDS: 
   NONE.
  
  POST-CONDS:
   The archive object is left ready to grow a volume group, export,
   or import.
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::completeImport()
{


---------------------------------------------------------------------
  METHOD: Archive::logFile  --DVM
       
   Sets the file to send logging output to.
 
  ARGS:
   FILE * lf  IN   File pointer.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Any logging will go to the supplied file.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),"getVolumeInfo() Unerased Space :\n");
        unerasedSpace->fprint(Trace::logFile());
        fprintf(Trace::logFile(),"getVolumeInfo() Erased Space :\n");
        erasedSpace->fprint(Trace::logFile());
    }
    
      Now determine dataStart
    if (dataStart) {
          Calculate from USEs whether root or not.
        PartitionAreaCalc partAreas(logicalBlockSize,
                                    media,
                                    uICBAddr,
                                    eICBAddr);
        *dataStart = partAreas.fileDataStart();
    }
      Restore prior values.
    media             = savedMT ;
    logicalSectorSize = savedLSS;
    logicalBlockSize  = savedLBS;
    return True;
}

 ----------------------------------------------------------------------
  METHOD: Archive::addLabeledVolume
 
     Adds a volume to the Archive if, after checking its blockSize
     and mediaType, as well as its vsname and its volume number, it
     finds that it fits in the open VolumeSet.
 
     Its mediaType, sectorSize, and volume set name must exactly
     match that of the current volume set, and the volume number must
     be less than or equal to the maxNumLabeledVols parameter
     supplied in the openArchive() method.
 
  ARGS:
     sid   IN  surface identifier
 
  RETURNS:
     void
  
  PRE-CONDS:  
  
  POST-CONDS:
  
  ERRORS:
   This clears out any existent error before doing anything.
  
  NOTES:
  
 ----------------------------------------------------------------------

void
NSR::Archive::addLabeledVolume( sid_t sid )
        fprintf(Trace::logFile(),"getVolumeInfo() Unerased Space :\n");
        unerasedSpace->fprint(Trace::logFile());
        fprintf(Trace::logFile(),"getVolumeInfo() Erased Space :\n");
        erasedSpace->fprint(Trace::logFile());
    }
    
      Now determine dataStart
    if (dataStart) {
          Calculate from USEs whether root or not.
        PartitionAreaCalc partAreas(logicalBlockSize,
                                    media,
                                    uICBAddr,
                                    eICBAddr);
        *dataStart = partAreas.fileDataStart();
    }
      Restore prior values.
    media             = savedMT ;
    logicalSectorSize = savedLSS;
    logicalBlockSize  = savedLBS;
    return True;
}

 ----------------------------------------------------------------------
  METHOD: Archive::addLabeledVolume
 
     Adds a volume to the Archive if, after checking its blockSize
     and mediaType, as well as its vsname and its volume number, it
     finds that it fits in the open VolumeSet.
 
     Its mediaType, sectorSize, and volume set name must exactly
     match that of the current volume set, and the volume number must
     be less than or equal to the maxNumLabeledVols parameter
     supplied in the openArchive() method.
 
  ARGS:
     sid   IN  surface identifier
 
  RETURNS:
     void
  
  PRE-CONDS:  
  
  POST-CONDS:
  
  ERRORS:
   This clears out any existent error before doing anything.
  
  NOTES:
  
 ----------------------------------------------------------------------

void
NSR::Archive::addLabeledVolume( sid_t sid )
        unerasedSpace->fprint(Trace::logFile());
        fprintf(Trace::logFile(),"getVolumeInfo() Erased Space :\n");
        erasedSpace->fprint(Trace::logFile());
    }
    
      Now determine dataStart
    if (dataStart) {
          Calculate from USEs whether root or not.
        PartitionAreaCalc partAreas(logicalBlockSize,
                                    media,
                                    uICBAddr,
                                    eICBAddr);
        *dataStart = partAreas.fileDataStart();
    }
      Restore prior values.
    media             = savedMT ;
    logicalSectorSize = savedLSS;
    logicalBlockSize  = savedLBS;
    return True;
}

 ----------------------------------------------------------------------
  METHOD: Archive::addLabeledVolume
 
     Adds a volume to the Archive if, after checking its blockSize
     and mediaType, as well as its vsname and its volume number, it
     finds that it fits in the open VolumeSet.
 
     Its mediaType, sectorSize, and volume set name must exactly
     match that of the current volume set, and the volume number must
     be less than or equal to the maxNumLabeledVols parameter
     supplied in the openArchive() method.
 
  ARGS:
     sid   IN  surface identifier
 
  RETURNS:
     void
  
  PRE-CONDS:  
  
  POST-CONDS:
  
  ERRORS:
   This clears out any existent error before doing anything.
  
  NOTES:
  
 ----------------------------------------------------------------------

void
NSR::Archive::addLabeledVolume( sid_t sid )
        fprintf(Trace::logFile(),"getVolumeInfo() Erased Space :\n");
        erasedSpace->fprint(Trace::logFile());
    }
    
      Now determine dataStart
    if (dataStart) {
          Calculate from USEs whether root or not.
        PartitionAreaCalc partAreas(logicalBlockSize,
                                    media,
                                    uICBAddr,
                                    eICBAddr);
        *dataStart = partAreas.fileDataStart();
    }
      Restore prior values.
    media             = savedMT ;
    logicalSectorSize = savedLSS;
    logicalBlockSize  = savedLBS;
    return True;
}

 ----------------------------------------------------------------------
  METHOD: Archive::addLabeledVolume
 
     Adds a volume to the Archive if, after checking its blockSize
     and mediaType, as well as its vsname and its volume number, it
     finds that it fits in the open VolumeSet.
 
     Its mediaType, sectorSize, and volume set name must exactly
     match that of the current volume set, and the volume number must
     be less than or equal to the maxNumLabeledVols parameter
     supplied in the openArchive() method.
 
  ARGS:
     sid   IN  surface identifier
 
  RETURNS:
     void
  
  PRE-CONDS:  
  
  POST-CONDS:
  
  ERRORS:
   This clears out any existent error before doing anything.
  
  NOTES:
  
 ----------------------------------------------------------------------

void
NSR::Archive::addLabeledVolume( sid_t sid )
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"addLabeledVol() Unerased Space :\n");
            vol.freeSpace.fprint(Trace::logFile());
            fprintf(Trace::logFile(),"addLabeledVol() Erased Space :\n");
            vol.erasedFreeSpace.fprint(Trace::logFile());
        }

          Now determine dataStart
        PartitionAreaCalc partAreas(logicalBlockSize,
                                    media,
                                    uICBAddr,
                                    eICBAddr);
        vol.fileDataStart = partAreas.fileDataStart();

          If it has an LVID Sequence, read it in.
        if ( vol.lvNumVols != 0 ) {
            LogicalVolumeIntegrityDesc *lvid = NULL;
            int nFileSets = 0;
              Read the LVID sequence.
            readLVIDSeq( vol, &lvid, &nFileSets );
            if (err) {
                delete lvid;
                return;
            }
              Fill in volume fields.
            vol.lvIntegrity        = lvid->integrityType();
            vol.lvIntegImplementID = lvid->implementID.id();
            delete lvid;
        }
        
          Add the volume into the table.
        volumeTable[vol.vid] = vol;
        if ( err = volumeTable.error() ) {
            return;
        }
    } else {
        if (hasAVD) {
            if (!err) {
                  volume not written by hp.
                err = error_Wrong_volume_set;
            }
            return;
        } else {
            err = error_Volume_not_labeled;   Use addUnlabeledVolume()
        }
    }
}

 ----------------------------------------------------------------------
  METHOD: Archive::isCompleteVolSet  --DVM
       
   This returns True if all labeled volumes in the set have been
   added. This means that there are no missing volumes (i.e., all
   volumes up through the highest-numbered volume have been added),
   and that the current highest-numbered volume is a valid root.
 
  ARGS:
   NONE.
 
  RETURNS:
   See above.
  
  PRE-CONDS:
   openArchive() must have been called.
  
  POST-CONDS:
   May set the error state.
  
  ERRORS:
   error_Empty_volume_set - Not complete if empty
   error_Not_root_volume  - Highest-numbered vol is not a root
   error_Missing_volume   - Holes in set.
  
  NOTES:
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::isCompleteVolSet( void )
            fprintf(Trace::logFile(),"addLabeledVol() Unerased Space :\n");
            vol.freeSpace.fprint(Trace::logFile());
            fprintf(Trace::logFile(),"addLabeledVol() Erased Space :\n");
            vol.erasedFreeSpace.fprint(Trace::logFile());
        }

          Now determine dataStart
        PartitionAreaCalc partAreas(logicalBlockSize,
                                    media,
                                    uICBAddr,
                                    eICBAddr);
        vol.fileDataStart = partAreas.fileDataStart();

          If it has an LVID Sequence, read it in.
        if ( vol.lvNumVols != 0 ) {
            LogicalVolumeIntegrityDesc *lvid = NULL;
            int nFileSets = 0;
              Read the LVID sequence.
            readLVIDSeq( vol, &lvid, &nFileSets );
            if (err) {
                delete lvid;
                return;
            }
              Fill in volume fields.
            vol.lvIntegrity        = lvid->integrityType();
            vol.lvIntegImplementID = lvid->implementID.id();
            delete lvid;
        }
        
          Add the volume into the table.
        volumeTable[vol.vid] = vol;
        if ( err = volumeTable.error() ) {
            return;
        }
    } else {
        if (hasAVD) {
            if (!err) {
                  volume not written by hp.
                err = error_Wrong_volume_set;
            }
            return;
        } else {
            err = error_Volume_not_labeled;   Use addUnlabeledVolume()
        }
    }
}

 ----------------------------------------------------------------------
  METHOD: Archive::isCompleteVolSet  --DVM
       
   This returns True if all labeled volumes in the set have been
   added. This means that there are no missing volumes (i.e., all
   volumes up through the highest-numbered volume have been added),
   and that the current highest-numbered volume is a valid root.
 
  ARGS:
   NONE.
 
  RETURNS:
   See above.
  
  PRE-CONDS:
   openArchive() must have been called.
  
  POST-CONDS:
   May set the error state.
  
  ERRORS:
   error_Empty_volume_set - Not complete if empty
   error_Not_root_volume  - Highest-numbered vol is not a root
   error_Missing_volume   - Holes in set.
  
  NOTES:
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::isCompleteVolSet( void )
            vol.freeSpace.fprint(Trace::logFile());
            fprintf(Trace::logFile(),"addLabeledVol() Erased Space :\n");
            vol.erasedFreeSpace.fprint(Trace::logFile());
        }

          Now determine dataStart
        PartitionAreaCalc partAreas(logicalBlockSize,
                                    media,
                                    uICBAddr,
                                    eICBAddr);
        vol.fileDataStart = partAreas.fileDataStart();

          If it has an LVID Sequence, read it in.
        if ( vol.lvNumVols != 0 ) {
            LogicalVolumeIntegrityDesc *lvid = NULL;
            int nFileSets = 0;
              Read the LVID sequence.
            readLVIDSeq( vol, &lvid, &nFileSets );
            if (err) {
                delete lvid;
                return;
            }
              Fill in volume fields.
            vol.lvIntegrity        = lvid->integrityType();
            vol.lvIntegImplementID = lvid->implementID.id();
            delete lvid;
        }
        
          Add the volume into the table.
        volumeTable[vol.vid] = vol;
        if ( err = volumeTable.error() ) {
            return;
        }
    } else {
        if (hasAVD) {
            if (!err) {
                  volume not written by hp.
                err = error_Wrong_volume_set;
            }
            return;
        } else {
            err = error_Volume_not_labeled;   Use addUnlabeledVolume()
        }
    }
}

 ----------------------------------------------------------------------
  METHOD: Archive::isCompleteVolSet  --DVM
       
   This returns True if all labeled volumes in the set have been
   added. This means that there are no missing volumes (i.e., all
   volumes up through the highest-numbered volume have been added),
   and that the current highest-numbered volume is a valid root.
 
  ARGS:
   NONE.
 
  RETURNS:
   See above.
  
  PRE-CONDS:
   openArchive() must have been called.
  
  POST-CONDS:
   May set the error state.
  
  ERRORS:
   error_Empty_volume_set - Not complete if empty
   error_Not_root_volume  - Highest-numbered vol is not a root
   error_Missing_volume   - Holes in set.
  
  NOTES:
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::isCompleteVolSet( void )
            fprintf(Trace::logFile(),"addLabeledVol() Erased Space :\n");
            vol.erasedFreeSpace.fprint(Trace::logFile());
        }

          Now determine dataStart
        PartitionAreaCalc partAreas(logicalBlockSize,
                                    media,
                                    uICBAddr,
                                    eICBAddr);
        vol.fileDataStart = partAreas.fileDataStart();

          If it has an LVID Sequence, read it in.
        if ( vol.lvNumVols != 0 ) {
            LogicalVolumeIntegrityDesc *lvid = NULL;
            int nFileSets = 0;
              Read the LVID sequence.
            readLVIDSeq( vol, &lvid, &nFileSets );
            if (err) {
                delete lvid;
                return;
            }
              Fill in volume fields.
            vol.lvIntegrity        = lvid->integrityType();
            vol.lvIntegImplementID = lvid->implementID.id();
            delete lvid;
        }
        
          Add the volume into the table.
        volumeTable[vol.vid] = vol;
        if ( err = volumeTable.error() ) {
            return;
        }
    } else {
        if (hasAVD) {
            if (!err) {
                  volume not written by hp.
                err = error_Wrong_volume_set;
            }
            return;
        } else {
            err = error_Volume_not_labeled;   Use addUnlabeledVolume()
        }
    }
}

 ----------------------------------------------------------------------
  METHOD: Archive::isCompleteVolSet  --DVM
       
   This returns True if all labeled volumes in the set have been
   added. This means that there are no missing volumes (i.e., all
   volumes up through the highest-numbered volume have been added),
   and that the current highest-numbered volume is a valid root.
 
  ARGS:
   NONE.
 
  RETURNS:
   See above.
  
  PRE-CONDS:
   openArchive() must have been called.
  
  POST-CONDS:
   May set the error state.
  
  ERRORS:
   error_Empty_volume_set - Not complete if empty
   error_Not_root_volume  - Highest-numbered vol is not a root
   error_Missing_volume   - Holes in set.
  
  NOTES:
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::isCompleteVolSet( void )
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),"addUnlabeledVol() Unerased Space :\n");
        newVolume.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"addUnlabeledVol() Erased Space :\n");
        newVolume.erasedFreeSpace.fprint(Trace::logFile());
    }
        
      Add to volumeTable
    volumeTable.append( newVolume );
    if (err=volumeTable.error()) {
        return;
    }

      FINALLY, place the label down onto the disk.
    writeVolumeLabel( volumeTable.rootVol() );
}

 ----------------------------------------------------------------------
  METHOD: Archive::completeGrow  --DVM
       
   This completes the volume set growth process.
 
   This writes root volume information on the current
   highest-numbered volume, including copying any fileset history
   from the prior root to the new root. This must be called before
   performing import or export, once beginGrow() has been
   called. The grown volume set is marked the same as the volume set
   was marked during beginGrow().
 
  ARGS: none.
 
  RETURNS: none.
  
  PRE-CONDITIONS:
   beginGrow() must have been called.
  
  POST-CONDITIONS:
   The Volume Set how has a valid root volume, although its integrity
   is still OPEN.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
void
NSR::Archive::completeGrow( void ) 
        fprintf(Trace::logFile(),"addUnlabeledVol() Unerased Space :\n");
        newVolume.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"addUnlabeledVol() Erased Space :\n");
        newVolume.erasedFreeSpace.fprint(Trace::logFile());
    }
        
      Add to volumeTable
    volumeTable.append( newVolume );
    if (err=volumeTable.error()) {
        return;
    }

      FINALLY, place the label down onto the disk.
    writeVolumeLabel( volumeTable.rootVol() );
}

 ----------------------------------------------------------------------
  METHOD: Archive::completeGrow  --DVM
       
   This completes the volume set growth process.
 
   This writes root volume information on the current
   highest-numbered volume, including copying any fileset history
   from the prior root to the new root. This must be called before
   performing import or export, once beginGrow() has been
   called. The grown volume set is marked the same as the volume set
   was marked during beginGrow().
 
  ARGS: none.
 
  RETURNS: none.
  
  PRE-CONDITIONS:
   beginGrow() must have been called.
  
  POST-CONDITIONS:
   The Volume Set how has a valid root volume, although its integrity
   is still OPEN.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
void
NSR::Archive::completeGrow( void ) 
        newVolume.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"addUnlabeledVol() Erased Space :\n");
        newVolume.erasedFreeSpace.fprint(Trace::logFile());
    }
        
      Add to volumeTable
    volumeTable.append( newVolume );
    if (err=volumeTable.error()) {
        return;
    }

      FINALLY, place the label down onto the disk.
    writeVolumeLabel( volumeTable.rootVol() );
}

 ----------------------------------------------------------------------
  METHOD: Archive::completeGrow  --DVM
       
   This completes the volume set growth process.
 
   This writes root volume information on the current
   highest-numbered volume, including copying any fileset history
   from the prior root to the new root. This must be called before
   performing import or export, once beginGrow() has been
   called. The grown volume set is marked the same as the volume set
   was marked during beginGrow().
 
  ARGS: none.
 
  RETURNS: none.
  
  PRE-CONDITIONS:
   beginGrow() must have been called.
  
  POST-CONDITIONS:
   The Volume Set how has a valid root volume, although its integrity
   is still OPEN.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
void
NSR::Archive::completeGrow( void ) 
        fprintf(Trace::logFile(),"addUnlabeledVol() Erased Space :\n");
        newVolume.erasedFreeSpace.fprint(Trace::logFile());
    }
        
      Add to volumeTable
    volumeTable.append( newVolume );
    if (err=volumeTable.error()) {
        return;
    }

      FINALLY, place the label down onto the disk.
    writeVolumeLabel( volumeTable.rootVol() );
}

 ----------------------------------------------------------------------
  METHOD: Archive::completeGrow  --DVM
       
   This completes the volume set growth process.
 
   This writes root volume information on the current
   highest-numbered volume, including copying any fileset history
   from the prior root to the new root. This must be called before
   performing import or export, once beginGrow() has been
   called. The grown volume set is marked the same as the volume set
   was marked during beginGrow().
 
  ARGS: none.
 
  RETURNS: none.
  
  PRE-CONDITIONS:
   beginGrow() must have been called.
  
  POST-CONDITIONS:
   The Volume Set how has a valid root volume, although its integrity
   is still OPEN.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
void
NSR::Archive::completeGrow( void ) 
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),"completeGrow() Unerased Space :\n");
        rootVol.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"completeGrow() Erased Space :\n");
        rootVol.erasedFreeSpace.fprint(Trace::logFile());
    }

      New volume sets start out as OPEN.
      Otherwise, propagate from previous Root.
    if ( preGrowthNumVols == 0 ) {
        rootVol.lvIntegrity = integ_Open;
    } else {
        rootVol.lvIntegrity = preGrowthRootVol.lvIntegrity;
    }
    writeIntegrityEntry( rootVol );
    if (err) { return; }
    
      Change the mode
    mode = mode_VolSet;
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::getVolSetInfo    --AP
       
   This returns a summary of the information currently held by the
   archive object about the current volume set.
 
   Note that if the isComplete field is False, the rest of the
   structure is simply zeroed out. Thus:
 
   Always filled in appropriately:
       expectedVols
       sectorSize
       mediaType
       volSetName
       volList
       isComplete
   Zeroed if volume set incomplete:
       rootSid
       integrityType
       writeProtectType
       fileSetList
   
   
  ARGS:
   VolumeSetInfo *vsip   OUT   all info about volume set
 
  RETURNS: void
  
  PRE-CONDS:
   NONE
  
  POST-CONDS:
   Error may be set, based upon whether or not a valid root volume
   exists. It should return the same errors as isCompleteVolSet().
  
  ERRORS:
   error_
  
  NOTES:
   volInfoList will be fill in only for those volumes which have been
   added to the Archive. Thus, volInfoList.entries() returns how many
   volumes have been added so far.
 
 ----------------------------------------------------------------------
void
NSR::Archive::getVolSetInfo( VolumeSetInfo *vsip ) 
        fprintf(Trace::logFile(),"completeGrow() Unerased Space :\n");
        rootVol.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"completeGrow() Erased Space :\n");
        rootVol.erasedFreeSpace.fprint(Trace::logFile());
    }

      New volume sets start out as OPEN.
      Otherwise, propagate from previous Root.
    if ( preGrowthNumVols == 0 ) {
        rootVol.lvIntegrity = integ_Open;
    } else {
        rootVol.lvIntegrity = preGrowthRootVol.lvIntegrity;
    }
    writeIntegrityEntry( rootVol );
    if (err) { return; }
    
      Change the mode
    mode = mode_VolSet;
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::getVolSetInfo    --AP
       
   This returns a summary of the information currently held by the
   archive object about the current volume set.
 
   Note that if the isComplete field is False, the rest of the
   structure is simply zeroed out. Thus:
 
   Always filled in appropriately:
       expectedVols
       sectorSize
       mediaType
       volSetName
       volList
       isComplete
   Zeroed if volume set incomplete:
       rootSid
       integrityType
       writeProtectType
       fileSetList
   
   
  ARGS:
   VolumeSetInfo *vsip   OUT   all info about volume set
 
  RETURNS: void
  
  PRE-CONDS:
   NONE
  
  POST-CONDS:
   Error may be set, based upon whether or not a valid root volume
   exists. It should return the same errors as isCompleteVolSet().
  
  ERRORS:
   error_
  
  NOTES:
   volInfoList will be fill in only for those volumes which have been
   added to the Archive. Thus, volInfoList.entries() returns how many
   volumes have been added so far.
 
 ----------------------------------------------------------------------
void
NSR::Archive::getVolSetInfo( VolumeSetInfo *vsip ) 
        rootVol.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"completeGrow() Erased Space :\n");
        rootVol.erasedFreeSpace.fprint(Trace::logFile());
    }

      New volume sets start out as OPEN.
      Otherwise, propagate from previous Root.
    if ( preGrowthNumVols == 0 ) {
        rootVol.lvIntegrity = integ_Open;
    } else {
        rootVol.lvIntegrity = preGrowthRootVol.lvIntegrity;
    }
    writeIntegrityEntry( rootVol );
    if (err) { return; }
    
      Change the mode
    mode = mode_VolSet;
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::getVolSetInfo    --AP
       
   This returns a summary of the information currently held by the
   archive object about the current volume set.
 
   Note that if the isComplete field is False, the rest of the
   structure is simply zeroed out. Thus:
 
   Always filled in appropriately:
       expectedVols
       sectorSize
       mediaType
       volSetName
       volList
       isComplete
   Zeroed if volume set incomplete:
       rootSid
       integrityType
       writeProtectType
       fileSetList
   
   
  ARGS:
   VolumeSetInfo *vsip   OUT   all info about volume set
 
  RETURNS: void
  
  PRE-CONDS:
   NONE
  
  POST-CONDS:
   Error may be set, based upon whether or not a valid root volume
   exists. It should return the same errors as isCompleteVolSet().
  
  ERRORS:
   error_
  
  NOTES:
   volInfoList will be fill in only for those volumes which have been
   added to the Archive. Thus, volInfoList.entries() returns how many
   volumes have been added so far.
 
 ----------------------------------------------------------------------
void
NSR::Archive::getVolSetInfo( VolumeSetInfo *vsip ) 
        fprintf(Trace::logFile(),"completeGrow() Erased Space :\n");
        rootVol.erasedFreeSpace.fprint(Trace::logFile());
    }

      New volume sets start out as OPEN.
      Otherwise, propagate from previous Root.
    if ( preGrowthNumVols == 0 ) {
        rootVol.lvIntegrity = integ_Open;
    } else {
        rootVol.lvIntegrity = preGrowthRootVol.lvIntegrity;
    }
    writeIntegrityEntry( rootVol );
    if (err) { return; }
    
      Change the mode
    mode = mode_VolSet;
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::getVolSetInfo    --AP
       
   This returns a summary of the information currently held by the
   archive object about the current volume set.
 
   Note that if the isComplete field is False, the rest of the
   structure is simply zeroed out. Thus:
 
   Always filled in appropriately:
       expectedVols
       sectorSize
       mediaType
       volSetName
       volList
       isComplete
   Zeroed if volume set incomplete:
       rootSid
       integrityType
       writeProtectType
       fileSetList
   
   
  ARGS:
   VolumeSetInfo *vsip   OUT   all info about volume set
 
  RETURNS: void
  
  PRE-CONDS:
   NONE
  
  POST-CONDS:
   Error may be set, based upon whether or not a valid root volume
   exists. It should return the same errors as isCompleteVolSet().
  
  ERRORS:
   error_
  
  NOTES:
   volInfoList will be fill in only for those volumes which have been
   added to the Archive. Thus, volInfoList.entries() returns how many
   volumes have been added so far.
 
 ----------------------------------------------------------------------
void
NSR::Archive::getVolSetInfo( VolumeSetInfo *vsip ) 
        for (i = 0; iverifyBlank(volumeTable[vid].sid(),
                    e.start.sector, B2S(e.len), &nonBlankSector ) == False) {
                    verified = False;
                }
            }
            if (verified) {
                elist.append( e );
                if ( err = elist.error() ) { delete used; return; }
                n -= B2S(e.len);
                nextents++;
                if (figuringEA) {
                    rootEAExtents++;
                    if ( rootEAExtents > extentsPerICB ) {
                          This should really never happen. 
                        err = error_Root_EA_FileEntry_full;
                        delete used;
                        return;
                    }
                    if (rootEAExtents > maxExtraExtents) {
                          will need to use additional sectors for ea space
                        n++;
                        maxExtraExtents += logicalBlockSize/16;
                    }
                }
            }
            if ( n <= 0 && !figuringEA) {
                 ---------------------------------------------------
                  All the space has been allocated and verified.
                  See if any additional space is needed for the
                  icb table. The icb table is to be stored as an
                  extended attribute of the root icb.
                 ---------------------------------------------------
                figuringEA = True;
                icbtea = new ICBTableEA(nextents+1);
                if (!icbtea) { err = error_No_free_store; delete used; return; }
                nBytesForEA += icbtea->attrLen();
                nBytesForEA += (nBytesForEA % 4) ? 4 - (nBytesForEA % 4) : 0;
                delete icbtea;
                n += 1;                    one sector for the EA ICB 
                n += B2S(nBytesForEA);     sectors for the EA
                root_ovf = 1 + B2S(nBytesForEA);
                maxExtraExtents = 1 + (logicalBlockSize-nBytesForEA)/16;   long_ad
            }
            if ( n > 0 )  {
                e = volumeTable[vid].alloc( n, False );
            }
        }

        used[ vid ] = True;

        if ( n > 0 ) {

              pick next volume
            valid_ID = False; totalFree = 0;
            for ( i=0; i totalFree ) { 
                        vid = i; totalFree = t; valid_ID = True;
                    }
                }
            }

            e = volumeTable[vid].alloc( n, False );

        }
    }

    delete [] used;

    if (!valid_ID) {
        err = error_No_free_space;
        return;
    }
    icbTable = new ICB_Table( elist, logicalBlockSize );
    if (!icbTable) {
        err = error_No_free_store;
        return;
    }
    endICBTable = False;
    vidCurrent = vid;
}

 ----------------------------------------------------------------------
  METHOD: Archive::beginExport  --DVM
       
   This begins the export process, naming the fileset.
 
  ARGS:
   fsname   IN  File Set Name
 
  RETURNS:
   NONE.
  
  PRE-CONDS:
   - A complete volume set must be loaded.
   - The Logical Volume must not be write-protected (soft OR hard).
   - markOpenIntegrity() must have been called.
   - setAvailSpace() must have been called for all vols with space.
   - WORM - All file sets are hard write-protected.
   - else - No file sets exist.
  
  POST-CONDS:
   Either the operation is successful, placing the Archive object
   into export mode, or an error state will be set.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
void
NSR::Archive::beginExport( const char *fsname )
                    fprintf(Trace::logFile(),"allocateICBTable(): verifying Extent :");
                    e.fprint(Trace::logFile());
                }
                UINT32 nonBlankSector;
                if (currentDevice->verifyBlank(volumeTable[vid].sid(),
                    e.start.sector, B2S(e.len), &nonBlankSector ) == False) {
                    verified = False;
                }
            }
            if (verified) {
                elist.append( e );
                if ( err = elist.error() ) { delete used; return; }
                n -= B2S(e.len);
                nextents++;
                if (figuringEA) {
                    rootEAExtents++;
                    if ( rootEAExtents > extentsPerICB ) {
                          This should really never happen. 
                        err = error_Root_EA_FileEntry_full;
                        delete used;
                        return;
                    }
                    if (rootEAExtents > maxExtraExtents) {
                          will need to use additional sectors for ea space
                        n++;
                        maxExtraExtents += logicalBlockSize/16;
                    }
                }
            }
            if ( n <= 0 && !figuringEA) {
                 ---------------------------------------------------
                  All the space has been allocated and verified.
                  See if any additional space is needed for the
                  icb table. The icb table is to be stored as an
                  extended attribute of the root icb.
                 ---------------------------------------------------
                figuringEA = True;
                icbtea = new ICBTableEA(nextents+1);
                if (!icbtea) { err = error_No_free_store; delete used; return; }
                nBytesForEA += icbtea->attrLen();
                nBytesForEA += (nBytesForEA % 4) ? 4 - (nBytesForEA % 4) : 0;
                delete icbtea;
                n += 1;                    one sector for the EA ICB 
                n += B2S(nBytesForEA);     sectors for the EA
                root_ovf = 1 + B2S(nBytesForEA);
                maxExtraExtents = 1 + (logicalBlockSize-nBytesForEA)/16;   long_ad
            }
            if ( n > 0 )  {
                e = volumeTable[vid].alloc( n, False );
            }
        }

        used[ vid ] = True;

        if ( n > 0 ) {

              pick next volume
            valid_ID = False; totalFree = 0;
            for ( i=0; i totalFree ) { 
                        vid = i; totalFree = t; valid_ID = True;
                    }
                }
            }

            e = volumeTable[vid].alloc( n, False );

        }
    }

    delete [] used;

    if (!valid_ID) {
        err = error_No_free_space;
        return;
    }
    icbTable = new ICB_Table( elist, logicalBlockSize );
    if (!icbTable) {
        err = error_No_free_store;
        return;
    }
    endICBTable = False;
    vidCurrent = vid;
}

 ----------------------------------------------------------------------
  METHOD: Archive::beginExport  --DVM
       
   This begins the export process, naming the fileset.
 
  ARGS:
   fsname   IN  File Set Name
 
  RETURNS:
   NONE.
  
  PRE-CONDS:
   - A complete volume set must be loaded.
   - The Logical Volume must not be write-protected (soft OR hard).
   - markOpenIntegrity() must have been called.
   - setAvailSpace() must have been called for all vols with space.
   - WORM - All file sets are hard write-protected.
   - else - No file sets exist.
  
  POST-CONDS:
   Either the operation is successful, placing the Archive object
   into export mode, or an error state will be set.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
void
NSR::Archive::beginExport( const char *fsname )
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"writeDirectory(): full volume");
            fprintf(Trace::logFile(),"- updating space for volNum[%d]\n",
                    vidCurrent);
        }
        updateSpace( volumeTable[vidCurrent] );   Write out free space.
        if (err) return;
        volumeTable[vidCurrent].markClean();

        Boolean valid_ID = True;
        UINT32 totalFree, t;
        UINT16 vid;

        valid_ID = False; totalFree = 0;
        for ( i=0; i totalFree ) { 
                vid = i; totalFree = t; valid_ID = True;
            }
        }

        if ( valid_ID )
            vidCurrent = vid;
        else
            err = error_No_free_space;
            return;
    }

      allocate directory from free space 
    Boolean verified = False;
    while (!verified)
    {
        *ext = volumeTable[vidCurrent].alloc( nBlocks, True );
        ext->len = totalBytes;
        if (!(ext->len)) { err = error_No_free_space; return; }
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                "writeDirectory  : at sid %3d sector %7d num_sectors %7d\n",
                ext->start.sid, ext->start.sector, nBlocks);
        }
        verified = True;
        if (media == media_Worm) {
            verified = False;
            UINT32 nonBlankSector;
              do a verify on this extent to make sure it can be written
            if (currentDevice->verifyBlank(volumeTable[vidCurrent].sid(),
                            ext->start.sector,B2S(ext->len),
                            &nonBlankSector) == True ) {
                verified = True;
            }
            else {
                fprintf(Trace::logFile(),"writeDirectory: WARNING: non-blank sector:%d\n",nonBlankSector);
            }
        }
    }
    
    BYTE *bp = new BYTE[S2B(nBlocks)];
    if (!bp) { err = error_No_free_store; return; }
    memset( bp, 0, S2B(nBlocks));
    UINT32 bytePtr = 0;

    for ( i=0; i < currentDirectory.contents.getCount(); i++ ) 
    {
        fid = new FileIdentifierDesc( currentDirectory.contents[i].name );
        if (!fid) { err = error_No_free_store; delete [] bp; return; }

        fid->icbAddr.extType    ( ext_Recorded );
        fid->icbAddr.logBlockNo ( currentDirectory.contents[i]
                                  .icbAddr.sector - partStartSector );
        fid->icbAddr.partRefNo  ( volumeTable.vid( currentDirectory
                                                  .contents[i].icbAddr.sid ) );
        fid->icbAddr.extLength  ( logicalBlockSize );

        if ( currentDirectory.contents[i].hidden ) fid->markHidden();
        
        if ( currentDirectory.contents[i].dir ) fid->markDir();
        if ( i == 0 ) fid->markParent();   parent is always first entry
        

        fid->toISO( bp + bytePtr, ext->start.sector + (bytePtr/logicalBlockSize) - partStartSector );
        if (err = fid->error()) { delete fid; delete [] bp; return; }
        bytePtr += fid->descLen();
        delete fid;
    }

    for ( i = 0; i < nBlocks; i++ )
    {
        writeBlock( volumeTable[vidCurrent].sid(), ext->start.sector + i, 
                     bp + (S2B(i)));
    }

    delete [] bp;

    if (err) return;

      mark directory as clean
    currentDirectory.markClean();
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::closeDirectory
       
   User is done with this directory. Pop the directory stack.
 
  ARGS:
 
  RETURNS:
  
  PRE-CONDS:  
  
  POST-CONDS: 
     currentDirectory: directoryStack.pop();
     
     directoryStack:   | currentDirectory (prev-1) |<-- top of stack
                       |---------------------------|
                       | currentDirectory (prev-2) |<-- top of stack - 1
  
  ERRORS:
     error_Directory_dirty
  
  NOTES: export
 ----------------------------------------------------------------------
void
NSR::Archive::closeDirectory()
            fprintf(Trace::logFile(),"writeDirectory(): full volume");
            fprintf(Trace::logFile(),"- updating space for volNum[%d]\n",
                    vidCurrent);
        }
        updateSpace( volumeTable[vidCurrent] );   Write out free space.
        if (err) return;
        volumeTable[vidCurrent].markClean();

        Boolean valid_ID = True;
        UINT32 totalFree, t;
        UINT16 vid;

        valid_ID = False; totalFree = 0;
        for ( i=0; i totalFree ) { 
                vid = i; totalFree = t; valid_ID = True;
            }
        }

        if ( valid_ID )
            vidCurrent = vid;
        else
            err = error_No_free_space;
            return;
    }

      allocate directory from free space 
    Boolean verified = False;
    while (!verified)
    {
        *ext = volumeTable[vidCurrent].alloc( nBlocks, True );
        ext->len = totalBytes;
        if (!(ext->len)) { err = error_No_free_space; return; }
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                "writeDirectory  : at sid %3d sector %7d num_sectors %7d\n",
                ext->start.sid, ext->start.sector, nBlocks);
        }
        verified = True;
        if (media == media_Worm) {
            verified = False;
            UINT32 nonBlankSector;
              do a verify on this extent to make sure it can be written
            if (currentDevice->verifyBlank(volumeTable[vidCurrent].sid(),
                            ext->start.sector,B2S(ext->len),
                            &nonBlankSector) == True ) {
                verified = True;
            }
            else {
                fprintf(Trace::logFile(),"writeDirectory: WARNING: non-blank sector:%d\n",nonBlankSector);
            }
        }
    }
    
    BYTE *bp = new BYTE[S2B(nBlocks)];
    if (!bp) { err = error_No_free_store; return; }
    memset( bp, 0, S2B(nBlocks));
    UINT32 bytePtr = 0;

    for ( i=0; i < currentDirectory.contents.getCount(); i++ ) 
    {
        fid = new FileIdentifierDesc( currentDirectory.contents[i].name );
        if (!fid) { err = error_No_free_store; delete [] bp; return; }

        fid->icbAddr.extType    ( ext_Recorded );
        fid->icbAddr.logBlockNo ( currentDirectory.contents[i]
                                  .icbAddr.sector - partStartSector );
        fid->icbAddr.partRefNo  ( volumeTable.vid( currentDirectory
                                                  .contents[i].icbAddr.sid ) );
        fid->icbAddr.extLength  ( logicalBlockSize );

        if ( currentDirectory.contents[i].hidden ) fid->markHidden();
        
        if ( currentDirectory.contents[i].dir ) fid->markDir();
        if ( i == 0 ) fid->markParent();   parent is always first entry
        

        fid->toISO( bp + bytePtr, ext->start.sector + (bytePtr/logicalBlockSize) - partStartSector );
        if (err = fid->error()) { delete fid; delete [] bp; return; }
        bytePtr += fid->descLen();
        delete fid;
    }

    for ( i = 0; i < nBlocks; i++ )
    {
        writeBlock( volumeTable[vidCurrent].sid(), ext->start.sector + i, 
                     bp + (S2B(i)));
    }

    delete [] bp;

    if (err) return;

      mark directory as clean
    currentDirectory.markClean();
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::closeDirectory
       
   User is done with this directory. Pop the directory stack.
 
  ARGS:
 
  RETURNS:
  
  PRE-CONDS:  
  
  POST-CONDS: 
     currentDirectory: directoryStack.pop();
     
     directoryStack:   | currentDirectory (prev-1) |<-- top of stack
                       |---------------------------|
                       | currentDirectory (prev-2) |<-- top of stack - 1
  
  ERRORS:
     error_Directory_dirty
  
  NOTES: export
 ----------------------------------------------------------------------
void
NSR::Archive::closeDirectory()
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                "writeDirectory  : at sid %3d sector %7d num_sectors %7d\n",
                ext->start.sid, ext->start.sector, nBlocks);
        }
        verified = True;
        if (media == media_Worm) {
            verified = False;
            UINT32 nonBlankSector;
              do a verify on this extent to make sure it can be written
            if (currentDevice->verifyBlank(volumeTable[vidCurrent].sid(),
                            ext->start.sector,B2S(ext->len),
                            &nonBlankSector) == True ) {
                verified = True;
            }
            else {
                fprintf(Trace::logFile(),"writeDirectory: WARNING: non-blank sector:%d\n",nonBlankSector);
            }
        }
    }
    
    BYTE *bp = new BYTE[S2B(nBlocks)];
    if (!bp) { err = error_No_free_store; return; }
    memset( bp, 0, S2B(nBlocks));
    UINT32 bytePtr = 0;

    for ( i=0; i < currentDirectory.contents.getCount(); i++ ) 
    {
        fid = new FileIdentifierDesc( currentDirectory.contents[i].name );
        if (!fid) { err = error_No_free_store; delete [] bp; return; }

        fid->icbAddr.extType    ( ext_Recorded );
        fid->icbAddr.logBlockNo ( currentDirectory.contents[i]
                                  .icbAddr.sector - partStartSector );
        fid->icbAddr.partRefNo  ( volumeTable.vid( currentDirectory
                                                  .contents[i].icbAddr.sid ) );
        fid->icbAddr.extLength  ( logicalBlockSize );

        if ( currentDirectory.contents[i].hidden ) fid->markHidden();
        
        if ( currentDirectory.contents[i].dir ) fid->markDir();
        if ( i == 0 ) fid->markParent();   parent is always first entry
        

        fid->toISO( bp + bytePtr, ext->start.sector + (bytePtr/logicalBlockSize) - partStartSector );
        if (err = fid->error()) { delete fid; delete [] bp; return; }
        bytePtr += fid->descLen();
        delete fid;
    }

    for ( i = 0; i < nBlocks; i++ )
    {
        writeBlock( volumeTable[vidCurrent].sid(), ext->start.sector + i, 
                     bp + (S2B(i)));
    }

    delete [] bp;

    if (err) return;

      mark directory as clean
    currentDirectory.markClean();
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::closeDirectory
       
   User is done with this directory. Pop the directory stack.
 
  ARGS:
 
  RETURNS:
  
  PRE-CONDS:  
  
  POST-CONDS: 
     currentDirectory: directoryStack.pop();
     
     directoryStack:   | currentDirectory (prev-1) |<-- top of stack
                       |---------------------------|
                       | currentDirectory (prev-2) |<-- top of stack - 1
  
  ERRORS:
     error_Directory_dirty
  
  NOTES: export
 ----------------------------------------------------------------------
void
NSR::Archive::closeDirectory()
            else {
                fprintf(Trace::logFile(),"writeDirectory: WARNING: non-blank sector:%d\n",nonBlankSector);
            }
        }
    }
    
    BYTE *bp = new BYTE[S2B(nBlocks)];
    if (!bp) { err = error_No_free_store; return; }
    memset( bp, 0, S2B(nBlocks));
    UINT32 bytePtr = 0;

    for ( i=0; i < currentDirectory.contents.getCount(); i++ ) 
    {
        fid = new FileIdentifierDesc( currentDirectory.contents[i].name );
        if (!fid) { err = error_No_free_store; delete [] bp; return; }

        fid->icbAddr.extType    ( ext_Recorded );
        fid->icbAddr.logBlockNo ( currentDirectory.contents[i]
                                  .icbAddr.sector - partStartSector );
        fid->icbAddr.partRefNo  ( volumeTable.vid( currentDirectory
                                                  .contents[i].icbAddr.sid ) );
        fid->icbAddr.extLength  ( logicalBlockSize );

        if ( currentDirectory.contents[i].hidden ) fid->markHidden();
        
        if ( currentDirectory.contents[i].dir ) fid->markDir();
        if ( i == 0 ) fid->markParent();   parent is always first entry
        

        fid->toISO( bp + bytePtr, ext->start.sector + (bytePtr/logicalBlockSize) - partStartSector );
        if (err = fid->error()) { delete fid; delete [] bp; return; }
        bytePtr += fid->descLen();
        delete fid;
    }

    for ( i = 0; i < nBlocks; i++ )
    {
        writeBlock( volumeTable[vidCurrent].sid(), ext->start.sector + i, 
                     bp + (S2B(i)));
    }

    delete [] bp;

    if (err) return;

      mark directory as clean
    currentDirectory.markClean();
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::closeDirectory
       
   User is done with this directory. Pop the directory stack.
 
  ARGS:
 
  RETURNS:
  
  PRE-CONDS:  
  
  POST-CONDS: 
     currentDirectory: directoryStack.pop();
     
     directoryStack:   | currentDirectory (prev-1) |<-- top of stack
                       |---------------------------|
                       | currentDirectory (prev-2) |<-- top of stack - 1
  
  ERRORS:
     error_Directory_dirty
  
  NOTES: export
 ----------------------------------------------------------------------
void
NSR::Archive::closeDirectory()
    if(Trace::logAlgorithms())
        fprintf(Trace::logFile(),"writeRootICB   FE: at sid %3d sector %7d\n",
                icbTable->currentAddr().sid, icbTable->currentAddr().sector);
    writeBlock( icbTable->currentAddr().sid, 
                icbTable->currentAddr().sector, bp );
    delete rfe;
    delete [] bp;

    if (err) return;

    endICBTable = False;
    icbTable->bumpPtr( 1 + nEASectors );

    if (icbTable->error() == error_End_of_ICB_table)
        endICBTable = True;
    else
        err = icbTable->error();
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeNextICB
       
   Write a non-root ICB.
 
   Verifies that the file id matches next ICB to be written.  Writes
   the next ICB and any required overflow sectors at the ICB pointer
   location on the ICB table.  It then increments the ICB pointer to
   the next available ICB.
 
  ARGS:
     Attributes &attr            IN    Attributes for this file or dir.
     const ExtentList &extList   IN    Locations of file data or directory.
     PathElementList pList       IN    The symbolic link information.
     Extent EAICBAddr            IN    Location of ICB for EAs associated
                                       with file or directory.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
   The icb table address is bumped the appropriate amount.
   endICBTable: may be set.
  
  ERRORS:
  
  NOTES: export
 ----------------------------------------------------------------------
void
NSR::Archive::writeNextICB( Attributes &attr,
                            const ExtentList &extList )
    if(Trace::logAlgorithms())
        fprintf(Trace::logFile(),"writeNextICB   FE: at sid %3d sector %7d\n",
            icbTbl->currentAddr().sid, icbTbl->currentAddr().sector);

    writeBlock( icbTbl->currentAddr().sid, 
                 icbTbl->currentAddr().sector, bp );
    
    delete fe;

    if (err) {
        delete [] bp;
        return;
    }

    icbTbl->bumpPtr( 1 );

    if (!isEAfe) {
        endICBTable = False;
        if (icbTbl->error() == error_End_of_ICB_table)
            endICBTable = True;
        else
            err = icbTbl->error();
    }

    if (err) {
        delete [] bp;
        return;
    }

      write required AEDs to hold remaining extents
    for ( UINT32 i=0; i extentsPerBlock ) 
            n = extentsPerBlock;
        else
            n = N;

        N -= n;

        aed = new AllocExtentDesc( (n + ( ovf != (i+1) )), ad_Long );
        if (!aed) {
            err = error_No_free_store;
            delete [] bp;
            return;
        }
        memset( bp, 0, logicalBlockSize );

          load long ads in allocation extent desc block
        adt = aed->longADT();
        for ( UINT32 j=0; jnextAddr().sector - partStartSector );
            adt[n].partRefNo  ( volumeTable.vid( icbTbl->nextAddr().sid ) );
            adt[n].extLength  ( logicalBlockSize );
            adt[n].markContinued();
        }

        aed->toISO( bp, icbTbl->currentAddr().sector - partStartSector );
        if (err = aed->error()) {
            delete aed;
            delete [] bp;
            return;
        }
        delete aed;

          write the AED block
        if(Trace::logAlgorithms())
            fprintf(Trace::logFile(),"writeNextICB  AED: at sid %3d sector %7d\n",
                icbTbl->currentAddr().sid, icbTbl->currentAddr().sector);
        writeBlock( icbTbl->currentAddr().sid, 
                    icbTbl->currentAddr().sector, bp );

        if (err) {
            delete [] bp;
            return;
        }

        endICBTable = False;
        icbTbl->bumpPtr( 1 );

        if (!isEAfe) {
            if (icbTbl->error() == error_End_of_ICB_table)
                endICBTable = True;
            else
                err = icbTbl->error();
        }
        if (err) {
            delete [] bp;
            return;
        }
        
    }
    delete [] bp;
    
    if (!isEAfe) {
        icbTbl->bumpPtr( nEASectors );
        if (icbTbl->error() == error_End_of_ICB_table) {
            endICBTable = True;
        }
        else {
            err = icbTbl->error();
        }
    }
}

void
NSR::Archive::writeNextICB( Attributes attr,       PRIVATE
                            PathElementList pList,
                            const NetwareTrusteeInfo &trustees,
                            const Extent &EAICBAddr) 
        if(Trace::logAlgorithms())
            fprintf(Trace::logFile(),"writeNextICB  AED: at sid %3d sector %7d\n",
                icbTbl->currentAddr().sid, icbTbl->currentAddr().sector);
        writeBlock( icbTbl->currentAddr().sid, 
                    icbTbl->currentAddr().sector, bp );

        if (err) {
            delete [] bp;
            return;
        }

        endICBTable = False;
        icbTbl->bumpPtr( 1 );

        if (!isEAfe) {
            if (icbTbl->error() == error_End_of_ICB_table)
                endICBTable = True;
            else
                err = icbTbl->error();
        }
        if (err) {
            delete [] bp;
            return;
        }
        
    }
    delete [] bp;
    
    if (!isEAfe) {
        icbTbl->bumpPtr( nEASectors );
        if (icbTbl->error() == error_End_of_ICB_table) {
            endICBTable = True;
        }
        else {
            err = icbTbl->error();
        }
    }
}

void
NSR::Archive::writeNextICB( Attributes attr,       PRIVATE
                            PathElementList pList,
                            const NetwareTrusteeInfo &trustees,
                            const Extent &EAICBAddr) 
    if(Trace::logAlgorithms())
        fprintf(Trace::logFile(),"writeNextICB   FE: at sid %3d sector %7d\n",
            feAddr.sid, feAddr.sector);
    writeBlock( feAddr.sid, feAddr.sector, bp );
    delete fe;
    delete [] bp;

    if (err)
        return;

      Create another buffer to hold the ISO version of the plist.
    bp = new BYTE[S2B(elist.numBlocks(logicalBlockSize))];
    if (!bp) {
        err = error_No_free_store;
        return;
    }
      Zero it out
    memset( bp, 0, S2B(elist.numBlocks(logicalBlockSize)));

      Now write the overflow sectors
      For each path element, convert to pathComponent
    UINT32 bytePtr = 0;
    for ( i=0; itoISO( bp + bytePtr );
        if (err = pc->error()) {
            delete pc;
            delete [] bp;
            return;
        }
        bytePtr += pc->descLen();
        delete pc;
    }
    for ( i=0; ibumpPtr( nEASectors );
    if (err)
        return;
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::completeExport
       
   Performs all actions appropriate for end of export.
 
   Writes nsr volume information on all volumes. Writes a closed
   integrity entry.
 
  ARGS: none.
 
  RETURNS: none.
  
  PRE-CONDS: 
  
  POST-CONDS: 
  
  ERRORS:
  
  NOTES:
 
   Updates each volume still marked dirty with space
   information. Then, writes FSD, LVID on root, and closes the
   logical volume. 
 
 ----------------------------------------------------------------------
void
NSR::Archive::completeExport()
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"writeNextICB path: at sid %3d sector %7d\n",
                    elist[i].start.sid, elist[i].start.sector);
        }
        writeBlock( elist[i].start.sid, elist[i].start.sector, bp + (S2B(i)));
    }

    delete [] bp;
    icbTable->bumpPtr( nEASectors );
    if (err)
        return;
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::completeExport
       
   Performs all actions appropriate for end of export.
 
   Writes nsr volume information on all volumes. Writes a closed
   integrity entry.
 
  ARGS: none.
 
  RETURNS: none.
  
  PRE-CONDS: 
  
  POST-CONDS: 
  
  ERRORS:
  
  NOTES:
 
   Updates each volume still marked dirty with space
   information. Then, writes FSD, LVID on root, and closes the
   logical volume. 
 
 ----------------------------------------------------------------------
void
NSR::Archive::completeExport()
    if(Trace::logAlgorithms())
        fprintf(Trace::logFile(),"readRootICB   FE: at sid %3d sector %7d\n",
                rootICB.sid, rootICB.sector);
    readBlock( rootICB.sid, rootICB.sector, bp);
    if (err) {
        delete rfe;
        delete [] bp;
        return;
    }
    rfe->fromISO( bp, rootICB.sector - partStartSector );
    if (err=rfe->error()) {
        delete rfe;
        delete [] bp;
        return;
    }
      First, get the Trustees
      (Also fills in Archive::icbTable)
    UINT32 nEASectors = 0;   Needed later
    loadEAs( rootICB, rfe, trustees, nEASectors);
    if (err) {
        delete rfe;
        delete [] bp;
        return;
    }
      If no icbTable by this point, BAD NEWS!
    if (!icbTable) {
        err = error_Invalid_ICB_table;
        delete rfe;
        delete [] bp;
        return;
    }
      Fill in rootDirAttr
    if (rootDirAttr != NULL) {
        rootDirAttr-> fromFileEntry(*rfe);
    }
      Fill in rootDirExt
    if (rootDirExt != NULL) {
        LongAllocDesc *adt = rfe->longADT();
        rootDirExt->start.sid    = volumeTable[adt[0].partRefNo()].sid();
        rootDirExt->start.sector = adt[0].logBlockNo() + partStartSector;
        rootDirExt->len          = adt[0].extLength();
    }
      Fill in implInfo
    if (implInfo != NULL) {
        rfe->implementID.getImplementInfo(*implInfo);
    }                                               
    delete rfe;
    delete [] bp;

    if (! readingICBTable ) {
          set the ICB pointer to the next sector after root ICB   
        endICBTable = False;
        icbTable->bumpPtr( 1 + nEASectors );
        if (icbTable->error() == error_End_of_ICB_table)
            endICBTable = True;
        else 
            err = icbTable->error() ;
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readNextICB
       
   Read the next ICB.
 
   Reads the next ICB from the ICB table. Also increments the 
   ICB pointer to the next ICB.  
 
   If extList returns NULL, the next ICB was not on the current
   volume.  The next volume is then specified by addr.
 
  ARGS:
     Attributes *attr           OUT    Attributes for next directory.
     ExtentList *extList        OUT    Location of next directory.
     Extent     *EAICBAddr      OUT    Location of ICB for EAs.
 
  RETURNS:
       -1    The end of the ICB table has been reached.
        0    The end of the ICB table has not been reached.
 
  PRE-CONDS: 
 
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 
 ----------------------------------------------------------------------
int
NSR::Archive::readNextICB( Address    *addr,
                           Attributes *attr,
                           ExtentList *extList )
    if(Trace::logAlgorithms())
        fprintf(Trace::logFile(),"readNextICB   FE: at sid %3d sector %7d\n",
                icbTable->currentAddr().sid,
                icbTable->currentAddr().sector);

    readBlock( icbTable->currentAddr().sid,
               icbTable->currentAddr().sector,
               bp );
    if (err) {
        delete [] bp;
        return 0;
    }
    fe = new FileEntry;
    if (!fe) {
        err = error_No_free_store;
        delete [] bp;
        return 0;
    }
    fe->fromISO( bp, icbTable->currentAddr().sector - partStartSector );
    if (err = fe->error()) {
        delete fe;
        delete [] bp;
        return 0;
    }
      load attributes
    attr->fromFileEntry(*fe);   was *attr = fe->getAttributes();
      check - should not be ea !
    if (attr->fileType() == file_ExtendedAttributes) {
        err = error_Unexpected_EA_icb;
        delete fe;
        delete [] bp;
        return 0;
    }
    getICBExtents( fe, extList );
    if (err) {
        delete fe;
        delete [] bp;
        return 0;
    }
      Fill in EAICBAddr
    if (EAICBAddr != NULL) {
        const LongAllocDesc & eaicba = fe->extendedAttrICB;
        EAICBAddr->start.sid    = volumeTable[eaicba.partRefNo()].sid();
        EAICBAddr->start.sector = eaicba.logBlockNo() + partStartSector;
        EAICBAddr->len          = eaicba.extLength();
    }
    
      load extended attributes
    UINT32 nEASectors = 0;
#ifdef TRUSTEES
    loadEAs( icbTable->currentAddr(),
             fe,
             trustees,
             nEASectors);
    if (err) {
        delete fe;
        delete [] bp;
        return 0;
    }
#endif  TRUSTEES
    delete fe;
    delete [] bp;

      set the ICB pointer to the next ICB
    endICBTable = False;
    icbTable->bumpPtr(1 + nEASectors);

    if (attr->fileType() == file_SymbolicLink) {                                            
        UINT32 nBlocks = B2S(extList->totalBytes());
        icbTable->bumpPtr( nBlocks );
    }                                                                    

    if (icbTable->error() == error_End_of_ICB_table)
        endICBTable = True;
    else 
        err = icbTable->error();

    return (endICBTable) ? -1 : 0;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readPath
 
     Read the path information for a symbolic link.
 
  ARGS:
     ExtentList &elist,       IN    Locations for path information
     PathElementList *pList   OUT   The path information.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import 
 ----------------------------------------------------------------------
void
NSR::Archive::readPath( ExtentList &elist,
                        PathElementList *pList ) 
        if(Trace::logAlgorithms())
            fprintf(Trace::logFile(),"readNextICB path: at sid %3d sector %7d\n",
                    elist[i].start.sid, elist[i].start.sector);
        readBlock( elist[i].start.sid, elist[i].start.sector, bp + (S2B(i)));
    }
    PathElement pe;
    PathComponent *pc;
    pList->clear();
    while ( bytePtr < elist.totalBytes() ) {

        pc = new PathComponent();
        if (!pc) {
            err = error_No_free_store;
            delete [] bp;
            return;
        }
        pc->fromISO( bp + bytePtr );
        if (err = pc->error()) {
            delete pc;
            delete [] bp;
            return;
        }
        pe = PathElement(*pc);
        bytePtr += pc->descLen();
        delete pc;
        pList->append( pe );
        if (err = pList->error()) {
            delete [] bp;
            return;
        }
    }
    delete [] bp;
    return;
}

 ----------------------------------------------------------------------
  METHOD: Archive::endOfICBTable  --AP
       
   Returns True if at end of ICB Table.
 
  ARGS:
   NONE
 
  RETURNS:
  
  PRE-CONDS:
  
  POST-CONDS:
  
  ERRORS:
  
  NOTES:
 
 ----------------------------------------------------------------------
NSR::Boolean 
NSR::Archive::endOfICBTable() 
void
NSR::Archive::logFile(FILE * lf) {
    Trace::logFile(lf);
}

 ----------------------------------------------------------------------
  METHOD: Archive::logErrors  --DVM
       
   Enables or disables the logging of the Archive Object's error
   state. Whenever a public method is exited, the error, if set, will
   be printed to the log, if a logFile has been specified.
 
  ARGS:
   Boolean   le  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Errors will or will not be logged to logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
  Private comments go here - will not be extracted into HTML.
 ----------------------------------------------------------------------
void
NSR::Archive::logErrors(Boolean le) {
    if (le) {
        Trace::defaultError(err);
    }
    Trace::logErrors(le);
}
 ----------------------------------------------------------------------
  METHOD: Archive::logAlgorithms  --DVM
       
   Enables or disables the logging of the Archive Object's
   miscellaneous algorithmic output.
 
  ARGS:
   Boolean   la  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Miscellaneous printf()s will or will not be logged to logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
  Private comments go here - will not be extracted into HTML.
 ----------------------------------------------------------------------
void
NSR::Archive::logAlgorithms(Boolean la) {
    Trace::logAlgorithms(la);
}

 ----------------------------------------------------------------------
  METHOD: Archive::logCalls  --DVM
       
   Enables or disables the logging of the entry and exit of all of
   the Archive Object's public methods.
 
  ARGS:
   Boolean   lc  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Entries and Exits to/from Archive Object's public methods will or
   will not be logged to the logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
  Private comments go here - will not be extracted into HTML.
 ----------------------------------------------------------------------
void
NSR::Archive::logCalls(Boolean lc) {
    Trace::logCalls(lc);
}
    
  _PRIVATE_ Methods
 ----------------------------------------------------------------------
  METHOD:  Archive::parseEA
 
     Decode any extended attributes associated with an ICB.
 
  ARGS:
     UINT32 eaLogicalSector,   IN    Logical block address of this EA.
                                     Needed in fromISO().
     UINT32 nBytesInEA,        IN    Number of bytes in EA.
     BYTE *ea,                 IN    Buffer containing ea info.
     trustees,                 OUT   Where to place ea info.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import 
 ----------------------------------------------------------------------
void
NSR::Archive::parseEA( UINT32 eaLogicalSector, UINT32 nBytesInEA, BYTE *ea, 
                       NetwareTrusteeInfo &trustees)
NSR::Archive::logFile(FILE * lf) {
    Trace::logFile(lf);
}

 ----------------------------------------------------------------------
  METHOD: Archive::logErrors  --DVM
       
   Enables or disables the logging of the Archive Object's error
   state. Whenever a public method is exited, the error, if set, will
   be printed to the log, if a logFile has been specified.
 
  ARGS:
   Boolean   le  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Errors will or will not be logged to logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
  Private comments go here - will not be extracted into HTML.
 ----------------------------------------------------------------------
void
NSR::Archive::logErrors(Boolean le) {
    if (le) {
        Trace::defaultError(err);
    }
    Trace::logErrors(le);
}
 ----------------------------------------------------------------------
  METHOD: Archive::logAlgorithms  --DVM
       
   Enables or disables the logging of the Archive Object's
   miscellaneous algorithmic output.
 
  ARGS:
   Boolean   la  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Miscellaneous printf()s will or will not be logged to logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
  Private comments go here - will not be extracted into HTML.
 ----------------------------------------------------------------------
void
NSR::Archive::logAlgorithms(Boolean la) {
    Trace::logAlgorithms(la);
}

 ----------------------------------------------------------------------
  METHOD: Archive::logCalls  --DVM
       
   Enables or disables the logging of the entry and exit of all of
   the Archive Object's public methods.
 
  ARGS:
   Boolean   lc  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Entries and Exits to/from Archive Object's public methods will or
   will not be logged to the logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
  Private comments go here - will not be extracted into HTML.
 ----------------------------------------------------------------------
void
NSR::Archive::logCalls(Boolean lc) {
    Trace::logCalls(lc);
}
    
  _PRIVATE_ Methods
 ----------------------------------------------------------------------
  METHOD:  Archive::parseEA
 
     Decode any extended attributes associated with an ICB.
 
  ARGS:
     UINT32 eaLogicalSector,   IN    Logical block address of this EA.
                                     Needed in fromISO().
     UINT32 nBytesInEA,        IN    Number of bytes in EA.
     BYTE *ea,                 IN    Buffer containing ea info.
     trustees,                 OUT   Where to place ea info.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import 
 ----------------------------------------------------------------------
void
NSR::Archive::parseEA( UINT32 eaLogicalSector, UINT32 nBytesInEA, BYTE *ea, 
                       NetwareTrusteeInfo &trustees)
                if(Trace::logAlgorithms())
                    fprintf(Trace::logFile(),"getICBExtents AED: at sid %3d sector %7d\n",
                        icbTable->currentAddr().sid, 
                        icbTable->currentAddr().sector);

                readBlock( icbTable->currentAddr().sid, 
                           icbTable->currentAddr().sector, bp );
                if (err) {
                    delete [] bp;
                    return;
                }
                delete aed;
                aed = new AllocExtentDesc( 1, ad_Long);
                if (!aed) {
                    err = error_No_free_store;
                    delete [] bp;
                    return;
                }
                aed->fromISO( bp, icbTable->currentAddr().sector - partStartSector );
                if (err = aed->error()) { 
                    delete aed; 
                    delete [] bp; 
                    return; 
                }
                noExtents = aed->numADTEnts();
                adt       = aed->longADT();
                i         = 0;
            } else {
                ext.start.sid   = volumeTable[adt[i].partRefNo()].sid();
                ext.start.sector = adt[i].logBlockNo() + partStartSector;
                ext.len          = adt[i].extLength();
                extList->append( ext );
                if (err = extList->error()) {
                    delete aed;
                    delete [] bp;
                    return;
                }
                i++;
            }
        }
    }
    delete aed; 
    delete [] bp; 
}

void
NSR::Archive::readSector( sid_t sid, UINT32 a, BYTE *bp )
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"can't write indirect entry icb at 0x%x\n",indirectEntryAddress);
        }
    }
    delete ie; 
    delete [] bp; 
}

 ----------------------------------------------------------------------
  METHOD:  Archive::getSeqExtent
       
     Get an extent for use in extending a USE or FSD sequence.
 
  Use up the previously allocated space. The allocation had to occur
  before the areas for the icb's was allocated so that the check for
  available space was accurate.
  ISSUE: this should be an extent list ??
 
  ARGS:
     sid_t sid,       IN    Surface ID.
     UINT32 nSectors,   IN    Number of sectors.
     Extent &newExtent  OUT   The extent.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     used only for WORM and only if sequences can grow > fileDataStart
 ----------------------------------------------------------------------
void 
NSR::Archive::getSeqExtent(sid_t sid, UINT32 nSectors, Extent &newExtent)
        else {
            fprintf(Trace::logFile(),"checkRootVolumeSpace: aed's for use won't be written\n");
        }
    }

     -------------------------------------------------------------
      check space for FSD. FSD is always one sector.
     -------------------------------------------------------------
    nSectors = 1;
    nRead = 0;
    lastDescNum = 0;
    nextBlankAddr = 0;
    lastRecordedAddr = 0;
    needsIndirect = False;
    getNextSeqAddress(rootSid,
                      volumeTable.rootVol().fsdsExt,
                      nSectors,&nRead,&nextBlankAddr,&lastDescNum,
                      &lastRecordedAddr,&needsIndirect,&bogusICBInfo);
    if (err) {
        if (err == error_Drive_blank_check && media == media_Worm) {
            err = error_None;
        }
    }
    if (needsIndirect) {
        if (!partitionSeqAllowed) {err = error_End_of_Sequence_space; return;}
        neededSectors += incrementalSeqSectors;
    }
    err = error_None;
}

NSR::UINT32
NSR::Archive::writeExtents(sid_t sid, ICB_Table *icbTbl, UINT32 n, BYTE *bp)
        if(Trace::logAlgorithms())
            fprintf(Trace::logFile(),"write extent:%d\n",icbTbl->currentAddr().sector);
        writeSector(sid, icbTbl->currentAddr().sector, bp);
        bp += logicalBlockSize;
        icbTbl->bumpPtr(1);
        nwritten++;
    }
    return nwritten;
}

NSR::UINT32
NSR::Archive::readExtent(const Extent &e, BYTE *bp)
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),"copyFSDS(): prevRootFSD:\n");
        prevRootFSD.fprint(Trace::logFile());
    }

    if ( media == media_Worm ) {
         -----------------------------------------------------------------
          Must find how long the previous FSD is. It must be moved to the
          new root. This will dictate the space requirements on new root
          volume for the old FSDS.
         ----------------------------------------------------------------

        Extent fsdHistoryExt;     Where new copies go.
        Extent lastFsdNextExt;    For chaining algorithm

        UINT32 nFSDSectors      = 0;
        UINT32 nSectors         = 1;
        UINT32 nextBlankAddr    = 0;
        UINT32 lastRecordedAddr = 0;
        UINT32 lastDescNum      = 0;
        Boolean needsIndirect   = False;
        getNextSeqAddress(prevRootSid,prevRootFSD,nSectors,&nFSDSectors,
                          &nextBlankAddr,&lastDescNum,
                          &lastRecordedAddr,&needsIndirect,&bogusICBInfo);
        if (err) {
            if (err == error_Drive_blank_check && media == media_Worm) {
                err = error_None;
            }
        }
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: fsd sequence length: %d\n",nFSDSectors);
            fprintf(Trace::logFile(),"copyFSDS: last fsd number: %d\n",lastDescNum);
            fprintf(Trace::logFile(),"copyFSDS: lastRecordedAddr:: %d\n",lastRecordedAddr);
        }

         ------------------------------------------------------------
          Allocate space for previous fsd's. Also verify it is blank.
          If there is not enough space, the whole export will fail.
         ------------------------------------------------------------
        Boolean verified = False;
        UINT32 nonBlankSector;
        while ( !verified ) 
        {
              Allocate it.
            fsdHistoryExt = newRootVol.alloc( nFSDSectors, True );
              Error if not enough room
            if (!(fsdHistoryExt.len)) {
                err = error_No_free_space;
                delete fsd;
                return;
            }
              Verify that it is blank
            if (currentDevice->
                verifyBlank( newRootSid, 
                             fsdHistoryExt.start.sector, 
                             B2S(fsdHistoryExt.len), 
                             &nonBlankSector) == True) {
                verified = True;
            }
            else {
                fprintf(Trace::logFile(),"copyFSDS: WARNING: non-blank sector %d\n",
                        nonBlankSector);
            }
        }

         ------------------------------------------------------------
          Move previous fsd's, in chunks for efficiency.
          Each extent of old FSDS is read, and then copied to the new
          root.
         ------------------------------------------------------------
        UINT32 nSectorsRead;
        UINT32 nWritten = 0;
        Extent prevfsde = prevRootFSD;
        Boolean done = False;
        while (!done) 
        {
             -------------------------------------------
              Allocate a buffer for the extent.
             -------------------------------------------
            bp = new BYTE[prevfsde.len];
            if (!bp) {
                err = error_No_free_store;
                delete fsd;
                return;
            }
             -------------------------------------------
              Read in the extent.
             -------------------------------------------
            nSectorsRead = readExtent(prevfsde, bp);
            if (err) {
                if ( err == error_Drive_blank_check ) {
                    done = True; 
                }
                else {
                    delete fsd;
                    delete [] bp;
                    return;
                }
            }
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"copyFSDS:read %d sectors of old sequence\n",
                        nSectorsRead);
            }

             -------------------------------------------
              write the extent (to the new volume).
             -------------------------------------------
            for (int j = 0; j < nSectorsRead; j++) 
            {
                 -------------------------------------------
                  First, convert to a valid FSD
                 -------------------------------------------
                fsd->fromISO( bp + (j*logicalSectorSize), 
                              prevfsde.start.sector + j - partStartSector);
                if (err=fsd->error()) { 
                    delete fsd;
                    delete [] bp;
                    return;
                }
                if (j == (nSectorsRead - 1)) {
                    lastFsdNextExt.start.sector = (fsd->nextExtent.logBlockNo() +
                                                   partStartSector );
                    lastFsdNextExt.len = fsd->nextExtent.extLength();
                }
                  If this was last extent of FSDS, set to continue in
                   normal FSDS location (held in newRootFSD).
                if (done && (j == (nSectorsRead - 1))) {
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"setting fsd->nextExtent\n"); }
                    fsd->nextExtent.partRefNo(volumeTable.rootVid());
                    fsd->nextExtent.logBlockNo(newRootFSD.start.sector
                                               -partStartSector);
                    fsd->nextExtent.extLength( newRootFSD.len );
                } else {
                      Otherwise, set nextExtent field to zeros,
                      indicating that the sequence continues with the
                      next sector (normal FSDS).
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"fsd->nextExtent len = 0\n"); }
                    fsd->nextExtent.partRefNo(0);
                    fsd->nextExtent.logBlockNo(0);
                    fsd->nextExtent.extLength(0);
                }

                 -------------------------------------------
                  Translate FSD back to ISO in buffer.
                 -------------------------------------------
                fsd->toISO( bp + (j*logicalSectorSize),
                            fsdHistoryExt.start.sector + nWritten 
                            - partStartSector );
                writeSector(newRootSid,
                            fsdHistoryExt.start.sector + nWritten, 
                            bp + (j*logicalSectorSize));
                if (err) {
                    delete fsd;
                    delete [] bp;
                    return;
                }
                nWritten++;
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
        fprintf(Trace::logFile(),"copyFSDS(): prevRootFSD:\n");
        prevRootFSD.fprint(Trace::logFile());
    }

    if ( media == media_Worm ) {
         -----------------------------------------------------------------
          Must find how long the previous FSD is. It must be moved to the
          new root. This will dictate the space requirements on new root
          volume for the old FSDS.
         ----------------------------------------------------------------

        Extent fsdHistoryExt;     Where new copies go.
        Extent lastFsdNextExt;    For chaining algorithm

        UINT32 nFSDSectors      = 0;
        UINT32 nSectors         = 1;
        UINT32 nextBlankAddr    = 0;
        UINT32 lastRecordedAddr = 0;
        UINT32 lastDescNum      = 0;
        Boolean needsIndirect   = False;
        getNextSeqAddress(prevRootSid,prevRootFSD,nSectors,&nFSDSectors,
                          &nextBlankAddr,&lastDescNum,
                          &lastRecordedAddr,&needsIndirect,&bogusICBInfo);
        if (err) {
            if (err == error_Drive_blank_check && media == media_Worm) {
                err = error_None;
            }
        }
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: fsd sequence length: %d\n",nFSDSectors);
            fprintf(Trace::logFile(),"copyFSDS: last fsd number: %d\n",lastDescNum);
            fprintf(Trace::logFile(),"copyFSDS: lastRecordedAddr:: %d\n",lastRecordedAddr);
        }

         ------------------------------------------------------------
          Allocate space for previous fsd's. Also verify it is blank.
          If there is not enough space, the whole export will fail.
         ------------------------------------------------------------
        Boolean verified = False;
        UINT32 nonBlankSector;
        while ( !verified ) 
        {
              Allocate it.
            fsdHistoryExt = newRootVol.alloc( nFSDSectors, True );
              Error if not enough room
            if (!(fsdHistoryExt.len)) {
                err = error_No_free_space;
                delete fsd;
                return;
            }
              Verify that it is blank
            if (currentDevice->
                verifyBlank( newRootSid, 
                             fsdHistoryExt.start.sector, 
                             B2S(fsdHistoryExt.len), 
                             &nonBlankSector) == True) {
                verified = True;
            }
            else {
                fprintf(Trace::logFile(),"copyFSDS: WARNING: non-blank sector %d\n",
                        nonBlankSector);
            }
        }

         ------------------------------------------------------------
          Move previous fsd's, in chunks for efficiency.
          Each extent of old FSDS is read, and then copied to the new
          root.
         ------------------------------------------------------------
        UINT32 nSectorsRead;
        UINT32 nWritten = 0;
        Extent prevfsde = prevRootFSD;
        Boolean done = False;
        while (!done) 
        {
             -------------------------------------------
              Allocate a buffer for the extent.
             -------------------------------------------
            bp = new BYTE[prevfsde.len];
            if (!bp) {
                err = error_No_free_store;
                delete fsd;
                return;
            }
             -------------------------------------------
              Read in the extent.
             -------------------------------------------
            nSectorsRead = readExtent(prevfsde, bp);
            if (err) {
                if ( err == error_Drive_blank_check ) {
                    done = True; 
                }
                else {
                    delete fsd;
                    delete [] bp;
                    return;
                }
            }
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"copyFSDS:read %d sectors of old sequence\n",
                        nSectorsRead);
            }

             -------------------------------------------
              write the extent (to the new volume).
             -------------------------------------------
            for (int j = 0; j < nSectorsRead; j++) 
            {
                 -------------------------------------------
                  First, convert to a valid FSD
                 -------------------------------------------
                fsd->fromISO( bp + (j*logicalSectorSize), 
                              prevfsde.start.sector + j - partStartSector);
                if (err=fsd->error()) { 
                    delete fsd;
                    delete [] bp;
                    return;
                }
                if (j == (nSectorsRead - 1)) {
                    lastFsdNextExt.start.sector = (fsd->nextExtent.logBlockNo() +
                                                   partStartSector );
                    lastFsdNextExt.len = fsd->nextExtent.extLength();
                }
                  If this was last extent of FSDS, set to continue in
                   normal FSDS location (held in newRootFSD).
                if (done && (j == (nSectorsRead - 1))) {
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"setting fsd->nextExtent\n"); }
                    fsd->nextExtent.partRefNo(volumeTable.rootVid());
                    fsd->nextExtent.logBlockNo(newRootFSD.start.sector
                                               -partStartSector);
                    fsd->nextExtent.extLength( newRootFSD.len );
                } else {
                      Otherwise, set nextExtent field to zeros,
                      indicating that the sequence continues with the
                      next sector (normal FSDS).
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"fsd->nextExtent len = 0\n"); }
                    fsd->nextExtent.partRefNo(0);
                    fsd->nextExtent.logBlockNo(0);
                    fsd->nextExtent.extLength(0);
                }

                 -------------------------------------------
                  Translate FSD back to ISO in buffer.
                 -------------------------------------------
                fsd->toISO( bp + (j*logicalSectorSize),
                            fsdHistoryExt.start.sector + nWritten 
                            - partStartSector );
                writeSector(newRootSid,
                            fsdHistoryExt.start.sector + nWritten, 
                            bp + (j*logicalSectorSize));
                if (err) {
                    delete fsd;
                    delete [] bp;
                    return;
                }
                nWritten++;
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: fsd sequence length: %d\n",nFSDSectors);
            fprintf(Trace::logFile(),"copyFSDS: last fsd number: %d\n",lastDescNum);
            fprintf(Trace::logFile(),"copyFSDS: lastRecordedAddr:: %d\n",lastRecordedAddr);
        }

         ------------------------------------------------------------
          Allocate space for previous fsd's. Also verify it is blank.
          If there is not enough space, the whole export will fail.
         ------------------------------------------------------------
        Boolean verified = False;
        UINT32 nonBlankSector;
        while ( !verified ) 
        {
              Allocate it.
            fsdHistoryExt = newRootVol.alloc( nFSDSectors, True );
              Error if not enough room
            if (!(fsdHistoryExt.len)) {
                err = error_No_free_space;
                delete fsd;
                return;
            }
              Verify that it is blank
            if (currentDevice->
                verifyBlank( newRootSid, 
                             fsdHistoryExt.start.sector, 
                             B2S(fsdHistoryExt.len), 
                             &nonBlankSector) == True) {
                verified = True;
            }
            else {
                fprintf(Trace::logFile(),"copyFSDS: WARNING: non-blank sector %d\n",
                        nonBlankSector);
            }
        }

         ------------------------------------------------------------
          Move previous fsd's, in chunks for efficiency.
          Each extent of old FSDS is read, and then copied to the new
          root.
         ------------------------------------------------------------
        UINT32 nSectorsRead;
        UINT32 nWritten = 0;
        Extent prevfsde = prevRootFSD;
        Boolean done = False;
        while (!done) 
        {
             -------------------------------------------
              Allocate a buffer for the extent.
             -------------------------------------------
            bp = new BYTE[prevfsde.len];
            if (!bp) {
                err = error_No_free_store;
                delete fsd;
                return;
            }
             -------------------------------------------
              Read in the extent.
             -------------------------------------------
            nSectorsRead = readExtent(prevfsde, bp);
            if (err) {
                if ( err == error_Drive_blank_check ) {
                    done = True; 
                }
                else {
                    delete fsd;
                    delete [] bp;
                    return;
                }
            }
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"copyFSDS:read %d sectors of old sequence\n",
                        nSectorsRead);
            }

             -------------------------------------------
              write the extent (to the new volume).
             -------------------------------------------
            for (int j = 0; j < nSectorsRead; j++) 
            {
                 -------------------------------------------
                  First, convert to a valid FSD
                 -------------------------------------------
                fsd->fromISO( bp + (j*logicalSectorSize), 
                              prevfsde.start.sector + j - partStartSector);
                if (err=fsd->error()) { 
                    delete fsd;
                    delete [] bp;
                    return;
                }
                if (j == (nSectorsRead - 1)) {
                    lastFsdNextExt.start.sector = (fsd->nextExtent.logBlockNo() +
                                                   partStartSector );
                    lastFsdNextExt.len = fsd->nextExtent.extLength();
                }
                  If this was last extent of FSDS, set to continue in
                   normal FSDS location (held in newRootFSD).
                if (done && (j == (nSectorsRead - 1))) {
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"setting fsd->nextExtent\n"); }
                    fsd->nextExtent.partRefNo(volumeTable.rootVid());
                    fsd->nextExtent.logBlockNo(newRootFSD.start.sector
                                               -partStartSector);
                    fsd->nextExtent.extLength( newRootFSD.len );
                } else {
                      Otherwise, set nextExtent field to zeros,
                      indicating that the sequence continues with the
                      next sector (normal FSDS).
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"fsd->nextExtent len = 0\n"); }
                    fsd->nextExtent.partRefNo(0);
                    fsd->nextExtent.logBlockNo(0);
                    fsd->nextExtent.extLength(0);
                }

                 -------------------------------------------
                  Translate FSD back to ISO in buffer.
                 -------------------------------------------
                fsd->toISO( bp + (j*logicalSectorSize),
                            fsdHistoryExt.start.sector + nWritten 
                            - partStartSector );
                writeSector(newRootSid,
                            fsdHistoryExt.start.sector + nWritten, 
                            bp + (j*logicalSectorSize));
                if (err) {
                    delete fsd;
                    delete [] bp;
                    return;
                }
                nWritten++;
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
            fprintf(Trace::logFile(),"copyFSDS: fsd sequence length: %d\n",nFSDSectors);
            fprintf(Trace::logFile(),"copyFSDS: last fsd number: %d\n",lastDescNum);
            fprintf(Trace::logFile(),"copyFSDS: lastRecordedAddr:: %d\n",lastRecordedAddr);
        }

         ------------------------------------------------------------
          Allocate space for previous fsd's. Also verify it is blank.
          If there is not enough space, the whole export will fail.
         ------------------------------------------------------------
        Boolean verified = False;
        UINT32 nonBlankSector;
        while ( !verified ) 
        {
              Allocate it.
            fsdHistoryExt = newRootVol.alloc( nFSDSectors, True );
              Error if not enough room
            if (!(fsdHistoryExt.len)) {
                err = error_No_free_space;
                delete fsd;
                return;
            }
              Verify that it is blank
            if (currentDevice->
                verifyBlank( newRootSid, 
                             fsdHistoryExt.start.sector, 
                             B2S(fsdHistoryExt.len), 
                             &nonBlankSector) == True) {
                verified = True;
            }
            else {
                fprintf(Trace::logFile(),"copyFSDS: WARNING: non-blank sector %d\n",
                        nonBlankSector);
            }
        }

         ------------------------------------------------------------
          Move previous fsd's, in chunks for efficiency.
          Each extent of old FSDS is read, and then copied to the new
          root.
         ------------------------------------------------------------
        UINT32 nSectorsRead;
        UINT32 nWritten = 0;
        Extent prevfsde = prevRootFSD;
        Boolean done = False;
        while (!done) 
        {
             -------------------------------------------
              Allocate a buffer for the extent.
             -------------------------------------------
            bp = new BYTE[prevfsde.len];
            if (!bp) {
                err = error_No_free_store;
                delete fsd;
                return;
            }
             -------------------------------------------
              Read in the extent.
             -------------------------------------------
            nSectorsRead = readExtent(prevfsde, bp);
            if (err) {
                if ( err == error_Drive_blank_check ) {
                    done = True; 
                }
                else {
                    delete fsd;
                    delete [] bp;
                    return;
                }
            }
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"copyFSDS:read %d sectors of old sequence\n",
                        nSectorsRead);
            }

             -------------------------------------------
              write the extent (to the new volume).
             -------------------------------------------
            for (int j = 0; j < nSectorsRead; j++) 
            {
                 -------------------------------------------
                  First, convert to a valid FSD
                 -------------------------------------------
                fsd->fromISO( bp + (j*logicalSectorSize), 
                              prevfsde.start.sector + j - partStartSector);
                if (err=fsd->error()) { 
                    delete fsd;
                    delete [] bp;
                    return;
                }
                if (j == (nSectorsRead - 1)) {
                    lastFsdNextExt.start.sector = (fsd->nextExtent.logBlockNo() +
                                                   partStartSector );
                    lastFsdNextExt.len = fsd->nextExtent.extLength();
                }
                  If this was last extent of FSDS, set to continue in
                   normal FSDS location (held in newRootFSD).
                if (done && (j == (nSectorsRead - 1))) {
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"setting fsd->nextExtent\n"); }
                    fsd->nextExtent.partRefNo(volumeTable.rootVid());
                    fsd->nextExtent.logBlockNo(newRootFSD.start.sector
                                               -partStartSector);
                    fsd->nextExtent.extLength( newRootFSD.len );
                } else {
                      Otherwise, set nextExtent field to zeros,
                      indicating that the sequence continues with the
                      next sector (normal FSDS).
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"fsd->nextExtent len = 0\n"); }
                    fsd->nextExtent.partRefNo(0);
                    fsd->nextExtent.logBlockNo(0);
                    fsd->nextExtent.extLength(0);
                }

                 -------------------------------------------
                  Translate FSD back to ISO in buffer.
                 -------------------------------------------
                fsd->toISO( bp + (j*logicalSectorSize),
                            fsdHistoryExt.start.sector + nWritten 
                            - partStartSector );
                writeSector(newRootSid,
                            fsdHistoryExt.start.sector + nWritten, 
                            bp + (j*logicalSectorSize));
                if (err) {
                    delete fsd;
                    delete [] bp;
                    return;
                }
                nWritten++;
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
            fprintf(Trace::logFile(),"copyFSDS: last fsd number: %d\n",lastDescNum);
            fprintf(Trace::logFile(),"copyFSDS: lastRecordedAddr:: %d\n",lastRecordedAddr);
        }

         ------------------------------------------------------------
          Allocate space for previous fsd's. Also verify it is blank.
          If there is not enough space, the whole export will fail.
         ------------------------------------------------------------
        Boolean verified = False;
        UINT32 nonBlankSector;
        while ( !verified ) 
        {
              Allocate it.
            fsdHistoryExt = newRootVol.alloc( nFSDSectors, True );
              Error if not enough room
            if (!(fsdHistoryExt.len)) {
                err = error_No_free_space;
                delete fsd;
                return;
            }
              Verify that it is blank
            if (currentDevice->
                verifyBlank( newRootSid, 
                             fsdHistoryExt.start.sector, 
                             B2S(fsdHistoryExt.len), 
                             &nonBlankSector) == True) {
                verified = True;
            }
            else {
                fprintf(Trace::logFile(),"copyFSDS: WARNING: non-blank sector %d\n",
                        nonBlankSector);
            }
        }

         ------------------------------------------------------------
          Move previous fsd's, in chunks for efficiency.
          Each extent of old FSDS is read, and then copied to the new
          root.
         ------------------------------------------------------------
        UINT32 nSectorsRead;
        UINT32 nWritten = 0;
        Extent prevfsde = prevRootFSD;
        Boolean done = False;
        while (!done) 
        {
             -------------------------------------------
              Allocate a buffer for the extent.
             -------------------------------------------
            bp = new BYTE[prevfsde.len];
            if (!bp) {
                err = error_No_free_store;
                delete fsd;
                return;
            }
             -------------------------------------------
              Read in the extent.
             -------------------------------------------
            nSectorsRead = readExtent(prevfsde, bp);
            if (err) {
                if ( err == error_Drive_blank_check ) {
                    done = True; 
                }
                else {
                    delete fsd;
                    delete [] bp;
                    return;
                }
            }
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"copyFSDS:read %d sectors of old sequence\n",
                        nSectorsRead);
            }

             -------------------------------------------
              write the extent (to the new volume).
             -------------------------------------------
            for (int j = 0; j < nSectorsRead; j++) 
            {
                 -------------------------------------------
                  First, convert to a valid FSD
                 -------------------------------------------
                fsd->fromISO( bp + (j*logicalSectorSize), 
                              prevfsde.start.sector + j - partStartSector);
                if (err=fsd->error()) { 
                    delete fsd;
                    delete [] bp;
                    return;
                }
                if (j == (nSectorsRead - 1)) {
                    lastFsdNextExt.start.sector = (fsd->nextExtent.logBlockNo() +
                                                   partStartSector );
                    lastFsdNextExt.len = fsd->nextExtent.extLength();
                }
                  If this was last extent of FSDS, set to continue in
                   normal FSDS location (held in newRootFSD).
                if (done && (j == (nSectorsRead - 1))) {
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"setting fsd->nextExtent\n"); }
                    fsd->nextExtent.partRefNo(volumeTable.rootVid());
                    fsd->nextExtent.logBlockNo(newRootFSD.start.sector
                                               -partStartSector);
                    fsd->nextExtent.extLength( newRootFSD.len );
                } else {
                      Otherwise, set nextExtent field to zeros,
                      indicating that the sequence continues with the
                      next sector (normal FSDS).
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"fsd->nextExtent len = 0\n"); }
                    fsd->nextExtent.partRefNo(0);
                    fsd->nextExtent.logBlockNo(0);
                    fsd->nextExtent.extLength(0);
                }

                 -------------------------------------------
                  Translate FSD back to ISO in buffer.
                 -------------------------------------------
                fsd->toISO( bp + (j*logicalSectorSize),
                            fsdHistoryExt.start.sector + nWritten 
                            - partStartSector );
                writeSector(newRootSid,
                            fsdHistoryExt.start.sector + nWritten, 
                            bp + (j*logicalSectorSize));
                if (err) {
                    delete fsd;
                    delete [] bp;
                    return;
                }
                nWritten++;
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
            else {
                fprintf(Trace::logFile(),"copyFSDS: WARNING: non-blank sector %d\n",
                        nonBlankSector);
            }
        }

         ------------------------------------------------------------
          Move previous fsd's, in chunks for efficiency.
          Each extent of old FSDS is read, and then copied to the new
          root.
         ------------------------------------------------------------
        UINT32 nSectorsRead;
        UINT32 nWritten = 0;
        Extent prevfsde = prevRootFSD;
        Boolean done = False;
        while (!done) 
        {
             -------------------------------------------
              Allocate a buffer for the extent.
             -------------------------------------------
            bp = new BYTE[prevfsde.len];
            if (!bp) {
                err = error_No_free_store;
                delete fsd;
                return;
            }
             -------------------------------------------
              Read in the extent.
             -------------------------------------------
            nSectorsRead = readExtent(prevfsde, bp);
            if (err) {
                if ( err == error_Drive_blank_check ) {
                    done = True; 
                }
                else {
                    delete fsd;
                    delete [] bp;
                    return;
                }
            }
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"copyFSDS:read %d sectors of old sequence\n",
                        nSectorsRead);
            }

             -------------------------------------------
              write the extent (to the new volume).
             -------------------------------------------
            for (int j = 0; j < nSectorsRead; j++) 
            {
                 -------------------------------------------
                  First, convert to a valid FSD
                 -------------------------------------------
                fsd->fromISO( bp + (j*logicalSectorSize), 
                              prevfsde.start.sector + j - partStartSector);
                if (err=fsd->error()) { 
                    delete fsd;
                    delete [] bp;
                    return;
                }
                if (j == (nSectorsRead - 1)) {
                    lastFsdNextExt.start.sector = (fsd->nextExtent.logBlockNo() +
                                                   partStartSector );
                    lastFsdNextExt.len = fsd->nextExtent.extLength();
                }
                  If this was last extent of FSDS, set to continue in
                   normal FSDS location (held in newRootFSD).
                if (done && (j == (nSectorsRead - 1))) {
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"setting fsd->nextExtent\n"); }
                    fsd->nextExtent.partRefNo(volumeTable.rootVid());
                    fsd->nextExtent.logBlockNo(newRootFSD.start.sector
                                               -partStartSector);
                    fsd->nextExtent.extLength( newRootFSD.len );
                } else {
                      Otherwise, set nextExtent field to zeros,
                      indicating that the sequence continues with the
                      next sector (normal FSDS).
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"fsd->nextExtent len = 0\n"); }
                    fsd->nextExtent.partRefNo(0);
                    fsd->nextExtent.logBlockNo(0);
                    fsd->nextExtent.extLength(0);
                }

                 -------------------------------------------
                  Translate FSD back to ISO in buffer.
                 -------------------------------------------
                fsd->toISO( bp + (j*logicalSectorSize),
                            fsdHistoryExt.start.sector + nWritten 
                            - partStartSector );
                writeSector(newRootSid,
                            fsdHistoryExt.start.sector + nWritten, 
                            bp + (j*logicalSectorSize));
                if (err) {
                    delete fsd;
                    delete [] bp;
                    return;
                }
                nWritten++;
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"copyFSDS:read %d sectors of old sequence\n",
                        nSectorsRead);
            }

             -------------------------------------------
              write the extent (to the new volume).
             -------------------------------------------
            for (int j = 0; j < nSectorsRead; j++) 
            {
                 -------------------------------------------
                  First, convert to a valid FSD
                 -------------------------------------------
                fsd->fromISO( bp + (j*logicalSectorSize), 
                              prevfsde.start.sector + j - partStartSector);
                if (err=fsd->error()) { 
                    delete fsd;
                    delete [] bp;
                    return;
                }
                if (j == (nSectorsRead - 1)) {
                    lastFsdNextExt.start.sector = (fsd->nextExtent.logBlockNo() +
                                                   partStartSector );
                    lastFsdNextExt.len = fsd->nextExtent.extLength();
                }
                  If this was last extent of FSDS, set to continue in
                   normal FSDS location (held in newRootFSD).
                if (done && (j == (nSectorsRead - 1))) {
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"setting fsd->nextExtent\n"); }
                    fsd->nextExtent.partRefNo(volumeTable.rootVid());
                    fsd->nextExtent.logBlockNo(newRootFSD.start.sector
                                               -partStartSector);
                    fsd->nextExtent.extLength( newRootFSD.len );
                } else {
                      Otherwise, set nextExtent field to zeros,
                      indicating that the sequence continues with the
                      next sector (normal FSDS).
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"fsd->nextExtent len = 0\n"); }
                    fsd->nextExtent.partRefNo(0);
                    fsd->nextExtent.logBlockNo(0);
                    fsd->nextExtent.extLength(0);
                }

                 -------------------------------------------
                  Translate FSD back to ISO in buffer.
                 -------------------------------------------
                fsd->toISO( bp + (j*logicalSectorSize),
                            fsdHistoryExt.start.sector + nWritten 
                            - partStartSector );
                writeSector(newRootSid,
                            fsdHistoryExt.start.sector + nWritten, 
                            bp + (j*logicalSectorSize));
                if (err) {
                    delete fsd;
                    delete [] bp;
                    return;
                }
                nWritten++;
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
                if (done && (j == (nSectorsRead - 1))) {
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"setting fsd->nextExtent\n"); }
                    fsd->nextExtent.partRefNo(volumeTable.rootVid());
                    fsd->nextExtent.logBlockNo(newRootFSD.start.sector
                                               -partStartSector);
                    fsd->nextExtent.extLength( newRootFSD.len );
                } else {
                      Otherwise, set nextExtent field to zeros,
                      indicating that the sequence continues with the
                      next sector (normal FSDS).
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"fsd->nextExtent len = 0\n"); }
                    fsd->nextExtent.partRefNo(0);
                    fsd->nextExtent.logBlockNo(0);
                    fsd->nextExtent.extLength(0);
                }

                 -------------------------------------------
                  Translate FSD back to ISO in buffer.
                 -------------------------------------------
                fsd->toISO( bp + (j*logicalSectorSize),
                            fsdHistoryExt.start.sector + nWritten 
                            - partStartSector );
                writeSector(newRootSid,
                            fsdHistoryExt.start.sector + nWritten, 
                            bp + (j*logicalSectorSize));
                if (err) {
                    delete fsd;
                    delete [] bp;
                    return;
                }
                nWritten++;
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
                      next sector (normal FSDS).
                    if(Trace::logAlgorithms()) { fprintf(Trace::logFile(),"fsd->nextExtent len = 0\n"); }
                    fsd->nextExtent.partRefNo(0);
                    fsd->nextExtent.logBlockNo(0);
                    fsd->nextExtent.extLength(0);
                }

                 -------------------------------------------
                  Translate FSD back to ISO in buffer.
                 -------------------------------------------
                fsd->toISO( bp + (j*logicalSectorSize),
                            fsdHistoryExt.start.sector + nWritten 
                            - partStartSector );
                writeSector(newRootSid,
                            fsdHistoryExt.start.sector + nWritten, 
                            bp + (j*logicalSectorSize));
                if (err) {
                    delete fsd;
                    delete [] bp;
                    return;
                }
                nWritten++;
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
                    fprintf(Trace::logFile(),"wrote new fsd at sid:%d\n",newRootSid); 
                    fprintf(Trace::logFile(),"               sector:%d\n",fsdHistoryExt.start.sector+j); 
                }
            }

             -----------------------------------------------------------
              Extent was full - no blank sectors, so look at nextExtent
              field of last FSD for where to continue.
             -----------------------------------------------------------
            if (!done) {
                 -----------------------------------------------------------
                  look for possible next extent identified in last fsd read
                 -----------------------------------------------------------
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
                if(Trace::logAlgorithms()) { 
                    fprintf(Trace::logFile(),"lastFsdNextExt.len:%d\n",
                            lastFsdNextExt.len);
                }
                prevfsde = lastFsdNextExt;
                 -----------------------------------------------------------
                  If there was no continuation, then it was the last FSD
                  that exists. Therefore, copy is complete. NOTE that
                  nextExtent field of FSD was already filled in inside
                  the loop.
                 -----------------------------------------------------------
                if (prevfsde.len == 0) {
                    done = True;
                }
            }
        }
          Update information in root volume.
        newRootFSD = fsdHistoryExt;

          Delete buffer
        delete [] bp;
        
    } else {                      media_Rewritable

          Simply need to copy the single FSD from previous root to
          new root.

          Error if a longer FSDS than a single sector
        if ( (prevRootFSD.len != logicalBlockSize) ||
             (newRootFSD.len  != logicalBlockSize) ) {
            err = error_Wrong_FSDS_extent_length;
            delete fsd;
            return;
        }
        
          Allocate a buffer for the extent.
        bp = new BYTE[prevRootFSD.len];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return;
        }

          Read in the extent.
        readExtent(prevRootFSD, bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }

          Dump out where read.
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
            fprintf(Trace::logFile(),"copyFSDS: read old FSDS at ");
            prevRootFSD.fprint(Trace::logFile());
        }

          Translate the FSD to match new address, etc.
        fsd->fromISO( bp, (prevRootFSD.start.sector
                           - partStartSector));
        if (err=fsd->error()) { 
            delete fsd;
            delete [] bp;
            return;
        }
        
          Set nextExtent field to zeros, indicating that the sequence
          continues with the next sector (normal FSDS).
          These should already be set, but...
        fsd->nextExtent.partRefNo(0);
        fsd->nextExtent.logBlockNo(0);
        fsd->nextExtent.extLength(0);

          Translate FSD back to ISO in buffer.
        fsd->toISO( bp, (newRootFSD.start.sector 
                         - partStartSector ));
          Write it out.
        writeSector(newRootSid,
                    newRootFSD.start.sector, 
                    bp);
        if (err) {
            delete fsd;
            delete [] bp;
            return;
        }
          Dump out where written
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
        if(Trace::logAlgorithms()) { 
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
            fprintf(Trace::logFile(),"copyFSDS(): wrote new fsd at: ");
            newRootFSD.fprint(Trace::logFile());
        }
#ifdef _FSDS_TD_
          Write a single TD to terminate sequence
        writeTD( newRootSid,
                 newRootVol.fsdsExt.start.sector - partStartSector +1,
                 newRootVol.fsdsExt.start.sector +1 );
          No check for errors, because this is the last action
#endif   _FSDS_TD_
        
          newRootFSD remains unchanged.

          Delete buffer
        delete [] bp;
    }
      delete FSD
    delete fsd;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readBlind
 
   This method reads a sector at the supplied address, and then if
   the sector contains a valid descriptor, reallocates the supplied
   buffer and then reads in the entire descriptor. The
   passed-in-by-reference SearchDescriptor is left reflecting the
   state of what it found on the disk.
 
  ARGS:
     sid_t sid,            IN      Surface ID
     UINT32 addr,          IN      Sector
     SearchDescriptor & sd IN      Search Descriptor
     BYTE **bpp            IN/OUT  Pointer to data
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import
 ----------------------------------------------------------------------
void
NSR::Archive::readBlind(sid_t sid,
                        UINT32 addr,
                        SearchDescriptor & sd, 
                        BYTE **bpp)
        if ( e.start.sector + ovf > e.start.sector + B2S(e.len) ) {
            fprintf(Trace::logFile(),"writeSpaceEntry: no room to write all extents: truncating\n");
            ovf = B2S(e.len) - 1;
            N = extentsPerUSE + (ovf * extentsPerAED);
        }
    }
    
    UINT32 n;
    if ( ovf > 0 )  {
        n = extentsPerUSE - 1;
    }
    else {
        n = N;
    }
    use = new UnallocatedSpaceEntry(n + (ovf > 0));
    if (!use) {
        err = error_No_free_store;
        delete [] bp;
        return;
    }
    N -= n;

      Initialize lastUSEICBInfo for getNextSeqAddress()
    lastUSEICBInfo.priNoDirectEntries = 0;
    lastUSEICBInfo.maxEntries = (B2S(e.len))/(ovf+1);
    lastUSEICBInfo.strategyParm = (B2S(e.len))/(ovf+1) - 1;

    if (media == media_Worm) {
        extent.start.sector = e.start.sector;
        extent.len = e.len;
        nRead = 0;
        lastDescNum = 0;
        lastRecordedAddr = 0;
        nextBlankAddr = 0;
        needsIndirect = False;
        getNextSeqAddress(sid,extent,1,&nRead,&nextBlankAddr,&lastDescNum,
                          &lastRecordedAddr,&needsIndirect,&lastUSEICBInfo);
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                    "writeSpaceEntry(): nextBlankAddr = %d;lastRecordedAddr = %d.\n",
                    nextBlankAddr,lastRecordedAddr);
        }
        if (err) {
            if (err == error_Drive_blank_check && media == media_Worm) {
                err = error_None;
            }
        }
        if (needsIndirect) {
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"writeSpaceEntry(): needs Indirect\n");
            }
            if (partitionSeqAllowed) {
                getSeqExtent(sid,incrementalSeqSectors,extent); 
                if (err) {
                    delete [] bp;
                    delete use;
                    return;
                }
                UINT16 strategyParm = incrementalSeqSectors - 1;
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "writeSpaceEntry(): writing Indirect at %d.\n",
                            nextBlankAddr);
                }
                writeIndirectEntry(sid, nextBlankAddr,extent,lastDescNum,
                                   incrementalSeqSectors,strategyParm);
                if (err) {
                    delete [] bp;
                    delete use;
                    return;
                }
                nextBlankAddr = extent.start.sector;
            }
            else {
                if (vid == volumeTable.rootVid()) {
                      need new root volume
                    err = error_End_of_Sequence_space;
                    delete [] bp;
                    delete use;
                    return;
                }
                else { 
                      non-root volume - write a use indicating no space left.
                    N = 0;
                    n = 0;
                    ovf = 0;
                }
            }
        }
        addr = nextBlankAddr;
    }
    use->icbTag.icbType(icb_UnallocatedSpaceEntry);     never changes
      !!! The following logic may not be correct. It ensures that MO
      conforms, but has not been checked for WORM conformance.
      - DVM 4/30/96
    if (media == media_Worm) {
        if (use->icbTag.priNoDirectEntries) {
            use->icbTag.priNoDirectEntries =
                lastUSEICBInfo.priNoDirectEntries + 1;
        }
        use->icbTag.strategyType   = 2;
        use->icbTag.maxEntries     = lastUSEICBInfo.maxEntries;
        use->icbTag.strategyParm   = lastUSEICBInfo.strategyParm;
        use->icbTag.parentICB.logicalBlockNo = e.start.sector - partStartSector;
        use->icbTag.parentICB.partitionRefNo = vid;
    } else {
        use->icbTag.priNoDirectEntries = 0;
        use->icbTag.strategyType       = 4;
        use->icbTag.maxEntries         = 1;
        use->icbTag.strategyParm       = 0;
        use->icbTag.parentICB.logicalBlockNo = 0;
        use->icbTag.parentICB.partitionRefNo = 0;
    }

    ShortAllocDesc *adt = use->shortADT();
    
    for ( j=0; j 0 ) {
        if (media != media_Worm) {
            adt[n].extType    ( ext_Recorded );
            adt[n].logBlockNo ( addr + 1 - partStartSector );
            adt[n].extLength  ( logicalBlockSize );
            adt[n].markContinued();
        }
        else {
            if (partitionSeqAllowed) {
                  get space for aed's
                getSeqExtent(sid,ovf,extent); 
                if (err) {
                    delete use;
                    delete [] bp;
                    return;
                }
                addr = extent.start.sector;
            }
            else {
                fprintf(Trace::logFile(),"writeSpace: aed's can't be written\n");
                adt[index].extType    ( spcType );
                adt[index].logBlockNo ( space[index].start.sector - partStartSector );
                adt[index].extLength  ( space[index].len );
                ovf = 0;
            }
        }
    }

      write the unallocated space entry
    memset( bp, 0, logicalBlockSize );
    use->toISO( bp, addr - partStartSector );
    if (err = use->error()) {
        delete use;
        delete [] bp;
        return;
    }
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),
                "writeSpaceEntry(): writing USE to sid %d sector %d\n",
                sid,addr);
    }
    writeBlock( sid, addr, bp );
    delete use;
    if (err) {
        delete [] bp;
        return;
    }
      write required AEDs to hold remaining extents
    if (media != media_Worm) {
        addr++;
    }
    else {
        addr = extent.start.sector;
    }
    for ( UINT32 i=0; i extentsPerAED ) 
            n = extentsPerAED - 1;
        else
            n = N;

        N -= n;

        aed = new AllocExtentDesc( (n + ( ovf != (i+1) )), ad_Short );
        if (!aed) {
            err = error_No_free_store;
            delete [] bp;
            return;
        }
        memset( bp, 0, logicalBlockSize );

          load short ads in allocation extent desc block
        adt = aed->shortADT();
        for ( j=0; jtoISO( bp, addr - partStartSector );
        if (err = aed->error()) {
            delete aed;
            delete [] bp;
            return;
        }
        delete aed;

          Dump it out if tracing
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                    "writeSpaceEntry(): writing AED to sid %d sector %d\n",
                    sid,addr);
        }

          write the AED block
        writeBlock( sid, addr, bp );

        if (err) {
            delete [] bp;
            return;
        }
    }
    delete [] bp;
    return;
}

 ----------------------------------------------------------------------
  METHOD: Archive::searchForDesc  --DVM
       
   Searches a single VDS for a specific descriptor type.
 
   If the descriptor is found, the vdsNum is returned, along with a
   modified extent. The address of the extent where the descriptor
   was found.
 
  ARGS:
   TagType descrType IN      Descriptor to search for
   Extent  seqExtent IN/OUT  Location to be searched
   UINT32  vdsNum    OUT     Volume Descriptor Sequence Number
 
  RETURNS:
   True if found; False if not.
  
  PRE-CONDITIONS: open archive object.
  
  POST-CONDITIONS:
  
  ERRORS:
   error_No_free_store
   misc device or fromISO errors
  
  NOTES:
   Does not currently follow VDS continuations. Could be added later.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::searchForDesc( TagType searchDescType,
                             Extent & seqExtent,
                             UINT32 & vdsNum ) 
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                    "writeSpaceEntry(): nextBlankAddr = %d;lastRecordedAddr = %d.\n",
                    nextBlankAddr,lastRecordedAddr);
        }
        if (err) {
            if (err == error_Drive_blank_check && media == media_Worm) {
                err = error_None;
            }
        }
        if (needsIndirect) {
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"writeSpaceEntry(): needs Indirect\n");
            }
            if (partitionSeqAllowed) {
                getSeqExtent(sid,incrementalSeqSectors,extent); 
                if (err) {
                    delete [] bp;
                    delete use;
                    return;
                }
                UINT16 strategyParm = incrementalSeqSectors - 1;
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "writeSpaceEntry(): writing Indirect at %d.\n",
                            nextBlankAddr);
                }
                writeIndirectEntry(sid, nextBlankAddr,extent,lastDescNum,
                                   incrementalSeqSectors,strategyParm);
                if (err) {
                    delete [] bp;
                    delete use;
                    return;
                }
                nextBlankAddr = extent.start.sector;
            }
            else {
                if (vid == volumeTable.rootVid()) {
                      need new root volume
                    err = error_End_of_Sequence_space;
                    delete [] bp;
                    delete use;
                    return;
                }
                else { 
                      non-root volume - write a use indicating no space left.
                    N = 0;
                    n = 0;
                    ovf = 0;
                }
            }
        }
        addr = nextBlankAddr;
    }
    use->icbTag.icbType(icb_UnallocatedSpaceEntry);     never changes
      !!! The following logic may not be correct. It ensures that MO
      conforms, but has not been checked for WORM conformance.
      - DVM 4/30/96
    if (media == media_Worm) {
        if (use->icbTag.priNoDirectEntries) {
            use->icbTag.priNoDirectEntries =
                lastUSEICBInfo.priNoDirectEntries + 1;
        }
        use->icbTag.strategyType   = 2;
        use->icbTag.maxEntries     = lastUSEICBInfo.maxEntries;
        use->icbTag.strategyParm   = lastUSEICBInfo.strategyParm;
        use->icbTag.parentICB.logicalBlockNo = e.start.sector - partStartSector;
        use->icbTag.parentICB.partitionRefNo = vid;
    } else {
        use->icbTag.priNoDirectEntries = 0;
        use->icbTag.strategyType       = 4;
        use->icbTag.maxEntries         = 1;
        use->icbTag.strategyParm       = 0;
        use->icbTag.parentICB.logicalBlockNo = 0;
        use->icbTag.parentICB.partitionRefNo = 0;
    }

    ShortAllocDesc *adt = use->shortADT();
    
    for ( j=0; j 0 ) {
        if (media != media_Worm) {
            adt[n].extType    ( ext_Recorded );
            adt[n].logBlockNo ( addr + 1 - partStartSector );
            adt[n].extLength  ( logicalBlockSize );
            adt[n].markContinued();
        }
        else {
            if (partitionSeqAllowed) {
                  get space for aed's
                getSeqExtent(sid,ovf,extent); 
                if (err) {
                    delete use;
                    delete [] bp;
                    return;
                }
                addr = extent.start.sector;
            }
            else {
                fprintf(Trace::logFile(),"writeSpace: aed's can't be written\n");
                adt[index].extType    ( spcType );
                adt[index].logBlockNo ( space[index].start.sector - partStartSector );
                adt[index].extLength  ( space[index].len );
                ovf = 0;
            }
        }
    }

      write the unallocated space entry
    memset( bp, 0, logicalBlockSize );
    use->toISO( bp, addr - partStartSector );
    if (err = use->error()) {
        delete use;
        delete [] bp;
        return;
    }
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),
                "writeSpaceEntry(): writing USE to sid %d sector %d\n",
                sid,addr);
    }
    writeBlock( sid, addr, bp );
    delete use;
    if (err) {
        delete [] bp;
        return;
    }
      write required AEDs to hold remaining extents
    if (media != media_Worm) {
        addr++;
    }
    else {
        addr = extent.start.sector;
    }
    for ( UINT32 i=0; i extentsPerAED ) 
            n = extentsPerAED - 1;
        else
            n = N;

        N -= n;

        aed = new AllocExtentDesc( (n + ( ovf != (i+1) )), ad_Short );
        if (!aed) {
            err = error_No_free_store;
            delete [] bp;
            return;
        }
        memset( bp, 0, logicalBlockSize );

          load short ads in allocation extent desc block
        adt = aed->shortADT();
        for ( j=0; jtoISO( bp, addr - partStartSector );
        if (err = aed->error()) {
            delete aed;
            delete [] bp;
            return;
        }
        delete aed;

          Dump it out if tracing
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                    "writeSpaceEntry(): writing AED to sid %d sector %d\n",
                    sid,addr);
        }

          write the AED block
        writeBlock( sid, addr, bp );

        if (err) {
            delete [] bp;
            return;
        }
    }
    delete [] bp;
    return;
}

 ----------------------------------------------------------------------
  METHOD: Archive::searchForDesc  --DVM
       
   Searches a single VDS for a specific descriptor type.
 
   If the descriptor is found, the vdsNum is returned, along with a
   modified extent. The address of the extent where the descriptor
   was found.
 
  ARGS:
   TagType descrType IN      Descriptor to search for
   Extent  seqExtent IN/OUT  Location to be searched
   UINT32  vdsNum    OUT     Volume Descriptor Sequence Number
 
  RETURNS:
   True if found; False if not.
  
  PRE-CONDITIONS: open archive object.
  
  POST-CONDITIONS:
  
  ERRORS:
   error_No_free_store
   misc device or fromISO errors
  
  NOTES:
   Does not currently follow VDS continuations. Could be added later.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::searchForDesc( TagType searchDescType,
                             Extent & seqExtent,
                             UINT32 & vdsNum ) 
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"writeSpaceEntry(): needs Indirect\n");
            }
            if (partitionSeqAllowed) {
                getSeqExtent(sid,incrementalSeqSectors,extent); 
                if (err) {
                    delete [] bp;
                    delete use;
                    return;
                }
                UINT16 strategyParm = incrementalSeqSectors - 1;
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "writeSpaceEntry(): writing Indirect at %d.\n",
                            nextBlankAddr);
                }
                writeIndirectEntry(sid, nextBlankAddr,extent,lastDescNum,
                                   incrementalSeqSectors,strategyParm);
                if (err) {
                    delete [] bp;
                    delete use;
                    return;
                }
                nextBlankAddr = extent.start.sector;
            }
            else {
                if (vid == volumeTable.rootVid()) {
                      need new root volume
                    err = error_End_of_Sequence_space;
                    delete [] bp;
                    delete use;
                    return;
                }
                else { 
                      non-root volume - write a use indicating no space left.
                    N = 0;
                    n = 0;
                    ovf = 0;
                }
            }
        }
        addr = nextBlankAddr;
    }
    use->icbTag.icbType(icb_UnallocatedSpaceEntry);     never changes
      !!! The following logic may not be correct. It ensures that MO
      conforms, but has not been checked for WORM conformance.
      - DVM 4/30/96
    if (media == media_Worm) {
        if (use->icbTag.priNoDirectEntries) {
            use->icbTag.priNoDirectEntries =
                lastUSEICBInfo.priNoDirectEntries + 1;
        }
        use->icbTag.strategyType   = 2;
        use->icbTag.maxEntries     = lastUSEICBInfo.maxEntries;
        use->icbTag.strategyParm   = lastUSEICBInfo.strategyParm;
        use->icbTag.parentICB.logicalBlockNo = e.start.sector - partStartSector;
        use->icbTag.parentICB.partitionRefNo = vid;
    } else {
        use->icbTag.priNoDirectEntries = 0;
        use->icbTag.strategyType       = 4;
        use->icbTag.maxEntries         = 1;
        use->icbTag.strategyParm       = 0;
        use->icbTag.parentICB.logicalBlockNo = 0;
        use->icbTag.parentICB.partitionRefNo = 0;
    }

    ShortAllocDesc *adt = use->shortADT();
    
    for ( j=0; j 0 ) {
        if (media != media_Worm) {
            adt[n].extType    ( ext_Recorded );
            adt[n].logBlockNo ( addr + 1 - partStartSector );
            adt[n].extLength  ( logicalBlockSize );
            adt[n].markContinued();
        }
        else {
            if (partitionSeqAllowed) {
                  get space for aed's
                getSeqExtent(sid,ovf,extent); 
                if (err) {
                    delete use;
                    delete [] bp;
                    return;
                }
                addr = extent.start.sector;
            }
            else {
                fprintf(Trace::logFile(),"writeSpace: aed's can't be written\n");
                adt[index].extType    ( spcType );
                adt[index].logBlockNo ( space[index].start.sector - partStartSector );
                adt[index].extLength  ( space[index].len );
                ovf = 0;
            }
        }
    }

      write the unallocated space entry
    memset( bp, 0, logicalBlockSize );
    use->toISO( bp, addr - partStartSector );
    if (err = use->error()) {
        delete use;
        delete [] bp;
        return;
    }
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),
                "writeSpaceEntry(): writing USE to sid %d sector %d\n",
                sid,addr);
    }
    writeBlock( sid, addr, bp );
    delete use;
    if (err) {
        delete [] bp;
        return;
    }
      write required AEDs to hold remaining extents
    if (media != media_Worm) {
        addr++;
    }
    else {
        addr = extent.start.sector;
    }
    for ( UINT32 i=0; i extentsPerAED ) 
            n = extentsPerAED - 1;
        else
            n = N;

        N -= n;

        aed = new AllocExtentDesc( (n + ( ovf != (i+1) )), ad_Short );
        if (!aed) {
            err = error_No_free_store;
            delete [] bp;
            return;
        }
        memset( bp, 0, logicalBlockSize );

          load short ads in allocation extent desc block
        adt = aed->shortADT();
        for ( j=0; jtoISO( bp, addr - partStartSector );
        if (err = aed->error()) {
            delete aed;
            delete [] bp;
            return;
        }
        delete aed;

          Dump it out if tracing
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                    "writeSpaceEntry(): writing AED to sid %d sector %d\n",
                    sid,addr);
        }

          write the AED block
        writeBlock( sid, addr, bp );

        if (err) {
            delete [] bp;
            return;
        }
    }
    delete [] bp;
    return;
}

 ----------------------------------------------------------------------
  METHOD: Archive::searchForDesc  --DVM
       
   Searches a single VDS for a specific descriptor type.
 
   If the descriptor is found, the vdsNum is returned, along with a
   modified extent. The address of the extent where the descriptor
   was found.
 
  ARGS:
   TagType descrType IN      Descriptor to search for
   Extent  seqExtent IN/OUT  Location to be searched
   UINT32  vdsNum    OUT     Volume Descriptor Sequence Number
 
  RETURNS:
   True if found; False if not.
  
  PRE-CONDITIONS: open archive object.
  
  POST-CONDITIONS:
  
  ERRORS:
   error_No_free_store
   misc device or fromISO errors
  
  NOTES:
   Does not currently follow VDS continuations. Could be added later.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::searchForDesc( TagType searchDescType,
                             Extent & seqExtent,
                             UINT32 & vdsNum ) 
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "writeSpaceEntry(): writing Indirect at %d.\n",
                            nextBlankAddr);
                }
                writeIndirectEntry(sid, nextBlankAddr,extent,lastDescNum,
                                   incrementalSeqSectors,strategyParm);
                if (err) {
                    delete [] bp;
                    delete use;
                    return;
                }
                nextBlankAddr = extent.start.sector;
            }
            else {
                if (vid == volumeTable.rootVid()) {
                      need new root volume
                    err = error_End_of_Sequence_space;
                    delete [] bp;
                    delete use;
                    return;
                }
                else { 
                      non-root volume - write a use indicating no space left.
                    N = 0;
                    n = 0;
                    ovf = 0;
                }
            }
        }
        addr = nextBlankAddr;
    }
    use->icbTag.icbType(icb_UnallocatedSpaceEntry);     never changes
      !!! The following logic may not be correct. It ensures that MO
      conforms, but has not been checked for WORM conformance.
      - DVM 4/30/96
    if (media == media_Worm) {
        if (use->icbTag.priNoDirectEntries) {
            use->icbTag.priNoDirectEntries =
                lastUSEICBInfo.priNoDirectEntries + 1;
        }
        use->icbTag.strategyType   = 2;
        use->icbTag.maxEntries     = lastUSEICBInfo.maxEntries;
        use->icbTag.strategyParm   = lastUSEICBInfo.strategyParm;
        use->icbTag.parentICB.logicalBlockNo = e.start.sector - partStartSector;
        use->icbTag.parentICB.partitionRefNo = vid;
    } else {
        use->icbTag.priNoDirectEntries = 0;
        use->icbTag.strategyType       = 4;
        use->icbTag.maxEntries         = 1;
        use->icbTag.strategyParm       = 0;
        use->icbTag.parentICB.logicalBlockNo = 0;
        use->icbTag.parentICB.partitionRefNo = 0;
    }

    ShortAllocDesc *adt = use->shortADT();
    
    for ( j=0; j 0 ) {
        if (media != media_Worm) {
            adt[n].extType    ( ext_Recorded );
            adt[n].logBlockNo ( addr + 1 - partStartSector );
            adt[n].extLength  ( logicalBlockSize );
            adt[n].markContinued();
        }
        else {
            if (partitionSeqAllowed) {
                  get space for aed's
                getSeqExtent(sid,ovf,extent); 
                if (err) {
                    delete use;
                    delete [] bp;
                    return;
                }
                addr = extent.start.sector;
            }
            else {
                fprintf(Trace::logFile(),"writeSpace: aed's can't be written\n");
                adt[index].extType    ( spcType );
                adt[index].logBlockNo ( space[index].start.sector - partStartSector );
                adt[index].extLength  ( space[index].len );
                ovf = 0;
            }
        }
    }

      write the unallocated space entry
    memset( bp, 0, logicalBlockSize );
    use->toISO( bp, addr - partStartSector );
    if (err = use->error()) {
        delete use;
        delete [] bp;
        return;
    }
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),
                "writeSpaceEntry(): writing USE to sid %d sector %d\n",
                sid,addr);
    }
    writeBlock( sid, addr, bp );
    delete use;
    if (err) {
        delete [] bp;
        return;
    }
      write required AEDs to hold remaining extents
    if (media != media_Worm) {
        addr++;
    }
    else {
        addr = extent.start.sector;
    }
    for ( UINT32 i=0; i extentsPerAED ) 
            n = extentsPerAED - 1;
        else
            n = N;

        N -= n;

        aed = new AllocExtentDesc( (n + ( ovf != (i+1) )), ad_Short );
        if (!aed) {
            err = error_No_free_store;
            delete [] bp;
            return;
        }
        memset( bp, 0, logicalBlockSize );

          load short ads in allocation extent desc block
        adt = aed->shortADT();
        for ( j=0; jtoISO( bp, addr - partStartSector );
        if (err = aed->error()) {
            delete aed;
            delete [] bp;
            return;
        }
        delete aed;

          Dump it out if tracing
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                    "writeSpaceEntry(): writing AED to sid %d sector %d\n",
                    sid,addr);
        }

          write the AED block
        writeBlock( sid, addr, bp );

        if (err) {
            delete [] bp;
            return;
        }
    }
    delete [] bp;
    return;
}

 ----------------------------------------------------------------------
  METHOD: Archive::searchForDesc  --DVM
       
   Searches a single VDS for a specific descriptor type.
 
   If the descriptor is found, the vdsNum is returned, along with a
   modified extent. The address of the extent where the descriptor
   was found.
 
  ARGS:
   TagType descrType IN      Descriptor to search for
   Extent  seqExtent IN/OUT  Location to be searched
   UINT32  vdsNum    OUT     Volume Descriptor Sequence Number
 
  RETURNS:
   True if found; False if not.
  
  PRE-CONDITIONS: open archive object.
  
  POST-CONDITIONS:
  
  ERRORS:
   error_No_free_store
   misc device or fromISO errors
  
  NOTES:
   Does not currently follow VDS continuations. Could be added later.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::searchForDesc( TagType searchDescType,
                             Extent & seqExtent,
                             UINT32 & vdsNum ) 
            else {
                fprintf(Trace::logFile(),"writeSpace: aed's can't be written\n");
                adt[index].extType    ( spcType );
                adt[index].logBlockNo ( space[index].start.sector - partStartSector );
                adt[index].extLength  ( space[index].len );
                ovf = 0;
            }
        }
    }

      write the unallocated space entry
    memset( bp, 0, logicalBlockSize );
    use->toISO( bp, addr - partStartSector );
    if (err = use->error()) {
        delete use;
        delete [] bp;
        return;
    }
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),
                "writeSpaceEntry(): writing USE to sid %d sector %d\n",
                sid,addr);
    }
    writeBlock( sid, addr, bp );
    delete use;
    if (err) {
        delete [] bp;
        return;
    }
      write required AEDs to hold remaining extents
    if (media != media_Worm) {
        addr++;
    }
    else {
        addr = extent.start.sector;
    }
    for ( UINT32 i=0; i extentsPerAED ) 
            n = extentsPerAED - 1;
        else
            n = N;

        N -= n;

        aed = new AllocExtentDesc( (n + ( ovf != (i+1) )), ad_Short );
        if (!aed) {
            err = error_No_free_store;
            delete [] bp;
            return;
        }
        memset( bp, 0, logicalBlockSize );

          load short ads in allocation extent desc block
        adt = aed->shortADT();
        for ( j=0; jtoISO( bp, addr - partStartSector );
        if (err = aed->error()) {
            delete aed;
            delete [] bp;
            return;
        }
        delete aed;

          Dump it out if tracing
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                    "writeSpaceEntry(): writing AED to sid %d sector %d\n",
                    sid,addr);
        }

          write the AED block
        writeBlock( sid, addr, bp );

        if (err) {
            delete [] bp;
            return;
        }
    }
    delete [] bp;
    return;
}

 ----------------------------------------------------------------------
  METHOD: Archive::searchForDesc  --DVM
       
   Searches a single VDS for a specific descriptor type.
 
   If the descriptor is found, the vdsNum is returned, along with a
   modified extent. The address of the extent where the descriptor
   was found.
 
  ARGS:
   TagType descrType IN      Descriptor to search for
   Extent  seqExtent IN/OUT  Location to be searched
   UINT32  vdsNum    OUT     Volume Descriptor Sequence Number
 
  RETURNS:
   True if found; False if not.
  
  PRE-CONDITIONS: open archive object.
  
  POST-CONDITIONS:
  
  ERRORS:
   error_No_free_store
   misc device or fromISO errors
  
  NOTES:
   Does not currently follow VDS continuations. Could be added later.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::searchForDesc( TagType searchDescType,
                             Extent & seqExtent,
                             UINT32 & vdsNum ) 
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),
                "writeSpaceEntry(): writing USE to sid %d sector %d\n",
                sid,addr);
    }
    writeBlock( sid, addr, bp );
    delete use;
    if (err) {
        delete [] bp;
        return;
    }
      write required AEDs to hold remaining extents
    if (media != media_Worm) {
        addr++;
    }
    else {
        addr = extent.start.sector;
    }
    for ( UINT32 i=0; i extentsPerAED ) 
            n = extentsPerAED - 1;
        else
            n = N;

        N -= n;

        aed = new AllocExtentDesc( (n + ( ovf != (i+1) )), ad_Short );
        if (!aed) {
            err = error_No_free_store;
            delete [] bp;
            return;
        }
        memset( bp, 0, logicalBlockSize );

          load short ads in allocation extent desc block
        adt = aed->shortADT();
        for ( j=0; jtoISO( bp, addr - partStartSector );
        if (err = aed->error()) {
            delete aed;
            delete [] bp;
            return;
        }
        delete aed;

          Dump it out if tracing
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                    "writeSpaceEntry(): writing AED to sid %d sector %d\n",
                    sid,addr);
        }

          write the AED block
        writeBlock( sid, addr, bp );

        if (err) {
            delete [] bp;
            return;
        }
    }
    delete [] bp;
    return;
}

 ----------------------------------------------------------------------
  METHOD: Archive::searchForDesc  --DVM
       
   Searches a single VDS for a specific descriptor type.
 
   If the descriptor is found, the vdsNum is returned, along with a
   modified extent. The address of the extent where the descriptor
   was found.
 
  ARGS:
   TagType descrType IN      Descriptor to search for
   Extent  seqExtent IN/OUT  Location to be searched
   UINT32  vdsNum    OUT     Volume Descriptor Sequence Number
 
  RETURNS:
   True if found; False if not.
  
  PRE-CONDITIONS: open archive object.
  
  POST-CONDITIONS:
  
  ERRORS:
   error_No_free_store
   misc device or fromISO errors
  
  NOTES:
   Does not currently follow VDS continuations. Could be added later.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::searchForDesc( TagType searchDescType,
                             Extent & seqExtent,
                             UINT32 & vdsNum ) 
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),
                    "writeSpaceEntry(): writing AED to sid %d sector %d\n",
                    sid,addr);
        }

          write the AED block
        writeBlock( sid, addr, bp );

        if (err) {
            delete [] bp;
            return;
        }
    }
    delete [] bp;
    return;
}

 ----------------------------------------------------------------------
  METHOD: Archive::searchForDesc  --DVM
       
   Searches a single VDS for a specific descriptor type.
 
   If the descriptor is found, the vdsNum is returned, along with a
   modified extent. The address of the extent where the descriptor
   was found.
 
  ARGS:
   TagType descrType IN      Descriptor to search for
   Extent  seqExtent IN/OUT  Location to be searched
   UINT32  vdsNum    OUT     Volume Descriptor Sequence Number
 
  RETURNS:
   True if found; False if not.
  
  PRE-CONDITIONS: open archive object.
  
  POST-CONDITIONS:
  
  ERRORS:
   error_No_free_store
   misc device or fromISO errors
  
  NOTES:
   Does not currently follow VDS continuations. Could be added later.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::searchForDesc( TagType searchDescType,
                             Extent & seqExtent,
                             UINT32 & vdsNum ) 
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),"writeVDS(): calling writeSpaceEntry() on UnerasedSpace.\n");
    }
    writeSpaceEntry(vol.sid(),ext_Allocated,
                    vol.freeSpace,
                    partAreas.unerasedSpcTblArea());
    
    if (media != media_Worm) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"writeVDS(): calling writeSpaceEntry() on ErasedSpace.\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeVolumeLabel
       
   Writes the nsr volume information.
 
   This does NOT check to see if the volume has a label. This should
   not be called if the volume is already labeled.
 
   This information is recorded below "fileDataStart". The information
   consists of anchor volume descriptors, main and reserve volume 
   descriptor sequences, and free space information.
 
   This does NOT write the root volume label information, and ALWAYS
   uses hard-coded addresses for the locations of its descriptors,
   etc.
 
  ARGS:
   Volume vol  IN      the volume to write nsr information to.
 
  RETURNS: none.
  
  PRE-CONDS: open archive object, and volumeTable[vid].fileDataStart
   must be filled in.
  
  POST-CONDS: error state may be set.
  
  ERRORS:
   error_
  
  NOTES:
   This is used solely by addUnlabeledVolume().
 
   LEGACY DOCUMENTATION:
   If this volume is the root, an integrity descriptor sequence and
   file set descriptor sequence will be written. In addition, the
   volume descriptor sequence will have a logical volume descriptor.
 
 ----------------------------------------------------------------------
void
NSR::Archive::writeVolumeLabel( Volume & vol )
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"writeVDS(): calling writeSpaceEntry() on ErasedSpace.\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeVolumeLabel
       
   Writes the nsr volume information.
 
   This does NOT check to see if the volume has a label. This should
   not be called if the volume is already labeled.
 
   This information is recorded below "fileDataStart". The information
   consists of anchor volume descriptors, main and reserve volume 
   descriptor sequences, and free space information.
 
   This does NOT write the root volume label information, and ALWAYS
   uses hard-coded addresses for the locations of its descriptors,
   etc.
 
  ARGS:
   Volume vol  IN      the volume to write nsr information to.
 
  RETURNS: none.
  
  PRE-CONDS: open archive object, and volumeTable[vid].fileDataStart
   must be filled in.
  
  POST-CONDS: error state may be set.
  
  ERRORS:
   error_
  
  NOTES:
   This is used solely by addUnlabeledVolume().
 
   LEGACY DOCUMENTATION:
   If this volume is the root, an integrity descriptor sequence and
   file set descriptor sequence will be written. In addition, the
   volume descriptor sequence will have a logical volume descriptor.
 
 ----------------------------------------------------------------------
void
NSR::Archive::writeVolumeLabel( Volume & vol )
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"writeFSD: fsd search at %d len:%d\n",fsdsExt.start.sector,fsdsExt.len);
        }
        getNextSeqAddress(vol.sid(),fsdsExt,1,&nRead,&nextBlankAddr,&lastDescNum,
                          &lastRecordedAddr,&needsIndirect,&bogusICBInfo);
        if (err) {
            if (err == error_Drive_blank_check && media == media_Worm) {
                err = error_None;
            }
        }
        if (needsIndirect) {
            if (!partitionSeqAllowed) {
                err = error_End_of_Sequence_space;
                delete fsd;
                delete [] bp;
                return;
            }
            else {
                getSeqExtent(vol.sid(),incrementalSeqSectors,fsdsExt); 
                if (err) {
                    delete fsd;
                    delete [] bp;
                    return;
                }
                fsd->nextExtent.extLength(fsdsExt.len);
                fsd->nextExtent.logBlockNo(fsdsExt.start.sector - partStartSector);
            }
        }
        address            = nextBlankAddr;
        fsd->fileSetNo     = lastDescNum;
        fsd->fileSetDescNo = lastDescNum + 1;
        fsd->writeProtect(wrprot_Hard);
    }

    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),
                "writeFSD(): writing to address %d, fileSetNo %d, fileSetDescNum %d\n",
                address, (UINT32)fsd->fileSetNo, (UINT32)fsd->fileSetDescNo);
    }
    
      Write FSD
    fsd->toISO( bp, address - partStartSector );
    if (err=fsd->error()) {
        delete fsd;
        delete [] bp;
        return;
    }
    writeBlock( vol.sid(), address, bp );
    if (err) {
        delete fsd;
        delete [] bp;
        return;
    }
      Nuke in-core FSD
    delete fsd;
#ifdef _FSDS_TD_
      !!! Shouldn't need this, since fsdsExt is only one sector long on Rewritable.
      Write a Terminating Descriptor
    if (media != media_Worm) {
        address++;
        writeTD( vol.sid(),
                 address - partStartSector,
                 address );
        if (err) {
            delete [] bp;
            return;
        }
    }
#endif   _FSDS_TD_
      Free buffer.
    delete [] bp;
    return;
}

 ----------------------------------------------------------------------
  METHOD: Archive::updateSpace  --DVM
       
   This updates the free space information on the supplied volume.
 
  ARGS:
   Volume vol IN   The volume to have its space updated. 
 
  RETURNS: none.
  
  PRE-CONDITIONS: The media must be already labeled. This is only
   valid in export mode.
  
  POST-CONDITIONS: This may set the error state.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
   This does NOT close the logical volume at all. It ONLY updates the
   free space on the surface.
 
 ----------------------------------------------------------------------
void
NSR::Archive::updateSpace( const Volume & vol )
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),
                "writeFSD(): writing to address %d, fileSetNo %d, fileSetDescNum %d\n",
                address, (UINT32)fsd->fileSetNo, (UINT32)fsd->fileSetDescNo);
    }
    
      Write FSD
    fsd->toISO( bp, address - partStartSector );
    if (err=fsd->error()) {
        delete fsd;
        delete [] bp;
        return;
    }
    writeBlock( vol.sid(), address, bp );
    if (err) {
        delete fsd;
        delete [] bp;
        return;
    }
      Nuke in-core FSD
    delete fsd;
#ifdef _FSDS_TD_
      !!! Shouldn't need this, since fsdsExt is only one sector long on Rewritable.
      Write a Terminating Descriptor
    if (media != media_Worm) {
        address++;
        writeTD( vol.sid(),
                 address - partStartSector,
                 address );
        if (err) {
            delete [] bp;
            return;
        }
    }
#endif   _FSDS_TD_
      Free buffer.
    delete [] bp;
    return;
}

 ----------------------------------------------------------------------
  METHOD: Archive::updateSpace  --DVM
       
   This updates the free space information on the supplied volume.
 
  ARGS:
   Volume vol IN   The volume to have its space updated. 
 
  RETURNS: none.
  
  PRE-CONDITIONS: The media must be already labeled. This is only
   valid in export mode.
  
  POST-CONDITIONS: This may set the error state.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
   This does NOT close the logical volume at all. It ONLY updates the
   free space on the surface.
 
 ----------------------------------------------------------------------
void
NSR::Archive::updateSpace( const Volume & vol )
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),"updateSpace() Unerased Space (on disk):\n");
        unerasedOnDisk.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Unerased Space (in memory):\n");
        vol.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (on disk):\n");
        erasedOnDisk.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (in memory):\n");
        vol.erasedFreeSpace.fprint(Trace::logFile());
    }

      Write out space table entries only if they have changed.
    if ( vol.freeSpace != unerasedOnDisk ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Unerased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.freeSpace,
                        partAreas.unerasedSpcTblArea());
    }
    
    if ( (media != media_Worm) &&
         (vol.erasedFreeSpace != erasedOnDisk) ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Erased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeIntegrityEntry
       
   Writes an integrity entry to the specified volume.
 
   IntegrityEntries are only valid for root volumes. This is offered
   to write to any volume because as volume sets are grown, the old
   roots may be marked CLOSED, and should be marked as OPEN as soon
   as the new root is marked as OPEN.
 
   LVIDs vary in size based on how many partitions are in their
   logical volume. This code sets the appropriate size, so that it
   matches the logical volume which references it.
 
  ARGS:
   Volume & vol              IN  Volume to write integrity entry to.
 
  RETURNS: none.
  
  PRE-CONDS:
   The volume must have a valid lvidsExt for this to work.
 
  POST-CONDS:
   An error may be set.
 
  ERRORS:
   error_
  
  NOTES: 
  
 ----------------------------------------------------------------------
void
NSR::Archive::writeIntegrityEntry( Volume & vol )
        fprintf(Trace::logFile(),"updateSpace() Unerased Space (on disk):\n");
        unerasedOnDisk.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Unerased Space (in memory):\n");
        vol.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (on disk):\n");
        erasedOnDisk.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (in memory):\n");
        vol.erasedFreeSpace.fprint(Trace::logFile());
    }

      Write out space table entries only if they have changed.
    if ( vol.freeSpace != unerasedOnDisk ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Unerased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.freeSpace,
                        partAreas.unerasedSpcTblArea());
    }
    
    if ( (media != media_Worm) &&
         (vol.erasedFreeSpace != erasedOnDisk) ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Erased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeIntegrityEntry
       
   Writes an integrity entry to the specified volume.
 
   IntegrityEntries are only valid for root volumes. This is offered
   to write to any volume because as volume sets are grown, the old
   roots may be marked CLOSED, and should be marked as OPEN as soon
   as the new root is marked as OPEN.
 
   LVIDs vary in size based on how many partitions are in their
   logical volume. This code sets the appropriate size, so that it
   matches the logical volume which references it.
 
  ARGS:
   Volume & vol              IN  Volume to write integrity entry to.
 
  RETURNS: none.
  
  PRE-CONDS:
   The volume must have a valid lvidsExt for this to work.
 
  POST-CONDS:
   An error may be set.
 
  ERRORS:
   error_
  
  NOTES: 
  
 ----------------------------------------------------------------------
void
NSR::Archive::writeIntegrityEntry( Volume & vol )
        unerasedOnDisk.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Unerased Space (in memory):\n");
        vol.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (on disk):\n");
        erasedOnDisk.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (in memory):\n");
        vol.erasedFreeSpace.fprint(Trace::logFile());
    }

      Write out space table entries only if they have changed.
    if ( vol.freeSpace != unerasedOnDisk ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Unerased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.freeSpace,
                        partAreas.unerasedSpcTblArea());
    }
    
    if ( (media != media_Worm) &&
         (vol.erasedFreeSpace != erasedOnDisk) ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Erased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeIntegrityEntry
       
   Writes an integrity entry to the specified volume.
 
   IntegrityEntries are only valid for root volumes. This is offered
   to write to any volume because as volume sets are grown, the old
   roots may be marked CLOSED, and should be marked as OPEN as soon
   as the new root is marked as OPEN.
 
   LVIDs vary in size based on how many partitions are in their
   logical volume. This code sets the appropriate size, so that it
   matches the logical volume which references it.
 
  ARGS:
   Volume & vol              IN  Volume to write integrity entry to.
 
  RETURNS: none.
  
  PRE-CONDS:
   The volume must have a valid lvidsExt for this to work.
 
  POST-CONDS:
   An error may be set.
 
  ERRORS:
   error_
  
  NOTES: 
  
 ----------------------------------------------------------------------
void
NSR::Archive::writeIntegrityEntry( Volume & vol )
        fprintf(Trace::logFile(),"updateSpace() Unerased Space (in memory):\n");
        vol.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (on disk):\n");
        erasedOnDisk.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (in memory):\n");
        vol.erasedFreeSpace.fprint(Trace::logFile());
    }

      Write out space table entries only if they have changed.
    if ( vol.freeSpace != unerasedOnDisk ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Unerased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.freeSpace,
                        partAreas.unerasedSpcTblArea());
    }
    
    if ( (media != media_Worm) &&
         (vol.erasedFreeSpace != erasedOnDisk) ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Erased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeIntegrityEntry
       
   Writes an integrity entry to the specified volume.
 
   IntegrityEntries are only valid for root volumes. This is offered
   to write to any volume because as volume sets are grown, the old
   roots may be marked CLOSED, and should be marked as OPEN as soon
   as the new root is marked as OPEN.
 
   LVIDs vary in size based on how many partitions are in their
   logical volume. This code sets the appropriate size, so that it
   matches the logical volume which references it.
 
  ARGS:
   Volume & vol              IN  Volume to write integrity entry to.
 
  RETURNS: none.
  
  PRE-CONDS:
   The volume must have a valid lvidsExt for this to work.
 
  POST-CONDS:
   An error may be set.
 
  ERRORS:
   error_
  
  NOTES: 
  
 ----------------------------------------------------------------------
void
NSR::Archive::writeIntegrityEntry( Volume & vol )
        vol.freeSpace.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (on disk):\n");
        erasedOnDisk.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (in memory):\n");
        vol.erasedFreeSpace.fprint(Trace::logFile());
    }

      Write out space table entries only if they have changed.
    if ( vol.freeSpace != unerasedOnDisk ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Unerased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.freeSpace,
                        partAreas.unerasedSpcTblArea());
    }
    
    if ( (media != media_Worm) &&
         (vol.erasedFreeSpace != erasedOnDisk) ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Erased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeIntegrityEntry
       
   Writes an integrity entry to the specified volume.
 
   IntegrityEntries are only valid for root volumes. This is offered
   to write to any volume because as volume sets are grown, the old
   roots may be marked CLOSED, and should be marked as OPEN as soon
   as the new root is marked as OPEN.
 
   LVIDs vary in size based on how many partitions are in their
   logical volume. This code sets the appropriate size, so that it
   matches the logical volume which references it.
 
  ARGS:
   Volume & vol              IN  Volume to write integrity entry to.
 
  RETURNS: none.
  
  PRE-CONDS:
   The volume must have a valid lvidsExt for this to work.
 
  POST-CONDS:
   An error may be set.
 
  ERRORS:
   error_
  
  NOTES: 
  
 ----------------------------------------------------------------------
void
NSR::Archive::writeIntegrityEntry( Volume & vol )
        fprintf(Trace::logFile(),"updateSpace() Erased Space (on disk):\n");
        erasedOnDisk.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (in memory):\n");
        vol.erasedFreeSpace.fprint(Trace::logFile());
    }

      Write out space table entries only if they have changed.
    if ( vol.freeSpace != unerasedOnDisk ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Unerased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.freeSpace,
                        partAreas.unerasedSpcTblArea());
    }
    
    if ( (media != media_Worm) &&
         (vol.erasedFreeSpace != erasedOnDisk) ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Erased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeIntegrityEntry
       
   Writes an integrity entry to the specified volume.
 
   IntegrityEntries are only valid for root volumes. This is offered
   to write to any volume because as volume sets are grown, the old
   roots may be marked CLOSED, and should be marked as OPEN as soon
   as the new root is marked as OPEN.
 
   LVIDs vary in size based on how many partitions are in their
   logical volume. This code sets the appropriate size, so that it
   matches the logical volume which references it.
 
  ARGS:
   Volume & vol              IN  Volume to write integrity entry to.
 
  RETURNS: none.
  
  PRE-CONDS:
   The volume must have a valid lvidsExt for this to work.
 
  POST-CONDS:
   An error may be set.
 
  ERRORS:
   error_
  
  NOTES: 
  
 ----------------------------------------------------------------------
void
NSR::Archive::writeIntegrityEntry( Volume & vol )
        erasedOnDisk.fprint(Trace::logFile());
        fprintf(Trace::logFile(),"updateSpace() Erased Space (in memory):\n");
        vol.erasedFreeSpace.fprint(Trace::logFile());
    }

      Write out space table entries only if they have changed.
    if ( vol.freeSpace != unerasedOnDisk ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Unerased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.freeSpace,
                        partAreas.unerasedSpcTblArea());
    }
    
    if ( (media != media_Worm) &&
         (vol.erasedFreeSpace != erasedOnDisk) ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Erased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeIntegrityEntry
       
   Writes an integrity entry to the specified volume.
 
   IntegrityEntries are only valid for root volumes. This is offered
   to write to any volume because as volume sets are grown, the old
   roots may be marked CLOSED, and should be marked as OPEN as soon
   as the new root is marked as OPEN.
 
   LVIDs vary in size based on how many partitions are in their
   logical volume. This code sets the appropriate size, so that it
   matches the logical volume which references it.
 
  ARGS:
   Volume & vol              IN  Volume to write integrity entry to.
 
  RETURNS: none.
  
  PRE-CONDS:
   The volume must have a valid lvidsExt for this to work.
 
  POST-CONDS:
   An error may be set.
 
  ERRORS:
   error_
  
  NOTES: 
  
 ----------------------------------------------------------------------
void
NSR::Archive::writeIntegrityEntry( Volume & vol )
        fprintf(Trace::logFile(),"updateSpace() Erased Space (in memory):\n");
        vol.erasedFreeSpace.fprint(Trace::logFile());
    }

      Write out space table entries only if they have changed.
    if ( vol.freeSpace != unerasedOnDisk ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Unerased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.freeSpace,
                        partAreas.unerasedSpcTblArea());
    }
    
    if ( (media != media_Worm) &&
         (vol.erasedFreeSpace != erasedOnDisk) ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Erased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeIntegrityEntry
       
   Writes an integrity entry to the specified volume.
 
   IntegrityEntries are only valid for root volumes. This is offered
   to write to any volume because as volume sets are grown, the old
   roots may be marked CLOSED, and should be marked as OPEN as soon
   as the new root is marked as OPEN.
 
   LVIDs vary in size based on how many partitions are in their
   logical volume. This code sets the appropriate size, so that it
   matches the logical volume which references it.
 
  ARGS:
   Volume & vol              IN  Volume to write integrity entry to.
 
  RETURNS: none.
  
  PRE-CONDS:
   The volume must have a valid lvidsExt for this to work.
 
  POST-CONDS:
   An error may be set.
 
  ERRORS:
   error_
  
  NOTES: 
  
 ----------------------------------------------------------------------
void
NSR::Archive::writeIntegrityEntry( Volume & vol )
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Unerased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.freeSpace,
                        partAreas.unerasedSpcTblArea());
    }
    
    if ( (media != media_Worm) &&
         (vol.erasedFreeSpace != erasedOnDisk) ) {
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Erased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeIntegrityEntry
       
   Writes an integrity entry to the specified volume.
 
   IntegrityEntries are only valid for root volumes. This is offered
   to write to any volume because as volume sets are grown, the old
   roots may be marked CLOSED, and should be marked as OPEN as soon
   as the new root is marked as OPEN.
 
   LVIDs vary in size based on how many partitions are in their
   logical volume. This code sets the appropriate size, so that it
   matches the logical volume which references it.
 
  ARGS:
   Volume & vol              IN  Volume to write integrity entry to.
 
  RETURNS: none.
  
  PRE-CONDS:
   The volume must have a valid lvidsExt for this to work.
 
  POST-CONDS:
   An error may be set.
 
  ERRORS:
   error_
  
  NOTES: 
  
 ----------------------------------------------------------------------
void
NSR::Archive::writeIntegrityEntry( Volume & vol )
        if(Trace::logAlgorithms()) {
            fprintf(Trace::logFile(),"Erased space changed -- calling writeSpaceEntry()\n");
        }
        writeSpaceEntry(vol.sid(),ext_Allocated,
                        vol.erasedFreeSpace,
                        partAreas.erasedSpcTblArea());
    }
    return;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::writeIntegrityEntry
       
   Writes an integrity entry to the specified volume.
 
   IntegrityEntries are only valid for root volumes. This is offered
   to write to any volume because as volume sets are grown, the old
   roots may be marked CLOSED, and should be marked as OPEN as soon
   as the new root is marked as OPEN.
 
   LVIDs vary in size based on how many partitions are in their
   logical volume. This code sets the appropriate size, so that it
   matches the logical volume which references it.
 
  ARGS:
   Volume & vol              IN  Volume to write integrity entry to.
 
  RETURNS: none.
  
  PRE-CONDS:
   The volume must have a valid lvidsExt for this to work.
 
  POST-CONDS:
   An error may be set.
 
  ERRORS:
   error_
  
  NOTES: 
  
 ----------------------------------------------------------------------
void
NSR::Archive::writeIntegrityEntry( Volume & vol )
            if(Trace::logAlgorithms()) {
                fprintf(Trace::logFile(),"icb table elist[%d] partRefNo:%d logBlockNo:%d sectors:%d\n",
                        i, volumeTable.vid( icbTblelist[i].start.sid), 
                        icbTblelist[i].start.sector - partStartSector,
                        B2S(icbTblelist[i].len));
            }
        }
    }

    nBytesForEA += (nBytesForEA % 4) ? 4 - (nBytesForEA % 4) : 0;
    eahd->applAttrLocation(nBytesForEA);

    if (embeddedEAs && !isRootICB && (nBytesForEA <= fe->unusedBytes(logicalBlockSize))) {
        embeddedEA = True;
        if (err = fe->setEALen(logicalBlockSize,nBytesForEA)) {
#ifdef TRUSTEES
            delete ntea; 
#endif   TRUSTEES
            delete icbea;
            delete eahd; 
            return; 
        }
    }
    else {
        embeddedEA = False;
        nEASectors = 1 + B2S(nBytesForEA);
        
          get an extent to be used in writing the eafe
        getICBExtentList(eafe_offset, elist, logicalBlockSize, True);
          get an extent list to be used in writing the easpace 
        getICBExtentList(eafe_offset+1, elistea, nBytesForEA, True);
        for (int idx = 0; idx < elistea.getCount(); idx++)
        {
            elist.append(elistea[idx]);
        }
        if (elist.error()) {
            err = error_Invalid_extentlist;
#ifdef TRUSTEES
            delete ntea; 
#endif   TRUSTEES
            delete icbea;
            delete eahd; 
            return; 
        }
        eaicbtbl = new ICB_Table( elist, logicalBlockSize );
        if (!eaicbtbl) {
            err = error_No_free_store;
#ifdef TRUSTEES
            delete ntea; 
#endif   TRUSTEES
            delete icbea;
            delete eahd; 
            return; 
        }
        eaicbtbl->resetPtr();
        
        eafe = new FileEntry(elistea.getCount());
        if (!eafe) {
            err = error_No_free_store;
#ifdef TRUSTEES
            delete ntea; 
#endif   TRUSTEES
            delete eaicbtbl;
            delete icbea;
            delete eahd; 
            return; 
        }
          fill in fe->extendedAttrICB field (LongAllocDesc)
        fe->extendedAttrICB.partRefNo(volumeTable.vid(sid));
        fe->extendedAttrICB.logBlockNo(eaicbtbl->currentAddr().sector - partStartSector);
        if(Trace::logAlgorithms())
            fprintf(Trace::logFile(),"fe->extendedAttrICB.logBlockNo:%d\n",fe->extendedAttrICB.logBlockNo());
        fe->extendedAttrICB.extLength(logicalBlockSize);
        fe->extendedAttrICB.extType(ext_Recorded);
       
        attr.fileType(file_ExtendedAttributes);

          write the eafe (recursion - be careful)
          side effects of this call:
            - the eaicbtbl->bumpPtr() will be called as necessary
        Extent nullExtent;        Constructor makes it all empty
        writeNextICB(attr,elistea,emptyTrustees,nullExtent,eaicbtbl,True);
        if (err) {
#ifdef TRUSTEES
            delete ntea; 
#endif   TRUSTEES
            delete eaicbtbl;
            delete icbea;
            delete eahd; 
            return; 
        }
    }

      set up pointer to the EA space
    if (embeddedEA) {
        eabp = (BYTE *)fe->embeddedEAp();
    }
    else {
        eabp = new BYTE[S2B(B2S(nBytesForEA))];
        if (!eabp) {
            err = error_No_free_store;
#ifdef TRUSTEES
            delete ntea; 
#endif   TRUSTEES
            delete eaicbtbl;
            delete icbea;
            delete eahd; 
            return; 
        }
        memset( eabp, 0, S2B(B2S(nBytesForEA)));
    }
    eap = eabp;

      fill in the extended attribute header descriptor
    if (embeddedEA) {
        eahd->toISO( eap, icbTable->currentAddr().sector - partStartSector );
    }
    else {
        eahd->toISO( eap, eaicbtbl->currentAddr().sector - partStartSector );
    }
    eap += eahd->descLen();

    if (icbea) {
        icbea->toISO( eap );
        eap += icbea->attrLen();
    }

#ifdef TRUSTEES
    if (ntea) {
        ntea->toISO( eap );
        eap += ntea->attrLen();
    }
#endif   TRUSTEES

    if (!embeddedEA) {
        writeExtents( sid, eaicbtbl, nEASectors-1, eabp);
    }

#ifdef TRUSTEES
    delete ntea; 
#endif   TRUSTEES
    delete [] eabp;
    delete eaicbtbl;
    delete icbea;
    delete eahd; 
    return; 
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readLVIDSeq
       
   Reads the logical volume integrity descriptor sequence.
 
  ARGS:
     Volume & vol             IN    volume to be checked
     LogicalVolumeIntegrityDesc **lvidpp,
                              OUT   Holds last recorded lvid.
     int *nFileSets           OUT   Number of open->close transitions.
 
  RETURNS:
  
  PRE-CONDS:
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import & export
 
 ----------------------------------------------------------------------
void
NSR::Archive::readLVIDSeq(const Volume & vol,
                          LogicalVolumeIntegrityDesc **lvidpp,
                          int *nFileSets)
        if(Trace::logAlgorithms())
            fprintf(Trace::logFile(),"fe->extendedAttrICB.logBlockNo:%d\n",fe->extendedAttrICB.logBlockNo());
        fe->extendedAttrICB.extLength(logicalBlockSize);
        fe->extendedAttrICB.extType(ext_Recorded);
       
        attr.fileType(file_ExtendedAttributes);

          write the eafe (recursion - be careful)
          side effects of this call:
            - the eaicbtbl->bumpPtr() will be called as necessary
        Extent nullExtent;        Constructor makes it all empty
        writeNextICB(attr,elistea,emptyTrustees,nullExtent,eaicbtbl,True);
        if (err) {
#ifdef TRUSTEES
            delete ntea; 
#endif   TRUSTEES
            delete eaicbtbl;
            delete icbea;
            delete eahd; 
            return; 
        }
    }

      set up pointer to the EA space
    if (embeddedEA) {
        eabp = (BYTE *)fe->embeddedEAp();
    }
    else {
        eabp = new BYTE[S2B(B2S(nBytesForEA))];
        if (!eabp) {
            err = error_No_free_store;
#ifdef TRUSTEES
            delete ntea; 
#endif   TRUSTEES
            delete eaicbtbl;
            delete icbea;
            delete eahd; 
            return; 
        }
        memset( eabp, 0, S2B(B2S(nBytesForEA)));
    }
    eap = eabp;

      fill in the extended attribute header descriptor
    if (embeddedEA) {
        eahd->toISO( eap, icbTable->currentAddr().sector - partStartSector );
    }
    else {
        eahd->toISO( eap, eaicbtbl->currentAddr().sector - partStartSector );
    }
    eap += eahd->descLen();

    if (icbea) {
        icbea->toISO( eap );
        eap += icbea->attrLen();
    }

#ifdef TRUSTEES
    if (ntea) {
        ntea->toISO( eap );
        eap += ntea->attrLen();
    }
#endif   TRUSTEES

    if (!embeddedEA) {
        writeExtents( sid, eaicbtbl, nEASectors-1, eabp);
    }

#ifdef TRUSTEES
    delete ntea; 
#endif   TRUSTEES
    delete [] eabp;
    delete eaicbtbl;
    delete icbea;
    delete eahd; 
    return; 
}

 ----------------------------------------------------------------------
  METHOD:  Archive::readLVIDSeq
       
   Reads the logical volume integrity descriptor sequence.
 
  ARGS:
     Volume & vol             IN    volume to be checked
     LogicalVolumeIntegrityDesc **lvidpp,
                              OUT   Holds last recorded lvid.
     int *nFileSets           OUT   Number of open->close transitions.
 
  RETURNS:
  
  PRE-CONDS:
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import & export
 
 ----------------------------------------------------------------------
void
NSR::Archive::readLVIDSeq(const Volume & vol,
                          LogicalVolumeIntegrityDesc **lvidpp,
                          int *nFileSets)
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),"readvds: sid: %d\n fsdsExt:\n",sid);
                    fsdsExt->fprint(Trace::logFile());
                }
            }

              Only valid if we wrote it.
            if ( (lvd->implementID.id() != NSR::ImplRegID::HPID ) &&
                 (lvd->implementID.id() != NSR::ImplRegID::HPEarlyID ) ) {
                validLVD = False;
            }
              Grab whether the volume is write protected or not.
            if (lvWriteProtect) {
                *lvWriteProtect = lvd->writeProtect();
            }
            
            delete lvd;
            break;
        
        case tag_IUVD:
            iuvd = new OSTAVolumeDesc();
            if (!iuvd) { 
                err = error_No_free_store; 
                delete [] bp;
                return False; 
            }
            iuvd->fromISO( bp, volDescSeqLoc + i );
            if (err=iuvd->error()) { 
                delete iuvd; 
                delete [] bp; 
                return False; 
            }
            if ( (iuvd->implementID.id() != NSR::ImplRegID::HPID ) &&
                 (iuvd->implementID.id() != NSR::ImplRegID::HPEarlyID ) ) {
                validIUVD = False;
            }
            delete iuvd;
            break;

        case tag_TD:
            validTD = True;     
            break;
        
        default:
              Simply ignore any descriptors of other types.
              NOT being picky -- e.g., even an FSD in the sequence
              would not cause any problems 
            break;
        }
        if ( sd.type() == tag_TD ) {
            break;
        }
    }

    delete [] bp;
    isHP_NSR = ( validAVD 
              && validPVD 
              && validPD 
              && validLVD 
              && validIUVD 
              && validTD ) ? True : False;

    return isHP_NSR;
}

 ----------------------------------------------------------------------
  METHOD: Archive::getICBTableFromLVID  --DVM
       
   Returns True if the LVID has an ICBTable.
   Sets the Archive object's error ONLY if it has trouble reading or
   constructing objects.
 
  ARGS: none
 
  RETURNS: True if LVID has a valid ICBTable in it.
  
  PRE-CONDITIONS: Must have a complete volume set loaded.
  
  POST-CONDITIONS: error state may be set.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::getICBTableFromLVID(void) {
      Clear out any errors.
    err = error_None;
    
      Not there if on Worm (we never supported it.)
    if (media == media_Worm) {
        return False;
    }
    
      Create an LVID.
    LogicalVolumeIntegrityDesc *lvid = NULL;
    int nFileSets = 0;
    readLVIDSeq( volumeTable.rootVol(),&lvid, &nFileSets );
    if (err) {
        delete lvid;
        return False;
    }

      Only closed ones count
    if (lvid->integrityType() != integ_Closed) {
        delete lvid;
        return False;
    }

      Only HP LVIDs count
    if ( ( lvid->implementID.id() != NSR::ImplRegID::HPID ) &&
         ( lvid->implementID.id() != NSR::ImplRegID::HPEarlyID ) ) {
        delete lvid;
        return False;
    }

      Fill in the ICB Table if present in LVID.
    Boolean foundIt( lvid->hasICBTable() );
    if ( foundIt ) {
        if(Trace::logAlgorithms()) fprintf(Trace::logFile(),"lvid icb table\n");
        ExtentList elist;
        Extent ext;
        LongAllocDesc *icbt = lvid->icbTbl();
        for (int j=0; j<(int)lvid->numEnts(); j++) 
        {
            ext.start.sid   = volumeTable[icbt[j].partRefNo()].sid();
            ext.start.sector = icbt[j].logBlockNo() + partStartSector;
            ext.len          = icbt[j].extLength();
            elist.append( ext );

            if (err=elist.error()) {
                delete lvid;
                return False;
            }
        }
          Reallocate ICBTable
        delete icbTable;
        icbTable = new ICB_Table( elist, logicalBlockSize );
          Error if out of memory.
        if (!icbTable) {
            err = error_No_free_store;
            delete lvid;
            return foundIt;
        }
          Error if icbTable in error.
        if (err=icbTable->error()) {
            delete lvid;
            return foundIt;
        }
    }
    delete lvid;
    return foundIt;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::getRootICB
       
     Gets the location of the root ICB. This works for Worm and
     Rewritable
 
  ARGS:
     INT32 fsnum       IN    File set number to find the root ICB of.
                             File sets are numbered 0,1,2,...
     Address &rootICB  OUT   Location of root ICB for fsnum.
 
  RETURNS: False if unable to obtain a valid address.
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   The Archive::err state variable will be set if false, and NOT if
   True.
  
  NOTES:
     import only. Could leave out the return value if we wanted,
     since it is NOT checked, and always sets the error.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::getRootICB( INT32 fsnum,Address &rootICB )
                    fprintf(Trace::logFile(),"readvds: sid: %d\n fsdsExt:\n",sid);
                    fsdsExt->fprint(Trace::logFile());
                }
            }

              Only valid if we wrote it.
            if ( (lvd->implementID.id() != NSR::ImplRegID::HPID ) &&
                 (lvd->implementID.id() != NSR::ImplRegID::HPEarlyID ) ) {
                validLVD = False;
            }
              Grab whether the volume is write protected or not.
            if (lvWriteProtect) {
                *lvWriteProtect = lvd->writeProtect();
            }
            
            delete lvd;
            break;
        
        case tag_IUVD:
            iuvd = new OSTAVolumeDesc();
            if (!iuvd) { 
                err = error_No_free_store; 
                delete [] bp;
                return False; 
            }
            iuvd->fromISO( bp, volDescSeqLoc + i );
            if (err=iuvd->error()) { 
                delete iuvd; 
                delete [] bp; 
                return False; 
            }
            if ( (iuvd->implementID.id() != NSR::ImplRegID::HPID ) &&
                 (iuvd->implementID.id() != NSR::ImplRegID::HPEarlyID ) ) {
                validIUVD = False;
            }
            delete iuvd;
            break;

        case tag_TD:
            validTD = True;     
            break;
        
        default:
              Simply ignore any descriptors of other types.
              NOT being picky -- e.g., even an FSD in the sequence
              would not cause any problems 
            break;
        }
        if ( sd.type() == tag_TD ) {
            break;
        }
    }

    delete [] bp;
    isHP_NSR = ( validAVD 
              && validPVD 
              && validPD 
              && validLVD 
              && validIUVD 
              && validTD ) ? True : False;

    return isHP_NSR;
}

 ----------------------------------------------------------------------
  METHOD: Archive::getICBTableFromLVID  --DVM
       
   Returns True if the LVID has an ICBTable.
   Sets the Archive object's error ONLY if it has trouble reading or
   constructing objects.
 
  ARGS: none
 
  RETURNS: True if LVID has a valid ICBTable in it.
  
  PRE-CONDITIONS: Must have a complete volume set loaded.
  
  POST-CONDITIONS: error state may be set.
  
  ERRORS:
   error_
   error_
   error_
  
  NOTES:
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::getICBTableFromLVID(void) {
      Clear out any errors.
    err = error_None;
    
      Not there if on Worm (we never supported it.)
    if (media == media_Worm) {
        return False;
    }
    
      Create an LVID.
    LogicalVolumeIntegrityDesc *lvid = NULL;
    int nFileSets = 0;
    readLVIDSeq( volumeTable.rootVol(),&lvid, &nFileSets );
    if (err) {
        delete lvid;
        return False;
    }

      Only closed ones count
    if (lvid->integrityType() != integ_Closed) {
        delete lvid;
        return False;
    }

      Only HP LVIDs count
    if ( ( lvid->implementID.id() != NSR::ImplRegID::HPID ) &&
         ( lvid->implementID.id() != NSR::ImplRegID::HPEarlyID ) ) {
        delete lvid;
        return False;
    }

      Fill in the ICB Table if present in LVID.
    Boolean foundIt( lvid->hasICBTable() );
    if ( foundIt ) {
        if(Trace::logAlgorithms()) fprintf(Trace::logFile(),"lvid icb table\n");
        ExtentList elist;
        Extent ext;
        LongAllocDesc *icbt = lvid->icbTbl();
        for (int j=0; j<(int)lvid->numEnts(); j++) 
        {
            ext.start.sid   = volumeTable[icbt[j].partRefNo()].sid();
            ext.start.sector = icbt[j].logBlockNo() + partStartSector;
            ext.len          = icbt[j].extLength();
            elist.append( ext );

            if (err=elist.error()) {
                delete lvid;
                return False;
            }
        }
          Reallocate ICBTable
        delete icbTable;
        icbTable = new ICB_Table( elist, logicalBlockSize );
          Error if out of memory.
        if (!icbTable) {
            err = error_No_free_store;
            delete lvid;
            return foundIt;
        }
          Error if icbTable in error.
        if (err=icbTable->error()) {
            delete lvid;
            return foundIt;
        }
    }
    delete lvid;
    return foundIt;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::getRootICB
       
     Gets the location of the root ICB. This works for Worm and
     Rewritable
 
  ARGS:
     INT32 fsnum       IN    File set number to find the root ICB of.
                             File sets are numbered 0,1,2,...
     Address &rootICB  OUT   Location of root ICB for fsnum.
 
  RETURNS: False if unable to obtain a valid address.
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   The Archive::err state variable will be set if false, and NOT if
   True.
  
  NOTES:
     import only. Could leave out the return value if we wanted,
     since it is NOT checked, and always sets the error.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::getRootICB( INT32 fsnum,Address &rootICB )
    if ( foundIt ) {
        if(Trace::logAlgorithms()) fprintf(Trace::logFile(),"lvid icb table\n");
        ExtentList elist;
        Extent ext;
        LongAllocDesc *icbt = lvid->icbTbl();
        for (int j=0; j<(int)lvid->numEnts(); j++) 
        {
            ext.start.sid   = volumeTable[icbt[j].partRefNo()].sid();
            ext.start.sector = icbt[j].logBlockNo() + partStartSector;
            ext.len          = icbt[j].extLength();
            elist.append( ext );

            if (err=elist.error()) {
                delete lvid;
                return False;
            }
        }
          Reallocate ICBTable
        delete icbTable;
        icbTable = new ICB_Table( elist, logicalBlockSize );
          Error if out of memory.
        if (!icbTable) {
            err = error_No_free_store;
            delete lvid;
            return foundIt;
        }
          Error if icbTable in error.
        if (err=icbTable->error()) {
            delete lvid;
            return foundIt;
        }
    }
    delete lvid;
    return foundIt;
}

 ----------------------------------------------------------------------
  METHOD:  Archive::getRootICB
       
     Gets the location of the root ICB. This works for Worm and
     Rewritable
 
  ARGS:
     INT32 fsnum       IN    File set number to find the root ICB of.
                             File sets are numbered 0,1,2,...
     Address &rootICB  OUT   Location of root ICB for fsnum.
 
  RETURNS: False if unable to obtain a valid address.
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   The Archive::err state variable will be set if false, and NOT if
   True.
  
  NOTES:
     import only. Could leave out the return value if we wanted,
     since it is NOT checked, and always sets the error.
 
 ----------------------------------------------------------------------
NSR::Boolean
NSR::Archive::getRootICB( INT32 fsnum,Address &rootICB )
    if(Trace::logAlgorithms()) {
        fprintf(Trace::logFile(),"getRootICB(fsnum = %d)\n",fsnum);
    }
      Use a local variable for return value
    Boolean foundRootICB(False);
    
      Instantiate some local variables
      fsdsExt NEEDS to be value, since it will be changed.
    Extent fsdsExt = volumeTable.rootVol().fsdsExt;
    BYTE *bp;

      Allocate the FSD
    FileSetDesc *fsd = new FileSetDesc;
    if (!fsd) {
        return False;
    }

    if (media != media_Worm) {
          Allocate sector-size buffer
        bp = new BYTE[logicalBlockSize];
        if (!bp) {
            err = error_No_free_store;
            delete fsd;
            return False;
        }

          Read the fsds extent
        readBlock( fsdsExt.start.sid,
                   fsdsExt.start.sector,
                   bp );
        if (err) {
            delete fsd;
            delete [] bp;
            return False;
        }
          Translate it from ISO.
        fsd->fromISO( bp, fsdsExt.start.sector - partStartSector );
        if (err=fsd->error()) { 
            delete fsd; 
            delete [] bp; 
            return False;
        }
          Error if not UDF or HP near-UDF
        if ( ! fsd->domainID.isUDF() &&
             ! fsd->domainID.isEarlyHP() ) {
            err = error_Non_UDF_volume_set;
            delete fsd;
            delete [] bp;
            return False;
        }
          Save off GLOBAL state:

          Set character set to be used by FIDs and PCs
          (NOTE that WORM need not do this, since it never used OCU)
        NSR::FileIdentifierDesc::readCSType(fsd->fileSetCharSet.charSetType());
        NSR::PathComponent::readCSType(fsd->fileSetCharSet.charSetType());

          Set rootICB, to be used by readRootICB()
        rootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
        rootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;

          Set the return value:
        foundRootICB = True;

          Free the buffer.
        delete [] bp;
    } else {   Now for WORM:
          Used to test for valid address at end
        rootICB.sector = 0;
          Need to hold the rootICB in case default fsnum is used.
        Address lastRootICB;
        lastRootICB.sector = 0;

         ----------------------------------------------
          Read previous fsd's, in chunks for efficiency
         ----------------------------------------------
        UINT32 nSectorsRead;
        Boolean done = False;
        while (!done)
        {
             ---------------------------------------------
              Read an extent worth of FSD Sequence
             ---------------------------------------------
            bp = new BYTE[fsdsExt.len];
            if (!bp) {
                err = error_No_free_store;
                delete fsd;
                return False;
            }
            nSectorsRead = readExtent(fsdsExt, bp);
            if (err) {
                if ( err == error_Drive_blank_check ) {
                    err = error_None;
                    done = True; 
                }
                else {
                    delete fsd;
                    delete [] bp;
                    return False;
                }
            }

             ---------------------------------------------
              convert fsd's, looking for match with fsnum 
             ---------------------------------------------
            for (int j = 0; j < nSectorsRead; j++)
            {
                fsd->fromISO( bp + (j*logicalSectorSize), 
                              fsdsExt.start.sector + j - partStartSector);
                if (err=fsd->error()) {
                    delete fsd;
                    delete [] bp;
                    return False;
                }
                  Error if not UDF or HP near-UDF
                if ( ! fsd->domainID.isUDF() &&
                     ! fsd->domainID.isEarlyHP() ) {
                    err = error_Non_UDF_volume_set;
                    delete fsd;
                    delete [] bp;
                    return False;
                }
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),"getRootICB(): saw fsnum=%d at sid=%d, sector=%d\n",
                            (UINT32)fsd->fileSetNo,
                            fsdsExt.start.sid,
                            fsdsExt.start.sector + j - partStartSector);
                }
                  Hold on to rootICB if default fileSetNo chosen (-1)
                if ( fsnum == -1) {
                    lastRootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
                    lastRootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): Held-root ICB at sid=%d; sect=%d\n",
                                (UINT32)lastRootICB.sid,
                                (UINT32)lastRootICB.sector);
                    }
                } else if (fsnum == fsd->fileSetNo) { 
                      Break out with done==True if match            
                    rootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
                    rootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): FOUND-root ICB at sid=%d; sect=%d\n",
                                (UINT32)rootICB.sid,
                                (UINT32)rootICB.sector);
                    }
                    done = True;
                    break;
                }
                  Break out with done==False if Continued
                if (fsd->nextExtent.extLength()) {
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),"getRootICB(): Continues");
                    }
                    done = False;
                    break;
                }
                  Break out with done==True if end of FSDSExtent
                if ( done && (fsnum == -1) && (j==(nSectorsRead-1))) {
                    rootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
                    rootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): FSDSEnd-rootICB at sid=%d,sect=%d\n",
                                (UINT32)rootICB.sid,
                                (UINT32)rootICB.sector);
                    }
                    done = True;
                    break;
                }
            }
              If Not done, then grab continuation extent.
            if (!done) {
                 ------------------------------
                  look for possible next extent
                 ------------------------------
                  !!! This is a faulty algorithm -- doesn't support
                  !!! non-zero nextExtents in middle of sequence. 
                fsd->fromISO( bp + ((nSectorsRead-1)*logicalSectorSize), 
                             fsdsExt.start.sector + nSectorsRead - 1 - partStartSector);
                if (err=fsd->error()) {
                    delete fsd; 
                    delete [] bp; 
                    return False; 
                }
                  Error if not UDF or HP near-UDF
                if ( ! fsd->domainID.isUDF() &&
                     ! fsd->domainID.isEarlyHP() ) {
                    err = error_Non_UDF_volume_set;
                    delete fsd;
                    delete [] bp;
                    return False;
                }
                fsdsExt.start.sid = volumeTable[fsd->nextExtent.partRefNo()].sid();
                fsdsExt.start.sector = fsd->nextExtent.logBlockNo() + partStartSector;
                fsdsExt.len = fsd->nextExtent.extLength();
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "getRootICB(): Continuing at sid=%d, sector=%d, len=%d\n",
                            (UINT32)fsdsExt.start.sid,
                            (UINT32)fsdsExt.start.sector,
                            (UINT32)fsdsExt.len);
                }
                if (!fsdsExt.len) {
                    done = True;
                }
            }
            delete [] bp;
        }
        if (fsnum == -1) {
            rootICB = lastRootICB;
        }
        foundRootICB = (rootICB.sector == 0) ? False : True;
    }
      Free the FSD
    delete fsd;

      Set the proper error if no other error encountered, but file
      set not found...
    if ( !foundRootICB && !err ) {
        err = error_File_set_not_found;
    }
    
      Return
    return foundRootICB;
}

  Misc utilities to sort out volume sets...
  Returns the size of the volume set, independent of how many are
  missing.
NSR::UINT16
NSR::Archive::sizeVolSet( VolumeTable & vt )
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),"getRootICB(): saw fsnum=%d at sid=%d, sector=%d\n",
                            (UINT32)fsd->fileSetNo,
                            fsdsExt.start.sid,
                            fsdsExt.start.sector + j - partStartSector);
                }
                  Hold on to rootICB if default fileSetNo chosen (-1)
                if ( fsnum == -1) {
                    lastRootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
                    lastRootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): Held-root ICB at sid=%d; sect=%d\n",
                                (UINT32)lastRootICB.sid,
                                (UINT32)lastRootICB.sector);
                    }
                } else if (fsnum == fsd->fileSetNo) { 
                      Break out with done==True if match            
                    rootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
                    rootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): FOUND-root ICB at sid=%d; sect=%d\n",
                                (UINT32)rootICB.sid,
                                (UINT32)rootICB.sector);
                    }
                    done = True;
                    break;
                }
                  Break out with done==False if Continued
                if (fsd->nextExtent.extLength()) {
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),"getRootICB(): Continues");
                    }
                    done = False;
                    break;
                }
                  Break out with done==True if end of FSDSExtent
                if ( done && (fsnum == -1) && (j==(nSectorsRead-1))) {
                    rootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
                    rootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): FSDSEnd-rootICB at sid=%d,sect=%d\n",
                                (UINT32)rootICB.sid,
                                (UINT32)rootICB.sector);
                    }
                    done = True;
                    break;
                }
            }
              If Not done, then grab continuation extent.
            if (!done) {
                 ------------------------------
                  look for possible next extent
                 ------------------------------
                  !!! This is a faulty algorithm -- doesn't support
                  !!! non-zero nextExtents in middle of sequence. 
                fsd->fromISO( bp + ((nSectorsRead-1)*logicalSectorSize), 
                             fsdsExt.start.sector + nSectorsRead - 1 - partStartSector);
                if (err=fsd->error()) {
                    delete fsd; 
                    delete [] bp; 
                    return False; 
                }
                  Error if not UDF or HP near-UDF
                if ( ! fsd->domainID.isUDF() &&
                     ! fsd->domainID.isEarlyHP() ) {
                    err = error_Non_UDF_volume_set;
                    delete fsd;
                    delete [] bp;
                    return False;
                }
                fsdsExt.start.sid = volumeTable[fsd->nextExtent.partRefNo()].sid();
                fsdsExt.start.sector = fsd->nextExtent.logBlockNo() + partStartSector;
                fsdsExt.len = fsd->nextExtent.extLength();
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "getRootICB(): Continuing at sid=%d, sector=%d, len=%d\n",
                            (UINT32)fsdsExt.start.sid,
                            (UINT32)fsdsExt.start.sector,
                            (UINT32)fsdsExt.len);
                }
                if (!fsdsExt.len) {
                    done = True;
                }
            }
            delete [] bp;
        }
        if (fsnum == -1) {
            rootICB = lastRootICB;
        }
        foundRootICB = (rootICB.sector == 0) ? False : True;
    }
      Free the FSD
    delete fsd;

      Set the proper error if no other error encountered, but file
      set not found...
    if ( !foundRootICB && !err ) {
        err = error_File_set_not_found;
    }
    
      Return
    return foundRootICB;
}

  Misc utilities to sort out volume sets...
  Returns the size of the volume set, independent of how many are
  missing.
NSR::UINT16
NSR::Archive::sizeVolSet( VolumeTable & vt )
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): Held-root ICB at sid=%d; sect=%d\n",
                                (UINT32)lastRootICB.sid,
                                (UINT32)lastRootICB.sector);
                    }
                } else if (fsnum == fsd->fileSetNo) { 
                      Break out with done==True if match            
                    rootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
                    rootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): FOUND-root ICB at sid=%d; sect=%d\n",
                                (UINT32)rootICB.sid,
                                (UINT32)rootICB.sector);
                    }
                    done = True;
                    break;
                }
                  Break out with done==False if Continued
                if (fsd->nextExtent.extLength()) {
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),"getRootICB(): Continues");
                    }
                    done = False;
                    break;
                }
                  Break out with done==True if end of FSDSExtent
                if ( done && (fsnum == -1) && (j==(nSectorsRead-1))) {
                    rootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
                    rootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): FSDSEnd-rootICB at sid=%d,sect=%d\n",
                                (UINT32)rootICB.sid,
                                (UINT32)rootICB.sector);
                    }
                    done = True;
                    break;
                }
            }
              If Not done, then grab continuation extent.
            if (!done) {
                 ------------------------------
                  look for possible next extent
                 ------------------------------
                  !!! This is a faulty algorithm -- doesn't support
                  !!! non-zero nextExtents in middle of sequence. 
                fsd->fromISO( bp + ((nSectorsRead-1)*logicalSectorSize), 
                             fsdsExt.start.sector + nSectorsRead - 1 - partStartSector);
                if (err=fsd->error()) {
                    delete fsd; 
                    delete [] bp; 
                    return False; 
                }
                  Error if not UDF or HP near-UDF
                if ( ! fsd->domainID.isUDF() &&
                     ! fsd->domainID.isEarlyHP() ) {
                    err = error_Non_UDF_volume_set;
                    delete fsd;
                    delete [] bp;
                    return False;
                }
                fsdsExt.start.sid = volumeTable[fsd->nextExtent.partRefNo()].sid();
                fsdsExt.start.sector = fsd->nextExtent.logBlockNo() + partStartSector;
                fsdsExt.len = fsd->nextExtent.extLength();
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "getRootICB(): Continuing at sid=%d, sector=%d, len=%d\n",
                            (UINT32)fsdsExt.start.sid,
                            (UINT32)fsdsExt.start.sector,
                            (UINT32)fsdsExt.len);
                }
                if (!fsdsExt.len) {
                    done = True;
                }
            }
            delete [] bp;
        }
        if (fsnum == -1) {
            rootICB = lastRootICB;
        }
        foundRootICB = (rootICB.sector == 0) ? False : True;
    }
      Free the FSD
    delete fsd;

      Set the proper error if no other error encountered, but file
      set not found...
    if ( !foundRootICB && !err ) {
        err = error_File_set_not_found;
    }
    
      Return
    return foundRootICB;
}

  Misc utilities to sort out volume sets...
  Returns the size of the volume set, independent of how many are
  missing.
NSR::UINT16
NSR::Archive::sizeVolSet( VolumeTable & vt )
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): FOUND-root ICB at sid=%d; sect=%d\n",
                                (UINT32)rootICB.sid,
                                (UINT32)rootICB.sector);
                    }
                    done = True;
                    break;
                }
                  Break out with done==False if Continued
                if (fsd->nextExtent.extLength()) {
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),"getRootICB(): Continues");
                    }
                    done = False;
                    break;
                }
                  Break out with done==True if end of FSDSExtent
                if ( done && (fsnum == -1) && (j==(nSectorsRead-1))) {
                    rootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
                    rootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): FSDSEnd-rootICB at sid=%d,sect=%d\n",
                                (UINT32)rootICB.sid,
                                (UINT32)rootICB.sector);
                    }
                    done = True;
                    break;
                }
            }
              If Not done, then grab continuation extent.
            if (!done) {
                 ------------------------------
                  look for possible next extent
                 ------------------------------
                  !!! This is a faulty algorithm -- doesn't support
                  !!! non-zero nextExtents in middle of sequence. 
                fsd->fromISO( bp + ((nSectorsRead-1)*logicalSectorSize), 
                             fsdsExt.start.sector + nSectorsRead - 1 - partStartSector);
                if (err=fsd->error()) {
                    delete fsd; 
                    delete [] bp; 
                    return False; 
                }
                  Error if not UDF or HP near-UDF
                if ( ! fsd->domainID.isUDF() &&
                     ! fsd->domainID.isEarlyHP() ) {
                    err = error_Non_UDF_volume_set;
                    delete fsd;
                    delete [] bp;
                    return False;
                }
                fsdsExt.start.sid = volumeTable[fsd->nextExtent.partRefNo()].sid();
                fsdsExt.start.sector = fsd->nextExtent.logBlockNo() + partStartSector;
                fsdsExt.len = fsd->nextExtent.extLength();
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "getRootICB(): Continuing at sid=%d, sector=%d, len=%d\n",
                            (UINT32)fsdsExt.start.sid,
                            (UINT32)fsdsExt.start.sector,
                            (UINT32)fsdsExt.len);
                }
                if (!fsdsExt.len) {
                    done = True;
                }
            }
            delete [] bp;
        }
        if (fsnum == -1) {
            rootICB = lastRootICB;
        }
        foundRootICB = (rootICB.sector == 0) ? False : True;
    }
      Free the FSD
    delete fsd;

      Set the proper error if no other error encountered, but file
      set not found...
    if ( !foundRootICB && !err ) {
        err = error_File_set_not_found;
    }
    
      Return
    return foundRootICB;
}

  Misc utilities to sort out volume sets...
  Returns the size of the volume set, independent of how many are
  missing.
NSR::UINT16
NSR::Archive::sizeVolSet( VolumeTable & vt )
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),"getRootICB(): Continues");
                    }
                    done = False;
                    break;
                }
                  Break out with done==True if end of FSDSExtent
                if ( done && (fsnum == -1) && (j==(nSectorsRead-1))) {
                    rootICB.sid = volumeTable[fsd->rootDirICB.partRefNo()].sid();
                    rootICB.sector = fsd->rootDirICB.logBlockNo() + partStartSector;
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): FSDSEnd-rootICB at sid=%d,sect=%d\n",
                                (UINT32)rootICB.sid,
                                (UINT32)rootICB.sector);
                    }
                    done = True;
                    break;
                }
            }
              If Not done, then grab continuation extent.
            if (!done) {
                 ------------------------------
                  look for possible next extent
                 ------------------------------
                  !!! This is a faulty algorithm -- doesn't support
                  !!! non-zero nextExtents in middle of sequence. 
                fsd->fromISO( bp + ((nSectorsRead-1)*logicalSectorSize), 
                             fsdsExt.start.sector + nSectorsRead - 1 - partStartSector);
                if (err=fsd->error()) {
                    delete fsd; 
                    delete [] bp; 
                    return False; 
                }
                  Error if not UDF or HP near-UDF
                if ( ! fsd->domainID.isUDF() &&
                     ! fsd->domainID.isEarlyHP() ) {
                    err = error_Non_UDF_volume_set;
                    delete fsd;
                    delete [] bp;
                    return False;
                }
                fsdsExt.start.sid = volumeTable[fsd->nextExtent.partRefNo()].sid();
                fsdsExt.start.sector = fsd->nextExtent.logBlockNo() + partStartSector;
                fsdsExt.len = fsd->nextExtent.extLength();
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "getRootICB(): Continuing at sid=%d, sector=%d, len=%d\n",
                            (UINT32)fsdsExt.start.sid,
                            (UINT32)fsdsExt.start.sector,
                            (UINT32)fsdsExt.len);
                }
                if (!fsdsExt.len) {
                    done = True;
                }
            }
            delete [] bp;
        }
        if (fsnum == -1) {
            rootICB = lastRootICB;
        }
        foundRootICB = (rootICB.sector == 0) ? False : True;
    }
      Free the FSD
    delete fsd;

      Set the proper error if no other error encountered, but file
      set not found...
    if ( !foundRootICB && !err ) {
        err = error_File_set_not_found;
    }
    
      Return
    return foundRootICB;
}

  Misc utilities to sort out volume sets...
  Returns the size of the volume set, independent of how many are
  missing.
NSR::UINT16
NSR::Archive::sizeVolSet( VolumeTable & vt )
                    if(Trace::logAlgorithms()) {
                        fprintf(Trace::logFile(),
                                "getRootICB(): FSDSEnd-rootICB at sid=%d,sect=%d\n",
                                (UINT32)rootICB.sid,
                                (UINT32)rootICB.sector);
                    }
                    done = True;
                    break;
                }
            }
              If Not done, then grab continuation extent.
            if (!done) {
                 ------------------------------
                  look for possible next extent
                 ------------------------------
                  !!! This is a faulty algorithm -- doesn't support
                  !!! non-zero nextExtents in middle of sequence. 
                fsd->fromISO( bp + ((nSectorsRead-1)*logicalSectorSize), 
                             fsdsExt.start.sector + nSectorsRead - 1 - partStartSector);
                if (err=fsd->error()) {
                    delete fsd; 
                    delete [] bp; 
                    return False; 
                }
                  Error if not UDF or HP near-UDF
                if ( ! fsd->domainID.isUDF() &&
                     ! fsd->domainID.isEarlyHP() ) {
                    err = error_Non_UDF_volume_set;
                    delete fsd;
                    delete [] bp;
                    return False;
                }
                fsdsExt.start.sid = volumeTable[fsd->nextExtent.partRefNo()].sid();
                fsdsExt.start.sector = fsd->nextExtent.logBlockNo() + partStartSector;
                fsdsExt.len = fsd->nextExtent.extLength();
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "getRootICB(): Continuing at sid=%d, sector=%d, len=%d\n",
                            (UINT32)fsdsExt.start.sid,
                            (UINT32)fsdsExt.start.sector,
                            (UINT32)fsdsExt.len);
                }
                if (!fsdsExt.len) {
                    done = True;
                }
            }
            delete [] bp;
        }
        if (fsnum == -1) {
            rootICB = lastRootICB;
        }
        foundRootICB = (rootICB.sector == 0) ? False : True;
    }
      Free the FSD
    delete fsd;

      Set the proper error if no other error encountered, but file
      set not found...
    if ( !foundRootICB && !err ) {
        err = error_File_set_not_found;
    }
    
      Return
    return foundRootICB;
}

  Misc utilities to sort out volume sets...
  Returns the size of the volume set, independent of how many are
  missing.
NSR::UINT16
NSR::Archive::sizeVolSet( VolumeTable & vt )
                if(Trace::logAlgorithms()) {
                    fprintf(Trace::logFile(),
                            "getRootICB(): Continuing at sid=%d, sector=%d, len=%d\n",
                            (UINT32)fsdsExt.start.sid,
                            (UINT32)fsdsExt.start.sector,
                            (UINT32)fsdsExt.len);
                }
                if (!fsdsExt.len) {
                    done = True;
                }
            }
            delete [] bp;
        }
        if (fsnum == -1) {
            rootICB = lastRootICB;
        }
        foundRootICB = (rootICB.sector == 0) ? False : True;
    }
      Free the FSD
    delete fsd;

      Set the proper error if no other error encountered, but file
      set not found...
    if ( !foundRootICB && !err ) {
        err = error_File_set_not_found;
    }
    
      Return
    return foundRootICB;
}

  Misc utilities to sort out volume sets...
  Returns the size of the volume set, independent of how many are
  missing.
NSR::UINT16
NSR::Archive::sizeVolSet( VolumeTable & vt )


---------------------------------------------------------------------
  METHOD: Archive::logErrors  --DVM
       
   Enables or disables the logging of the Archive Object's error
   state. Whenever a public method is exited, the error, if set, will
   be printed to the log, if a logFile has been specified.
 
  ARGS:
   Boolean   le  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Errors will or will not be logged to logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
void
NSR::Archive::logErrors(Boolean le) {
    if (le) {
        Trace::defaultError(err);
    }
    Trace::logErrors(le);
}
 ----------------------------------------------------------------------
  METHOD: Archive::logAlgorithms  --DVM
       
   Enables or disables the logging of the Archive Object's
   miscellaneous algorithmic output.
 
  ARGS:
   Boolean   la  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Miscellaneous printf()s will or will not be logged to logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
  Private comments go here - will not be extracted into HTML.
 ----------------------------------------------------------------------
void
NSR::Archive::logAlgorithms(Boolean la) {
    Trace::logAlgorithms(la);
}

 ----------------------------------------------------------------------
  METHOD: Archive::logCalls  --DVM
       
   Enables or disables the logging of the entry and exit of all of
   the Archive Object's public methods.
 
  ARGS:
   Boolean   lc  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Entries and Exits to/from Archive Object's public methods will or
   will not be logged to the logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
  Private comments go here - will not be extracted into HTML.
 ----------------------------------------------------------------------
void
NSR::Archive::logCalls(Boolean lc) {
    Trace::logCalls(lc);
}
    
  _PRIVATE_ Methods
 ----------------------------------------------------------------------
  METHOD:  Archive::parseEA
 
     Decode any extended attributes associated with an ICB.
 
  ARGS:
     UINT32 eaLogicalSector,   IN    Logical block address of this EA.
                                     Needed in fromISO().
     UINT32 nBytesInEA,        IN    Number of bytes in EA.
     BYTE *ea,                 IN    Buffer containing ea info.
     trustees,                 OUT   Where to place ea info.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import 
 ----------------------------------------------------------------------
void
NSR::Archive::parseEA( UINT32 eaLogicalSector, UINT32 nBytesInEA, BYTE *ea, 
                       NetwareTrusteeInfo &trustees)
    }
    Trace::logErrors(le);
}
 ----------------------------------------------------------------------
  METHOD: Archive::logAlgorithms  --DVM
       
   Enables or disables the logging of the Archive Object's
   miscellaneous algorithmic output.
 
  ARGS:
   Boolean   la  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Miscellaneous printf()s will or will not be logged to logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
  Private comments go here - will not be extracted into HTML.
 ----------------------------------------------------------------------
void
NSR::Archive::logAlgorithms(Boolean la) {
    Trace::logAlgorithms(la);
}

 ----------------------------------------------------------------------
  METHOD: Archive::logCalls  --DVM
       
   Enables or disables the logging of the entry and exit of all of
   the Archive Object's public methods.
 
  ARGS:
   Boolean   lc  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Entries and Exits to/from Archive Object's public methods will or
   will not be logged to the logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
  Private comments go here - will not be extracted into HTML.
 ----------------------------------------------------------------------
void
NSR::Archive::logCalls(Boolean lc) {
    Trace::logCalls(lc);
}
    
  _PRIVATE_ Methods
 ----------------------------------------------------------------------
  METHOD:  Archive::parseEA
 
     Decode any extended attributes associated with an ICB.
 
  ARGS:
     UINT32 eaLogicalSector,   IN    Logical block address of this EA.
                                     Needed in fromISO().
     UINT32 nBytesInEA,        IN    Number of bytes in EA.
     BYTE *ea,                 IN    Buffer containing ea info.
     trustees,                 OUT   Where to place ea info.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import 
 ----------------------------------------------------------------------
void
NSR::Archive::parseEA( UINT32 eaLogicalSector, UINT32 nBytesInEA, BYTE *ea, 
                       NetwareTrusteeInfo &trustees)


---------------------------------------------------------------------
  METHOD: Archive::logCalls  --DVM
       
   Enables or disables the logging of the entry and exit of all of
   the Archive Object's public methods.
 
  ARGS:
   Boolean   lc  IN   True -> enables; False -> disables.
 
  RETURNS:
   void
  
  PRE-CONDITIONS:
   none.
  
  POST-CONDITIONS:
   Entries and Exits to/from Archive Object's public methods will or
   will not be logged to the logFile.
  
  ERRORS:
   none.
  
  NOTES:
   This affects the static members of Trace.
 
 ----------------------------------------------------------------------
void
NSR::Archive::logCalls(Boolean lc) {
    Trace::logCalls(lc);
}
    
  _PRIVATE_ Methods
 ----------------------------------------------------------------------
  METHOD:  Archive::parseEA
 
     Decode any extended attributes associated with an ICB.
 
  ARGS:
     UINT32 eaLogicalSector,   IN    Logical block address of this EA.
                                     Needed in fromISO().
     UINT32 nBytesInEA,        IN    Number of bytes in EA.
     BYTE *ea,                 IN    Buffer containing ea info.
     trustees,                 OUT   Where to place ea info.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import 
 ----------------------------------------------------------------------
void
NSR::Archive::parseEA( UINT32 eaLogicalSector, UINT32 nBytesInEA, BYTE *ea, 
                       NetwareTrusteeInfo &trustees)
NSR::Archive::logCalls(Boolean lc) {
    Trace::logCalls(lc);
}
    
  _PRIVATE_ Methods
 ----------------------------------------------------------------------
  METHOD:  Archive::parseEA
 
     Decode any extended attributes associated with an ICB.
 
  ARGS:
     UINT32 eaLogicalSector,   IN    Logical block address of this EA.
                                     Needed in fromISO().
     UINT32 nBytesInEA,        IN    Number of bytes in EA.
     BYTE *ea,                 IN    Buffer containing ea info.
     trustees,                 OUT   Where to place ea info.
 
  RETURNS:
  
  PRE-CONDS: 
  
  POST-CONDS:
  
  ERRORS:
   error_
  
  NOTES:
     import 
 ----------------------------------------------------------------------
void
NSR::Archive::parseEA( UINT32 eaLogicalSector, UINT32 nBytesInEA, BYTE *ea, 
                       NetwareTrusteeInfo &trustees)