MINI MINI MANI MO

Path : /opt/oracle/product/18c/dbhomeXE/lib/
File Upload :
Current File : //opt/oracle/product/18c/dbhomeXE/lib/asmcmdambr.pm

# Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      asmcmdambr - ASM CoMmanD line interface ASM Metadata Backup and 
#                   Restore module
#
#    DESCRIPTION
#      This module implements ASM Metadata Backup and Restore Utility. 
#
#    NOTES
#      usage: asmcmdcore [-p] [command]
#
#    MODIFIED     (MM/DD/YY)
#    apfwkr        04/11/18 - Backport mnollen_mdbrfs from main
#    mnollen       02/24/18 - 27261443: FS md_backup md_restore fix
#    prabbala      10/12/17 - 26896659: md_backup: ignore _vam_active
#    yilhu         09/20/17 - 26594742: md_backup: ignore compatible.patch.%
#    prabbala      08/11/17 - 26537307: md_backup: ignore read-only attributes
#    prabbala      07/25/17 - 25895224: Only skip dir creation with sys alias
#    prabbala      07/25/17 - 26236295: do not restore ate_conversion_done attr
#    anovelo       01/30/17 - 25425760: Don't set default cell.sparse_dg
#    diguzman      05/30/16 - 19654070 Little change at _no_instance_cmd routine
#    mnollen       08/13/15 - #21569440 - Add backup and restore ACFS metadata
#    aramacha      04/17/15 - #20772945: decrement alias array lens on delete
#    saklaksh      04/06/15 - #20835241 - fix md_backup coalesce bug 
#    ykatada       10/03/14 - #19617921: use bind_param for strings in WHERE
#    aramacha      08/07/14 - #19017309 - fix ORA-936 & cursor leak - md_backup 
#    pvenkatr      11/03/13 - #17604066 - Added QUORUM support for md_backup &
#                             md_restore commands. 
#    manuegar      05/08/13 - Bug13951456 Support bind parameters
#    pvenkatr      02/01/13 - bug 16240652, 16240319 - excluded 
#                             PHYS_META_REPLICATED attribute while restore.
#    pvenkatr      08/24/11 - Removed flags hash table - using from XML
#    adileepk      06/20/11 - Connection Pooling.
#    adileepk      10/25/10 - Changes made to integrate the parser module with
#                             asmcmd.
#    moreddy       05/06/10 - bug 8667038 NLS for error messages
#    pvenkatr      03/31/10 - Syntax, description, example - all from xml
#    moreddy       03/22/10 - Adding more tracing
#    canuprem      02/11/10 - bug 9327020
#    moreddy       01/18/10 - Adding trace messages
#    pvenkatr      09/03/09 - Help message from xml file
#    sanselva      04/06/09 - ASMCMD long options and consistency
#    heyuen        10/30/08 - lrg-3668050
#    heyuen        10/14/08 - use dynamic modules
#    rlong         08/07/08 - 
#    atsukerm      07/17/08 - rename sage to cell
#    nchoudhu      07/17/08 - xbranchmerge sage from pt.oos2
#    heyuen        03/27/08 - Backport heyuen_bug-6487591 from main
#    mpopeang      08/07/08 - bug7259156: backup/restore messages
#    heyuen        07/28/08 - use command properties array
#    nchoudhu      07/21/08 - xbranchmerge sage from 11.1.0.7
#    heyuen        04/15/08 - reorder help messages
#    heyuen        02/01/08 - enable restore dirs inside system dirs
#    heyuen        11/28/07 - add sage_only attribute for diskgroup restore
#    heyuen        09/12/07 - change error msgs to asmcmdshare
#    heyuen        05/25/07 - add return codes for errors
#    pbagal        04/13/07 - Add ASMCMD comment in all SQL
#    heyuen        03/14/07 - add backup of v$asm_attribute
#    hqian         03/09/07 - fix identation and comments
#    hqian         03/02/07 - fix md_restore flag -l and messages
#    hqian         07/20/06 - #5397026: new asmcmdglobal_no_instance_callbacks 
#    msharang      04/20/06 - Add creation of SQL file during restore 
#    msharang      04/20/06 - Override options must match diskgroups 
#                             discovered 
#    msharang      04/20/06 - Fix restore for multiple diskgroups 
#    msharang      04/06/06 - Better error checking 
#    msharang      03/28/06 - Creation
#
#############################################################################
#
############################ Functions List #################################
#
#############################################################################

package asmcmdambr;
require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(asmcmdambr_init
                 asmcmdambr_process_cmd
                 asmcmdambr_process_help
                 );

use strict;
use Cwd;
use DBI qw(:sql_types);
use Getopt::Long qw(:config no_ignore_case bundling);
use Data::Dumper;
use IO::Handle;
use Config;
use File::stat;
use File::Path;
use File::Copy;
use File::Spec::Functions qw(catfile splitdir);
use File::Basename;
use List::Util qw[min max];

use asmcmdglobal;
use asmcmdshare;
use asmcmdparser;
use asmcmdexceptions;
use asmcmdvol;

####################### ASMCMDAMBR Global Constants ######################

#
# This list is used for is_cmd call back primarily.  All other data from XML
#
our (%asmcmdambr_cmds) = ('md_backup'  => {},
                          'md_restore' => {} 
                         );

####################### ASMCMDAMBR Global Variables ######################
our (%supported_types) = ();

#needed to finish the file with a non-negative assignment
our $true_dummy = 1;

######
# Globals used for xml 
######
my ($xmlparser) ="";
my $xmlparsed = 0;
my ($text)= "";
my ($optName)="";
my ($optNode) = "option";
# hash table of data for each command
my (%aOptTxt)  = ();
my (%aOptOpt1) = ();
my (%aOptOpt2) = ();
my (%aOptEnd)  = ();

sub is_asmcmd
{
  return 1;
}

########
# NAME
#   asmcmdambr_init
#
# DESCRIPTION
#   This function initializes the asmcmdambr module.  For now it simply 
#   registers its callbacks with the asmcmdglobal module.
#
# PARAMETERS
#   None
#
# RETURNS
#   Null
#
# NOTES
#   Only asmcmdcore_main() calls this routine.
########
sub init
{
  # All of the arrays defined in the asmcmdglobal module must be 
  # initialized here.  Otherwise, an internal error will result.
  push (@asmcmdglobal_command_callbacks, \&asmcmdambr_process_cmd);
  push (@asmcmdglobal_help_callbacks, \&asmcmdambr_process_help);
  push (@asmcmdglobal_command_list_callbacks, \&asmcmdambr_get_asmcmd_cmds);
  push (@asmcmdglobal_is_command_callbacks, \&asmcmdambr_is_cmd);
  push (@asmcmdglobal_is_wildcard_callbacks, \&asmcmdambr_is_wildcard_cmd);
  push (@asmcmdglobal_syntax_error_callbacks, \&asmcmdambr_syntax_error);
  push (@asmcmdglobal_no_instance_callbacks, \&asmcmdambr_is_no_instance_cmd);
  %asmcmdglobal_cmds = (%asmcmdglobal_cmds, %asmcmdambr_cmds);

  #Perform ASMCMD consistency check if enabled
  if($asmcmdglobal_hash{'consistchk'} eq 'y')
  {
     if(!asmcmdshare_check_option_consistency(%asmcmdambr_cmds))
     {
       exit 1;
     }
  }
}

########
# NAME
#   asmcmdambr_process_cmd
#
# DESCRIPTION
#   This routine calls the appropriate routine to process the command 
#   specified by $asmcmdglobal_hash{'cmd'}.
#
# PARAMETERS
#   dbh       (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   1 if command is found in the asmcmdambr module; 0 if not.
#
# NOTES
#   Only asmcmdcore_shell() calls this routine.
########
sub asmcmdambr_process_cmd 
{
  my ($dbh) = @_;
  my ($succ) = 0;

  # Get current command from global value, which is set by 
  # asmcmdambr_parse_asmcmd_args() and by asmcmdcore_shell().
  my ($cmd) = $asmcmdglobal_hash{'cmd'};

  # Declare and initialize hash of function pointers, each designating a 
  # routine that processes an ASMCMDAMBR command.
  my (%cmdhash) = ( md_backup      => \&asmcmdambr_process_backup ,
                    md_restore     => \&asmcmdambr_process_restore );

  if (defined ( $cmdhash{ $cmd } ))
  {    # If user specifies a known command, then call routine to process it. #
    $cmdhash{ $cmd }->($dbh);
    $succ = 1;
  }

  return $succ;
}

########
# NAME
#   asmcmdambr_process_backup
#
# DESCRIPTION
#   This function performs AMBR backup operation. It connects to ASM instance
#   and gathers enough information about the diskgroups we are interested
#   in, creates an intermediate file, that stores this information to be
#   processed later by restore operation.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdambr_process_cmd() calls this function.
########
sub asmcmdambr_process_backup
{
  my ($dbh) = shift;

  my (%args);                             # Argument hash used by getopts(). #
  my (%norm);      # See asmcmdshare_normalize_path() return value comments. #
  my (@eargs);                                   # Array of error arguments. #
  my ($ret);                     # asmcmdambr_parse_int_args() return value. #
  my ($path);                            # Create directory under this path. #
  my ($sth, $sth1, $qry, $row);                       # SQL query statement. #
  my (@diskgroup_names);                 # Name of diskgroup to be backed up #
  my ($dgnamesstr);            # Diskgroup names string as specified by user #
  my (@diskgroup_set);        # Top level hash table that stores all dg info #
  my ($i);                                                        # Iterator #
  my ($curpos);                           
  my ($FILE);                            # File handle for intermediate file #
  my ($intermediate_file_name); 
  my ($max_key);
  my ($dgname);                                     # current diskgroup name #
  my ($stmt);                                            # stmt for querying #
  my (@diskgroup_drl);                 # DRL Diskgroup names to be backed up #
  my ($acfs_secencr_save) = 0 ;                         # Flag save security #
  my ($acfs_loaded) = 0;                             # Flag to identify acfs #
  my (@arroutput, $output);
  my ($ostype, $osprefix);

  my @attr_to_ignore = (
                        # 26236295: _vam_active is set automatically while 
                        # creating a sparse DG or while changing compatible.asm
                        # to >=18.1. Skip the attribute from backup
                        '_vam_active',
                        # bug 26594742: Disallow md_backup from backing up 
                        # compatible.patch.% because it is not supposed to be
                        # changed. 
                        'compatible.patch.rdbms',
                        'compatible.patch.asm'
                       );

  # Get the supported files in ASM
  $stmt = 'select description from x$ksfdftyp';
  eval
  {
     $sth = asmcmdshare_do_select($dbh, $stmt);
  };
  if(asmcmdexceptions::catch())
  {
     @eargs = ("");
     asmcmdshare_error_msg(9352, \@eargs);
     asmcmdexceptions::throw("asmcmdexceptions");
  }

  while (defined ($row = asmcmdshare_fetch($sth)))
  {
    $supported_types{uc($row->{'DESCRIPTION'})} = 1;
  }
  asmcmdshare_finish($sth);

  # Get option parameters, if any
  $ret = asmcmdambr_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  #Set the correct options if deprecated options were used and print WARNING.
  asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);
 
  # -b options is deprecated as this argument is mandatory bug#6960868
  ($intermediate_file_name) = @{$args{'md_backup'}};

  # Store the information fetched in intermediate file to be
  # used later as part of AMBR restore 
  # We open using mode '>' means open for write only, and clobber existing file
  # so test if a file already exists
  if ( -e $intermediate_file_name)
  {
    (@eargs) = ($intermediate_file_name);
    asmcmdshare_error_msg(9357, \@eargs);
    return;
  }

  if (defined($args{'G'}))
  {
    # User has specified one or more diskgroups using -g option.
    # We will perform backup of only those diskgroups. 

    # Process the ',' separated list of diskgroup names
    @diskgroup_names = ();
    foreach (@{$args{'G'}})
    {
      $_ = uc($_);
      push(@diskgroup_names, $_);
      asmcmdshare_print "Disk group metadata to be backed up: $_\n";
    }
  }
  else
  {
    # By default backup all diskgroups mounted by this instance
    $qry = 'select NAME from v$asm_diskgroup where state=\'MOUNTED\'';
    $sth = asmcmdshare_do_select($dbh, $qry);

    while (defined ($row = asmcmdshare_fetch($sth)))
    {
      push (@diskgroup_names, uc($row->{'NAME'}));
    }
    asmcmdshare_finish($sth);

    if (!@diskgroup_names)
    {
      # No mounted diskgroups found, nothing can be done after this,
      # print error and return
      asmcmdshare_error_msg(9351, undef);
      return;
    }

    foreach (@diskgroup_names)
    {
      asmcmdshare_print "Disk group metadata to be backed up: $_\n";
    }
  } 

  asmcmdshare_trace(5, "asmcmdambr_process_backup(): ". 
                          "Diskgroups: @diskgroup_names ", 'y', 'n');

  # Check if ACFS is loaded for backup
  $output = catfile ("$ENV{ORACLE_HOME}","bin");
  $output = catfile ($output,"acfsdriverstate");
  $output = asmcmdambr_acfs_execCmd("$output loaded");
  @arroutput = @$output;
  if (grep(/ACFS-9203.*/, @arroutput)) # ACFS-9203: true
  {
    $acfs_loaded = 1;
    ($ostype, $osprefix) = asmcmdambr_acfs_getOSType();
    # Enable security backup
    if (defined($args{'acfs_sec_encr'}) && ($ostype ne 'AIX'))
    {
       $acfs_secencr_save = $intermediate_file_name;
    }
  }
  else
  {
    asmcmdshare_trace(4, "asmcmdambr_process_backup(): Skipping ".
        "backup of ACFS files, as ACFS: is not loaded ", 'y', 'n');
  }
  
  # Now that we know what diskgroups to work on, define data structure that 
  # will hold information about the diskgroups, then execute SQL commands
  # to gather information about the diskgroups.  
  foreach $dgname (@diskgroup_names)
  {
    my ($current_diskgroup_number);
    my (%diskgroup_info);                            # Diskgroup info record #
    my (%disks_info);                                    # Disks info record #
    my (%disks_info_array);                        # Disks info record array #
    my (%attribute_info_array);                # Attribute info record array #
    my (%alias_info);                                    # Alias info record #
    my (%alias_info_array);                        # Alias info record array #
    my ($alias_info_array_length);       # Length of Alias info record array #
    my (%template_info);                              # Template info record #
    my (%template_info_array);                  # Template info record array #
    my (%advm_acfs_info);                                  # USM info record #
    my (@drlvolname) = ();                                       # DRL list  #
    my (%diskgroup_record);        # Record to store information for this dg # 
    my ($iter1);
    my ($iter2);
    my ($temp);
    my (%to_delete);

    # Fetch diskgroup information
    $qry = 'select GROUP_NUMBER, TYPE, COMPATIBILITY, DATABASE_COMPATIBILITY,
            ALLOCATION_UNIT_SIZE, STATE from v$asm_diskgroup WHERE NAME = ?';

    $sth = asmcmdshare_do_prepare($dbh, $qry);
    $sth->bind_param(1,$dgname,SQL_VARCHAR);
    asmcmdshare_do_execute($sth);
    $row = asmcmdshare_fetch($sth);

    if (!defined($row))
    {
      # Could not find any row for current diskgroup, maybe user specified
      # incorrect diskgroup name, print error and fail command; syntax error.
      (@eargs) = ($dgname);
      asmcmdshare_error_msg(9349, \@eargs);
      return;
    }
    asmcmdshare_finish($sth);

    if (uc($row->{'STATE'}) eq 'DISMOUNTED')
    {
      # The diskgroup is dismounted, print error message and fail command;
      # syntax error.
      (@eargs) = ($dgname);
      asmcmdshare_error_msg(9350, \@eargs);
      return;
    }

    $current_diskgroup_number = $row->{'GROUP_NUMBER'};

    %diskgroup_info = (
      DGNAME => $dgname,
      DGTYPE => $row->{'TYPE'},
      DGTORESTORE => 0,
      DGCOMPAT => $row->{'COMPATIBILITY'},
      DGDBCOMPAT => $row->{'DATABASE_COMPATIBILITY'},
      DGAUSZ => $row->{'ALLOCATION_UNIT_SIZE'},
    );

    asmcmdshare_trace(5, "asmcmdambr_process_backup(): diskgroup_info: ".  
                           Dumper(\%diskgroup_info), 'y', 'n');

    # Fetch Disk information
    # We fetch information for all disks, not just MEMBER disks, 
    # which means during restore time, we will try to create a diskgroup 
    # with all those disks. If user wants to do some filtering, he always
    # has the option of creating an output SQL file as part of restore
    # and editing it.
    # Should we look only for MEMBER disks ??
    $qry = 'select d.NAME, d.PATH, d.TOTAL_MB, d.FAILGROUP, d.FAILGROUP_TYPE
            from v$asm_disk d, v$asm_diskgroup g 
            where g.GROUP_NUMBER = d.GROUP_NUMBER 
            and g.NAME = ?';

    $sth = asmcmdshare_do_prepare($dbh, $qry);
    $sth->bind_param(1,$dgname,SQL_VARCHAR);
    asmcmdshare_do_execute($sth);

    # We group disks based on their failgroups, so we have a complex
    # structure which has (1) A level 3 Hash table for all disks 
    # (disks_info_array) each entry of which is (2) a level 2 Hash table
    # of failgroups (disks_in_failgroup) each entry of which is (3)
    # a level 1 hash table per disk that has the v$asm_disk column names
    # as its keys and stores the corresponding column values.
    while (defined ($row = asmcmdshare_fetch($sth)))
    {
      my (%disks_in_failgroup_table);
      my ($disks_in_failgroup); #reference
 
      if (defined($disks_info_array{$row->{'FAILGROUP'}}))
      {
        $disks_in_failgroup = $disks_info_array{$row->{'FAILGROUP'}};
      }
      else
      {
        $disks_in_failgroup = \%disks_in_failgroup_table;
      }
       
      # One table per disk
      $disks_info{'DGNAME'} = $dgname;
      $disks_info{'NAME'} = $row->{'NAME'};  
      $disks_info{'PATH'} = $row->{'PATH'};
      $disks_info{'TOTAL_MB'} = $row->{'TOTAL_MB'};
      $disks_info{'FAILGROUP'} = $row->{'FAILGROUP'};
      $disks_info{'QUORUM'} = $row->{'FAILGROUP_TYPE'};

      asmcmdshare_trace(5, "asmcmdambr_process_backup(): disks_info: ".
                             Dumper(\%disks_info), 'y', 'n');
      # One table per failgroup
      $disks_in_failgroup->{$row->{'NAME'}} = { %disks_info };

      # One table per diskgroup for all failgroups in it
      $disks_info_array{$row->{'FAILGROUP'}} = { %{$disks_in_failgroup} };
    }# end while #
    asmcmdshare_finish($sth);

    # Fetch attribute information
    # Just for ASM diskgroup compatibility versions 11.1 or newer
    if ( asmcmdshare_version_cmp($diskgroup_info{'DGCOMPAT'}, 
                                 "11.1.0.0.0") >= 0 )
    {
      # bug 26537307: Query only those attributes that are not read-only, 
      # with the exception of au_size which is required and used during the 
      # creation of diskgroups.
      $qry = 'select a.GROUP_KFENV, a.NAME_KFENV as NAME, a.VALUE_KFENV
              as VALUE, decode(bitand(a.flags_kfenv, 2), 2, \'Y\', \'N\')
              as F from x$kfenv a, v$asm_diskgroup g where
              g.GROUP_NUMBER = a.GROUP_KFENV and 
              (a.readonly_kfenv = 0 OR lower(a.name_kfenv) = \'au_size\') and
              g.name = ?';

      $sth = asmcmdshare_do_prepare($dbh, $qry);
      $sth->bind_param(1,$dgname,SQL_VARCHAR);
      asmcmdshare_do_execute($sth);

      while (defined($row = asmcmdshare_fetch($sth)))
      {
        my $attrnm = lc($row->{'NAME'});

        # do not save the attribute that has to be ignored (e.g _vam_active)
        if (($row->{'F'} ne 'Y') and !grep(/^\b$attrnm\b$/, @attr_to_ignore))
        {
          $attribute_info_array{ uc($row->{'NAME'}) } = $row->{'VALUE'};
        }
      } # end while #
      asmcmdshare_finish($sth);
    }

    # Fetch alias information
    # We are only interested in user created aliase directories
    $qry = 'select a.GROUP_NUMBER, a.NAME, a.REFERENCE_INDEX, a.SYSTEM_CREATED
            from v$asm_alias a, v$asm_diskgroup g 
            where g.GROUP_NUMBER = a.GROUP_NUMBER and 
            a.ALIAS_DIRECTORY = \'Y\' 
            and g.NAME = ?';

    $sth = asmcmdshare_do_prepare($dbh, $qry);
    $sth->bind_param(1,$dgname,SQL_VARCHAR);
    asmcmdshare_do_execute($sth);

    $curpos = 0;

    # First pass, store only alias name temporarily
    while (defined ($row = asmcmdshare_fetch($sth)))
    {
      $alias_info{'DGNAME'} = $dgname;
      $alias_info{'ALIASNAME'} = $row->{'NAME'};
      $alias_info{'REFERENCE_INDEX'} = $row->{'REFERENCE_INDEX'};
      $alias_info{'SYSTEM_CREATED'} = $row->{'SYSTEM_CREATED'};
      $alias_info{'LEVEL'} = 0;  # To be used for sorting entries later
      $alias_info_array{$curpos} = { %alias_info };
      asmcmdshare_trace(5, "asmcmdambr_process_backup(): alias_info: ".
                             Dumper(\%alias_info), 'y', 'n');
      $curpos++;
    } # end while #
    asmcmdshare_finish($sth);

    $alias_info_array_length = $curpos;
    asmcmdshare_trace(5, "asmcmdambr_process_backup(): arr_len: ".
                             $alias_info_array_length, 'y', 'n');

    # We need to build full path for the alias directory in order to be 
    # able to re-create it at a later point.
    foreach $curpos (keys %alias_info_array)
    {
      my ($current_alias_name) = $alias_info_array{$curpos}{'ALIASNAME'};
      my ($current_reference_index) = 
          $alias_info_array{$curpos}{'REFERENCE_INDEX'};
      my ($current_parent_index);
      my ($current_alias_path);
      my ($level);
      my ($skip_index);

      $qry = 'select PARENT_INDEX from v$asm_alias 
              where group_number = ?' .
             ' and REFERENCE_INDEX = ?' .
             ' and name = ?';

      $sth = asmcmdshare_do_prepare($dbh, $qry);
      $sth->bind_param(1,$current_diskgroup_number);
      $sth->bind_param(2,$current_reference_index);
      $sth->bind_param(3,$current_alias_name,SQL_VARCHAR);
      asmcmdshare_do_execute($sth);
      $row = asmcmdshare_fetch($sth);
      if (defined ($row))
      {
        $current_parent_index = $row->{'PARENT_INDEX'};
        $current_alias_path = $current_alias_name;
        asmcmdshare_trace(5, "asmcmdambr_process_backup(): paren_idx: ".
                                $current_parent_index ." cur_alias_p: ".
                                  $current_alias_path, 'y', 'n');

        # Build full path name for the alias directory starting from alias
        # name and working upwards towards diskgroup name 
        $level = 0;
        $skip_index = 0;
        for (;;)
        {
          if ($current_parent_index == ($current_diskgroup_number << 24))
          {
            # We have reached diskgroup level, prepend diskgroup name
            # to the path and break out of loop
            if ($level) 
            {
              asmcmdshare_finish($sth1);
            }
            last;
          }

          $qry = 'select NAME, PARENT_INDEX from v$asm_alias 
                  where GROUP_NUMBER = ?' .
                 ' and REFERENCE_INDEX = ?';

          $sth1 = asmcmdshare_do_prepare($dbh, $qry);
          $sth1->bind_param(1,$current_diskgroup_number,SQL_INTEGER);
          $sth1->bind_param(2,$current_parent_index,SQL_INTEGER);
          asmcmdshare_do_execute($sth1);
          $row = asmcmdshare_fetch($sth1);
          if (defined ($row))
          {
            $current_alias_path = 
              $row->{'NAME'} . '/' . $current_alias_path;
            $current_parent_index = $row->{'PARENT_INDEX'};
            asmcmdshare_trace(5, "asmcmdambr_process_backup(): paren_idx: ".
                                $current_parent_index ." cur_alias_p: ".
                                  $current_alias_path, 'y', 'n');
            $level++;
            asmcmdshare_finish($sth1);
          }
          else
          {
            # No matching rows, so we can't traverse the parent index any 
            # further, skip the current index undergoing metadata 
            # changes.
            $skip_index = 1;
            asmcmdshare_trace(5, "asmcmdambr_process_backup(): skip index ".
                                $current_parent_index ." in directory path ".
                                  $current_alias_path, 'y', 'n');
            asmcmdshare_finish($sth1);
            last;
          }
        } # end for #

        if (! $skip_index)
        {
          asmcmdshare_print "Current alias directory path: ".
                              "$current_alias_path\n";
          $alias_info_array{$curpos}{'ALIASNAME'} = $current_alias_path;
          $alias_info_array{$curpos}{'LEVEL'} = $level;
        }
      }
      asmcmdshare_finish($sth);
    }

    #discard the directories of filetype names
    #select the entries to delete
    foreach $curpos (keys %alias_info_array)
    {
      my ($path, @path_arr);

      $path = $alias_info_array{$curpos}{'ALIASNAME'} . "\n";
      $path =~ s/\s+$//;

      @path_arr = split (/\//, $path);

      # bug 25895224: User created dir can be with same name as supported
      # file types (e.g backupset). Only discard system created dir.
      if ($#path_arr == 1)
      {
        if (defined($supported_types{$path_arr[1]}) &&
            ($alias_info_array{$curpos}{'SYSTEM_CREATED'} eq 'Y'))
        {
          $to_delete{$curpos} = 1;
        }
      }
    }

    $max_key = $alias_info_array_length;
    #delete the records
    foreach (keys %to_delete)
    {
      delete ($alias_info_array{$_});
      $alias_info_array_length = $alias_info_array_length - 1;
    }

    # coalesce the alias_info_array, filling the holes with existing entries
    for ($iter1 = 0; $iter1 < $max_key; $iter1++)
    {
      if (defined($alias_info_array{$iter1}))
      {
        next;
      }
      for ($iter2 = $iter1+1; 
           !defined($alias_info_array{$iter2}) && $iter2 < $max_key;
           $iter2++){};

      if (defined($alias_info_array{$iter2}))
      {
        $alias_info_array{$iter1} = $alias_info_array{$iter2};
      }
      delete($alias_info_array{$iter2});
      $alias_info_array_length = $alias_info_array_length - 1;
    }

    # Sort alias info array based on level, we do this because restore
    # operation needs to restore alias directory based on level since 
    # parent directory needs to be restored before any of its children
    # are restored.
    for ($iter1 = 0; $iter1 < $alias_info_array_length; $iter1++)
    {
      for ($iter2 = 0; $iter2 < $alias_info_array_length - 1; $iter2++)
      {
        if ($alias_info_array{$iter2}{'LEVEL'} > 
            $alias_info_array{$iter2+1}{'LEVEL'} )
        {
          $temp = $alias_info_array{$iter2};
          $alias_info_array{$iter2} = 
            $alias_info_array{$iter2+1};
          $alias_info_array{$iter2+1} = $temp;
        }
      }#end fop iter2
    }#end for iter1

    # Fetch template information
    $qry = 'select g.GROUP_NUMBER, t.NAME, t.REDUNDANCY, t.STRIPE, t.SYSTEM 
            from v$asm_template t, v$asm_diskgroup g where 
            g.GROUP_NUMBER = t.GROUP_NUMBER and 
            g.NAME = ?';

    $sth = asmcmdshare_do_prepare($dbh, $qry);
    $sth->bind_param(1,$dgname,SQL_VARCHAR);
    asmcmdshare_do_execute($sth);

    $curpos = 0;
    while (defined ($row = asmcmdshare_fetch($sth)))
    {
      $template_info{'DGNAME'} = $dgname;
      $template_info{'TEMPNAME'} = $row->{'NAME'};
      $template_info{'REDUNDANCY'} = $row->{'REDUNDANCY'};
      $template_info{'STRIPE'} = $row->{'STRIPE'};
      $template_info{'SYSTEM'} = $row->{'SYSTEM'};

      $template_info_array{$curpos} = { %template_info };
      $curpos++;
    } #end while #
    asmcmdshare_finish($sth);
    
    ###############################################################
    # ACFS backup
    ###############################################################

    if ($acfs_loaded)
    {
      my ($drldg, $advm, $acfs);
      ($advm, $acfs, $drldg) =
      asmcmdambr_acfs_backup($dbh, $dgname, $ostype, $osprefix,
                             $acfs_secencr_save);
      $advm_acfs_info{'ADVM'} = $advm if ($advm); 
      $advm_acfs_info{'ACFS'} = $acfs if ($acfs); 
      push (@diskgroup_drl, $drldg) if ($drldg && $advm);
    }

    # Add DRL DG for backup
    # The ADVM DRL is a special case. In 12.2, this can live on a 
    # separate DG. To restore ADVM, we need to back up both DG
    if (($acfs_loaded) && ($dgname eq $diskgroup_names[-1]) &&
        (@diskgroup_drl > 0))
    {
      my %lastdg = map {$_ => 1} @diskgroup_names;
      my %hash   = map {$_,1} @diskgroup_drl;
      @diskgroup_drl = keys %hash;
      foreach (@diskgroup_drl)
      {
        if (!exists($lastdg{$_}))
        {
          push (@diskgroup_names, $_);
          asmcmdshare_print "Disk group metadata to be backed up: ".
                            "$_\n";
        }
      }
      @diskgroup_drl = ();
    } # end if drl #

    # Create final record for this diskgroup
    # If the instance version is greater than 11.1, check the attributes.
    $diskgroup_record{'DGINFO'}       = {%diskgroup_info};
    $diskgroup_record{'DISKSINFO'}    = {%disks_info_array};
    $diskgroup_record{'ATTRINFO'}     = {%attribute_info_array}
                                        if (%attribute_info_array);
    $diskgroup_record{'ALIASINFO'}    = {%alias_info_array};
    $diskgroup_record{'TEMPLATEINFO'} = {%template_info_array};
    $diskgroup_record{'USMINFO'}      = {%advm_acfs_info}
                                        if (%advm_acfs_info);

    # Store this record in the array containing records for all diskgroups
    push (@diskgroup_set, \%diskgroup_record);
  } # end for #

  # We untaint/tell perl to trust the user specified intermediate file name 
  # by matching it with "match all" regular expression and resetting the 
  # variable to the match. This is not a safe way to untaint outside data 
  # but we already checked that the intermediate file specified by user does not
  # already exist, thus making sure that we will not accidently overwrite 
  # an existing file/directory, hence it is safe to do so at this point.
  ($intermediate_file_name) = $intermediate_file_name =~ m/(.*)/;

  if (!open (FILE, ">", $intermediate_file_name))
  {
    @eargs = ($intermediate_file_name, $!);
    asmcmdshare_error_msg(9345, \@eargs);
    return; 
  }

  # Write string version of diskgroup data to intermediate file 
  print FILE Data::Dumper->Dump([\@diskgroup_set], ['*diskgroup_set']);
  close (FILE);

  return;
}

########
# NAME
#   asmcmdambr_process_restore
#
# DESCRIPTION
#   This function performs AMBR restore operation. It reads and interprets 
#   the input restore file and version of the diskgroup to be restored, 
#   and then for all diskgroups to be restored, it creates appropriate 
#   restore commands depending on the version.
#
# PARAMETERS
#   dbh   (IN) - initialized database handle, must be non-null.
#
# RETURNS
#   Null.
#
# NOTES
#   Only asmcmdambr_process_cmd() can call this routine.
########
sub asmcmdambr_process_restore
{
  my ($dbh) = shift;

  my (%args);                             # Argument hash used by getopts(). #
  my (@eargs);                                   # Array of error arguments. #
  my ($ret);                     # asmcmdambr_parse_int_args() return value. #
  my ($sth, $sth1, $qry, $row);                       # SQL query statement. #
  my ($stmt);                                                 # SQL command. #
  my ($defreadmode);                                # Default file read mode #
  my (@diskgroup_names);                  # Name of diskgroup to be restored #
  my ($dgnamesstr);            # Diskgroup names string as specified by user # 
  my (@diskgroup_set);      # Record to store information for this diskgroup #
  my ($i);                                                        # Iterator #
  my ($FILE);                            # File handle for intermediate file #
  my ($SQLFILE);                            # File name for restore SQL file #
  my ($PERLFILE);                     # File name for restore acfs perl file #
  my ($SNAPFILE);                                         # Snap file handle #
  my ($intermediate_file_name);                     # Intermediate file name #
  my ($restoremode) = 'full';                    # Mode of restore operation #
  my ($overridestr);                                       # Override string #
  my (@override_options);                 # Override options <odldg>:<newdg> #
  my (%diskgroups_replace);   
  my ($ignore_errors) = 0; # Whether to proceed with next fiskgroups on error #
  my ($create_restore_sql) = 0; # Execute restore or dump restore SQL to file #
  my ($restore_sql_file_name); # SQL file where to write the restore commands #
  my ($file_content);                    # Contents of the intermediate file. #
  my ($asm_instance_version);                          # ASM instance version #
  my ($compatible_rdbms);
  my ($compatible_asm);
  my ($au_size);
  my ($sec_size);
  my ($smart_scan_capable);
  my (@arroutput, $output);                       # Capture cmd exec output #
  my ($acfs_loaded) = 1;        # Flag specifies if acfs drivers are loaded #
  my ($acfs_sec_encr) = 0;        # Flag to restore security and encryption #
  my ($acfs_audit) = 0;              # Flag specifies to restore acfs audit #
  my ($secuser, $secgrp);        # Store given security user and group acfs #
  my ($aumgruser, $aumgrgrp, $augrpditor);    # Store given audit opts acfs #
  my (@advmA);                            # Save volume meta data to restore #
  my (@acfsA);                                # Save file system for restore #
  my (@dgs);                  # Save diskgroup new:old name for acfs restore #

  # Get the ASM version of the instance we are connected to
  $asm_instance_version = asmcmdshare_get_asm_version($dbh);
  return unless defined ($asm_instance_version);

  # Get option parameters, if any.
  $ret = asmcmdambr_parse_int_args($asmcmdglobal_hash{'cmd'}, \%args);
  return unless defined ($ret);

  #Set the correct options if deprecated options were used and print WARNING.
  asmcmdshare_handle_deprecation($asmcmdglobal_hash{'cmd'},\%args);

  # -b options is deprecated as this argument is mandatory bug#6960868
  # Read Intermediate file into memory.
  ($intermediate_file_name) = @{$args{'md_restore'}}; 
  
  if (!open (FILE, "< $intermediate_file_name"))
  {
    # Cannot open intermediate file, nothing can be done, print error 
    # and exit
    @eargs = ($intermediate_file_name, $!);
    asmcmdshare_error_msg(9345, \@eargs);
    return; 
  }
  FILE->untaint();  
  # Remember the default read mode #  
  $defreadmode = $/;
  # Read in file all at once #
  undef $/;
  $file_content = <FILE>;
  # Restore the default #
  $/ = $defreadmode;
  close (FILE);

  if ($file_content !~ /^\@diskgroup_set/)
  {
    # The intermediate file must begin with the string "@diskgroup_set".
    # If not, then we must not have a valid intermediate file.
    @eargs = ($intermediate_file_name);
    asmcmdshare_error_msg(9347, \@eargs);
    return;
  }

  # Now evaluate the file contents into an array for hashes.
  @diskgroup_set = eval $file_content;
  if ($@)
  {
    # Error evaluating the intermediate file, print error and return.
    @eargs = ($intermediate_file_name);
    asmcmdshare_error_msg(9347, \@eargs);
    return; 
  } 

  if (!@diskgroup_set)
  {
    # Backup file specified by user is empty or cannot be interpreted.
    @eargs = ($intermediate_file_name);
    asmcmdshare_error_msg(9356, \@eargs);
    return;
  }


  # The following section checks the sanity of the command parameters. 
  # Errors will not be ignored.

  if (defined($args{'G'}))
  {
    my ($backup_diskgroup_name);   # Name of a disk group that was backed up. #
    my ($found) = 0;      # Whether or not a disk group in the list is found. #
    my ($iter1);
    my ($iter2);

    # User has specified one or more diskgroups using -g option.
    # We will perform backup of only those diskgroups. 
    @diskgroup_names = ();
    foreach (@{$args{'G'}})
    {
      $_ = uc($_);
      push(@diskgroup_names, $_);
    }
    $dgnamesstr = join(',', @diskgroup_names);
   
    # If user has specified list of diskgroups to restore, restore only them.
    # Make sure that we find information for those diskgroups in backup file,
    # if any one is missing, we print error and exit.
    for ($iter1 = 0; $iter1 < @diskgroup_names; $iter1++)
    {
      $found = 0;

      # Now look for the user-specified disk group in the backup intermediate
      # file.
      for ($iter2 = 0; $iter2 < @diskgroup_set; $iter2++)
      {
        $backup_diskgroup_name = $diskgroup_set[$iter2]{DGINFO}{DGNAME};

        if ($diskgroup_names[$iter1] eq $backup_diskgroup_name)
        {
          $found = 1;
          $diskgroup_set[$iter2]{DGINFO}{DGTORESTORE} = 1;
          last; # break out of loop
        }
      } # end for iter2 # 

      if (!$found)
      {
        # User specified a diskgroup to be restored but we could not find
        # information for that diskgroup in backup file. Print an error 
        # and exit.
        @eargs = ($diskgroup_names[$iter1]);
        asmcmdshare_error_msg(9355, \@eargs);
        return;
      }
    } # end for iter1 #   
  }
  else
  { # No -G flag specified. #
    my ($iter2);

    # By default restore all diskgroups specified in backup file.
    undef $dgnamesstr;
    for ($iter2 = 0; $iter2 < @diskgroup_set; $iter2++)
    {
      $diskgroup_set[$iter2]{DGINFO}{DGTORESTORE} = 1;
    }
  }

  if (defined($args{'o'}))
  {
    # Currently only a replacement diskgroup name can be specified
    # using override option, only valid syntax is 
    # -o '<olddgname>:<newdgname>,...', check input for syntax
    @override_options = @{$args{'o'}};

    # Loop through the override options one at a time.
    for ($i = 0; $i < @override_options; $i++)
    {
      my ($olddg);
      my ($newdg);
      my ($iter2);
      my ($found);

      $override_options[$i] = uc($override_options[$i]);
      ($olddg, $newdg) = split (/:/, $override_options[$i]);
      # Make sure the old diskgroup name specified in override options
      # actually matches some diskgroup to be restored.
      for ($iter2 = 0; $iter2 < @diskgroup_set; $iter2++)
      {
        if (($diskgroup_set[$iter2]{DGINFO}{DGTORESTORE} == 1) &&
            uc($diskgroup_set[$iter2]{DGINFO}{DGNAME}) eq $olddg)
        {
          $found = 1;
          last;
        }
      }

      if (!$found)
      {
        # Invalid Diskgroup name specified in override options
        # Print error and exit
        @eargs = ($olddg);
        asmcmdshare_error_msg(9359, \@eargs);
        return;
      }

      $diskgroups_replace{$olddg} = $newdg;
    }
  }

  $restoremode = 'newdg' if(defined($args{'newdg'}));
  $restoremode = 'nodg' if(defined($args{'nodg'}));
  $restoremode = 'full' if(defined($args{'full'}));

  if (defined($args{'silent'}))
  {
    # User has asked us to proceed with other diskgroups in case of 
    # error with current one, by default we exit on occurrence of an
    # error.
    $ignore_errors = 1;
  }       

  if (defined($args{'S'}))
  {
    # User has asked us to dump SQL commands for ASM restore to an 
    # output SQL file instead of doing the restore.
    $create_restore_sql = 1;
    $restore_sql_file_name = $args{'S'};

    # The file cannot already exist. We don't overwrite existing files.
    if ( -e $restore_sql_file_name)
    {
      @eargs = ($restore_sql_file_name);
      asmcmdshare_error_msg(9357, \@eargs);
      return;    
    }
    $PERLFILE = (split(/\.sql$/, $restore_sql_file_name))[0] . ".pl";
    if ( -e $PERLFILE)
    {
      @eargs = ($PERLFILE);
      asmcmdshare_error_msg(9357, \@eargs);
      return;    
    }

    # Untaint the input
    ($restore_sql_file_name) = $restore_sql_file_name =~ m/(.*)/;

    if ( !($restore_sql_file_name =~  /([\w]+)(\.[\w+])*/) )
    {
      $restore_sql_file_name = "";
    }
 
    if (!open (SQLFILE, "> $restore_sql_file_name"))
    {
      @eargs = ($restore_sql_file_name, $!);
      asmcmdshare_error_msg(9345, \@eargs);
      return; 
    } 
    if (!open (PERLFILE, "> $PERLFILE"))
    {
      @eargs = ($PERLFILE, $!);
      asmcmdshare_error_msg(9345, \@eargs);
      return; 
    } 
    $SQLFILE = $restore_sql_file_name;
    SQLFILE->untaint();
    PERLFILE->untaint();
  } 
  else
  {
    $PERLFILE = $intermediate_file_name . ".pl";
    if (!open (PERLFILE, "> $PERLFILE"))
    {
      @eargs = ($PERLFILE, $!);
      asmcmdshare_error_msg(9345, \@eargs);
      return; 
    } 
    PERLFILE->untaint();
  }

  if (defined($args{'acfs_sec_encr'}))
  {
    # User has asked us to proceed with security and/or encryption restore 
    $acfs_sec_encr = 1;
    ($secuser, $secgrp) = split(":", $args{'acfs_sec_encr'});
    # Check if given user and group exist;
    if (!$create_restore_sql)
    {
      if (!getpwnam($secuser))
      {
        # invalid user name '%s' specified for %s  
        @eargs = ($secuser,"security");
        asmcmdshare_error_msg(9366, \@eargs);
        return; 
      }
      if (!getgrnam($secgrp))
      {
        # invalid group name '%s' specified for %s  
        @eargs = ($secgrp,"security"); 
        asmcmdshare_error_msg(9367, \@eargs);
        return; 
      }
    }
    # User has asked us to proceed with audit restore 
    if (defined($args{'acfs_audit'}))
    {
      $acfs_audit = 1;
      ($aumgruser, $aumgrgrp, $augrpditor) = 
        split(":", $args{'acfs_audit'});
      # Check if given user and groups exist
      if (!$create_restore_sql)
      {
        if (!getpwnam($aumgruser))
        {
          # invalid user name '%s' specified for %s
          @eargs = ($aumgruser,"audit");
          asmcmdshare_error_msg(9366, \@eargs);
          return; 
        }
        if (!getgrnam($aumgrgrp))
        {
          # invalid group name '%s' specified for %s
          @eargs = ($aumgrgrp,"audit");
          asmcmdshare_error_msg(9367, \@eargs);
          return; 
        }
        if (!getgrnam($augrpditor))
        {
          # invalid group name '%s' specified for %s
          @eargs = ($augrpditor,"audit");
          asmcmdshare_error_msg(9367, \@eargs);
          return; 
        }
      }
    }
  }

  # End parameter checking section.

  # New section begins here: restore the disk groups; errors can be ignored
  # if the -i flag is set.

  # We fetched information about diskgroups to work on, now look at them
  # one at a time and re-create the ones user wants us to re-create.
  for ($i = 0; $i < @diskgroup_set; $i++)
  {
    my $old_diskgroup_name     = $diskgroup_set[$i]{DGINFO}{DGNAME};
    my $new_diskgroup_name;
    my $current_redundancy     = $diskgroup_set[$i]{DGINFO}{DGTYPE};
    my $diskgroup_compatibility= $diskgroup_set[$i]{DGINFO}{DGCOMPAT};
    my $disks_info_array       = $diskgroup_set[$i]{DISKSINFO};      #reference
    my $alias_info_array       = $diskgroup_set[$i]{ALIASINFO};      #reference
    my $template_info_array    = $diskgroup_set[$i]{TEMPLATEINFO};   #reference
    my $advm_acfs_info         = $diskgroup_set[$i]{USMINFO};        #reference
    my $attribute_info_array;
    my $template;
    my $alias;
    my $found;
    my $iter1;
    my $attribute_name;

    # If the ASM version is smaller than the diskgroup compatibility
    # of the backup, then we can't restore.
    if (asmcmdshare_version_cmp($asm_instance_version,
                                $diskgroup_compatibility) < 0)
    {
      @eargs = ($diskgroup_compatibility);
      asmcmdshare_error_msg(9361, \@eargs);
      return;
    }

    # If the diskgroup is version 11.1 or more, it has an 
    # attribute directory to restore
    if (asmcmdshare_version_cmp($diskgroup_compatibility, "11.1.0.0.0") >= 0)
    {
        $attribute_info_array = $diskgroup_set[$i]{ATTRINFO}; #reference
    }

    # If the user specified the -g option, then make sure the group 
    # we're trying to restore is actually one of the ones the user
    # specified to restore using the -g option.
    if (defined($dgnamesstr))
    {
      $found = 0;
      for ($iter1 = 0; $iter1 < @diskgroup_names; $iter1++)
      {
        if ($diskgroup_names[$iter1] eq $old_diskgroup_name)
        {
          $found = 1;
          last; # break out of loop
        }   
      }

      # If the group is not one that the user specified, skip it.
      if (!$found)
      {
        next;
      }
    }

    asmcmdshare_print "Current Diskgroup metadata being restored: $old_diskgroup_name\n";

    if ($restoremode eq 'newdg' && 
        defined($diskgroups_replace{$old_diskgroup_name}))
    {
      # Replace diskgroup name and disks/failgroups names with new
      # values in diskgroup_set
      # $$$ implement replace  disks/failgroups names
      $new_diskgroup_name = uc($diskgroups_replace{$old_diskgroup_name});
      asmcmdshare_print "Current Diskgroup name replace by: $new_diskgroup_name\n";
    }
    else
    {
      $new_diskgroup_name = uc($old_diskgroup_name);
    }

    # Recreate the disk group if -t nodg is not specified.
    if ($restoremode ne 'nodg')
    {
      my ($first_disk_in_failgroup);  # Boolean to include FAILGROUP keyword. #
      my ($current_failgroup_disks); # Iterator for failgroups in disk group. #
      my ($current_disk);                # Iterator for disks in a failgroup. #
      my ($current_disk_info);         # Pointer to hash of disk information. #
      my ($current_failgroup_disks_table);  # Pointer to hash of disks in FG. #

      # We have to create the diskgroup
      # Create SQL command grouping disks based on failgroups 
      if (uc($current_redundancy) eq 'EXTERN')
      {
        $current_redundancy = 'EXTERNAL';
      }
      $stmt = 'create diskgroup ' . $new_diskgroup_name . ' '
              . $current_redundancy . ' redundancy '; 

      # Iterate through the list of failgroups in the disk group and
      # format them into the disk group creation SQL.
      foreach $current_failgroup_disks (keys %{$disks_info_array})
      {
        # Set to TRUE so that we print the FAILGROUP and DISK keywords.
        $first_disk_in_failgroup = 1;

        # Set pointer to hash of disks in current failgroup indexed by
        # $current_failgroup_disks.
        $current_failgroup_disks_table = 
                     $disks_info_array->{$current_failgroup_disks};

        # Iterate through the list of disks in the failgroup and format
        # them into the disk group creation SQL.
        foreach $current_disk (keys %{$current_failgroup_disks_table})
        {
          # Set pointer to disk information hash.
          $current_disk_info = $current_failgroup_disks_table->{$current_disk};

          # Include the FAILGROUP and DISK keywords only once--at the
          # very beginning.
          if ($first_disk_in_failgroup)
          {
            # The FAILGROUP keyword can be included only if the disk group
            # is not an EXTERNAL redundancy disk group.
            if ($current_redundancy ne 'EXTERNAL')
            {
              # if QUORUM is specified add it, REGULAR is default.
              if ($current_disk_info->{'QUORUM'} eq "QUORUM")
              {
                $stmt .= ' QUORUM ';
              }
              $stmt .= 'failgroup ' 
                    . $current_disk_info->{'FAILGROUP'} . ' disk ';
            }
            else
            {
              if($current_disk_info->{'QUORUM'} eq "QUORUM")
              {
                $stmt .= ' QUORUM ';
              }
              $stmt .= ' disk ';
            }

            $first_disk_in_failgroup = 0;
          }
          else
          {
            # Comma separate the disk names if not the first disk
            # in disk group.
            $stmt .= ', ';
          }

          # Now include the disk information in the recreation SQL 
          # statement for the disk in question.
          $stmt .= '\'' . $current_disk_info->{'PATH'} . '\' name '
                   . $current_disk_info->{'NAME'} . ' size '
                   . $current_disk_info->{'TOTAL_MB'} . 'M ';
        }
      }

      # Restore au_size, compatible.asm, and compatible.rdbms
      # if the diskgroup is version 11.1 or higher
      if( asmcmdshare_version_cmp($diskgroup_compatibility,
                                  "11.1.0.0.0" ) >= 0)
      {       
        #take the parameters from disk group creation and delete
        #them from the array
        if(defined($attribute_info_array->{'COMPATIBLE.ASM'}))
        {
          $compatible_asm = $attribute_info_array->{'COMPATIBLE.ASM'};
          $stmt .= 'attribute \'compatible.asm\' = \'' . $compatible_asm. '\'' ;
          delete($attribute_info_array->{'COMPATIBLE.ASM'});
        }
                   
        if(defined($attribute_info_array->{'COMPATIBLE.RDBMS'}))
        {
          $compatible_rdbms = $attribute_info_array->{'COMPATIBLE.RDBMS'};
          $stmt .= ', \'compatible.rdbms\' = \'' . $compatible_rdbms . '\'';
          delete($attribute_info_array->{'COMPATIBLE.RDBMS'});
        }
                   
        if(defined($attribute_info_array->{'AU_SIZE'}))
        {
          $au_size = $attribute_info_array->{'AU_SIZE'};
          $stmt .= ', \'au_size\' = \'' . $au_size . '\'';
          delete($attribute_info_array->{'AU_SIZE'});
        }

        if (defined($attribute_info_array->{'SECTOR_SIZE'}))
        {
          $sec_size = $attribute_info_array->{'SECTOR_SIZE'};
          delete($attribute_info_array->{'SECTOR_SIZE'});
          $stmt .= ', \'sector_size\' = \''. $sec_size . '\'';
        }

        if (defined($attribute_info_array->{'CELL.SMART_SCAN_CAPABLE'}))
        {
          $smart_scan_capable = $attribute_info_array->{'CELL.SMART_SCAN_CAPABLE'};
          delete($attribute_info_array->{'CELL.SMART_SCAN_CAPABLE'});
          $stmt .= ', \'cell.smart_scan_capable\' = \''. $smart_scan_capable . '\'';
        }
      }

      if ($create_restore_sql)
      { 
        # If we are creating a SQL file, simply print the disk group
        # creation SQL statement to it.
        print SQLFILE "$stmt" . ";\n";
      }
      else
      {
        # Otherwise, execute the SQL statement to re-create the disk group.
        eval
        {
           $ret = asmcmdshare_do_stmt($dbh, $stmt);
        };
        if (asmcmdexceptions::catch())
        {
          # Creation of diskgroup failed, nothing can be further done for 
          # this diskgroup; print error and proceed to next diskgroup 
          # if -i flag is specified by user, else exit.
          @eargs = ($DBI::errstr);
          asmcmdshare_error_msg(9352, \@eargs);
          if ($ignore_errors)
          {
            next;
          }
          else
          {
             # Propogate the exception upward till it is handled by
             # asmcmdcore_main()
             asmcmdexceptions::throw("asmcmdexceptions");
          }
        }

        asmcmdshare_print "Diskgroup $new_diskgroup_name created!\n";
      }

    } # end if restoremode ne 'nodg' #

    # At this point we have either created required diskgroup or mounted
    # the diskgroup if 'nodg' option is specified, now restore attributes, 
    # alias and template directories


    # Restore Attributes
    if( asmcmdshare_version_cmp($diskgroup_compatibility,
                                "11.1.0.0.0" ) >= 0)
    { 
      foreach $attribute_name (keys %{$attribute_info_array})
      {
        my ($attribute_value)=$attribute_info_array->{$attribute_name};

        # bug25425760: 'cell.sparse_dg' appears in a diskgroup (even if it is
        #   not Exadata) when the attribute 'compatible.asm' is updated to
        #   12.1.0.2. However, 'cell.sparse_dg' cannot be set using 'set 
        #   attribute'. If the default value for this attribute is used,
        #   skip adding it since it should already have it.
        if (lc($attribute_name) eq "cell.sparse_dg" &&
            lc($attribute_value) eq "allnonsparse")
        {
          next;
        }

        $stmt = 'alter diskgroup /*ASMCMD AMBR*/' . $new_diskgroup_name .
                ' set attribute \'' . $attribute_name .
                '\' = \''. $attribute_value . '\'';
        
        if ($create_restore_sql)
        {
          # If we are creating a SQL file, simply print the disk group
          # creation SQL statement to it.
          print SQLFILE "$stmt" . ";\n";
        }
        else
        {
          # Otherwise, execute the SQL statement to modify or re-create the
          # attributes.
          eval
          {
             $ret = asmcmdshare_do_stmt($dbh, $stmt);
          };
          if (asmcmdexceptions::catch())
          {
            # Create/alter attribute failed, print error.
            @eargs = ($DBI::errstr);
            asmcmdshare_error_msg(9360, \@eargs);
            if ($ignore_errors)
            {
              next;
            }
            else
            {
              # Propogate the exception upwards
              asmcmdexceptions::throw("asmcmdexceptions");
            }

            asmcmdshare_print "Attribute $attribute_name created!\n";
          }
        }
      }
    }

    # Restore Templates
    foreach $template (keys %{$template_info_array}) 
    {
      my ($current_template_name) = 
          $template_info_array->{$template}{'TEMPNAME'};
      my ($current_template_redundancy) = 
          $template_info_array->{$template}{'REDUNDANCY'};
      my ($current_template_stripe) = 
          $template_info_array->{$template}{'STRIPE'};
      my ($alter_or_add);

      if (uc($template_info_array->{$template}{'SYSTEM'}) eq 'Y') 
      {
        # System template, it should have already been created as part 
        # of 'create diskgroup' command, 
        # execute alter statements for all template attributes
        $alter_or_add = 'alter';
      }
      else
      {
        # User created template, create a new one
        $alter_or_add = 'add';
      }

      # The keyword for unprotected in the v$ view is different from
      # the SQL keyword, so modify it.
      if (uc($current_template_redundancy) eq 'UNPROT')
      {
          $current_template_redundancy = 'UNPROTECTED';
      }

      # Now construct the SQL command to modify or recreate the
      # the templates.
      $stmt = 'alter diskgroup /*ASMCMD AMBR*/' . $new_diskgroup_name . ' ' 
                . $alter_or_add . ' template ' 
                . '"' . $current_template_name . '"' . ' attributes ('
                . $current_template_redundancy . ' ' 
                . $current_template_stripe . ')';

      if ($create_restore_sql)
      {
        # If we are creating a SQL file, simply print the disk group
        # creation SQL statement to it.
        print SQLFILE "$stmt" . ";\n";
      }
      else
      {
        # Otherwise, execute the SQL statement to modify or re-create the
        # templates.
        eval
        {
           $ret = asmcmdshare_do_stmt($dbh, $stmt);
        };
        if (asmcmdexceptions::catch())
        {
          # Create/alter template failed, print error.
          @eargs = ($DBI::errstr);
          asmcmdshare_error_msg(9353, \@eargs);
          if ($ignore_errors)
          {
            next;
          }
          else
          {
             # Propogate the exception upwards
             asmcmdexceptionms::throw("asmcmdexceptions");
          }
        }

        if ($alter_or_add eq 'add')
        {
          asmcmdshare_print "User template $current_template_name created!\n";
        }
        else
        {
          asmcmdshare_print "System template $current_template_name modified!\n";
        }
      }

    }  # end for templates #     

    # Restore Alias directories
    # foreach $alias (keys %{$alias_info_array}) 
    # Note: ambr currently restores only user-created alias directories.
    #       System created directories or filenames obviously cannot
    #       be restored this way. User-created aliases also cannot be
    #       restored because the system-created filename is likely
    #       unknown at this point in time.

    # if a user directory is created inside a system created directory,
    # it will be restored through dbms_diskgroup
    for ($alias = 0; $alias < (keys %{$alias_info_array}); $alias++)
    {
      # Get alias directory name.
      my ($current_alias_directory_name) = 
          $alias_info_array->{$alias}{'ALIASNAME'};

      my @path = split(/\//,$current_alias_directory_name);
      my $type;

      # bug 25895224: User created dir can be with same name as supported
      # file types (e.g backupset). Only discard system created dir.
      if (defined($path[1]) && defined($supported_types{uc($path[1])}) &&
          ($alias_info_array->{$alias}->{'SYSTEM_CREATED'} eq 'Y'))
      {
        # this is a system created directory
        # Construct full-path of the directory.
        $current_alias_directory_name = '+' . $new_diskgroup_name . '/' . 
                                        $current_alias_directory_name;

        $stmt = 'begin' . "\n";
        $stmt .= 'dbms_diskgroup.createdir(\'' . 
                 $current_alias_directory_name . '\');' . "\n";
        $stmt .= "end;\n/";
      }
      else
      {
        # Construct full-path of the directory.
        $current_alias_directory_name = '+' . $new_diskgroup_name . '/' . 
                                        $current_alias_directory_name;

        # Construct the SQL to re-create the user-specified alias directory.
        $stmt = 'alter diskgroup /*ASMCMD AMBR */ ' . $new_diskgroup_name
              . ' add directory \''
              . $current_alias_directory_name . '\';';
      }

      if ($create_restore_sql)
      {
        print SQLFILE "$stmt" . "\n";
      }
      else
      {
        $stmt =~ s/\;$//;

        eval
        {
           $ret = asmcmdshare_do_stmt($dbh, $stmt);
        };
        if (asmcmdexceptions::catch())
        {
          # Create alias directory failed, print error.
          @eargs = ($DBI::errstr);
          asmcmdshare_error_msg(9354, \@eargs);
          if ($ignore_errors)
          {
            next;
          }
          else
          {
             # propogate the exception upwards
             asmcmdexceptions::throw("asmcmdexceptions");
          }
        }

        asmcmdshare_print "Directory $current_alias_directory_name re-created!\n";
      }
    } # end for alias #  
  
    if ($advm_acfs_info)
    {
      push (@advmA, $advm_acfs_info->{'ADVM'});
      push (@acfsA, $advm_acfs_info->{'ACFS'});
      push (@dgs, "$new_diskgroup_name:$old_diskgroup_name");
    }

  } # end for #
  
  # Check if ACFS drivers are loaded
  $output = catfile ("$ENV{ORACLE_HOME}","bin");
  $output = catfile ($output,"acfsdriverstate");
  $output = asmcmdambr_acfs_execCmd("$output loaded");
  @arroutput = @$output;
  if (!grep(/ACFS-9203.*/, @arroutput)) # ACFS-9203: true
  {
    $acfs_loaded = 0;
    # ACFS is not loaded
    asmcmdshare_error_msg(9368) if ( 0 < @advmA);
  }

  my $perl_has_contents = 0; # Flag used to know perl file has content
  if ($acfs_loaded || $create_restore_sql)
  {
    my (%fstmt);
    my (%secencr);
    my ($advm, $acfs);
    my (@stmts, @perlstmts);
    my ($dg);
    my ($ostype, $osprefix) = asmcmdambr_acfs_getOSType();

    # The following loop will search and verify required disk groups are 
    # created before trying to restore.
    my @drlmissing;
    foreach $advm (@advmA)
    {
      foreach (keys %{$advm})
      {
        my ($drldgname) =  $advm->{$_}{DRLDGNAME};
        if ($drldgname)
        {
          if ($dg = (grep(/:$drldgname$/i, @dgs))[0])
          {
            $drldgname = (split(':', $dg))[0]; # get new dg name 
          }
          # Query DRLDG to ensure it exists.
          if (!$create_restore_sql)
          {
            $qry = 'select NAME from v$asm_diskgroup where NAME = ?';
            $sth = asmcmdshare_do_prepare($dbh, $qry);
            $sth->bind_param(1,$drldgname,SQL_VARCHAR);
            asmcmdshare_do_execute($sth);
            if (!defined ($row = asmcmdshare_fetch($sth)))
            {
              push (@drlmissing, $drldgname);
            }
            asmcmdshare_finish($sth);             
          }
          elsif (! grep(/^$drldgname:.*/i, @dgs))
          {
            # DRLDG is not listed on command for restore 
            push (@drlmissing, $drldgname);
          }
        }
      }
      if (($advm eq $advmA[-1]) && (@drlmissing > 0))
      {
        # Create a unique array of diskgroups 
        my %hashdrl = map { $_, 1 } @drlmissing;
        @drlmissing = keys %hashdrl;
        asmcmdshare_print " * The following DRL Disk group(s)" .
                          " are not restored:\n";
        foreach (@drlmissing)
        {
          asmcmdshare_print "  $_\n";
        }
        if (!$ignore_errors)
        {
	  # disk group containing auxiliary volume not found 
          asmcmdshare_error_msg(9363);
          goto DO_EXCEPTION;
        }
        else
        {
          asmcmdshare_print "Creating DRL volumes as non-DRL volume" .
                            "\n";
        }
      }
    }
   
    # ADVM recovery
    foreach $advm (@advmA)
    {
      foreach (keys %{$advm}) 
      {
        my ($volname)        =  $advm->{$_}{'NAME'};
        my ($voldg)          =  $advm->{$_}{'DGNAME'};
        my ($volsize)        =  $advm->{$_}{'SIZE'};
        my ($redundancy)     =  $advm->{$_}{'REDUNDANCY'};
        my ($stripe_width)   =  $advm->{$_}{'WIDTH'};
        my ($stripe_columns) =  $advm->{$_}{'COLUMNS'};
        my ($primary_zone)   =  $advm->{$_}{'PRIMREG'};
        my ($mirror_zone)    =  $advm->{$_}{'MIRRREG'};
        my ($volstate)       =  $advm->{$_}{'STATE'};
        my ($drldgname)      =  $advm->{$_}{'DRLDGNAME'};
        @stmts = ();

        $dg = (grep(/:$voldg$/i, @dgs))[0];
        if ($dg)
        {
           $voldg = (split(':', $dg))[0];
        }
        else
        {
          asmcmdshare_print "Failed to find Diskgroup: $voldg " .
                            "for volume $volname\n";
          goto DO_EXCEPTION if (!$ignore_errors);
          next;
        }

        # Set values for sql statement
        $volsize = $volsize . "M";
        $mirror_zone = "MIRROR" . $mirror_zone;
        # Error code 15462 prevents stripe width greater than 1024
        $stripe_width = 'default' if ($stripe_width > 1024);
        $stripe_width = $stripe_width . "k" 
          if ($stripe_width ne "default");
        # Error code 15336 prevents stripe width if stripe columns = 1
        $stripe_columns = 'default' 
          if ($stripe_width ne "default" && $stripe_columns == 1);

        # Construct SQL statement to re-create volume.
        $stmt = asmcmdvol_volcreate_sql( $dbh, $voldg, $volname, 
                  $volsize, $redundancy, $stripe_columns, 
                  $stripe_width, $primary_zone, $mirror_zone, 1 
                );
        # Construct statement for DRL
        if ($drldgname)
        {
          if ($dg = (grep(/:$drldgname$/i, @dgs))[0])
          {
            $drldgname = (split(':', $dg))[0]; # get new dg name 
          }
          my $tmpstmt = (split('ATTRIBUTE', $stmt))[0] .
                        " RECOVERY LOG IN GROUP \'" .
                        $drldgname . "\' ATTRIBUTE" .
                        (split('ATTRIBUTE', $stmt))[-1];    
          # Query DRLDG to ensure it exists.
          if (!$create_restore_sql)
          {
            $qry = 'select NAME from v$asm_diskgroup where NAME = ?';
            $sth = asmcmdshare_do_prepare($dbh, $qry);
            $sth->bind_param(1,$drldgname,SQL_VARCHAR);
            asmcmdshare_do_execute($sth);
            if (defined ($row = asmcmdshare_fetch($sth)))
            {
              asmcmdshare_print "Restoring ADVM $volname on DG ".
                                "$voldg DRL $drldgname\n";
              $stmt = $tmpstmt;
            }
            else
            {
              asmcmdshare_print "Restoring ADVM $volname on DG ".
                                "$voldg no DRL\n";
            }
            asmcmdshare_finish($sth);             
          }
          elsif (grep(/^$drldgname:.*/i, @dgs))
          {
            asmcmdshare_print "Restoring ADVM $volname on DG ".
                              "$voldg DRL $drldgname\n";
            $stmt = $tmpstmt;
            push (@stmts, "prompt Creating volume: $volname " .
                   "in Diskgroup $voldg with DRL $drldgname");
          }
          else
          {
            # DRLDG does not exist, restore as no DRL vol.
            asmcmdshare_print "Restoring ADVM $volname on DG ".
                              "$voldg no DRL\n";
          }
        }
        else
        {
          asmcmdshare_print "Restoring ADVM $volname on DG $voldg\n";
          push (@stmts, "prompt Creating volume: $volname in" .
                       " Diskgroup $voldg")if ($create_restore_sql);
        }
        # Add Volume creation statement to the poll 
        push (@stmts, $stmt);

        if ($volstate eq 'DISABLED')
        {
          push (@stmts, "alter diskgroup $voldg disable " .
                         "volume $volname");
        }

        foreach $stmt (@stmts)
        {
          $stmt = $stmt . ';';
          if ($create_restore_sql)
          {
            print SQLFILE "$stmt" . "\n";
          }
          else
          {
            $stmt =~ s/\;$//;
            eval
            {
              $ret = asmcmdshare_do_stmt($dbh, $stmt);
            };
            if (asmcmdexceptions::catch())
            {
              # Create alias directory failed, print error.
              asmcmdshare_print ":: ERROR\n";
              # ADD or ALTER VOLUME failed\n%s
              @eargs = ($DBI::errstr);
              asmcmdshare_error_msg(9362, \@eargs);
              goto DO_EXCEPTION if (!$ignore_errors);
              next;
            }
          }
        }
        asmcmdshare_print "$volname created!\n";
      } # end for advm #
      if ($create_restore_sql && ($advm eq $advmA[-1]))
      {
        my $spoolfile = (split(/\.pl$/, $PERLFILE))[0] 
                          . "fspool.txt";
        $fstmt{'option'}  = 'sqlfile_end'; 
        $fstmt{'spool'}   = $spoolfile; 
        print SQLFILE asmcmdambr_acfs_getStmt({%fstmt}) . "\n"; 
      }
    }
    
    # ACFS recovery
    foreach $acfs (@acfsA)
    {
      # Restore Filesystem
      @perlstmts = ();
      $fstmt{'osprefix'}  = $osprefix;
      $fstmt{'ostype'}    = $ostype;
 
      if ($create_restore_sql && (!$secencr{'perlinit'}))
      {
        $fstmt{'option'}  = 'perlinit'; 
        $fstmt{'sqlfile'} = $SQLFILE; 
        push (@perlstmts,asmcmdambr_acfs_getStmt({%fstmt}));
        $secencr{'perlinit'} = "YES";
      }

      foreach (keys %{$acfs})
      {
        my ($mntpthperm)     = $acfs->{$_}{'MNTPTHPERM'};
        my ($arzmax)         = $acfs->{$_}{'RESIZEMAX'};
        my ($arzinc)         = $acfs->{$_}{'RESIZEINCREMENT'};
        my ($fsplugin)       = $acfs->{$_}{PLUGIN};
        my ($snapshots)      = $acfs->{$_}{SNAPSHOT};
        my ($accelinfo)      = $acfs->{$_}{AUXVOLUME};
        my ($fssec)          = $acfs->{$_}{SECURITY} || 0;
        my ($fsencr)         = $acfs->{$_}{ENCRYPTION} || 0;
        my ($fsaudit)        = $acfs->{$_}{AUDIT};
        my ($cmd);

        $fstmt{'mountpath'}  = $acfs->{$_}{'MOUNTPATH'};
        $fstmt{'volname'}    = $acfs->{$_}{'VOLUMENAME'};
        $fstmt{'dg'}         = $acfs->{$_}{'DGNAME'};
        $fstmt{'size'}       = " ";
        $fstmt{'blksz'}      = $acfs->{$_}{'METADATABLOCKSIZE'};
        $fstmt{'compression'}= $acfs->{$_}{'COMPRESSION'} || 0;
        $fstmt{'accel'}      = " ";
        $fstmt{'dirperm'}    = (split(' ', $mntpthperm))[0];
        $fstmt{'dirgid'}     = (split(':', $mntpthperm))[-1];
        $fstmt{'diruid'}     = (split(':',((split(' ',
                               $mntpthperm))[-1])))[0]; 

        if ($ostype eq 'Solaris' || $ostype eq 'AIX')
        {
          $fstmt{'compat'} = "${osprefix}o c=" .
                             $acfs->{$_}{'ACFSCOMPATIBILITY'};
        }
        else
        {
          $fstmt{'compat'} = "${osprefix}c " . $acfs->{$_}{'ACFSCOMPATIBILITY'};
        }
        
        if ($ostype eq 'AIX')
        {
          $acfs->{$_}{'TOTALSIZE'} = int ($acfs->{$_}{'TOTALSIZE'} / 1024);
          $fstmt{'size'} = "${osprefix}s" .
                             $acfs->{$_}{'TOTALSIZE'} . "K";
        }
        else
        {
          $fstmt{'size'} = $acfs->{$_}{'TOTALSIZE'};
        }
        # Get current DG name
        if ($dg = (grep(/:$fstmt{'dg'}$/i, @dgs))[0])
        {
          $fstmt{'dg'} = (split(':', $dg))[0];
        }
        else
        {
          @eargs = ($fstmt{'mountpath'}, $fstmt{'dg'},
                    $fstmt{'volname'});
          asmcmdshare_error_msg(9355, \@eargs);
          goto DO_EXCEPTION if (!$ignore_errors);
          next;
        }
        if ($accelinfo)
        {
          ($fstmt{'acceldg'}, $fstmt{'accelname'}) = 
             split(':', $accelinfo);
          if ($dg = (grep(/:$fstmt{'acceldg'}$/i, @dgs))[0])
          {
            # Get new DG name
            $fstmt{'acceldg'} = (split(':', $dg))[0];
          }
        }

        $stmt = "Restoring ACFS $fstmt{'mountpath'} on VOL ".
                "$fstmt{'volname'}";
        $stmt .= " ACCEL $fstmt{'accelname'} "if ($accelinfo);
	$stmt .= "\n";
        asmcmdshare_print "$stmt";

        # 1. Check all FS values 
        # Metadata block size only 4096 value is accepted since new 
        # system may have a bigger sector size than current machine.
        if ( $fstmt{'blksz'} == 4096 )
        {
          if ($ostype eq 'Solaris' || $ostype eq 'AIX')
          {
            $fstmt{'blksz'} = "${osprefix}o i=" . $fstmt{'blksz'};
          }
          else
          {
            $fstmt{'blksz'} = "${osprefix}i " . $fstmt{'blksz'};
          }
        }
        else
        {
          $fstmt{'blksz'} = " ";
        }

        # Initialize perl when no -S option is provided
        if ((!$secencr{'perlinit'}) && (! $create_restore_sql))
        {
          $fstmt{'option'} = 'perl';
          push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
          $secencr{'perlinit'} = "YES";
        }

        # Initialize Security, Encryption and Audit
        if ($fsencr ne '0' && $acfs_sec_encr && 
            !$secencr{'ENCRYPTION'})
        {
          # Encryption will be initialized with SSO wallet only,
          # if not initialized previously. This is done once.
          $stmt = "acfsutil encr info ${osprefix}t";
          $fstmt{'option'} ='encrinit';
          $fstmt{'arg'}   = $stmt;
          push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
          $secencr{'ENCRYPTION'} = 'ON';
        }
        if ($fssec ne "0" && $acfs_sec_encr && 
            !$secencr{'SECURITY'})
        {
          # Security will be initialized with given user and group,
          # if not initialized previously. This is done once
          $stmt = "acfsutil sec info ${osprefix}i";
          $fstmt{'user'}   = $secuser;
          $fstmt{'group'}  = $secgrp; 
          $fstmt{'arg'}    = $stmt;
          $fstmt{'option'} = 'secinit';
          push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
          $secencr{'SECURITY'} = 'ON';
        }  
        if ($fsaudit && $acfs_audit && !$secencr{'AUDIT'} &&
            ($secencr{'ENCRYPTION'} || $secencr{'SECURITY'}))
        {
          # Audit will be initialized with given manager group and
          # audit group if not initialized previously. This is done once
          $stmt = "acfsutil audit info ${osprefix}i";
          $fstmt{'user'}   = $aumgruser;
          $fstmt{'group'}  = $aumgrgrp; 
          $fstmt{'auopts'} = $augrpditor;
          $fstmt{'option'} = 'auditinit';
          $fstmt{'arg'}    = $stmt;
          push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
          $secencr{'AUDIT'} = 'ON';
        }

        # 2. Make file system
        if (!$create_restore_sql)     
        {
          # Retrieve volume device information
          $qry = 'select device_kfvol from x$kfvol ' .
                 'where name_kfvol = ? ' .
                 'and dgname_kfvol = ?';
          $sth = asmcmdshare_do_prepare($dbh, $qry);
          $sth->bind_param(1,$fstmt{'volname'});
          $sth->bind_param(2,$fstmt{'dg'},SQL_VARCHAR);
          asmcmdshare_do_execute($sth);
          if (defined ($row = asmcmdshare_fetch($sth)))
          {
            $fstmt{'volume'} = $row->{'DEVICE_KFVOL'};
          }
          else
          {
            asmcmdshare_print ":: ERROR \n";
            # volume device for VOLUME: '%s' in DG: '%s' does not exist 
            @eargs = ($fstmt{'volname'}, $fstmt{'dg'});
            asmcmdshare_error_msg(9364, \@eargs);
            goto DO_EXCEPTION if (!$ignore_errors);
            next;
          }
          asmcmdshare_finish($sth);

          # Retrieve accelerator volume device information
          if ($accelinfo)
          {
            $qry = 'select device_kfvol from x$kfvol ' .
                   'where name_kfvol = ?' .
                   ' and dgname_kfvol = ?';
            $sth = asmcmdshare_do_prepare($dbh, $qry);
            $sth->bind_param(1,$fstmt{'accelname'});
            $sth->bind_param(2,$fstmt{'acceldg'},SQL_VARCHAR);
            asmcmdshare_do_execute($sth);
            if (defined ($row = asmcmdshare_fetch($sth)))
            {
              $fstmt{'accel'} = $row->{'DEVICE_KFVOL'};
              if ($ostype eq 'Linux' || $ostype eq 'Windows')
              {
                $fstmt{'accel'} = "${osprefix}a " . $fstmt{'accel'};
              }
              elsif ($ostype eq 'AIX')
              {
                $fstmt{'accel'} = "${osprefix}o a=" . $fstmt{'accel'};
              }
              else
              {
                $fstmt{'accel'} = " ";
              }
            }
            else
            {
              asmcmdshare_print ":: ERROR \n";
              # volume device for VOLUME: '%s' in DG: '%s' does not exist
              @eargs = ("$fstmt{'accelname'}","$fstmt{'acceldg'}");
              asmcmdshare_error_msg(9364, \@eargs);
              goto DO_EXCEPTION if (!$ignore_errors);
              next;
            }
            asmcmdshare_finish($sth);
          }
          # Create ACFS
          $fstmt{'option'} = 'mkfs';
          $stmt = asmcmdambr_acfs_getStmt({%fstmt});
          $output = asmcmdambr_acfs_execCmd($stmt);
          @arroutput = @$output;
          if (grep (/ACFS-0.*/, @arroutput))
          {
            asmcmdshare_print ":: ERROR \n";
	    # failed to make file system\n%s
            @eargs = ($stmt);
            asmcmdshare_error_msg(9365,\@eargs);
            goto DO_EXCEPTION if (!$ignore_errors);
            next;
          }
        }
        else 
        {
          $fstmt{'volume'} = "\$voldev";
          if ($accelinfo)
          {
            if (! grep(/^$fstmt{'acceldg'}:/i, @dgs))
            {
              # Fail when Accel DG is not listed for restore
              asmcmdshare_print ":: ERROR \n";
              # volume device for VOLUME: '%s' in DG: '%s' does not exist
              @eargs = ("$fstmt{'accelname'}","$fstmt{'acceldg'}");
              asmcmdshare_error_msg(9364, \@eargs);
              goto DO_EXCEPTION if (!$ignore_errors);
              next;
            }
            $fstmt{'accel'} = "\$acceldev";
            $fstmt{'option'} = 'getacceldev';
            push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
          }
          $fstmt{'option'} = 'getdev';
          push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
          $fstmt{'option'} = 'mkfs';
          $stmt = asmcmdambr_acfs_getStmt({%fstmt});
          $stmt =~ s/^ \s+|\s+$//g; # Remove trailing spaces 
          push (@perlstmts, "  \$status = runCmd(\" $stmt \");\n" .
          "Tlog (\"to make file system\",\$status,\"" .
          "$fstmt{'mountpath'}\");\n    if (!\$status){\n"); 
        }
        # Create MountPath
        $fstmt{'option'} = 'mountpoint';
        push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
       
        # Mount ACFS
        $fstmt{'option'} = 'mount';
        push (@perlstmts,asmcmdambr_acfs_getStmt({%fstmt}));

        # Set compression
        if ($fstmt{'compression'} ne '0')
        {
          $fstmt{'option'} = 'compression';
          push (@perlstmts,asmcmdambr_acfs_getStmt({%fstmt}));
        }

        # Set resize
        if ($arzmax ne "N/A" || $arzinc ne "N/A")
        {
          $stmt = "acfsutil size ";
          $stmt = $stmt . "${osprefix}a $arzinc "if ($arzinc ne "N/A");
          $stmt = $stmt . "${osprefix}x $arzmax "if ($arzmax ne "N/A");
          $stmt = $stmt . "$fstmt{'mountpath'}";
          $stmt = "        \$status = runCmd(\" $stmt\" );\n" .
          "        Tlog (\"setup autoresize\",\$status,\"" .
          "$fstmt{'mountpath'}\");\n";
          push (@perlstmts, $stmt);
        }
        
        # Set plug in
        if ($fsplugin)
        {
          my ($metric, $tag, $interval);
          ($metric, $tag, $interval) =  split(':', $fsplugin);
          $stmt = "acfsutil plugin enable ${osprefix}m $metric ";
          $stmt = $stmt . "${osprefix}t $tag " if ($tag);
          $stmt = $stmt . "${osprefix}i ${interval}s "if ($interval>0);
          $stmt = $stmt . "$fstmt{'mountpath'}";
          push (@perlstmts,"        \$status = runCmd(\" $stmt\");\n" .
          "           Tlog (\"setup plugin\",\$status,\"" .
          "$fstmt{'mountpath'}\");\n");
        }

        # Set Encryption
        if (($fsencr ne '0') && $acfs_sec_encr)
        {
          # 1.- encr set 
          # 2.- encr on
          $fstmt{'option'} = 'encr';
          $fstmt{'encr'}   = $fsencr;
          push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
        }

        # Set Security
        if ($fssec ne '0' && $acfs_sec_encr)
        {
          # 0.- request password;
          # 1.- prepare file system
          # 2.- copy file from backup to $mntpath/.Security/backup
          # 3.- sec load backup file
          $fstmt{'option'} = 'sec';
          $fstmt{'user'}   = $secuser;
          $fstmt{'group'}  = $secgrp; 
          $fstmt{'file'}   = $fssec;
          
          push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
        }

        # Set Audit
        if ($fsaudit && $acfs_audit)
        {
          # 1.- change to audit user
          # 2.- audit enable sec
          # 3.- audit enable encr
          $fstmt{'option'} = 'audit';
          $fstmt{'user'}   = $aumgruser;
          $fstmt{'group'}  = $aumgrgrp; 
          $fstmt{'auopts'} = $fsaudit;

          push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
        }

        # Create a file with snapshots
        if ($snapshots)
        {
          if (!$SNAPFILE)
          {
            $SNAPFILE = (split(/\.pl$/, $PERLFILE))[0] . "snap.txt";
          }
          # open snap file handle
          my $snapH;
          if (! open ($snapH, ">>", $SNAPFILE))
          {
            asmcmdshare_print ":: ERROR\n";
            @eargs = ($SNAPFILE, $!);
            asmcmdshare_error_msg(9345, \@eargs);
            goto DO_EXCEPTION if (!$ignore_errors);
          }
          else
          {
            push ( my @snapshots, \%{$snapshots});
            print $snapH Data::Dumper->Dump([\@snapshots],
                                  ["$fstmt{'mountpath'}"]);
            print $snapH "\n";
            close $snapH;
          }
        }

        # ADD closing statements to perl file
        push (@perlstmts, "\n         } #mount\n    } #mkdir \n");
        if ($create_restore_sql)
        {
          push (@perlstmts,"   } #mkfs\n");          
          $fstmt{'option'} = 'getdevend';
          push (@perlstmts, asmcmdambr_acfs_getStmt({%fstmt}));
        }

        # Dump calls to file
        if (@perlstmts > 0)
        {
          # Check file handle is still open
          if (tell(PERLFILE) != -1)
          {
            print PERLFILE "@perlstmts";
            $perl_has_contents = 1; 
          }
          else
          {
            @eargs = ($PERLFILE, $!);
            asmcmdshare_error_msg(9345, \@eargs);
            return; 
          }
        }
        @perlstmts = ();
        asmcmdshare_print "$fstmt{'mountpath'} modified!\n";
      }  # end for acfs FS lvl #
    } # end for acfs DG lvl#
  }
  # Close restore SQL file
  if ($create_restore_sql)
  {
    close (SQLFILE);
  }
  if ($perl_has_contents && (tell(PERLFILE) != -1))
  {   
    # Dump closing stmt to perl file when file has contents, !-z and -s
    # are not used for check since writing may be slower than size check.
    $stmt =  "\nclose (\$fh) if (\$verbose);\nprint \"" .
    "\\n\\n***************************************\\n  Summary" .
    "\\n***************************************\\n\\n\";\n"; 
    $stmt = $stmt . "foreach my \$num (0..1){\n" .
    " if (\$dolog{\$num})  {\n" .
    "    print \"\\nError -The following errors were encountered\\n\\n\" " .
    "if(\$num != 0);\n" .
    "    foreach (keys %{\$dolog{\$num}}){\n" .
    "      print \"For file system: \$_\\n\";\n" .
    "      print \"    \$_\\n\" foreach (\@{\$dolog{\$num}{\$_}});\n" .
    "    }\n  }\n}\n"; 
    print PERLFILE "$stmt";
  }
  close (PERLFILE);
  unlink $PERLFILE if (-z $PERLFILE);
  if ($SNAPFILE)
  {
    if (-z $SNAPFILE)
    {
      unlink $SNAPFILE ;
    }
    else
    {
      asmcmdshare_print "Snapshot info can be found in \'$SNAPFILE" .
                        "\'\n";
    }
  }
  if (-e $PERLFILE)
  {
    if (!$create_restore_sql)
    {
      asmcmdshare_print "Execute \"\# perl $PERLFILE\" to mount " .
                         "ACFS\n";
    }
    else
    {
      asmcmdshare_print "ACFS restore file \'$PERLFILE\' created " .
                         "successfully\n";
    }
  }
  return;

DO_EXCEPTION:
  # Close Files and do exception
  if ($create_restore_sql)
  {
    close (SQLFILE);
  }
  close (PERLFILE);
  unlink $PERLFILE;
  unlink $SNAPFILE if ($SNAPFILE);
  asmcmdexceptions::throw("asmcmdexceptions");
}

########
# NAME
#   asmcmdambr_process_help
#
# DESCRIPTION
#   This function is the help function for the ASMCMDAMBR module.
#
# PARAMETERS
#   command     (IN) - display the help message for this command.
#
# RETURNS
#   1 if command found; 0 otherwise.
########
sub asmcmdambr_process_help 
{
  my ($command) = shift;       # User-specified argument; show help on $cmd. #
  my ($desc);                                # Command description for $cmd. #
  my ($succ) = 0;                         # 1 if command found, 0 otherwise. #

  if (asmcmdambr_is_cmd ($command)) 
  {                              # User specified a command name to look up. #
    $desc = asmcmdshare_get_help_desc($command);
    asmcmdshare_print "$desc\n";
    $succ = 1;
  }

  return $succ;
}

########
# NAME
#   asmcmdambr_is_cmd
#
# DESCRIPTION
#   This routine checks if a user-entered command is one of the known
#   ASMCMD internal commands that belong to the ASMCMDAMBR module.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is one of the known commands, false otherwise.
########
sub asmcmdambr_is_cmd 
{
  my ($arg) = shift;

  return defined ($asmcmdambr_cmds{ $arg });
}

########
# NAME
#   asmcmdambr_is_wildcard_cmd
#
# DESCRIPTION
#   This routine determines if an ASMCMDAMBR command allows the use 
#   of wild cards.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   True if $arg is a command that can take wildcards as part of its argument, 
#   false otherwise.
########
sub asmcmdambr_is_wildcard_cmd 
{
  my ($arg) = shift;

  return defined ($asmcmdambr_cmds{ $arg }) && 
         (asmcmdshare_get_cmd_wildcard ($arg) eq "true" ) ;
}

########
# NAME
#   asmcmdambr_is_no_instance_cmd
#
# DESCRIPTION
#   This routine determines if a command can run without an ASM instance.
#
# PARAMETERS
#   arg   (IN) - user-entered command name string.
#
# RETURNS
#   1 if $arg is a command that can run without an ASM instance or it does not
#   belong to this module
#   0 if $arg is a command that needs to connect to an ASM instance
#   -1 if $arg is a command that may use an ASM instance.
#
# NOTES
#   The asmcmdambr module currently supports no command that can run 
#   without an ASM instance.
########
sub asmcmdambr_is_no_instance_cmd 
{
  my ($arg) = shift;
  my ($rc);

  return 1 unless defined($asmcmdambr_cmds{$arg});

  $rc = asmcmdshare_get_cmd_noinst($arg);
  if ($rc eq "true")
  {
    return 1;
  }
  elsif ($rc eq "undef")
  {
    return -1;
  }

  return 0;
}

########
# NAME
#   asmcmdambr_parse_int_args
#
# DESCRIPTION
#   This routine parses the arguments for flag options for ASMCMDAMBR 
#   internal commands.  
#
# PARAMETERS
#   cmd      (IN)  - user-entered command name string.
#   args_ref (OUT) - hash of user-specified flag options for a command, 
#                    populated by getopts().
#
# RETURNS
#   Zero on success; undefined on error.
#
# NOTES
#   $cmd must already be verified as a valid ASMCMDAMBR internal command.
########
sub asmcmdambr_parse_int_args 
{
  my ($cmd, $args_ref) = @_;
  my ($key);
  my (@string);

  #include deprecated options if any
  if($asmcmdglobal_deprecated_options{ $cmd })
  {
    foreach my $key(keys %{$asmcmdglobal_deprecated_options{ $cmd }})
    {
      #include only if the option is changed and not discontinued
      push(@string, $asmcmdglobal_deprecated_options{$cmd}{$key}[0]);
    }
  }

  # Use asmcmdparser_parse_issued_command() from the asmcmdparser package to parse arguments for
  # internal commands.  These arguments are stored in @ARGV.
  if (!asmcmdparser_parse_issued_command($cmd, $args_ref, \@string))
  {
    # Print correct command format if syntax error #
    asmcmdambr_syntax_error($cmd);
    return undef;
  }

  return 0;
}

########
# NAME
#   asmcmdambr_syntax_error
#
# DESCRIPTION
#   This function prints the correct syntax for a command to STDERR, used 
#   when there is a syntax error.  This function is responsible for 
#   only ASMCMDAMBR commands.
#
# PARAMETERS
#   cmd   (IN) - user-entered command name string.
#
# RETURNS
#   1 if the command belongs to this module; 0 if command not found.
#
# NOTES
#   These errors are user-errors and not internal errors.  They are of type
#   record, not signal.  
# 
#   N.B. Functions in this module can call this function directly, without
#   calling the asmcmdshare::asmcmdshare_syntax_error equivalent.  The
#   latter is used only by the asmcmdcore module.
########
sub asmcmdambr_syntax_error 
{
  my ($cmd) = shift;
  my ($cmd_syntax);                               # Correct syntax for $cmd. #
  my ($succ) = 0;

  # display syntax only for commands from this module.
  if ( asmcmdambr_is_cmd($cmd))
  {
    $cmd_syntax = asmcmdshare_get_help_syntax($cmd);  # Get syntax for $cmd. #
    $cmd_syntax = asmcmdshare_trim_str ($cmd_syntax);   # Trim blank spaces #

    asmcmdshare_printstderr ':usage: ' . $cmd_syntax . "\n";
    asmcmdshare_printstderr ':help:  help ' . $cmd . "\n";
    if ($asmcmdglobal_hash{'mode'} eq 'n')
    {
      $asmcmdglobal_hash{'e'} = -1;
    }
  }

  return $succ;
}


########
# NAME
#   asmcmdambr_get_asmcmd_cmds
#
# DESCRIPTION
#   This routine constructs a string that contains a list of the names of all 
#   ASMCMD internal commands and returns this string.
#
# PARAMETERS
#   None.
#
# RETURNS
#   A string contain a list of the names of all ASMCMD internal commands.
#
# NOTES
#   Used by the help command and by the error command when the user enters
#   an invalid internal command.
#
#   IMPORTANT: the commands names must be preceded by eight (8) spaces of
#              indention!  This formatting is mandatory.
########
sub asmcmdambr_get_asmcmd_cmds 
{
  return asmcmdshare_filter_invisible_cmds (%asmcmdambr_cmds);
}

########
# NAME
#   asmcmdambr_acfs_backup
#
# DESCRIPTION
#   This routine constructs 2 hashes one hash has ADVM backup metadata
#   and the second has ACFS metadata. 
#
# PARAMETERS
#   dbh               (IN) - initialized database handle, non-null.
#   dgname            (IN) - Current DG name to backup.
#   ostype            (IN) - OS name which we are running.
#   osprefix          (IN) - Command prefix for OS.
#   acfs_secencr_save (IN) - Security backup path.
#
# RETURNS
#   %advm (OUT) - Volume metadata that belongs to given diskgroup.
#   %acfs (OUT) - File system metadata that belongs to given diskgroup
#   @drldg (OUT) - Returns a DRL diskgroup's name if DRLDG exists.
#
# NOTES
#   Used by asmcmdambr_backup command to get volume and 
#   file system meta-data for backup.
#
########

sub asmcmdambr_acfs_backup
{
  my ($dbh, $dgname, $ostype, $osprefix, $acfs_secencr_save) = @_;
  my ($sth, $sth1, $qry, $row);             # SQL query statement. #
  my ($output, @arroutput);             # Command execution variables #
  my @drldg;                            # List container of DRL DG #
  # Volume variables #
  my $advm = "";
  my (@volume_name);               # Container for list of volumes #
  my ($volume_name);                              # Current volume #
  my (%advm_info);          # Hash containing all volume meta-data #
  # File System variables #
  my $acfs = "";
  my @mountpath;                # Container for list of mountpaths #
  my $mountpath;                               # Current mountpath #
  my (%acfs_info);          # Hash containing all file system data #

  ################################################################
  # Backup volume
  ################################################################
  # 1. Filter volumes per diskgroup
  $qry = 'select NAME_KFVOL from x$kfvol where ' .
         'DGNAME_KFVOL = ?';
  $sth = asmcmdshare_do_prepare($dbh, $qry);
  $sth->bind_param(1, $dgname,SQL_VARCHAR);
  asmcmdshare_do_execute($sth);
  while (defined ($row = asmcmdshare_fetch($sth)))
  {
    if ($row->{'NAME_KFVOL'}){
      push (@volume_name, uc($row->{'NAME_KFVOL'}));
    }
  }
  asmcmdshare_finish($sth);
  foreach $volume_name (@volume_name)
  {
    my (%vol_info);                  # Volume info record #
    my ($vol_info);                  # Volume info record #
    # 2. Query each volume metadata
    $qry = 'select a.VOLUME_NAME, a.SIZE_MB, a.STRIPE_COLUMNS, ' .
           'a.STATE, a.REDUNDANCY, a.STRIPE_WIDTH_K, ' .
           'a.PRIMARY_REGION, a.MIRROR_REGION, g.DRLDGNAME_KFVOL ' .
           'from v$asm_volume a, x$kfvol g ' .
           'where a.VOLUME_NAME = ? ' .
           'and g.NAME_KFVOL = a.VOLUME_NAME ' .
           'and DGNAME_KFVOL = ?';
    $sth = asmcmdshare_do_prepare($dbh, $qry);
    $sth->bind_param(1,$volume_name);
    $sth->bind_param(2,$dgname,SQL_VARCHAR);
    asmcmdshare_do_execute($sth);
    if (defined ($row = asmcmdshare_fetch($sth)))
    {
      # 3. Add DRL diskgroup
      if ((defined $row->{'DRLDGNAME_KFVOL'}) &&
          ($row->{'DRLDGNAME_KFVOL'} ne ""))
      { 
        $vol_info{'DRLDGNAME'} = $row->{'DRLDGNAME_KFVOL'};
        push (@drldg, $row->{'DRLDGNAME_KFVOL'});
      }
      # 4. Dump each volume metadata in a hash
      $vol_info{'DGNAME'}     = $dgname;
      $vol_info{'NAME'}       = $row->{'VOLUME_NAME'};
      $vol_info{'SIZE'}       = $row->{'SIZE_MB'};
      $vol_info{'REDUNDANCY'} = $row->{'REDUNDANCY'};
      $vol_info{'COLUMNS'}    = $row->{'STRIPE_COLUMNS'};
      $vol_info{'WIDTH'}      = $row->{'STRIPE_WIDTH_K'};
      $vol_info{'PRIMREG'}    = $row->{'PRIMARY_REGION'};
      $vol_info{'MIRRREG'}    = $row->{'MIRROR_REGION'};
      $vol_info{'STATE'}      = $row->{'STATE'};
      $advm_info{ $volume_name } = { %vol_info };
      asmcmdshare_print "ADVM metadata to be backed up: " .
                        "$volume_name\n";
      asmcmdshare_trace(5, "asmcmdambr_process_backup(): vol_info: " .
                           Dumper(\%vol_info), 'y', 'n');
    } # end while #
    asmcmdshare_finish($sth);         
  } # end foreach volume #
  $advm = {%advm_info} if (%advm_info);
  ##############################################################
  # Backup File system 
  ##############################################################
  # 1. Get all existing mount paths
  $qry = "acfsutil info fs ${osprefix}o mountpoints";
  $output = asmcmdambr_acfs_execCmd ($qry);
  if ( grep (/ACFS-.*/, @$output))
  {
    # No filesystem mounted
    return ( $advm, $acfs, (@drldg) );
  }
  foreach (@$output)
  {
    $_ =~ s/^ \s+|\s+$//g; 
    push (@mountpath, $_);
  }
  foreach $mountpath (@mountpath)
  {
    my (%fs_info);                 # Mountpath info record #
    my ($fs_info);
    my (@acfs_util);
    # 2. Get Basic file system information
    if ($ostype eq 'AIX')
    {
      @acfs_util = (
                   "VOLUMENAME",
                   "TOTALSIZE",
                   "RESIZEINCREMENT",
                   "RESIZEMAX",
                   "METADATABLOCKSIZE",
                   "COMPRESSION",
                   "AUXVOLUME"
                    );
      $qry = "acfsutil info fs $mountpath ${osprefix}o " .
          "primaryvolume,totalspace," .
          "autoresizeincrement,autoresizemax,metablocksize," .
          "iscompression,auxvolume" .
          " 2>/dev/null";  
    }
    elsif ($ostype eq 'Linux')
    {
      @acfs_util = (
                   "VOLUMENAME",
                   "TOTALSIZE",
                   "RESIZEINCREMENT",
                   "RESIZEMAX",
                   "METADATABLOCKSIZE",
                   "COMPRESSION",
                   "SECURITY",
                   "ENCRYPTION",
                   "AUXVOLUME"
                   );
      $qry =  "acfsutil info fs $mountpath ${osprefix}o " .
              "primaryvolume,totalspace," .
              "autoresizeincrement,autoresizemax,metablocksize," .
              "iscompression,issecurity,isencryption,auxvolume 2>/dev/null";
    }
    elsif ($ostype eq 'Windows') 
    {
      @acfs_util = (
                   "VOLUMENAME",
                   "TOTALSIZE",
                   "RESIZEINCREMENT",
                   "RESIZEMAX",
                   "METADATABLOCKSIZE",
                   "COMPRESSION",
                   "SECURITY",
                   "ENCRYPTION",
                   "AUXVOLUME"
                   );
      $qry =  "acfsutil info fs $mountpath ${osprefix}o " .
              "primaryvolume,totalspace," .
              "autoresizeincrement,autoresizemax,metablocksize," .
              "iscompression,issecurity,isencryption,auxvolume 2>/dev/null";
    }
    else 
    {
      @acfs_util = (
                   "VOLUMENAME",
                   "TOTALSIZE",
                   "RESIZEINCREMENT",
                   "RESIZEMAX",
                   "METADATABLOCKSIZE",
                   "COMPRESSION",
                   "SECURITY",
                   "ENCRYPTION"
                   );
      $qry =  "acfsutil info fs $mountpath ${osprefix}o ".
              "primaryvolume,totalspace," .
              "autoresizeincrement,autoresizemax,metablocksize," .
              "iscompression,issecurity,isencryption 2>/dev/null";
    }
    $output = asmcmdambr_acfs_execCmd ($qry);
    my $iter = 0;
    foreach (@$output)
    {
      my ($key) = $acfs_util[$iter] || "" ;
      $_ =~ s/^ \s+|\s+$//g; 
      $iter++;
 
      # 3. Check file system volumes belong to given diskgroup
      #    Back-up is done per diskgroup
      if (($key eq 'VOLUMENAME')||
          ($key eq 'AUXVOLUME' && $_ ne " "))
      {
        $qry = 'select NAME_KFVOL, DGNAME_KFVOL ' .
               'from x$kfvol ' .
               'where DEVICE_KFVOL = ?';
        $sth = asmcmdshare_do_prepare($dbh, $qry);
        $sth->bind_param(1,$_,SQL_VARCHAR);
        asmcmdshare_do_execute($sth) if $sth;
        if (defined ($row = asmcmdshare_fetch($sth)))
        {
          if ($key eq 'VOLUMENAME')
          {
            last if ($row->{'DGNAME_KFVOL'} ne $dgname);
            $fs_info{$key} = $row->{'NAME_KFVOL'};
          }
          else
          {
            $fs_info{$key} = $row->{'DGNAME_KFVOL'} . ":" .
            $row->{'NAME_KFVOL'};
          }
        }
        asmcmdshare_finish($sth);
      }
      else
      {
        $fs_info{$key} = $_;
      }
    }
 
    # 4. Skip file system if it does not belong to current dg
    next if (!$fs_info{'VOLUMENAME'});
    # Save diskgroup name
    $fs_info{'DGNAME'} = $dgname;
    # Save mountpath
    $fs_info{'MOUNTPATH'} = $mountpath;
 
    # 5. Get acfs specific attribute info      
    # Get ACFS compatibility
    $qry = "acfsutil compat get $mountpath";
    $output = asmcmdambr_acfs_execCmd ($qry);
    @arroutput = @$output;
    $output = (split(':', $arroutput[-1]))[-1];
    $output =~ s/^ \s+|\s+$//g; # Remove trailing spaces 
    $fs_info{'ACFSCOMPATIBILITY'} = $output 
                                    if (! grep (/ACFS-1.*/, @arroutput));
 
    # Get current mount path permissions
    $output = stat($mountpath);
    $output = sprintf("%04o %s:%s", $output->mode & 07777,
                      $output->uid, $output->gid);
    $fs_info{'MNTPTHPERM'} = $output;
 
    # Get plugin info
    $qry = "acfsutil plugin info $mountpath 2>&1";
    $output = asmcmdambr_acfs_execCmd($qry);
    @arroutput = @$output;
    if ( @arroutput == 5 && (! grep (/ACFS-1.*/, @arroutput)) )
    {
      my $metric = (split(':', $arroutput[1]))[-1];
      my $tag = (split(':', $arroutput[2]))[-1] // "";
      my $interval = (split(':', $arroutput[-1]))[-1] // "";
      $metric =~ s/^ \s+|\s+$//g; 
      $tag =~ s/^ \s+|\s+$//g if ($tag); 
      $interval =~ s/^ \s+|\s+$//g if ($interval); 
      $fs_info{'PLUGIN'} = $metric . ":" . $tag . ":" . $interval;
    }
 
    ###
    # Backup Snapshots
    #
    # The output received from acfsutil has the following format
    # Each snap has 7 lines of attributes, 
    # calculate the given attributes position
    # it's done this way to avoid multi language problems
    ###
    $qry = "acfsutil snap info " . $mountpath;
    $output = asmcmdambr_acfs_execCmd($qry);
    @arroutput = @$output;
    if (@arroutput > 4 && (! grep (/ACFS-1.*/, @arroutput)) )
    {
      my %acfs_snap;
      my %snap_info;
      my $lineref=0;
      my $str;
      $str = (split(':', $arroutput[-2]))[-1];
      $str =~ s/^\s+|\s+$//g;
      my ($snapnumber);
      my $snapcounter =1;
      for ($snapnumber = $str; $snapnumber > 0 ; $snapnumber--)
      {
        $str = (split(':', $arroutput[$lineref]))[-1]; # Get value
        $str =~ s/^\s+|\s+$//g;
        $snap_info{'NAME'} = $str; # snapone
        $lineref = $lineref + 2;
        $str = (split(':', $arroutput[$lineref]))[-1]; # Get value
        $str =~ s/^\s+|\s+$//g;
        $snap_info{'ACCESS'} = $str; # RO || RW
        $lineref++;
        $str = (split(':', $arroutput[$lineref]))[-1]; # Get value
        $str =~ s/^\s+|\s+$//g;
        $snap_info{'PARENT'} = $str; # mountpoint || snap
        $lineref = $lineref + 5; # Go to next snap first line
        $acfs_snap{$snapcounter} = { %snap_info };
        $snapcounter++;
      }
      $fs_info{'SNAPSHOT'} = { %acfs_snap };
    }
 
    # Get Security
    if ($acfs_secencr_save && $fs_info{'SECURITY'})
    {
      SECURITY:
      {
        if ($fs_info{'SECURITY'} == 1)
        {
          # 1. Check for permissions
          my $secdir = catfile ($mountpath ,'.Security');
          $secdir = catfile ($secdir , 'backup');
          $secdir = catfile ($secdir,'secbackup.xml');
          # User must be sec admin or 
          #   part of backup operators realm
          #   in order to see the file.
          if (! -e $secdir)
          {
            asmcmdshare_print "* ACFS Not enough permissions "
                  . "to backup $mountpath security \n";
            last SECURITY;
          }
          # 2. Set new security file name
          my $nwsecfile = $dgname . "." . $fs_info{'VOLUMENAME'} .
                          ".secbackup.xml";
          my $secbkuppth = dirname($acfs_secencr_save);
          $secbkuppth = catfile ($secbkuppth,"security");
          # 3. Create directory to store security backup files
          if (! -d  $secbkuppth)
          {
            mkdir $secbkuppth;         
            if (! -d  $secbkuppth)
            {
              asmcmdshare_print "* ACFS Failed to create " .
                                "${secbkuppth} directory\n";
              last SECURITY;
            }
          }
          
          # 4. Copy file to new location
          $secbkuppth = catfile ($secbkuppth, $nwsecfile);
          copy ($secdir, $secbkuppth);
          if (!-e $secbkuppth )
          {
            asmcmdshare_print "*ACFS failed to copy $secdir to" .
                              " ${secbkuppth}${$nwsecfile}\n";
            last SECURITY;
          }
          $fs_info{'SECURITY'} = $nwsecfile;
        }    
      }
    }
    else
    {
      $fs_info{'SECURITY'} = 0;   
    }
 
    # Encryption has the following output
    #
    #  File system: /tkfv_mounts/TKFVVOL1
    #    Encryption status: OFF
    #    Algorithm: AES 128-bits
    #    Key length: 16 bytes
    if ($acfs_secencr_save && $fs_info{'ENCRYPTION'})
    {
      if ($fs_info{'ENCRYPTION'} == 1)
      {
        $qry = "acfsutil encr info ${osprefix}m $mountpath";
        $output = asmcmdambr_acfs_execCmd($qry);
        @arroutput  = @$output;
        if ( ! grep (/ACFS-1.*/, @arroutput) )
        {
          my $str = "";
          $str = (split(':', $arroutput[2]))[-1]; # AES 128-bits
          $str =~ s/^\s+|\s+$//g; # Remove trailing spaces
          ($fs_info{'ENCRYPTION'}, $str) = split(' ', $str);
          $str = ":" . (split('-', $str))[0]; # 128-bits
          $fs_info{'ENCRYPTION'} = $fs_info{'ENCRYPTION'} . $str;
        }
        else
        {
          asmcmdshare_print "*ACFS ERROR \n @arroutput \n";
        }
      }    
    }
    elsif ($fs_info{'ENCRYPTION'})
    {
        $fs_info{'ENCRYPTION'}=0;
    }
 
    # Audit command output
    #
    #  Auditing information for mount point 'mountpath':
    #  Maximum Audit trail size  : 10 MB
    #  Archive file      : 'NOT PRESENT'
    #  Audit sources:
    #     Security   : 'ENABLED'
    #     Encryption : 'DISABLED'
    if ($acfs_secencr_save && 
        ($fs_info{'ENCRYPTION'} || $fs_info{'SECURITY'}))
    {
      # 1. Get audit information
      $qry = "acfsutil audit info ${osprefix}m $mountpath";
      $output = asmcmdambr_acfs_execCmd($qry);
      @arroutput = @$output;
      if (! grep (/ACFS-1.*/, @arroutput))
      {
        my %fs_audit;
        my $str;
        # 2. Verify if audit is enabled for security
        $str = (split(':', $arroutput[-2]))[-1];
        $str =~ s/^\s+|\s+$//g;
        if ($str eq '\'ENABLED\'')
        {
          # 2.1 Verify security file is in backup location
          if ($fs_info{'SECURITY'} =~ $dgname)
          {
            $fs_audit{'SECURITY'} = 'ENABLED';
          }
        }
        # 3. Verify if audit is enabled for encryption
        $str = (split(':', $arroutput[-1]))[-1];
        $str =~ s/^\s+|\s+$//g;         # Remove trailing spaces
        if ($str eq '\'ENABLED\'')
        {
          if ($fs_info{'ENCRYPTION'} ne "0")
          {
            $fs_audit{'ENCRYPTION'} = 'ENABLED';
          }
        }
        # 4. Dump audit to mount path hash
        $fs_info{'AUDIT'} = {%fs_audit} if (%fs_audit);
      }   
    }
 
    # 6. Dump file system metadata
    $acfs_info{ $mountpath } = { %fs_info };     
    asmcmdshare_print "ACFS metadata to be backed up:$mountpath\n";
    asmcmdshare_trace(5, "asmcmdambr_process_backup():acfs_info: " .
                             Dumper(\%acfs_info), 'y', 'n');
  } # end foreach #
  $acfs = {%acfs_info} if (%acfs_info);
  return ($advm, $acfs, (@drldg));
}

########
# NAME
#   asmcmdambr_acfs_getStmt
#
# DESCRIPTION
#   This routine returns requested statement required
#   to create perl file for File system restore.
#
# PARAMETERS
#   option        (IN) - Option to request of xml file.
#   mountpath     (IN) - Mount path of file system.
#   dg            (IN) - DG name that belongs to volume.
#   acceldg       (IN) - DG name that belongs to accelerator volume.
#   ostype        (IN) - OS name.
#   osprefix      (IN) - Command prefix for current OS.
#   security flag (IN) - Security backup path.
#   spool         (IN) - Spool file name.
#   sqlfile       (IN) - Sql file name.
#   compat        (IN) - Acfs compatibility version.
#   size          (IN) - File system size.
#   accel         (IN) - Accelerator device value.
#   blksz         (IN) - Acfs metadata block size.
#   volume        (IN) - Volume device value
#   dirperm       (IN) - Mount path permissions
#   diruid        (IN) - Mount path uid
#   dirgid        (IN) - Mount path gid
#   user          (IN) - User id for security and/or audit
#   group         (IN) - Group id for security and/or audit
#   auopts        (IN) - Audit backup metadata
#   encr          (IN) - Encryption backup metadata
#   file          (IN) - Security backup file name
#   arg           (IN) - Argument provided in restore
#
# RETURNS
#   A string for the given option.
#
# NOTES
#
########
sub asmcmdambr_acfs_getStmt
{

  my ($args) = @_;
  my $string = "";
  if (!$args)
  {
    return 0;
  }
  # Parse xml file.
  if (!$xmlparsed)
  {
    asmcmdambr_acfs_parse_xml ();
  }
  # Get statements
  if ($args->{'option'} eq 'sqlfile_end')
  {
    # Add arguments to String
    $_ = $aOptTxt{$args->{'option'}}; 
    $_ =~ s/args\{spool}/$args->{'spool'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'perlinit')
  {
    # Add arguments to String
    $_ = $aOptTxt{'header'}; 
    $_ .= $aOptOpt1{'header'}; 
    $_ .= $aOptOpt2{'header'}; 
    $_ =~ s/args\{spool}/$args->{'spool'}/g;
    $_ =~ s/args\{sqlfile}/$args->{'sqlfile'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'perl')
  {
    # Add arguments to String
    $_ = $aOptTxt{'header'}; 
    $_ .= $aOptOpt2{'header'}; 
    $_ =~ s/args\{spool}/$args->{'spool'}/g;
    $_ =~ s/args\{sqlfile}/$args->{'sqlfile'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'mkfs')
  {
    # Add arguments to String
    if ($args->{'ostype'} eq "Linux")
    {
      $_ = $aOptTxt{$args->{'option'}}; 
    }
    elsif ($args->{'ostype'} eq "Solaris")
    {
      $args->{'volume'} =~ s/asm\//asm\/r/;
      $_ = $aOptOpt1{$args->{'option'}}; 
    }
    elsif ($args->{'ostype'} eq "AIX")
    {
      $_ = $aOptOpt2{$args->{'option'}}; 
    }
    elsif ($args->{'ostype'} eq "Windows")
    {
      $_ = $aOptEnd{$args->{'option'}}; 
    }
    $_ =~ s/^ \s+|\s+$//g;
    $_ =~ s/args\{compat}/$args->{'compat'}/g;
    $_ =~ s/args\{size}/$args->{'size'}/g;
    $_ =~ s/args\{accel}/$args->{'accel'}/g;
    $_ =~ s/args\{blksz}/$args->{'blksz'}/g;
    $_ =~ s/args\{volume}/$args->{'volume'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'mount')
  {
    # Add arguments to String
    if ($args->{'ostype'} eq "Linux")
    {
      $_ = $aOptTxt{$args->{'option'}}; 
    }
    elsif ($args->{'ostype'} eq "Solaris")
    {
      $_ = $aOptOpt1{$args->{'option'}}; 
    }
    elsif ($args->{'ostype'} eq "AIX")
    {
      $_ = $aOptOpt2{$args->{'option'}}; 
    }
    elsif ($args->{'ostype'} eq "Windows")
    {
      $_ = $aOptEnd{$args->{'option'}}; 
    }
    $_ =~ s/args\{volume}/$args->{'volume'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'getdev')
  {
    # Add arguments to String
    $_ = $aOptTxt{$args->{'option'}}; 
    if ($args->{'accel'})
    {
      # Get accelerator if exists 
      $_ .= $aOptOpt1{$args->{'option'}}; 
    }
    $_ =~ s/args\{dg}/$args->{'dg'}/g;
    $_ =~ s/args\{volname}/$args->{'volname'}/g;
    $_ =~ s/args\{acceldg}/$args->{'acceldg'}/g;
    $_ =~ s/args\{accelname}/$args->{'accelname'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'getdevend')
  {
    # Add arguments to String
    $_ = " ";
    if ($args->{'accel'})
    {
      # Get accelerator if exists 
      $_ .= $aOptOpt2{'getdev'}; 
    }
    $_ .= $aOptEnd{'getdev'}; 
    $_ =~ s/args\{dg}/$args->{'dg'}/g;
    $_ =~ s/args\{volname}/$args->{'volname'}/g;
    $_ =~ s/args\{acceldg}/$args->{'acceldg'}/g;
    $_ =~ s/args\{accelname}/$args->{'accelname'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'mountpoint')
  {
    # Add arguments to String
    $_ = $aOptTxt{$args->{'option'}}; 
    $_ =~ s/args\{dirperm}/$args->{'dirperm'}/g;
    $_ =~ s/args\{diruid}/$args->{'diruid'}/g;
    $_ =~ s/args\{dirgid}/$args->{'dirgid'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'compression')
  {
    # Add arguments to String
    $_ = $aOptTxt{$args->{'option'}}; 
    $string = $_;
  }
  elsif ($args->{'option'} eq 'encrinit')
  {
    $_ = $aOptTxt{$args->{'option'}}; 
    $_ =~ s/args\{info}/$args->{'arg'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'encr')
  {
    my ($algorithm, $keyln) = split(':', $args->{'encr'});
    $_ = $aOptOpt1{'encrinit'}; 
    $_ =~ s/args\{algorithm}/$algorithm/g;
    $_ =~ s/args\{keyln}/$keyln/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'secinit')
  {
    $_ = $aOptTxt{$args->{'option'}}; 
    $_ =~ s/args\{info}/$args->{'arg'}/g;
    $_ =~ s/args\{secuser}/$args->{'user'}/g;
    $_ =~ s/args\{secgrp}/$args->{'group'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'sec')
  {
    my $dest = catfile ($args->{'mountpath'},".Security");
    $dest = catfile ($dest,"backup");
    $_ = $aOptOpt1{'secinit'}; 
    $_ =~ s/args\{secfile}/$args->{'file'}/g;
    $_ =~ s/args\{user}/$args->{'user'}/g;
    $_ =~ s/args\{grp}/$args->{'group'}/g;
    $_ =~ s/args\{dest}/$dest/g;
    $string = $_;
    my  $fssec = $args->{'file'};
    my  $secgrp = $args->{'group'};
    my  $secuser = $args->{'user'};
  }
  elsif ($args->{'option'} eq 'auditinit')
  {
    $_ = $aOptTxt{$args->{'option'}}; 
    $_ =~ s/args\{info}/$args->{'arg'}/g;
    $_ =~ s/args\{mgruser}/$args->{'user'}/g;
    $_ =~ s/args\{mgrgrp}/$args->{'group'}/g;
    $_ =~ s/args\{grpditor}/$args->{'auopts'}/g;
    $string = $_;
  }
  elsif ($args->{'option'} eq 'audit')
  {
    foreach (keys %{$args->{'auopts'}})
    {
      $_ = "encr" if ($_ eq 'ENCRYPTION');
      $_ = "sec" if ($_ eq 'SECURITY');
      $string = $string . $aOptOpt1{'auditinit'};
      $string =~ s/args\{_}/$_/g;
      $string =~ s/args\{user}/$args->{'user'}/g;
      $string =~ s/args\{grp}/$args->{'group'}/g;
    }
  }
  $string =~ s/args\{mountpath}/$args->{'mountpath'}/g;
  $string =~ s/args\{prefix}/$args->{'osprefix'}/g;

  return ($string);
}

########
# NAME
#   asmcmdambr_acfs_execCmd
#
# DESCRIPTION
#   This routine will run a command with qx and
#   it will return STDOUT,STDERR and exit status.
#
# PARAMETERS
#   cmd (IN) - Command to execute.
#
# RETURNS
#   $output: returns command STDOUT and STDERR output.
#
# NOTES
#
########
sub asmcmdambr_acfs_execCmd
{
  my $command = join ' ', @_;
  my @output = qx{$command 2>&1};
  return (\@output);
}

########
# NAME
#   asmcmdambr_acfs_getOSType
#
# DESCRIPTION
#   This routine will return which OS we are running on
#   it will return OSTYPE,OSPREFIX
#
# PARAMETERS
#   none
#
# RETURNS
#   $ostype: returns the OS name in which we are running.
#   $osprefix: returns osprefix type to run acfs commands. 
#
# NOTES
#   This is needed since acfs works with OS specific commands.
#
########
sub asmcmdambr_acfs_getOSType
{
  my $osprefix;
  my $ostype = $Config{'osname'};
  $ostype = "Linux" ; # if ($Config{'osname'} =~ /linux/i   );
  $ostype = "Windows" if ($Config{'osname'} =~ /MSWin/i );
  $ostype = "Windows" if ($Config{'osname'} =~ /Windows_NT/i ); 
  $ostype = "Solaris" if ($Config{'osname'} =~ /solaris/i );
  $ostype = "Solaris" if ($Config{'osname'} =~ /SunOS/i   );
  $ostype = "AIX"     if ($Config{'osname'} =~ /aix/i   );
  $osprefix = "-"; # command line option osprefix
  $osprefix = "/" if ( $ostype eq "Windows");

  return ($ostype, $osprefix);
}

##############
# NAME
#   asmcmdshare_parse_xml_help
#
# DESCRIPTION
#   This function parses the asmcmdambr.xml file
#
# PARAMETERS
#   none
#
# RETURNS
#   none
##############
sub asmcmdambr_acfs_parse_xml
{
  $text = "" ;    # clear the text.
  # The file asmcmdambracfs.xml is parsed only if it exists.
  my @xmlfiles = ("$ENV{'ORACLE_HOME'}/rdbms" .
                  "/src/client/tools/asmcmd/modules/asmcmdambracfs.xml" );

  #set the handlers
  #  xmlStartNodeHandler -> will be called whenever a new node is started
  #  xmlTextNodeHandler  -> will be called whenever text node (multi line)
  #  xmlEndNodeHandler   -> will be called whenever a node ends.

  $xmlparser = XML::Parser->new( Handlers => {
                             Start => \&asmcmdambr_xmlStartNodeHandler,
                             Char  => \&asmcmdambr_xmlTextNodeHandler,
                             End   => \&asmcmdambr_xmlEndNodeHandler } ) ;
  # parse the xml files
  foreach (@xmlfiles)
  {
    $xmlparser->parsefile ($_) if (-r $_);
  }

  $xmlparsed = 1 ;       # to avoid parsing xml file repeatedly.
}
##############
# NAME
#   asmcmdambr_xmlStartNodeHandler
#
# DESCRIPTION
#   This function is a callback - called whenever a start-node occurs
#
# PARAMETERS
#   expat   (IN) - XML parser
#   element (IN) - Element to parse from XML
#   attrs   (IN) - Attributes from element in XML file
#
# RETURNS
#   none
##############
sub asmcmdambr_xmlStartNodeHandler
{
  my ($expat, $element, %attrs)=@_;
  # only interested in <command> nodes
  if ($element eq $optNode)
  {
    # need to have cmdName attribute
    if (defined($attrs{'name'}))
    {
      $optName = $attrs{'name'} ;
      $text = "" ;
    }
  }
}
##############
# NAME
#   asmcmdambr_xmlTextNodeHandler
#
# DESCRIPTION
#   This function is a callback - called on text node data .
#
# PARAMETERS
#   e      (IN) - Expat XML parser
#   string (IN) - Append multi lines to string
#
# RETURNS
#   none
##############
sub asmcmdambr_xmlTextNodeHandler
{
  my($e, $string)=@_;

  # in case of multi-line, this callback is called for each line,
  # append and collect all lines together.

  $text .= $string ;
}
##############
# NAME
#   asmcmdambr_xmlEndNodeHandler
#
# DESCRIPTION
#   This function is a callback  - called on endnode.
#
# PARAMETERS
#   expat   (IN) - XML parser
#   element (IN) - Element to parse from XML
#   attrs   (IN) - Attributes from element in XML file
#
# RETURNS
#   none
##############
sub asmcmdambr_xmlEndNodeHandler
{
  my ($expat, $element, %attrs)=@_;
  # Text node
  if ( $element eq "text" )
  {
    $aOptTxt{$optName} = $text ;
  }

  # Option1 command node.
  if ($element eq "opt1")
  {
    $aOptOpt1{$optName} = $text ;
  }

  # Option 2 node
  if ($element eq "opt2")
  {
    $aOptOpt2{$optName} = $text ;
  }
  # End node
  if ($element eq "end")
  {
    $aOptEnd{$optName} = $text ;
  }
  $text = "";
}

1;

OHA YOOOO