-------------------------------------------------------------------------------
                       Archive Import Export Module

     The arcImEx (Archive Import Export) module provides a few monolithic
functions intended to hide the complexity of the NSR Archive object from the
user.  The nsrArchiveTree class is used as an intermediate representation
between the NSR metadata forms and whatever representation the application
writer is using.  Additionally, arcImEx provides a function to hide the details
of using the hpACDev autochanger device object (insofar as this is possible).
Listed below are the data structures and functions provided by the arcImEx
module.

-------------------------------------------------------------------------------
FUNCTION:  configDevice(String *devf,int ask,dlist *srfcList)

     This method creates an hpACDev object bound to the argument passthru
device file.

ARGUMENTS:

     String  *devf      IN      Path of autochanger device file
     int      ask       IN      If nonzero, query the user for drive device
                                file mappings
     dlist   *srfcList  IN/OUT  List of surfaces to map to sid_t's.

RETURNS:

     Returns an hpACDev * on success, NULL on failure.

PRECONDITIONS:

--Argument device file has appropriate permissions (read/write) and corresponds
  to the passthru character special device file for the autochanger's
  controller device.
--Argument surface list contains at least one valid entry.

POSTCONDITIONS:

--An hpACDev object is created, and the surfaces specified in the srfcList
  parameter is mapped to sid_t's.  The surfaces' sid_t mappings are
  added to the entries in the srfcList.
--If the ask parameter was nonzero, the user is queried for drive device
  files.
--All successfully mapped surfaces have sid_t values added to their
  entries in the srfcList parameter; any surfaces which cannot be mapped
  (e.g., they are not physically present in the autochanger) are removed
  from the srfcList.

DISCUSSION:

     The dlist structure is a generic doubly-linked list implemented as a
C toolkit.  It takes generic (void *) data pointers and puts them into a
doubly-linked list.  

     The srfcList dlist is expected to contain data items of type srfcEntry.
The srfcEntry struct is given below:

    struct srfcEntry {
        int             slot;
        int             side;
        sid_t           sid;
        ExtentList      elist;          // Used primarily in export
        char           *volname;        // Used primarily in export

        srfcEntry(void) : slot(0),
                          side(0),
                          sid(SID_T_INVALID),
                          volname(NULL) {}
        ~srfcEntry(void) {
            if(volname != NULL) {
                delete [] volname;
            }
        }
    };

     Upon entry to configDevice(), the argument srfcList is expected to
contain one or more srfcEntrys which have the slot and side fields filled in.
The side field should be 0 for side 'A' and nonzero for side 'B'.  If the
surface is successfully mapped in the hpACDev object, the sid field for its
srfcEntry entry will be filled in with the sid_t assigned to it by the
hpACDev.

     The elist field, used primarily by exportArchive(), represents the
surface's free space extent list, which is used by the NSR Archive object
to determine where it can write metadata.  Similarly, the volname
field is used by exportArchive() to indicate the Volume name.  This name
should not exceed 32 characters in length.

     Following is an example sequence of generating a dlist for use with
configDevice().

    dlist       *srfcList;
    srfcEntry   *sptr;

    srfcList = dlistCreate(srfc_cmp);   // Create the dlist

    // Add a couple of surfaces

    sptr = new srfcEntry;
    sptr->slot = 3;
    sptr->side = 0;                     // Surface 3A

    // Add the surface to the list.  We have three alternatives:
    // dlistAppend(), which adds the new entry to the tail of the list.
    // dlistInsert(), which adds the entry in sorted order according to
    //                the comparison function used to create the list.
    // dlistPrepend(), which adds the entry to the head of the list.

    dlistAppend(srfcList,sptr);         // Add the surface to the list

    sptr = new srfcEntry;
    sptr->slot = 3;
    sptr->side = 1;                     // Surface 3B

    dlistAppend(srfcList,sptr);         // Add the surface to the list

    // Now configure our autochanger device

    hpACDev     *acdev;

    acdev = configDevice("/dev/ac.passthru",1,srfcList);

     To extract data from the dlist, the user must use the dlist_node
structure.  Following is an example of how to print out the sid_t's assigned
to the surfaces after a call to configDevice().

/* 
** In dlist.h:
**
**    typedef struct dlist_node 
**    {
**      struct dlist_node       *next;
**      struct dlist_node       *prev;
**      void                    *data;
**    } dlist_node;
*/

    dlist_node  *dnode;

    dnode = dlistHead(srfcList);
    while(dnode != NULL) {
        sptr = (srfcEntry *) dnode->data;
        printf("Surface %d%c is mapped to sid_t %d\n",
               sptr->side,
               sptr->slot ? 'B' : 'A',
               sptr->sid);
        dnode = dnode->next;
    }

     If the ask parameter is nonzero, configDevice() interactively queries
the user for disk drive character special device files for each of the drives
in the autochanger.  The dialogue looks similar to the following:

Please enter device file for drive 1, tgt=3, lun=0:  /dev/rdsk/c201d3s0
Please enter device file for drive 2, tgt=4, lun=0:  /dev/rdsk/c201d4s0

     Note that the device files entered by the user must have appropriate
read-write permissions. 

     If the ask parameter is zero, the hpACDev attempts to create temporary
device files for the drives in the autochanger.  Since the hpACDev code
uses the mknod() system call, the executing process must be root in order for
the automatic device-file generation to succeed.

-------------------------------------------------------------------------------
FUNCTION:  importArchive(hpACDev *acdev,dlist *srfcList,nsrArchiveTree& tree)

     This function imports metadata from the NSR volume set specified by the
argument srfcList.  Upon successful completion, the metadata from the imported
volumes is contained in the argument nsrArchiveTree, and the root of the tree
is returned.

ARGUMENTS:

     hpACDev         *acdev     IN     Autochanger device object containing the
                                       surfaces to import
     dlist           *srfcList  IN     List of surfaces and their sid_t
                                       mappings to import
     nsrArchiveTree&  tree      IN/OUT Archive tree

RETURNS:

     Upon success, returns the root of the imported directory tree.  On any
error, the function returns NULL_NSRTREENODE.

PRECONDITIONS:

--The argument hpACDev was successfully constructed and contains surface
  mappings for all surfaces in the srfcList.  It also contains appropriate
  drive mappings if necessary (required for non-root users).
--The argument srfcList contains at least one surface to import.

POSTCONDITIONS:

--The nsrArchiveTree is populated with the imported directory tree.
--The root node of the imported directory tree is returned.

DISCUSSION:

     Upon any kind of error, processing of the import halts immediately.
This function prints status messages to stdout periodically as it progresses
through the import process, and any kind of error message is printed to
stderr.

     Please see the discussion of configDevice() for information on the
the format of the data items contained in the srfcList parameter.

-------------------------------------------------------------------------------
FUNCTION:  exportArchive(nsrArchiveTree& tree,nsrTreeNode root,hpACDev *acdev,
                         dlist *srfcList,char *vsname,char *fsname,
                         MediaType mtype,UINT32 ssize,UINT32 dataStartSector)

     This function exports metadata from the directory tree structure
described by the nsrArchiveTree to NSR volumes given by the argument
srfcList.

ARGUMENTS:

     nsrArchiveTree& tree             IN  Archive tree containing directory
                                          structure information and file
                                          metadata
     nsrTreeNode     root             IN  Root of the directory tree to be
                                          exported
     hpACDev        *acdev            IN  Autochanger device object for export
     dlist          *srfcList         IN  List of surfaces to export data to
     char           *vsname           IN  Volume set name
     char           *fsname           IN  File set name
     MediaType       mtype            IN  Media type (NSR::MO or NSR::WORM)
     UINT32          ssize            IN  Sector size of export volumes
     UINT32          dataStartSector  IN  Start sector for file data on volumes

PRECONDITIONS:

--The nsrArchiveTree is fully populated with the directory tree structure
  to be exported
--The root nsrTreeNode parameter is a valid node
--The hpACDev parameter has all surface maps necessary for the surfaces in
  the srfcList, and has appropriate drive mappings if necessary (for non-root
  users)
--The srfcList contains at least one surface for export
--The vsname parameter is at most 96 bytes in length
--The fsname parameter is at most 32 bytes in length
--The mtype parameter is NSR::MO (WORM is not yet supported)
--The ssize parameter is valid (typically 512 or 1024)
--The dataStartSector is valid and is the same for all volumes for the export
--Each entry in the srfcList has a valid freespace ExtentList and volume name
  (maximum length of 32 bytes).

POSTCONDITIONS:

--Metadata from the directory tree specified by the tree and root parameters
  is exported to the NSR volumes.
--The root volume is the last surface specified in the srfcList.

DISCUSSION:

     Upon any kind of error, processing of the import halts immediately.
This function prints status messages to stdout periodically as it progresses
through the export process, and any kind of error message is printed to
stderr.  This function does not yet support export to WORM media.

     Please see the discussion of configDevice() for information on the
the format of the data items contained in the srfcList parameter.

-------------------------------------------------------------------------------
FUNCTION:  srfc_cmp(void *d1,void *d2)

     This function is necessary for use with the dlist doubly-linked list
routines.  It is a comparison function for storing srfcEntry structures,
necessary for the configDevice(), importArchive(), and exportArchive()
functions, in a dlist.  This function will not normally be called directly
by user code, but is used in conjunction with the dlistCreate() function.

ARGUMENTS:

     void  *d1   IN  First data item to compare
     void  *d2   IN  Second data item to compare

RETURNS:

     Returns a value less than 0 if d1 compares less than d2;
             a value equal to 0 if d1 compares equal to d2;
             a value greater than 0 if d1 compares greater than d2.

PRECONDITIONS:

--Both d1 and d2 are non-NULL.

POSTCONDTIONS:

     See Return values.

DISCUSSION:

     When used in a dlist containing srfcEntry structs, this comparison
function produces a list in sorted order (if data items are added with the
dlistInsert() function) according to slot and side.  Example:  1A 1B 4B 5A 5B.

-------------------------------------------------------------------------------
FUNCTION:  srfc_free(void *d1,void *d2)

     This function is useful for using the dlist doubly-linked list routines
to store srfcEntry structures, which is necessary for the configDevice(),
importArchive(), and exportArchive() functions.  This function is an apply
function used to free the memory associated with srfcEntry structs in a dlist.
This function will not normally be called directly by user code, but is used
in conjunction with the dlistApply() function.

ARGUMENTS:

     void *d1   IN   Data item to free

RETURNS:

     Returns 0 on success, 1 on failure.

PRECONDITIONS:

--d1 is non-NULL

POSTCONDITIONS:

     See above.

-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
                        CLASS:  nsrArchiveTree

     The nsrArchiveTree class, in conjunction with the nsrTreeNode class,
provides the user with an in-memory representation of a directory tree
structure.  The nsrArchiveTree is designed to allow arbitrarily large
directory structures to be held virtually in memory.  Its use model is
slightly different from a standard n-ary tree data structure, as the
nsrArchiveTree class itself is not a tree node, but instead acts as a
repository for nsrTreeNode nodes.  When a node is required, the user may
request a node from the nsrArchiveTree.  All of the data within the node
is retrieved through member functions of the nsrArchiveTree class rather
than the nsrTreeNode class.  This interface is necessary to allow the
nsrArchiveTree to page node data into and out of memory.  An nsrTreeNode
should be thought of as an opaque handle to a specific node within a directory
tree structure.

     The nsrArchiveTree is provided to allow the user with an intermediate
representation of file metadata to be used for importing NSR volumes into
an online filesystem, or for exporting metadata from an online filesystem to
NSR volumes.  In its ultimate implementation, the nsrArchiveTree will provide
caching functionality to allow huge directory trees to be represented in
a virtually in-memory form.

     An nsrTreeNode represents the information normally available in a Unix
inode or an NSR ICB.  Although the nsrTreeNode is only a handle to node
information, it may be thought of as containing the following data members:

     Address            address;        // ICB address
     Attributes         attr;           // Attributes
     char *             filename;       // Filename
     ExtentList         elist;          // For files and directories
     PathElementList    plist;          // For symbolic links
     UINT32             nchildren;      // Number of children
     nsrTreeNode        *child;         // Array of child pointers

     The public member functions of the nsrArchiveTree allow both read
and write (l-value and r-value) access to these data members.  NOTE:
currently, implementation plans for the pagable representation of the
nsrArchiveTree do not allow for true persistance of the tree nodes.  As a
result, l- and r-values returned by the various access member functions
are guaranteed to be valid only until the next access of a different
tree node.  Hence, any modifications or reading of data from a particular
tree node should be done at the same time, before another tree node's
data is accessed.  Reference or pointer parameters returned by the
access methods are not guaranteed to be valid across accesses to different
tree nodes.

Example:

    char *name_one,*name_two;
    int  len;

    name_one = tree.filename(first_tree_node);
    printf("File one name is %s\n",name_one);           // Valid
    name_two = tree.filename(second_tree_node);
    printf("File two name is %s\n",name_two);           // Valid
    len = strlen(name_one);                             // INVALID!

     In this example, the strlen() call is potentially invalid because
the tree.filename() call to the second tree node may have caused a page-out
of the first tree node's data, and so the name_one pointer may point
to data that is no longer associated with the first tree node.

Listed below is a description of all public member functions of the
nsrArchiveTree.

-------------------------------------------------------------------------------
METHOD:  allocNode(UINT32 numChildren)

     Allocates an nsrTreeNode from the nsrArchiveTree.

ARGUMENTS:

     UINT32  numChildren  IN  Number of child pointers to allocate for node

RETURNS:

     Returns an nsrTreeNode on success, NULL_NSRTREENODE on failure.

PRECONDITIONS:

     None.

POSTCONDITIONS:

     See Return values.

-------------------------------------------------------------------------------
METHOD:  allocNode(Address& addr,UINT32 numChildren)

     An alternate signature for the node allocation method.  This form
initializes the Address data member of the new node.

ARGUMENTS:

     UINT32    numChildren  IN  Number of child pointers to allocate for node
     Address&  addr         IN  Node's ICB Address

RETURNS:

     Returns an nsrTreeNode on success, NULL_NSRTREENODE on failure.

PRECONDITIONS:

     None.

POSTCONDITIONS:

     See Return values.

-------------------------------------------------------------------------------
METHOD:  resize(nsrTreeNode node,UINT32 newsize)

     This method resizes the child pointer array for the specified node.  As
many of the old child pointer values are retained as possible.

ARGUMENTS:

     nsrTreeNode   node     IN  Node to resize
     UINT32        newsize  IN  New size for the child pointer array

RETURNS:

     Returns 0 on success, -1 on failure (memory allocation failure).

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

--The child pointer array will be resized to the specified size.
--If the newsize is greater than the old size, all of the old child pointer
  values will be retained intact in entries [0,oldsize - 1].  The pointer
  values in indices [oldsize,newsize - 1] will be initialized to
  NULL_NSRTREENODE.
--If the newsize is smaller than the old size, the child pointer entries
  in indices [0,newsize - 1] will be retained intact.
-------------------------------------------------------------------------------
METHOD:  isValid(nsrTreeNode node)

     This method determines whether the specified node is valid--i.e., it has
a valid child pointer array and filename buffer.

ARGUMENTS:

     nsrTreeNode   node   IN  Node for validity check

RETURNS:

     Returns 1 if the node is valid, 0 otherwise.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

     See Return values.

-------------------------------------------------------------------------------
METHOD:  search(nsrTreeNode node,Address& addr)

     This method searches the specified node's children for a child with the
the argument ICB address.  NOTE:  This function does NOT search recursively.
It only searches the immediate children of the specified node.

ARGUMENTS:

     nsrTreeNode   node   IN  Node to search.
     Address&      addr   IN  Address to search for.

RETURNS:

     Returns an nsrTreeNode pointing to the child containing the argument
address if found; otherwise returns NULL_NSRTREENODE.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS

     See Return values.

-------------------------------------------------------------------------------
METHOD:  address(nsrTreeNode node,sid_t sid,UINT32 sector)

     This method sets the Address of the specified node.

ARGUMENTS:

     nsrTreeNode  node      IN  Node to modify
     sid_t        sid       IN  New sid of Address
     UINT32       sector    IN  New sector of Address

RETURNS:

     None.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

--The node's address will be set to the specified values.

-------------------------------------------------------------------------------
METHOD:  address(nsrTreeNode node,Address& addr)

     Alternate signature for the set-address method.

ARGUMENTS:

     nsrTreeNode  node      IN  Node to modify
     Address&     Address   IN  New Address

RETURNS:

     None.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

--The node's address will be set to the specified values.

-------------------------------------------------------------------------------
METHOD:  filename(nsrTreeNode node,char *newname)

     This method sets the node's filename to the specified value.  Note that
the setting of the filename is performed with value-based semantics:  The
argument character string will be copied into the node's filename buffer.

ARGUMENTS:

     nsrTreeNode  node      IN  Node to modify
     char        *newname   IN  New filename

RETURNS:

     None.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

     See above.

-------------------------------------------------------------------------------
METHOD:  attr(nsrTreeNode node,const Attributes& attr)

     This method sets the node's Attributes to the given value.

ARGUMENTS:

     nsrTreeNode  node      IN  Node to modify
     Attributes&  attr      IN  New attributes

RETURNS:

     None.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

     See above.

-------------------------------------------------------------------------------
METHOD:  child(nsrTreeNode node,UINT32 which,nsrTreeNode newval)

     This method sets the specified child pointer, as given by the index
which, to the value newval.

ARGUMENTS:

     nsrTreeNode  node      IN  Node to modify
     UINT32       which     IN  Index of child pointer to set
     nsrTreeNode  newvalue  IN  Value to point child pointer to

RETURNS:

     None.

PRECONDITIONS:

--The specified node is a valid handle.
--The newnode is a valid handle.
--The index which is in the range [0,nchildren - 1]

POSTCONDITIONS:

     See above.

DISCUSSION:

     If the index which is out of range, this method does nothing.

-------------------------------------------------------------------------------
METHOD:  elist(nsrTreeNode node,ExtentList& newlist)

     This method sets the node's ExtentList to be equal to the argument
list.  The setting is performed with value-based semantics.

ARGUMENTS:

     nsrTreeNode  node      IN  Node to modify
     ExtentList&  newlist   IN  New ExtentList value

RETURNS:

     None.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

     See above.

-------------------------------------------------------------------------------
METHOD:  filename(nsrTreeNode node)

     Returns a pointer to the filename character buffer.  Note that, while
it is possible to use this pointer to modify the filename, it is recommended
that the filename(nsrTreeNode node,char *newname) method be used instead to
change the filename.

ARGUMENTS:

     nsrTreeNode  node     IN  Node from which to retrieve information

RETURNS:

     Returns a pointer to the filename character buffer.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

     See Return values.

-------------------------------------------------------------------------------
METHOD:  attr(nsrTreeNode node)

     This method returns a reference to the node's Attributes.

ARGUMENTS:

     nsrTreeNode  node     IN  Node from which to retrieve information

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

     See Return values.

-------------------------------------------------------------------------------
METHOD:  address(nsrTreeNode node)

     This method returns a reference to the node's ICB Address.

ARGUMENTS:

     nsrTreeNode  node     IN  Node from which to retrieve information

RETURNS:

     This method returns a reference to the node's ICB Address.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

     See Return values.

-------------------------------------------------------------------------------
METHOD:  getChild(nsrTreeNode node,UINT32 which)

     This method returns an nsrTreeNode handle to the specified child.

ARGUMENTS:

     nsrTreeNode  node     IN  Node from which to retrieve information
     UINT32       which    IN  Index into the child pointer array.

RETURNS:

     Returns an nsrTreeNode handle to the specified child if the which index
is in the range [0,nchildren - 1]; returns NULL_NSRTREENODE otherwise.

PRECONDITIONS:

--The specified node is a valid handle.
--The which index is in the range [0,nchildren - 1]

POSTCONDITIONS:

     See Return values.

-------------------------------------------------------------------------------
METHOD:  nchildren(nsrTreeNode node)

     This method returns the number of child pointers in the node.

ARGUMENTS:

     nsrTreeNode  node     IN  Node from which to retrieve information

RETURNS:

     Returns the number of child pointers in the node.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

     See Return values.

-------------------------------------------------------------------------------
METHOD:  plist(nsrTreeNode node)

     This method returns a reference to the node's PathElementList.

ARGUMENTS:

     nsrTreeNode  node     IN  Node from which to retrieve information

RETURNS:

     This method returns a reference to the node's PathElementList.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

     See Return values.

-------------------------------------------------------------------------------
METHOD:  elist(nsrTreeNode node)

     This method returns a reference to the node's ExtentList.

ARGUMENTS:

     nsrTreeNode  node     IN  Node from which to retrieve information

RETURNS:

     This method returns a reference to the node's ExtentList.

PRECONDITIONS:

--The specified node is a valid handle.

POSTCONDITIONS:

     See Return values.

-------------------------------------------------------------------------------
