#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2003,2006 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)37 1.45.1.2 src/avs/fs/mmfs/ts/admin/mmimportfs.sh, mmfs, avs_rgpfs24, rgpfs24s002a 4/21/06 14:48:31
##############################################################################
#
# Usage:
#   mmimportfs {Device|all} -i ImportfsFile [-S ChangeSpecFile] [-R]
#
# where
#   Device        is the file system to be imported.
#                 If Device is "all", then all file systems in
#                 the input file are imported.  Free disks, if any,
#                 are imported as well.
#                 Note:  To request a file system with the name "all",
#                        specify "/dev/all" to distinguish from the
#                        keyword "all" (import all file systems).
#
#   -i ImportfsFile   is the name of a file with the file system
#                     information.  It must be created by mmexportfs.
#
#   -S ChangeSpecFile  a file containing a detailed description of changes
#                 to be made to the file systems during the import step.
#
# Undocumented option:
#
#   -R            remove the specified file system from the mmsdrfs file
#                 and replace it with the data from the import file.
#                 If 'all' is specified, all file system and free disk
#                 information that may currently exist is removed from
#                 the mmsdrfs file and is replaced with the data from
#                 the import file.
#
##############################################################################

# Include global declarations and service routines.
. /usr/lpp/mmfs/bin/mmglobfuncs
. /usr/lpp/mmfs/bin/mmsdrfsdef
. /usr/lpp/mmfs/bin/mmfsfuncs

sourceFile="mmimportfs.sh"
[[ -n $DEBUG || -n $DEBUGmmimportfs ]] && set -x
$mmTRACE_ENTER "$*"


# Local work files.  Names should be of the form:
#   fn=${tmpDir}fn.${mmcmd}.$$
existingDisks=${tmpDir}existingDisks.${mmcmd}.$$
recoveryFiles=${tmpDir}recoveryFiles.${mmcmd}.$$
importedFreeDisks=${tmpDir}importedFreeDisks.${mmcmd}.$$
resetFreeDisks=${tmpDir}resetFreeDisks.${mmcmd}.$$
disksToUnfence=${tmpDir}disksToUnfence.${mmcmd}.$$
aixNodes=${tmpDir}aixNodes.${mmcmd}.$$

# The following declarations are the prefixes for the file names
# that will contain information relevant to a specific file system.
# The actual file names consist of the prefix followed by a device name.
importFile=${tmpDir}importFile.${mmcmd}.$$
resetNSDs=${tmpDir}resetNSDs.${mmcmd}.$$
resetLVs=${tmpDir}resetLVs.${mmcmd}.$$

LOCAL_FILES=" $existingDisks $recoveryFiles $importedFreeDisks $resetFreeDisks \
              $aixNodes $disksToUnfence $importFile.* $resetNSDs.* $resetLVs.* "

# Local variable declarations
usageMsg=571
iflag=""
Cflag=""
Sflag=""

existingMountPoints=""
maxDiskNumber=0

# Local functions:


##############################################################################
#
# Function:  Given the source and target cluster types, determine whether
#            the present version of mmimportfs can import the file system.
#
#            Note:  Not all possible cluster type combinations are supported.
#                   In some cases, simplifying assumptions are made.
#
# Input:     $1 - source cluster type
#            $2 - target cluster type
#
# Output:    None
#
# Returns:   0 - the two cluster types are compatible.
#            1 - the two cluster types are not compatible.
#
##############################################################################
function checkClusterCompatibility  # <source> <target>
{
  sourceFile="mmimportfs.sh"
  [[ -n $DEBUG || -n $DEBUGcheckClusterCompatibility ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset source=$1
  typeset target=$2

  typeset rc

  case $source in
    lc )
      [[ $target = lc     ]] && rc=0
      [[ $target = single ]] && rc=0
      ;;

    sp )
      [[ $target = lc     ]] && rc=0
      [[ $target = single ]] && rc=0
      ;;

    rpd )
      [[ $target = lc     ]] && rc=0
      [[ $target = single ]] && rc=0
      ;;

    hacmp )
      [[ $target = lc     ]] && rc=0
      [[ $target = single ]] && rc=0
      ;;

    single )
      [[ $target = lc     ]] && rc=0
      [[ $target = single ]] && rc=0
      ;;

     * )
      # Unknown source cluster type.
      rc=1
      ;;
  esac

  return $rc

}  #----- end of function checkClusterCompatibility ----------------


###########################################################################
#
# Function:  See if there are any disks that will need to be recovered
#            because of a previous failure of mmimportfs.
#
# Input:     $1 - source cluster type
#            $2 - output file name
#
# Output:    File containing the disk names that may have failed a previous
#            tspreparedisk -m call.  Each line has the following format:
#              <diskName> <checksum> <timestamp> <server> <fullPathName>
#
# Returns:   0 - no errors
#            1 - error encountered
#
###########################################################################
function getDiskRecoveryInformation  # <source> <outputFile>
{
  sourceFile="mmimportfs.sh"
  [[ -n $DEBUG || -n $DEBUGgetDiskRecoveryInformation ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset source=$1
  typeset outputFile=$2

  typeset server

  $rm -f $outputFile

  # If we are not creating NSDs, there is nothing to worry about.
  [[ $source = lc || $source = single ]] &&  \
    return 0

  # Create a list of all disk recovery files created by tspreparedisk.
  # Such files, if any, will be stored in /var/mmfs/tmp/mmimportfs on
  # the primary and backup server configuration nodes.  The file names
  # have the following format:
  #   tspreparedisk.diskDesc.<diskName>.<checksum>.<timestamp>
  #
  # Put the results in a file with the following format:
  #   <diskName> <checksum> <timestamp> <server> <fullPathName>
  #
  for server in $(print -- $primaryServer $backupServer)
  do
    run onNode $server lsSGDescFile 2>/dev/null |  \
      $awk -F. '
        { print $3" "$4" "$5" '${server}' "$0 }
      ' >> $outputFile
  done

  # Sort the result so that if there is more than one recovery file
  # for any given disk, the most recent version appears first.
  $sort -k 1,1 -k 3,3nr -o $outputFile $outputFile
  checkForErrors "sort $outputFile" $?

  return 0

}  #----- end of function getDiskRecoveryInformation ---------------


#########################################################################
#
# Function:  Restore the disk and SG descriptors of the specified disk.
#
# Input:     $1 - diskName
#            $2 - file created by function getDiskRecoveryInformation
#
# Output:    None.
#
# Returns:   0 - no errors
#            1 - error encountered
#
#########################################################################
function recoverOldSGdescriptor  # <diskName> <recoveryInfoFile>
{
  sourceFile="mmimportfs.sh"
  [[ -n $DEBUG || -n $DEBUGrecoverOldSGdescriptor ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset diskName=$1
  typeset recoveryInfoFile=$2

  typeset recoveryInfo checksum serverNode fqName sumOutput newSum
  typeset rc=0

  # See if there is a recovery file for this disk.
  recoveryInfo=$($grep -w $diskName $recoveryInfoFile)

  # Return if there is nothing to do.
  [[ -z $recoveryInfo ]] &&  \
    return 0

  print -u2 -- "$mmcmd: Recovering SG descriptor area of disk $diskName"

  # Parse the information from the recoveryInfoFile.
  set -f ; set -- $recoveryInfo ; set +f
  checksum=$2
  serverNode=$4
  fqName=$5

  # Retrieve the file if it isn't already on our node.
  if [[ ! -e $fqName ]]
  then
    # Ensure the target directory for rcp exists.
    $mkdir -p ${fqName%/*}

    # Copy the file from the server node.
    $rcp ${serverNode}:${fqName} $fqName
    checkForErrors "rcp ${serverNode}:${fqName}" $?

    # Verify the checksum.
    sumOutput=$($sum $fqName)
    rc=$?
    set -f ; set -- $sumOutput ; set +f
    newSum=$1
    if [[ $checksum != $newSum ]]
    then
      printErrorMsg 379 $mmcmd $serverNode $ourNodeName
      return 1
    fi
  fi  # end of if [[ ! -e $fqName ]]

  # Restore the disk and SG descriptors of the disk.
  $dd if=$fqName of=/dev/$diskName seek=1 2>$errMsg
  rc=$?
  if [[ $rc -ne 0 ]]
  then
    [[ -s $errMsg ]] && $cat $errMsg 1>&2
    $rm -f $errMsg
    print -u2 "$mmcmd: dd if=$fqName of=/dev/$diskName seek=1 failed with rc $rc."
    print -u2 "$mmcmd: Error recovering disk $diskName from a previous failure."
    return $rc
  fi
  $rm -f $errMsg

  print -u2 -- "$mmcmd: Disk $diskName successfully recovered."

  # At this point the disk was successfully restored.
  # Remove all SG descriptor files for this disk and return.
  $rm -f ${fqName%/*}/tspreparedisk.diskDesc.${diskName}.*
  for server in $(print -- $primaryServer $backupServer)
  do
    run onNode $server rmSGDescFile $diskName
  done

  return 0

}  #----- end of function recoverOldSGdescriptor -------------------


############################################################################
#
# Function:  Import a disk into cluster type single.  Depending on the
#            source cluster types, this may also involve the conversion
#            of the disk into an NSD.
#
#            Note:  The disk is assumed to be accessible on the local node.
#
# Input:     $1 - source cluster type
#            $2 - current SG_DISKS line for the disk
#            $3 - target nodesetId
#
# Output:    $1 - result: error, success.
#            $2 - updated SG_DISKS line.
#
# Returns:   0 - everything worked OK
#            1 - error encountered
#
############################################################################
function importDisk_single  # <source> <diskLine> <targetNodeset>
{
  sourceFile="mmimportfs.sh"
  [[ -n $DEBUG || -n $DEBUGimportDisk_single ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset sourceCluster=$1
  typeset currentDiskLine=$2
  typeset targetNodesetId=$3

  typeset rc=0
  typeset result="success"
  typeset invokingCommand="mmimportfs"
  typeset updatedDiskLine createNsdOutput tspreparediskOutput
  typeset diskName magicWord rc2 pvid nsdSubtype nsdSubtypeDiskname

  # Parse the current SG_DISKS line for the disk.
  IFS=":"
  set -f ; set -A v -- - $currentDiskLine ; set +f
  IFS="$IFS_sv"
  diskName=${v[$DISK_NAME_Field]}
  [[ -z ${v[$NSD_SUBTYPE_Field]} ]] &&  \
    v[$NSD_SUBTYPE_Field]="generic"
  nsdSubtype=${v[$NSD_SUBTYPE_Field]}
  aixPvidValue=${v[$PVID_Field]}
  [[ ${v[$NODESETID_Field]} = $FREE_DISK ]] &&  \
    invokingCommand="mmimportfs_freeDisk"
  [[ ${v[$NODESETID_Field]} != $FREE_DISK ]] &&  \
    v[$NODESETID_Field]=$targetNodesetId

  if [[ $sourceCluster != lc && $sourceCluster != single ]]
  then
    # The source cluster type is sp, rpd, or hacmp.
    # In all of these cases we may have to import
    # the underlying logical volumes and, if applicable,
    # recreate the VSDs.

    # For now, we assume that the disks are already in place.

    # See if this disk needs to be recovered from a previous
    # partial SG descriptor migration.
    if [[ -s $recoveryFiles ]]
    then
      recoverOldSGdescriptor $diskName $recoveryFiles
      rc=$?
      if [[ $rc -ne 0 ]]
      then
        print -- "error"
        return $rc
      fi
    fi

    # Convert the disk into an NSD.
    createNsdOutput=$(createNsd $diskName no $invokingCommand NULL)
    rc=$?

    # The output of createNsd consists of a one-word NSD subtype value,
    # followed by the output from tspreparedisk.  Separate the two parts.
    nsdSubtype=${createNsdOutput%% *}
    tspreparediskOutput=${createNsdOutput#* }

    # Set the value for the nsd subtype diskname if the nsd subtype
    # is lv or vsd.
    nsdSubtypeDiskname=""
    [[ $nsdSubtype = lv || $nsdSubtype = vsd ]] && nsdSubtypeDiskname=$diskName

    # Parse the output from the tspreparedisk command.
    IFS=":"
    set -f ; set -- $tspreparediskOutput ; set +f
    magicWord=$1
    rc2=$2
    pvid=$3
    IFS="$IFS_sv"

    if [[ $rc  -ne 0 || $nsdSubtype = error ||
          $rc2 -ne 0 || $magicWord != tspreparedisk ]]
    then
      # Try to make sense of the error.
      if [[ $nsdSubtype = error ]]
      then
        : # determineNsdSubtype failed.  Messages were already issued.

      elif [[ $magicWord = tspreparedisk ]]
      then
        # tspreparedisk was executed but was not happy about something.
        if [[ $rc2 = 1 ]]       # E_PERM
        then
          # Permission was denied for disk.
          printErrorMsg 523 $mmcmd "/dev/$diskName"
        elif [[ $rc2 = 2 ]]     # E_NOENT
        then
          # The disk was not found.
          printErrorMsg 524 $mmcmd "/dev/$diskName"
        elif [[ $rc2 = 5 ]]     # E_IO
        then
          # I/O error
          printErrorMsg 525 $mmcmd "/dev/$diskName"
        elif [[ $rc2 = $MM_DeviceNotFound ]]    # E_NODEV
        then
          # A return code of ENODEV was returned for the disk.
          printErrorMsg 187 $mmcmd $ourNodeName "/dev/$diskName"
        elif [[ $rc2 = 27 ]]    # E_FBIG
        then
          # The disk is too large.
          printErrorMsg 508 $mmcmd "/dev/$diskName"
        elif [[ $rc2 = 50 ]]    # E_NOCONNECT
        then
          # The disk is fenced out.
          printErrorMsg 395 $mmcmd $ourNodeName "/dev/$diskName"
        else
          # Show the result from tspreparedisk.
          print -u2 "$tspreparediskOutput"
          # Unexpected error from tspreparedisk
          printErrorMsg 171 "$mmcmd" "tspreparedisk $diskName" $rc2
        fi  # end if [[ $rc2 = 1 ]]
      else
        # Something is really wrong.  Output error information if any.
        [[ -n $createNsdOutput ]] && print -u2 "$createNsdOutput"
      fi  # end if [[ $nsdSubtype = error ]]

      # We failed to create an NSD from this disk.
      printErrorMsg 47 $mmcmd $diskName
      result="error"
      rc=1

    else
      # At this point the disk was successfully defined as an NSD.
      # Add the missing or updated information.
      v[$PVID_Field]=$pvid
      v[$NSD_SUBTYPE_Field]=$nsdSubtype
      v[$NSD_SUBTYPE_DISKNAME_Field]=$nsdSubtypeDiskname
      if [[ $nsdSubtype = lv ]]
      then
        v[$AIX_PVID_Field]=$aixPvidValue
      else
        v[$AIX_PVID_Field]=""
      fi

      # Remove any leftover files from tspreparedisk -m.
      $rm -f ${fqName%/*}/tspreparedisk.diskDesc.${diskName}.*
    fi  # end if [[ $rc  -ne 0 || $nsdSubtype = error ||  ...
  fi  # end of if [[ $sourceCluster != lc && $sourceCluster != single ]]

  # Ensure certain fields in the SG_DISKS line have
  # appropriate values for cluster type single.
  v[$NSD_PRIMARY_NODE_Field]=""
  v[$NSD_BACKUP_NODE_Field]=""
  v[$DAEMON_NSD_PRIMARY_Field]=""
  v[$DAEMON_NSD_BACKUP_Field]=""
  v[$DISK_TYPE_Field]="disk"
  updatedDiskLine=$(print_newLine)

  # Return to the caller the updated SG_DISKS line.
  print -- "$result" "$updatedDiskLine"
  return $rc

}  #----- end of function importDisk_single ------------------------


###########################################################################
#
# Function:  Import a disk into an lc cluster.  Depending on the source
#            cluster types, this may involve one or more of the following
#            operations to make the disk usable in the new cluster:
#            import the base logical volume, make/redefine a VSD,
#            make an NSD out of the disk, verify NSD servers belong
#            to the right nodeset, etc.
#
# Input:     $1 - source cluster type
#            $2 - current SG_DISKS line for the disk
#            $3 - target nodesetId
#            $4 - (optional) disk descriptor spec file
#
# Output:    $1 - result; error, success, successServersRemoved.
#            $2 - updated SG_DISKS line.
#
# Returns:   0 - everything worked OK
#            1 - error encountered
#
###########################################################################
function importDisk_lc  # <source> <diskLine> <targetNodeset> [<specFile>]
{
  sourceFile="mmimportfs.sh"
  [[ -n $DEBUG || -n $DEBUGimportDisk_lc ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset sourceCluster=$1
  typeset currentDiskLine=$2
  typeset targetNodesetId=$3
  typeset specFile=$4

  typeset rc=0
  typeset diskDesc=""
  typeset result="success"
  typeset invokingCommand="mmimportfs"
  typeset diskName server backup nodesetId validateDescriptorOutput
  typeset updatedDiskLine createNsdOutput tspreparediskOutput
  typeset magicWord rc2 pvid nsdSubtype nsdSubtypeDiskname invokedNode

  # Parse the current SG_DISKS line for the disk.
  IFS=":"
  set -f ; set -A v -- - $currentDiskLine ; set +f
  IFS="$IFS_sv"
  diskName=${v[$DISK_NAME_Field]}
  nsdSubtype=${v[$NSD_SUBTYPE_Field]}
  aixPvidValue=${v[$PVID_Field]}
  [[ -z $nsdSubtype ]] && nsdSubtype="generic"
  [[ ${v[$NODESETID_Field]} = $FREE_DISK ]] &&  \
    invokingCommand="mmimportfs_freeDisk"
  [[ ${v[$NODESETID_Field]} != $FREE_DISK ]] &&  \
    v[$NODESETID_Field]=$targetNodesetId
  nodesetId=${v[$NODESETID_Field]}

  # If a change file is specified (-S option),
  # see if it contains a descriptor for this disk.
  [[ -s $specFile ]] &&  \
    diskDesc=$($grep -e "[$BLANKchar$TABchar]$diskName:"   \
                     -e "^$diskName:" $specFile 2>/dev/null)

  if [[ -n $diskDesc ]]
  then
    # If a disk descriptor was specified,
    # parse it to get the server names.
    IFS=':'
    set -f ; set -- $diskDesc ; set +f
    server=$2
    backup=$3
    IFS="$IFS_sv"  # Restore the default IFS setting.

    # If a primary server was specified, make sure that it is valid
    # and convert it if necessary to an admin adapter name, since it
    # may be needed for the subsequent createNsd call.
    # We don't bother to check the backup server here, since it will
    # be checked later by the validateAndConvertNsdDescriptor routine.
    if [[ -n $server ]]
    then
      server=$(checkAndConvertNodeValue $server $REL_HOSTNAME_Field)
      [[ $? -ne 0 ]] && cleanupAndExit
    fi
#   if [[ -n $backup ]]
#   then
#     backup=$(checkAndConvertNodeValue $backup $REL_HOSTNAME_Field)
#     [[ $? -ne 0 ]] && cleanupAndExit
#   fi

  else
    # Otherwise, use the current NSD server names, if any.
    if [[ $sourceCluster = lc ]]
    then
      server=${v[$NSD_PRIMARY_NODE_Field]}
      backup=${v[$NSD_BACKUP_NODE_Field]}
    else
      server=""
      backup=""
    fi
  fi  # end of if [[ -n $diskDesc ]]

  if [[ $sourceCluster != lc && $sourceCluster != single ]]
  then
    # The source cluster type is sp, rpd, or hacmp.
    # In all of these cases we may have to import
    # the underlying logical volumes and, if applicable,
    # recreate the VSDs.

    :  # For now, we assume that the disks are already in place.

  fi  # end of if [[ $sourceCluster != lc && $sourceCluster != single ]]

  # If the user did not specify a new disk descriptor,
  # generate one from the current SG_DISKS line.
  [[ -z $diskDesc ]] &&  \
    diskDesc="$diskName:$server:$backup:"

  # If NSD server names were not specified by the user,
  # or cannot be reasonably defaulted by us, the user
  # will have to use mmchnsd to assign NSD servers.
  [[ -z $server ]] &&  \
    result=successServersRemoved

  # Verify the correctness of the NSD server nodes.
  validateDescriptorOutput=$(validateAndConvertNsdDescriptor "$diskDesc"  \
                   $currentDiskLine $CHANGE_NSD $nodesetId NULL oldDiskUsage)
  rc=$?
  if [[ $rc -eq 0 ]]
  then
    # If there are no problems, parse the result to get the new SG_DISKS line.
    set -f ; set -- $validateDescriptorOutput ; set +f
    updatedDiskLine="$1"

    # Parse the line that was returned by the validate function
    # to pick up fields that may have been changed.
    IFS=":"
    set -f ; set -A v -- - $updatedDiskLine ; set +f
    IFS="$IFS_sv"

  else
    # Remove the NSD servers.  The user will have to run mmchnsd.
    v[$NSD_PRIMARY_NODE_Field]=""
    v[$NSD_BACKUP_NODE_Field]=""
    v[$DAEMON_NSD_PRIMARY_Field]=""
    v[$DAEMON_NSD_BACKUP_Field]=""
    result=successServersRemoved
    rc=0
  fi

  # Convert the disk into an NSD.
  if [[ $sourceCluster != lc && $sourceCluster != single ]]
  then
    # See if this disk needs to be recovered from a previous
    # partial SG descriptor migration.
    if [[ -s $recoveryFiles ]]
    then
      recoverOldSGdescriptor $diskName $recoveryFiles
      rc=$?
      if [[ $rc -ne 0 ]]
      then
        print -- "error"
        return $rc
      fi
    fi

    # Generate and write an unique NSD volume id on sector 2 of the disk.
    # If an NSD server is specified, invoke the function on that node.
    # This rule does not apply when the underlying disk is a VSD.
    # For VSDs we should have access to the disk from the local node.
    if [[ $sourceCluster = sp || -z $server || $server = $ourNodeName ]]
    then
      invokedNode=$ourNodeName
      createNsdOutput=$(createNsd $diskName no $invokingCommand NULL)
      rc=$?
    else
      invokedNode=$server
      createNsdOutput=$(run on1 $server  \
                        createNsd $diskName no $invokingCommand NULL)
      rc=$?
    fi

    # The output of createNsd consists of a one-word NSD subtype value,
    # followed by the output from tspreparedisk.  Separate the two parts.
    nsdSubtype=${createNsdOutput%% *}
    tspreparediskOutput=${createNsdOutput#* }

    # Set the value for the nsd subtype diskname if the nsd subtype
    # is lv, vsd or file; this name is persistent across the nodes.
    nsdSubtypeDiskname=""
    [[ $nsdSubtype = lv || $nsdSubtype = vsd || $nsdSubtype = file ]] &&  \
      nsdSubtypeDiskname=$diskName

    # Parse the output from the tspreparedisk command.
    IFS=":"
    set -f ; set -- $tspreparediskOutput ; set +f
    magicWord=$1
    rc2=$2
    pvid=$3
    IFS="$IFS_sv"

    if [[ $rc  -ne 0 || $nsdSubtype = error ||
          $rc2 -ne 0 || $magicWord != tspreparedisk ]]
    then
      # Try to make sense of the error.
      if [[ $nsdSubtype = error ]]
      then
        :  # determineNsdSubtype failed.  Messages were already issued.

      elif [[ $magicWord = tspreparedisk ]]
      then
        # tspreparedisk was executed but was not happy about something.
        if [[ $rc2 = 1 ]]       # E_PERM
        then
          # Permission was denied for disk.
          printErrorMsg 523 $mmcmd "/dev/$diskName"
        elif [[ $rc2 = 2 ]]     # E_NOENT
        then
          # The disk was not found.
          printErrorMsg 524 $mmcmd "/dev/$diskName"
        elif [[ $rc2 = 5 ]]     # E_IO
        then
          # I/O error
          printErrorMsg 525 $mmcmd "/dev/$diskName"
        elif [[ $rc2 = $MM_DeviceNotFound ]]    # E_NODEV
        then
          # A return code of ENODEV was returned for the disk.
          printErrorMsg 187 $mmcmd $invokedNode "/dev/$diskName"
        elif [[ $rc2 = 27 ]]    # E_FBIG
        then
          # The disk is too large.
          printErrorMsg 508 $mmcmd "/dev/$diskName"
        elif [[ $rc2 = 50 ]]    # E_NOCONNECT
        then
          # The disk is fenced out.
          printErrorMsg 395 $mmcmd $invokedNode "/dev/$diskName"
        else
          # Show the result from tspreparedisk.
          print -u2 "$tspreparediskOutput"
          # Unexpected error from tspreparedisk.
          printErrorMsg 171 "$mmcmd" "tspreparedisk $diskName" $rc2
        fi  # end if [[ $rc2 = 1 ]]
      else
        # Something is really wrong.  Output error information if any.
        [[ -n $createNsdOutput ]] && print -u2 "$createNsdOutput"
      fi  # end if [[ $nsdSubtype = error ]]

      # We failed to create an NSD from this disk.
      printErrorMsg 47 $mmcmd $diskName
      result="error"
      rc=1

    else
      # At this point the disk was successfully defined as an NSD.
      # Add missing or updated information.
      v[$PVID_Field]=$pvid
      v[$NSD_SUBTYPE_DISKNAME_Field]=$nsdSubtypeDiskname
      if [[ $nsdSubtype = lv ]]
      then
        v[$AIX_PVID_Field]=$aixPvidValue
      else
        v[$AIX_PVID_Field]=""
      fi

      # Remove any leftover files from tspreparedisk -m.
      $rm -f ${fqName%/*}/tspreparedisk.diskDesc.${diskName}.*
      for server in $(print -- $primaryServer $backupServer)
      do
        run onNode $server rmSGDescFile $diskName
      done
    fi  # end if [[ $rc  -ne 0 || $nsdSubtype = error || ... ]]

  fi  # end of if [[ $sourceCluster != lc && $sourceCluster != single ]]

  # Add the remaining missing or updated information and rebuild the line.
  v[$DISK_TYPE_Field]=nsd
  v[$NSD_SUBTYPE_Field]=$nsdSubtype
  updatedDiskLine=$(print_newLine)

  # Return to the caller the updated SG_DISKS line.
  print -- "$result" "$updatedDiskLine"
  return $rc

}  #----- end of function importDisk_lc ----------------------------


###########################################################################
#
# Function:  Unfence logical volumes and remove PR registrations.
#
# Input:     $1 - file with the nodes on which to unfence
#            $2 - SG_DISKS lines for the disks to unfence
#
# Output:    None
#
# Returns:   0 - everything worked OK
#            1 - error encountered
#
###########################################################################
function unfenceLogicalVolumes  # <nodeFile> <diskFile>
{
  sourceFile="mmimportfs.sh"
  [[ -n $DEBUG || -n $DEBUGunfenceLogicalVolumes ]] && set -x
  $mmTRACE_ENTER "$*"

  typeset nodefile=$1
  typeset diskLines=$2

  typeset rc=0
  typeset unfenceDisksOutput outputLine nodeName magicWord rc2
  typeset failedNode errorFound

  # Give up if any of the input files is empty.
  [[ ! -s $nodefile || ! -s $diskLines ]] &&  \
    return 0

  $rm -f $tmpfile

  # Exclude the local host from the list of nodes.
  $grep -v -w $ourNodeName $nodefile > ${nodefile}tmp

  # If there are remote nodes left in the file,
  # unfence the disks on those nodes.
  if [[ -s ${nodefile}tmp ]]
  then
    print -- "$mmcmd:  Attempting to unfence the disks.  This may take a while ... "

    # Create a copy of the file to propagate.
    $cp $diskLines ${diskLines}tmp
    checkForErrors "cp $diskLines ${diskLines}tmp" $?

    # Perform the tasks on all remote nodes.
    $mmdsh -f 10 -vF ${nodefile}tmp -I ${diskLines}tmp  \
       $mmremote unfenceDisks ${diskLines}tmp >$tmpfile 2>&1
    rc=$?
  fi

  # Unfence the disks on the local node.
  # Append the result to the output from the remote nodes.
  if [[ $osName = AIX ]]
  then
    unfenceDisksOutput=$(unfenceDisks $diskLines)
    rc=$?

    # Append the output to the output from the remote nodes
    # so that we can process the overall result.
    print -- "$ourNodeName: $unfenceDisksOutput" >>$tmpfile
  fi

  # Ignore the results and declare a success.  If we were not
  # successful on some node, the mount will fail and the user
  # will have to run mmremote unfenceDisks or fix the disks
  # by himself.  Our problem is that we do not have a good
  # way to tell whether the unfence process should work on
  # any given node or not.  And we most definitely do not
  # want to fail the mmimportfs command for such a reason.
  $rm -f ${nodefile}tmp ${diskLines}tmp
  return 0

}  #----- end of function unfenceLogicalVolumes --------------------



#######################
# Mainline processing
#######################

#################################
# Process the command arguments.
#################################
[[ $arg1 = '-?' || $arg1 = '-h' || $arg1 = '--help' || $arg1 = '--' ]] &&  \
  syntaxError "help" $usageMsg

[[ $argc -lt 3 ]] &&  \
  syntaxError "missingArgs" $usageMsg

device=$arg1   # Save the stripe group device (always the first parameter).
shift 1        # Drop the device name from the parameter list.

while getopts :C:i:RS: OPT
do
  case $OPT in
    C) syntaxError "obsoleteOption" $usageMsg "-$OPT"
       ;;

    i) # name of input file
       [[ -n $iflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       fsImportData=$OPTARG
       ;;

    R) # replace existing file systems.
       [[ -n $Rflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Rflag=yes
       ;;

    S) # file with change specifications.
       [[ -n $Sflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Sflag=yes
       changeSpecFile=$OPTARG
       ;;

    :) syntaxError "missingValue" $usageMsg $OPTARG
       ;;

    +[CiRS]) syntaxError "invalidOption" $usageMsg $OPT
       ;;

    *) # invalid option specified
       syntaxError "invalidOption" $usageMsg $OPTARG
       ;;
  esac
done

shift OPTIND-1
[[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1

# Check the input file parameters.
if [[ ! -f $fsImportData || ! -r $fsImportData ]]
then
  # Can't read the input file.
  printErrorMsg 43 $mmcmd $fsImportData
  cleanupAndExit
fi
if [[ ! -s $fsImportData ]]
then
  # Input file is empty.
  printErrorMsg 329 $mmcmd $fsImportData
  cleanupAndExit
fi

if [[ -n $Sflag && ( ! -f $changeSpecFile || ! -r $changeSpecFile ) ]]
then
  # Can't read the input file.
  printErrorMsg 43 $mmcmd $changeSpecFile
  cleanupAndExit
fi
if [[ -n $Sflag && ! -s $changeSpecFile ]]
then
  # Input file is empty.
  printErrorMsg 329 $mmcmd $changeSpecFile
  cleanupAndExit
fi

# Process the input device name parameter.
[[ $device = all ]] && importAll=true
deviceName=${device#/dev+(/)}   # Strip /dev from the device name.


#####################################################################
# Set up trap exception handling and call the gpfsInit function.
# It will ensure that the local copy of the mmsdrfs and the rest of
# the GPFS system files are up-to-date and will obtain the sdr lock.
#####################################################################
trap pretrap HUP INT QUIT KILL
gpfsInitOutput=$(gpfsInit $lockId)
setGlobalVar $? $gpfsInitOutput

targetNodesetId=$nsId

# Determine the lookup order for resolving host names.
[[ $osName != AIX ]] && resolveOrder=$(setHostResolveOrder)


######################################################################
# Create a new version of the mmsdrfs file that will receive the data
# and collect information for the existing file systems, disks, etc.
# If the -R option is specified, remove the affected lines from the
# mmsdrfs file.
######################################################################
$rm -f $newsdrfs $existingDisks $allClusterNodes $aixNodes
IFS=":"
exec 3<&-
exec 3< $mmsdrfsFile
while read -u3 sdrfsLine
do
  # Parse the line.
  set -f ; set -A v -- - $sdrfsLine ; set +f
  IFS="$IFS_sv"
  printLine=true

  # Change some of the fields depending on the type of line.
  case ${v[$LINE_TYPE_Field]} in

    $VERSION_LINE )  # This is the global header line.
       # Increment the generation number.
       newGenNumber=${v[$SDRFS_GENNUM_Field]}+1
       v[$SDRFS_GENNUM_Field]=$newGenNumber

       targetCluster=${v[$CLUSTER_TYPE_Field]}
       if [[ $MMMODE = lc ]]
       then
         [[ ${v[$HIGHEST_GPFS_DISK_NBR_Field]} -lt $initialDiskNumber ]] &&  \
           v[$HIGHEST_GPFS_DISK_NBR_Field]=$initialDiskNumber
         prevMaxDiskNumber=${v[$HIGHEST_GPFS_DISK_NBR_Field]}
       fi
       ;;

    $MEMBER_NODE )  # This line describes a node that belongs to some nodeset.
       # Create a file with the names of all nodes in the cluster.
       print -- "${v[$REL_HOSTNAME_Field]}" >> $allClusterNodes
       checkForErrors "writing to file $allClusterNodes" $?

       # Create separate files with the names of the AIX nodes.
       if [[ ${v[$OS_NAME_Field]} = "AIX" ]]
       then
         print -- "${v[$REL_HOSTNAME_Field]}" >> $aixNodes
         checkForErrors "writing to file $aixNodes" $?
       fi
       ;;

    $SG_HEADR )  # This is the header line for some file system.
       # We are starting the processing of a new file system.  If the file
       # system should be replaced, remove the line from the mmsdrfs file.
       if [[ (${v[$DEV_NAME_Field]} = $deviceName || $device = all) &&
              -n $Rflag && ${v[$FS_TYPE_Field]} = $localfs ]]
       then
         # This file system should be replaced.
         replaceThisFileSystem=true
         printLine=false

       else
         replaceThisFileSystem=""

         # Create a list of the already assigned minor numbers.
         existingMinorNumbers="$existingMinorNumbers ${v[$DEV_MINOR_Field]}"

         # If this file systems belongs to the nodeset on which we
         # are running, remember its name.  It will be needed shortly
         # to figure out the value of the GPFS device major number.
         [[ ${v[$NODESETID_Field]} = $nsId ]] &&  \
           fqDeviceName="/dev/${v[$DEV_NAME_Field]}"
       fi  # end if [[ (${v[$DEV_NAME_Field]} = $deviceName || $device = all) ...
       ;;

    $SG_ETCFS )  # This line is a stanza line for one of the filesystems.
       if [[ -n $replaceThisFileSystem ]]
       then
         printLine=false
       else
         # If this is the first line in the stanza,
         # save the device name and mount point.
         if [[ ${v[$LINE_NUMBER_Field]} = $MOUNT_POINT_Line ]]
         then
           fsInfo="${v[$DEV_NAME_Field]}:${v[$ETCFS_TEXT_Field]}:${v[$NODESETID_Field]}"
           existingFsInfo="$fsInfo $existingFsInfo"

           # Note:  the trailing blanks in the following lists are important.
           existingFileSystems="$existingFileSystems ${v[$DEV_NAME_Field]} "
           existingMountPoints="$existingMountPoints ${v[$ETCFS_TEXT_Field]} "
         fi
       fi  # end if [[ -n $replaceThisFileSystem ]]
       ;;

    $SG_MOUNT )
       [[ -n $replaceThisFileSystem ]] &&  \
         printLine=false
       ;;

    $SG_DISKS )  # This line describes a disk.
       # Decide if this disk is to be replaced.  If the disk belongs to some
       # file system, the replaceThisFileSystem flag already has the correct
       # value.  If this is a free disk, it will be replaced only if all file
       # systems are being replaced.  Note that the replaceThisFileSystem flag
       # is used to control the replacing of disks that are part of some file
       # system as well as the replacing of free disks.
       if [[ ${v[$NODESETID_Field]} = $FREE_DISK ]]
       then
         if [[ $device != all ]]
         then
           replaceThisFileSystem=""
         else
           replaceThisFileSystem=true
         fi
       fi  # end if [[ ${v[$NODESETID_Field]} = $FREE_DISK ]]

       if [[ -n $replaceThisFileSystem ]]
       then
         printLine=false
       else
         # Create a file with the names of all existing disks.
         # It doesn't matter if the disks are free or not.
         print -- "${v[$DISK_NAME_Field]}" >> $existingDisks
         checkForErrors "writing to file $existingDisks" $?

         # Keep track of the highest NSD number assigned to a disk.
         if [[ ${v[$DISK_NAME_Field]} = gpfs+([0-9])nsd ]]
         then
           nsdName=${v[$DISK_NAME_Field]}
           nsdNumber=${nsdName#gpfs}
           nsdNumber=${nsdNumber%nsd}
           [[ $nsdNumber -gt $maxDiskNumber ]] && maxDiskNumber=$nsdNumber
         fi
       fi  # end if [[ -n $replaceThisFileSystem ]]
       ;;

    * )  # Pass all other lines without a change.
       ;;

  esac  # end Change some of the fields

  # Build and write the line to the new mmsdrfs file.
  if [[ $printLine = true ]]
  then
    print_newLine >> $newsdrfs
    checkForErrors "writing to file $newsdrfs" $?
  fi

  IFS=":"  # Change the separator back to ":" for the next iteration.

done  # end while read -u3 sdrfsLine

IFS="$IFS_sv"  # Restore the default IFS settings.


###########################################################################
# Figure out what should the GPFS file systems device major number be.
###########################################################################
if [[ $osName = Linux && -n $fqDeviceName ]]
then
  devMajor=$(LC_ALL=C $ls -l $fqDeviceName 2>/dev/null | $awk ' { print $5 } ')
  devMajor=${devMajor%,*}
fi
[[ -z $devMajor ]] && devMajor=$defaultMajorNumber


###########################################################################
# If the source is hacmp or LV based rpd cluster, make an attempt
# to remove any PR settings and unfence the disks on all nodes in
# the new cluster.  This processing is by necessity time consuming.
#
# Notes:  The main loop is further down.  This is preliminary processing
#         and a necessary evil.  Some of the error checking is duplicated
#         here to allow us to get out faster if there is an obvious error.
###########################################################################
$rm -f $disksToUnfence $resetLVs.*
IFS=":"
exec 3<&-
exec 3< $fsImportData
while read -u3 sdrfsLine
do
  # Parse the line.
  set -f ; set -A v -- - $sdrfsLine ; set +f

  IFS="$IFS_sv"    # Restore the default IFS settings.
  printLine=true   # Assume the line will be printed.

  case ${v[$LINE_TYPE_Field]} in

    $VERSION_LINE )  # This is the global header line.
       sourceCluster=${v[$CLUSTER_TYPE_Field]}
       [[ -z $sourceCluster ]] && sourceCluster=sp
       checkClusterCompatibility $sourceCluster $targetCluster
       rc=$?
       if [[ $rc -ne 0 ]]
       then
         # Incompatible cluster types.
         printErrorMsg 44 $mmcmd $sourceCluster $targetCluster
         cleanupAndExit
       fi
       getDiskRecoveryInformation $sourceCluster $recoveryFiles

       # Skip the preliminary processing if LVs are not possible.
       [[ $sourceCluster != rpd && $sourceCluster != hacmp ]] &&  \
         break

       # Skip the preliminary processing if -R is specified.
       [[ -n $Rflag ]] &&  \
         break
       ;;

    $SG_HEADR )  # This is the header line for some file system.
       # Starting the processing of a new file system.
       # See if the file system should be imported.
       if [[ ${v[$DEV_NAME_Field]} = $deviceName || -n $importAll ]]
       then
         importThisFileSystem=true
       else
         importThisFileSystem=""
       fi

       # Verify that the target cluster does not already
       # have a file system with the same name.
       if [[ -n $importThisFileSystem &&
             $existingFileSystems = *" ${v[$DEV_NAME_Field]} "* ]]
       then
         importThisFileSystem=""
       fi
       ;;

    $SG_ETCFS )  # This line is a stanza line for one of the file systems.
       # If this is the first line in the stanza for a file system
       # being imported, verify that the mount point does not already
       # exist in the target cluster.
       if [[ ${v[$LINE_NUMBER_Field]} = $MOUNT_POINT_Line &&
             -n $importThisFileSystem &&
             $existingMountPoints = *" ${v[$ETCFS_TEXT_Field]} "* ]]
       then
         importThisFileSystem=""
       fi
       ;;

    $SG_DISKS )  # This line describes a disk.
       # Create the device suffix to be used for temp files.
       if [[ ${v[$DEV_NAME_Field]} = $NO_DEVICE ]]
       then
         # Avoid any name collision by using ":", a prohibited character.
         suffix=_FREEDISK:_
       else
         suffix=${v[$DEV_NAME_Field]}
       fi

       # Decide if this disk is to be imported.  If the disk belongs to some
       # file system, the importThisFileSystem flag already has the correct
       # value.  If this is a free disk, it will be imported only if all file
       # systems are being imported.  Note that the importThisFileSystem flag
       # is used to control the importing of disks that are part of some file
       # system as well as the importing of free disks.
       if [[ ${v[$NODESETID_Field]} = $FREE_DISK ]]
       then
         if [[ -z $importAll ]]
         then
           importThisFileSystem=""
         else
           importThisFileSystem=true
         fi
       fi

       # If this disk belongs to a file system being imported, verify that
       # there are no name conflicts.  If there is a name conflict with a
       # disk that is part of a file system, that file system is dropped.
       # If the conflict is with a free disk, only that disk is dropped.
       if [[ -n $importThisFileSystem ]]
       then
         # Check the name.
         conflictingDisk=$($grep -w ${v[$DISK_NAME_Field]} $existingDisks 2>/dev/null)
         if [[ -n $conflictingDisk ]]
         then
           # There is already a disk with the same name.
           if [[ ${v[$DEV_NAME_Field]} != $NO_DEVICE ]]
           then
             $rm -f $resetLVs.$suffix
           fi
           importThisFileSystem=""
         fi  # end if [[ -n $conflictingDisk ]]
       fi  # end of if [[ -n $importThisFileSystem ]]

       # If this is a PR-capable logical volume,
       # add the line to the disks to be unfenced.
       if [[ -n $importThisFileSystem      &&
             ${v[$DISK_TYPE_Field]} = "lv" &&
             ${v[$DISK_SUBTYPE_Field]} != "other" ]]
       then
         print -- ${sdrfsLine} >> $resetLVs.$suffix
         checkForErrors "writing to file $resetLVs.$suffix" $?
       fi
       ;;

    * )  # Pass all other lines without a change.
       ;;

  esac  # end of case ${v[$LINE_TYPE_Field]} in

  IFS=":"  # Change the separator back to ":" for the next iteration.

done  # end while read -u3 sdrfsLine

IFS="$IFS_sv"  # Restore the default IFS settings.

# Combine all resetLVs files.
for fs in $($ls $resetLVs.* 2>/dev/null)
do
  $cat $fs >> $disksToUnfence
  checkForErrors "writing to file $disksToUnfence" $?
done

# If there are disks to unfence, do it now.
if [[ -s $disksToUnfence ]]
then
  unfenceLogicalVolumes $aixNodes $disksToUnfence
  rc=$?
fi


###########################################################################
# Go through the input file with the exportfs information and extract
# in separate files the information for each individual file system
# that is to be imported.  Verify that there are no name conflicts and
# adjust the target nodeset id if necessary.
###########################################################################
$rm -f $importFile.* $resetNSDs.* $importedFreeDisks $resetFreeDisks
IFS=":"
exec 3<&-
exec 3< $fsImportData
while read -u3 sdrfsLine
do
  # Parse the line.
  set -f ; set -A v -- - $sdrfsLine ; set +f

  IFS="$IFS_sv"    # Restore the default IFS settings.
  printLine=true   # Assume the line will be printed.

  case ${v[$LINE_TYPE_Field]} in

 # The version line has been processed in the preliminary pass.
 #  $VERSION_LINE )  # This is the global header line.
 #     sourceCluster=${v[$CLUSTER_TYPE_Field]}
 #     checkClusterCompatibility $sourceCluster $targetCluster
 #     rc=$?
 #     if [[ $rc -ne 0 ]]
 #     then
 #       # Incompatible cluster types.
 #       printErrorMsg 44 $mmcmd $sourceCluster $targetCluster
 #       cleanupAndExit
 #     fi
 #     getDiskRecoveryInformation $sourceCluster $recoveryFiles
 #     ;;

    $SG_HEADR )  # This is the header line for some file system.
       # Starting the processing of a new file system.
       # See if the file system should be imported.
       if [[ ${v[$DEV_NAME_Field]} = $deviceName || -n $importAll ]]
       then
         # This file system should be imported.
         importThisFileSystem=true
         if [[ -n $importAll ]]
         then
           someFsFound=true
         else
           specifiedFsFound=true
         fi
         # Put an informational message:  processing file system ...
         print -- ""  # Output a blank separator line.
         printInfoMsg 250 $mmcmd ${v[$DEV_NAME_Field]}
       else
         # This file system should not be imported.
         importThisFileSystem=""
       fi # end of if [[ ${v[$DEV_NAME_Field]} = $deviceName || -n $importAll ]]

       # Verify that the target cluster does not already
       # have a file system with the same name.
       if [[ -n $importThisFileSystem &&
             $existingFileSystems = *" ${v[$DEV_NAME_Field]} "* ]]
       then
         # There is already a file system with the same name.
         printErrorMsg 48 $mmcmd ${v[$DEV_NAME_Field]}
         failedFileSystems="${failedFileSystems}\n\t${v[$DEV_NAME_Field]}"
         importThisFileSystem=""
       fi

       # If the file system will be imported and there are no problems so far,
       # start building temporary files with the file system information.
       if [[ -n $importThisFileSystem ]]
       then
         # First, figure out what the device minor number should be.
         # Keep trying until we get an unused minor number.  If this
         # does not work, we'll leave the device minor field null
         # hoping that mmfsmknod will take care of things later on.
         rc=0
         newDevMinor=""
         while [[ -z $newDevMinor && rc -eq 0 ]]
         do
           # Assign a minor number to the file system.
           devMinor=$(assignDevMinor "$existingMinorNumbers")
           rc=$?
           if [[ $rc -eq 0 ]]
           then
             # See if the new device number is not being used by somebody on
             # the local node.  This is a crude check but better than nothing.
             # Going and checking all nodes in the nodeset is too expensive.
             inuse=$($ls -lL /dev 2>/dev/null |  \
                   $grep "^${fsDeviceType}.* $devMajor, *$devMinor ")

             # The number seems to be free, use it.
             [[ -z $inuse ]] && newDevMinor=$devMinor

             # Add the number to the list of existing number.
             existingMinorNumbers="$existingMinorNumbers $devMinor"

           fi  # end of if [[ $rc -eq 0 ]]
         done # end of while [[ -z $newDevMinor && rc -eq 0 ]]

         # Change some of the fields as needed and put the line out.
         v[$NODESETID_Field]=$targetNodesetId
         v[$DEV_MINOR_Field]=$devMinor
         print_newLine >> $importFile.${v[$DEV_NAME_Field]}
         checkForErrors "writing to file $importFile.${v[$DEV_NAME_Field]}" $?
       fi
       ;;

    $SG_ETCFS )  # This line is a stanza line for one of the file systems.
       # If this is the first line in the stanza for a file system
       # being imported, verify that the mount point does not already
       # exist in the target cluster.
       if [[ ${v[$LINE_NUMBER_Field]} = $MOUNT_POINT_Line &&
             -n $importThisFileSystem &&
             $existingMountPoints = *" ${v[$ETCFS_TEXT_Field]} "* ]]
       then
         # There is already a file system with the same mount point.
         # Tell the guy to use mmchfs -T to change the existing mount point.
         printErrorMsg 49 $mmcmd ${v[$DEV_NAME_Field]} ${v[$ETCFS_TEXT_Field]}
         failedFileSystems="${failedFileSystems}\n\t${v[$DEV_NAME_Field]}"
         $rm -f $importFile.${v[$DEV_NAME_Field]} $resetNSDs.${v[$DEV_NAME_Field]}
         importThisFileSystem=""
       fi

       # If the file system will be imported and there are no problems so far,
       # add the lines to the temporary files with the file system information.
       if [[ -n $importThisFileSystem ]]
       then
         v[$NODESETID_Field]=$targetNodesetId
         print_newLine >> $importFile.${v[$DEV_NAME_Field]}
         checkForErrors "writing to file $importFile.${v[$DEV_NAME_Field]}" $?
       fi
       ;;

    $SG_MOUNT )  # This line has the mount options.
       # If the file system will be imported and there are no problems so far,
       # add the line to the temporary file with the file system information.
       if [[ -n $importThisFileSystem ]]
       then
         v[$NODESETID_Field]=$targetNodesetId
         print_newLine >> $importFile.${v[$DEV_NAME_Field]}
         checkForErrors "writing to file $importFile.${v[$DEV_NAME_Field]}" $?
       fi
       ;;

    $SG_DISKS )  # This line describes a disk.
       # Decide if this disk is to be imported.  If the disk belongs to some
       # file system, the importThisFileSystem flag already has the correct
       # value.  If this is a free disk, it will be imported only if all file
       # systems are being imported.  Note that the importThisFileSystem flag
       # is used to control the importing of disks that are part of some file
       # system as well as the importing of free disks.
       if [[ ${v[$NODESETID_Field]} = $FREE_DISK ]]
       then
         if [[ -z $importAll ]]
         then
           importThisFileSystem=""
         else
           importThisFileSystem=true
           # If not done already, put an informational message.
           if [[ -z $freeDiskStartMsg ]]
           then
             # Put an informational message:  processing free disks ...
             print -- ""  # Output a blank separator line.
             printInfoMsg 98 $mmcmd
             freeDiskStartMsg=issued
           fi
         fi  # end if [[ -z $importAll ]]
       fi  # end if [[ ${v[$NODESETID_Field]} = $FREE_DISK ]]

       # If this disk belongs to a file system being imported, verify that
       # there are no name conflicts.  If there is a name conflict with a
       # disk that is part of a file system, that file system is dropped.
       # If the conflict is with a free disk, only that disk is dropped.
       if [[ -n $importThisFileSystem ]]
       then
         # Put an informational message:  processing disk ...
         printInfoMsg 251 $mmcmd ${v[$DISK_NAME_Field]}
         # Check the name.
         conflictingDisk=$($grep -w ${v[$DISK_NAME_Field]} $existingDisks 2>/dev/null)
         if [[ -n $conflictingDisk ]]
         then
           # There is already a disk with the same name.
           printErrorMsg 51 $mmcmd ${v[$DISK_NAME_Field]}
           if [[ ${v[$DEV_NAME_Field]} != $NO_DEVICE ]]
           then
             failedFileSystems="${failedFileSystems}\n\t${v[$DEV_NAME_Field]}"
             $rm -f $importFile.${v[$DEV_NAME_Field]} $resetNSDs.${v[$DEV_NAME_Field]}
           fi
           importThisFileSystem=""
         fi  # end if [[ -n $conflictingDisk ]]
       fi  # end of if [[ -n $importThisFileSystem ]]

       # If the file system will be imported and there are no problems so far,
       # add the lines to the temporary files with the file system information.
       if [[ -n $importThisFileSystem ]]
       then
         # Ensure the disk is of the correct type for the cluster.
         importDiskOutput=$(importDisk_$targetCluster  \
              $sourceCluster $sdrfsLine $targetNodesetId $changeSpecFile)
         rc=$?

         # The output of importDisk consists of a one word overall result,
         # followed by the updated SG_DISKS line.  Separate the two parts.
         importDiskResult=${importDiskOutput%% *}
         newDiskLine="${importDiskOutput#* }"
         if [[ $importDiskResult = success* ]]
         then
           if [[ ${v[$DEV_NAME_Field]} = $NO_DEVICE ]]
           then
             print -- ${newDiskLine} >> $importedFreeDisks
             checkForErrors "writing to file $importedFreeDisks" $?
           else
             print -- ${newDiskLine} >> $importFile.${v[$DEV_NAME_Field]}
             checkForErrors "writing to file $importFile.${v[$DEV_NAME_Field]}" $?
           fi

           if [[ $importDiskResult = successServersRemoved ]]
           then
             if [[ ${v[$DEV_NAME_Field]} = $NO_DEVICE ]]
             then
               print -- "\t${v[$DISK_NAME_Field]}" >> $resetFreeDisks
               checkForErrors "writing to file $resetFreeDisks" $?
             else
               print -- "\t${v[$DISK_NAME_Field]}" >> $resetNSDs.${v[$DEV_NAME_Field]}
               checkForErrors "writing to file $resetNSDs.${v[$DEV_NAME_Field]}" $?
             fi
           fi  # end if [[ $importDiskResult = successServersRemoved ]]

           # Keep track of the highest NSD number assigned to a disk.
           if [[ ${v[$DISK_NAME_Field]} = gpfs+([0-9])nsd ]]
           then
             nsdName=${v[$DISK_NAME_Field]}
             nsdNumber=${nsdName#gpfs}
             nsdNumber=${nsdNumber%nsd}
             [[ $nsdNumber -gt $maxDiskNumber ]] && maxDiskNumber=$nsdNumber
           fi

         else
           # There is a problem with this disk;
           # free disk errors are reported but ignored.
           printErrorMsg 50 $mmcmd ${v[$DISK_NAME_Field]}
           if [[ ${v[$DEV_NAME_Field]} != $NO_DEVICE ]]
           then
             failedFileSystems="${failedFileSystems}\n\t${v[$DEV_NAME_Field]}"
             $rm -f $importFile.${v[$DEV_NAME_Field]} $resetNSDs.${v[$DEV_NAME_Field]}
             importThisFileSystem=""
           fi
         fi  # end of if [[ $importDiskResult = success* ]]
       fi  # end of if [[ -n $importThisFileSystem ]]
       ;;

    * )  # Pass all other lines without a change.
       ;;

  esac  # end of case ${v[$LINE_TYPE_Field]} in

  IFS=":"  # Change the separator back to ":" for the next iteration.

done  # end while read -u3 sdrfsLine

IFS="$IFS_sv"  # Restore the default IFS settings.


###################################################################
# Add to the new mmsdrfs file the information for all file systems
# that have not encountered any problems so far.
###################################################################
for fs in $($ls $importFile.* 2>/dev/null)
do
  $cat $fs >> $newsdrfs
  checkForErrors "writing to file $newsdrfs" $?
  fsToImport="$fsToImport\n\t${fs#$importFile.}"
done

# Add the imported free disks, if any.
if [[ -s $importedFreeDisks ]]
then
  $cat $importedFreeDisks >> $newsdrfs
  checkForErrors "writing to file $newsdrfs" $?
fi

# If nothing was imported, there is no sense continuing.
if [[ -z $fsToImport && ! -s $importedFreeDisks ]]
then
  # There is nothing to do.  Issue appropriate messages.
  if [[ -n $importAll && -z $someFsFound ]]
  then
    # There is no file system information in the input file.
    printErrorMsg 273 $mmcmd $fsImportData
  elif [[ -z $importAll && -z $specifiedFsFound ]]
  then
    # The file system was not found in the input file.
    printErrorMsg 274 $mmcmd $device $fsImportData
  else
    :  # nothing here for now
  fi

  # Tell the user which file systems had problems.
  if [[ -n $failedFileSystems ]]
  then
    # Report the file systems that were not imported.
    print -- ""   # Output a blank separator line.
    printErrorMsg 275 $mmcmd "$failedFileSystems"
  fi

  # The command failed.
  printErrorMsg 389 $mmcmd
  cleanupAndExit
fi


########################################################
# Update the HIGHEST_GPFS_DISK_NBR_Field if necessary.
########################################################
$rm -f $tmpfile   # Make sure there is nothing in the file.
if [[ $maxDiskNumber -gt $prevMaxDiskNumber && $MMMODE = lc ]]
then
  $awk -F:  '                                                                 \
     # If this is the global header line, change the max NSD number.          \
     /^'$GLOBAL_ID:$VERSION_LINE:'/ {                                         \
        { $'$HIGHEST_GPFS_DISK_NBR_Field' = "'$maxDiskNumber'" }              \
        { print  $1":" $2":" $3":" $4":" $5":" $6":" $7":" $8":" $9":"$10":"  \
                $11":"$12":"$13":"$14":"$15":"$16":"$17":"$18":"$19":"$20":"  \
                $21":"$22":"$23":"$24":"$25":"$26":"$27":" >> "'$tmpfile'" }  \
        { next }                                                              \
     }                                                                        \
     # All other lines are echoed without change.                             \
     { print $0 >> "'$tmpfile'" }                                             \
  ' $newsdrfs
  checkForErrors awk $?

  # The file was updated successfully.
  $mv $tmpfile $newsdrfs
  checkForErrors "mv $outfile $newsdrfs" $?
fi


############################################
# Sort the new version of the mmsdrfs file.
############################################
LC_ALL=C $SORT_MMSDRFS $newsdrfs -o $newsdrfs
checkForErrors "sorting $newsdrfs" $?


############################################################
# Replace the mmsdrfs file in the sdr with the new version.
############################################################
print -- ""   # Output a blank separator line.
print -- "$mmcmd: Committing the changes ..."

trap "" HUP INT QUIT KILL
gpfsObjectInfo=$(commitChanges  \
   $nsId $nsId $gpfsObjectInfo $newGenNumber $newsdrfs $primaryServer)
rc=$?
if [[ $rc -ne 0 ]]
then
  # Cannot replace the file in the sdr.
  printErrorMsg 381 $mmcmd
  cleanupAndExit
fi


###################################################################
# Unlock the sdr.
###################################################################
freeLockOnServer $primaryServer $ourNodeNumber >/dev/null
sdrLocked=no
trap posttrap HUP INT QUIT KILL


######################################################################
# Issue messages summarizing the results and what remains to be done.
######################################################################
print -- ""   # Output a blank separator line.
# Tell the user which file systems were successfully imported.
[[ -n $fsToImport ]] &&  \
  print -u2 "$mmcmd: The following file systems were successfully imported: $fsToImport"

# Tell the user which disks may need new server nodes.
for fs in $($ls $resetNSDs.* 2>/dev/null)
do
  print -u2 "$mmcmd: The NSD servers for the following disks from file system ${fs#$resetNSDs.} were reset or not defined:"
  $cat $fs 1>&2
  mmchnsdMessageNeeded=true
done
# Add the imported free disks, if any.
if [[ -s $resetFreeDisks ]]
then
  print -u2 "$mmcmd: The NSD servers for the following free disks were reset or not defined:"
  $cat $resetFreeDisks 1>&2
  mmchnsdMessageNeeded=true
fi
[[ -n $mmchnsdMessageNeeded ]] &&  \
  print -u2 "$mmcmd: Use the mmchnsd command to assign NSD servers as needed."

# Tell the user which file systems were not imported.
[[ -n $failedFileSystems ]] &&  \
  print -u2 "$mmcmd: The following file systems were not imported: $failedFileSystems"


#################################################################
# Propagate the new mmsdrfs file.  This process is asynchronous.
#################################################################
propagateSdrfsFile async $allClusterNodes $newsdrfs $newGenNumber rereadnsd

cleanupAndExit 0

