?? NEWTITLE := 'NOS/VE Device Management' ??
MODULE dmm$recover_volume;
?? RIGHT := 110 ??

{ PURPOSE:
{  The purpose of this module is to recover a mass storage volume.
{  Recovering a volume involves cleaning up any permanent files
{  left attached in write mode after the normal file recovery
{  process (if normal file recovery took place), and verifying the
{  integrity of any device files.  Last of all the mainframe being
{  recovered is logged out of the volume.

?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc mme$condition_codes
*copyc dmt$active_volume_table_index
*copyc dmt$device_file_list_index
*copyc dmt$error_condition_codes
*copyc dmt$file_descriptor_entry
*copyc dmt$ms_device_allocation_table
*copyc dmt$ms_device_file_list_entry
*copyc ost$status
?? POP ??
*copyc gff$old_file_hash
*copyc clp$put_job_output
*copyc dmp$close_file
*copyc dmp$destroy_device_file
*copyc dmp$evacuate_active_device_log
*copyc dmp$issue_message_to_ascii_log
*copyc dmp$log_abnormal_status
*copyc dmp$logout_recovered_mainframe
*copyc dmp$open_dat
*copyc dmp$open_dflt
*copyc dmp$open_directory
*copyc dmp$open_login_table
*copyc dmp$process_device_log_entry
*copyc dmp$search_login_table
*copyc dmp$search_vol_directory_name
*copyc mmp$write_modified_pages
*copyc osp$disestablish_cond_handler
*copyc osp$establish_condition_handler
*copyc osp$set_status_abnormal
*copyc pmp$continue_to_cause
*copyc pmp$convert_binary_unique_name
*copyc dmv$active_volume_table
*copyc dmv$display_recovery_messages
?? TITLE := '  dmp$recover_volume', EJECT ??

  PROCEDURE [XDCL] dmp$recover_volume
    (    avt_index: dmt$active_volume_table_index;
     VAR status: ost$status);

    VAR
      p_login_table: ^dmt$ms_mainframe_login_table,
      lt_entry_index: dmt$login_table_entries,
      status_p: ^ost$status;

    dmp$open_login_table (dmv$active_volume_table.table_p^ [avt_index].mass_storage.p_login_table,
          osc$os_ring_1, osc$tsrv_ring, mmc$sar_read, mmc$as_sequential, p_login_table, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

  /login_table_open/
    BEGIN

      dmp$search_login_table (p_login_table, dmc$production_login_entry, lt_entry_index);

      IF (lt_entry_index = 0) OR (lt_entry_index = dmv$active_volume_table.table_p^ [avt_index].
            mass_storage.mainframe_assigned.log_in_index) THEN
        EXIT /login_table_open/; {----->
      IFEND;

      dmp$evacuate_active_device_log (avt_index, status);
      IF NOT status.normal THEN
        EXIT /login_table_open/; {----->
      IFEND;

      cleanup_volume_files (avt_index, p_login_table^.body [lt_entry_index].mainframe_assigned, status);
      IF NOT status.normal THEN
        EXIT /login_table_open/; {----->
      IFEND;

      logout_mf_being_recovered (p_login_table, avt_index, lt_entry_index, status);
    END /login_table_open/;

    IF status.normal THEN
      status_p := ^status;
    ELSE
      PUSH status_p;
    IFEND;

    dmp$close_file (p_login_table, status_p^);

  PROCEND dmp$recover_volume;
?? TITLE := '  cleanup_volume_files', EJECT ??

  PROCEDURE cleanup_volume_files
    (    avt_index: dmt$active_volume_table_index;
         mainframe_assigned: dmt$mainframe_assigned;
     VAR status: ost$status);

    VAR
      dfl_index: dmt$device_file_list_index,
      p_dflt: ^dmt$ms_device_file_list_table,
      status_p: ^ost$status;

?? NEWTITLE := 'DFL_CONDITION_HANDLER', EJECT ??

    PROCEDURE dfl_condition_handler
      (    condition: pmt$condition;
           condition_descriptor: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR status: ost$status);

      VAR
        error_message: string (140),
        ignore_status: ost$status,
        message_length: integer,
        message_line1: string (70),
        message_line2: string (70),
        p_sac: ^mmt$segment_access_condition;

      IF condition.selector = mmc$segment_access_condition THEN
        CASE condition.segment_access_condition.identifier OF
        = mmc$sac_io_read_error =
          STRINGREP (message_line1, message_length, ' *** IO ERROR ON SYSTEM FILE TABLE - DFL - FOR ',
                dmv$active_volume_table.table_p^ [avt_index].mass_storage.recorded_vsn);
          clp$put_job_output (message_line1 (1, message_length), ignore_status);
          STRINGREP (message_line2, message_length, ' DFL_INDEX= ', dfl_index, ' - cleanup_volume_files ***');
          clp$put_job_output (message_line2 (1, message_length), ignore_status);
          STRINGREP (error_message, message_length, message_line1, message_line2);
          osp$set_status_abnormal ('MM', mme$io_read_error, error_message (1, message_length), status);
          dmp$log_abnormal_status (status);
          dmp$close_file (p_dflt, ignore_status);
          status.normal := TRUE;
          EXIT cleanup_volume_files; {----->
        ELSE
        CASEND;
      IFEND;

      pmp$continue_to_cause (pmc$execute_standard_procedure, status);

    PROCEND dfl_condition_handler;
?? OLDTITLE ??
?? NEWTITLE := 'P$LOG_MESSAGE', EJECT ??

    PROCEDURE p$log_message
      (    dfl_index: dmt$device_file_list_index;
           device_file: boolean;
       VAR status: ost$status);

      VAR
        character_string_length: integer,
        current_msg_position: integer,
        msg_string: string (70),
        status_p: ^ost$status;

      current_msg_position := 1;
      msg_string (current_msg_position, * ) := ' VSN - ';
      current_msg_position := current_msg_position + 7;
      msg_string (current_msg_position, * ) := dmv$active_volume_table.table_p^ [avt_index].mass_storage.
            recorded_vsn;
      current_msg_position := current_msg_position + STRLENGTH (dmv$active_volume_table.table_p^ [avt_index].
            mass_storage.recorded_vsn) + 1;
      msg_string (current_msg_position, * ) := 'UNABLE TO CLEANUP DEVICE FILE INDEX - ';
      current_msg_position := current_msg_position + 38 + 1;
      STRINGREP (msg_string (current_msg_position, * ), character_string_length, dfl_index);

      IF device_file THEN
        dmp$log_abnormal_status (status);
        osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_cleanup_file,
              'unable to cleanup device file - cleanup_volume_files', status);
      ELSE { gfc$fk_job_permanent_file, gfc$fk_catalog
        dmp$log_abnormal_status (status);
        status.normal := TRUE;
      IFEND;
      dmp$issue_message_to_ascii_log (msg_string, dmv$display_recovery_messages);

    PROCEND p$log_message;
?? OLDTITLE ??
?? EJECT ??

    dmp$open_dflt (dmv$active_volume_table.table_p^ [avt_index].mass_storage.p_device_file_list_table,
          osc$os_ring_1, osc$tsrv_ring, mmc$sar_read, mmc$as_sequential, p_dflt, status);
    IF NOT status.normal THEN
      dmp$log_abnormal_status (status);
      osp$set_status_abnormal (dmc$device_manager_ident, dme$open_dflt_failure,
            'unable to open device file list - cleanup_volume_files', status);
      RETURN; {----->
    IFEND;

    osp$establish_condition_handler (^dfl_condition_handler, FALSE);

  /dflt_open/
    FOR dfl_index := 1 TO UPPERBOUND (p_dflt^.entries) DO
      IF p_dflt^.entries [dfl_index].flags = dmc$dfle_assigned_to_file THEN
        CASE p_dflt^.entries [dfl_index].file_kind OF
        = gfc$fk_device_file =
          cleanup_device_file (avt_index, ^p_dflt^.entries [dfl_index], dfl_index, status);
          IF NOT status.normal THEN
            p$log_message (dfl_index, TRUE {device file} , status);
            EXIT /dflt_open/; {----->
          IFEND;

        = gfc$fk_job_permanent_file, gfc$fk_catalog =
          IF mainframe_assigned.log_in_index IN p_dflt^.entries [dfl_index].login_set THEN
            cleanup_permanent_file (avt_index, ^p_dflt^.entries [dfl_index], dfl_index, mainframe_assigned,
                  status);
            IF NOT status.normal THEN
              p$log_message (dfl_index, FALSE {perm file / catalog} , status);
            IFEND;
          IFEND;
        ELSE
          osp$set_status_abnormal (dmc$device_manager_ident, dme$unrecognizable_file_type,
                'dmp$recover_volume', status);
          EXIT /dflt_open/; {----->
        CASEND;
      IFEND;
    FOREND /dflt_open/;

    osp$disestablish_cond_handler;

    IF status.normal THEN
      status_p := ^status;
    ELSE
      PUSH status_p;
    IFEND;

    dmp$close_file (p_dflt, status_p^);

  PROCEND cleanup_volume_files;
?? TITLE := '  cleanup_device_file', EJECT ??

  PROCEDURE cleanup_device_file
    (    avt_index: dmt$active_volume_table_index;
         p_dfl_entry: ^dmt$ms_device_file_list_entry;
         dfl_index: dmt$device_file_list_index;
     VAR status: ost$status);

    VAR
      directory_entry_index: dmt$directory_index,
      directory_index: dmt$directory_index,
      log_entry: dmt$dl_entry,
      p_directory: ^dmt$ms_volume_directory,
      status_p: ^ost$status;

    dmp$open_directory (dmv$active_volume_table.table_p^ [avt_index].mass_storage.p_directory, osc$os_ring_1,
          osc$tsrv_ring, mmc$sar_read, mmc$as_sequential, p_directory, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    directory_entry_index := 0;

  /search_directory/
    FOR directory_index := 1 TO UPPERBOUND (p_directory^.entries) DO
      IF NOT p_directory^.entries [directory_index].entry_available THEN
        IF p_dfl_entry^.global_file_name = p_directory^.entries [directory_index].global_file_name THEN
          directory_entry_index := directory_index;
          EXIT /search_directory/; {----->
        IFEND;
      IFEND;
    FOREND /search_directory/;

    IF directory_entry_index = 0 THEN
      log_entry.kind := dmc$dl_purge_file;
      log_entry.purge_file_block.global_file_name := p_dfl_entry^.global_file_name;
      log_entry.purge_file_block.dfl_index := dfl_index;
      log_entry.purge_file_block.file_byte_address := p_dfl_entry^.file_byte_address;

      dmp$process_device_log_entry (avt_index, log_entry, status);
    IFEND;

    IF status.normal THEN
      status_p := ^status;
    ELSE
      PUSH status_p;
    IFEND;

    dmp$close_file (p_directory, status_p^);

  PROCEND cleanup_device_file;
?? TITLE := '  cleanup_permanent_file', EJECT ??

  PROCEDURE cleanup_permanent_file
    (    avt_index: dmt$active_volume_table_index;
         p_dfl_entry: ^dmt$ms_device_file_list_entry;
         dfl_index: dmt$device_file_list_index;
         mainframe_assigned: dmt$mainframe_assigned;
     VAR status: ost$status);

    VAR
      allocated_length: amt$file_byte_address,
      ignore_status: ost$status,
      initialized_length: amt$file_byte_address,
      log_entry: dmt$dl_entry,
      msg_string: string (75),
      msg_string_length: integer,
      unique_name: ost$name;

    dmp$retrieve_allocation_info (p_dfl_entry, avt_index, allocated_length, initialized_length, status);

    IF status.normal THEN
      log_entry.kind := dmc$dl_update_file_length;
      log_entry.file_length_block.global_file_name := p_dfl_entry^.global_file_name;
      log_entry.file_length_block.dfl_index := dfl_index;

      log_entry.file_length_block.eof_specified := TRUE;
      log_entry.file_length_block.eof := initialized_length;

      {amc$file_byte_limit is used to flag this file as having gone
      {through a recovery without image while attached in write mode.
      {dmp$build_fat_for_existing_file will detect this and fixup both
      {eof and eoi to reflect the maximum initialized length for all subfiles.

      log_entry.file_length_block.eoi_specified := TRUE;
      log_entry.file_length_block.eoi := amc$file_byte_limit;

      dmp$process_device_log_entry (avt_index, log_entry, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

      log_entry.kind := dmc$dl_update_fmd_length;
      log_entry.fmd_length_block.global_file_name := p_dfl_entry^.global_file_name;
      log_entry.fmd_length_block.dfl_index := dfl_index;

      log_entry.fmd_length_block.fmd_length_specified := TRUE;
      log_entry.fmd_length_block.fmd_length := allocated_length;

      log_entry.fmd_length_block.logical_length_specified := TRUE;
      log_entry.fmd_length_block.logical_length := initialized_length;

      dmp$process_device_log_entry (avt_index, log_entry, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

    ELSE

      dmp$log_abnormal_status (status);
      pmp$convert_binary_unique_name (p_dfl_entry^.global_file_name, unique_name, ignore_status);
      STRINGREP (msg_string, msg_string_length, ' VSN - ', dmv$active_volume_table.table_p^ [avt_index].
            mass_storage.recorded_vsn, ' DFL INDEX - ', dfl_index, ' GFN - ', unique_name);
      dmp$issue_message_to_ascii_log (msg_string (1, msg_string_length), dmv$display_recovery_messages);

      log_entry.kind := dmc$dl_file_damaged;
      log_entry.file_damaged_block.global_file_name := p_dfl_entry^.global_file_name;
      log_entry.file_damaged_block.dfl_index := dfl_index;
      log_entry.file_damaged_block.add_damage := $dmt$file_damage [dmc$allocation_chain_broken];
      log_entry.file_damaged_block.remove_damage := $dmt$file_damage [];

      dmp$process_device_log_entry (avt_index, log_entry, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;
    IFEND;

    log_entry.kind := dmc$dl_detach_file;
    log_entry.attach_file_block.global_file_name := p_dfl_entry^.global_file_name;
    log_entry.attach_file_block.dfl_index := dfl_index;
    log_entry.attach_file_block.mainframe_assigned := mainframe_assigned;

    dmp$process_device_log_entry (avt_index, log_entry, status);

  PROCEND cleanup_permanent_file;
?? TITLE := '  logout_mf_being_recovered', EJECT ??

  PROCEDURE logout_mf_being_recovered
    (    p_login_table: ^dmt$ms_mainframe_login_table;
         avt_index: dmt$active_volume_table_index;
         lt_entry_index: dmt$login_table_entries;
     VAR status: ost$status);

    dmp$logout_recovered_mainframe (avt_index, lt_entry_index, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    dmp$evacuate_active_device_log (avt_index, status);

  PROCEND logout_mf_being_recovered;
?? TITLE := '  dmp$retrieve_allocation_info', EJECT ??

  PROCEDURE [XDCL] dmp$retrieve_allocation_info
    (    p_dfl_entry: ^dmt$ms_device_file_list_entry;
         avt_index: dmt$active_volume_table_index;
     VAR allocated_length: amt$file_byte_address;
     VAR initialized_length: amt$file_byte_address;
     VAR status: ost$status);

    VAR
      bytes_per_au: amt$file_byte_address,
      current_allocation_unit_dau: dmt$dau_address,
      entry_p: ^dmt$ms_device_allocation_unit,
      p_dat: ^dmt$ms_device_allocation_table,
      status_p: ^ost$status;

    status.normal := TRUE;
    allocated_length := 0;
    initialized_length := 0;

    IF p_dfl_entry^.dau_chain_status = dmc$dau_chain_not_linked THEN
      RETURN; {----->
    IFEND;

    dmp$open_dat (dmv$active_volume_table.table_p^ [avt_index].mass_storage.p_device_allocation_table,
          osc$os_ring_1, osc$tsrv_ring, mmc$sar_read, mmc$as_sequential, p_dat, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    current_allocation_unit_dau := p_dfl_entry^.first_dau_address;
    bytes_per_au := p_dfl_entry^.daus_per_allocation_unit * p_dat^.header.bytes_per_dau;

  /dat_open/
    WHILE TRUE DO

      entry_p := ^p_dat^.body [current_allocation_unit_dau];
      IF (entry_p^.dau_status <> dmc$dau_assigned_to_file) AND
            (entry_p^.dau_status <> dmc$dau_ass_to_file_swr_flawed) THEN
        osp$set_status_abnormal (dmc$device_manager_ident, dme$premature_end_of_dat_chain,
              'dmp$retrieve_allocation_info - dau not assigned file', status);
        EXIT /dat_open/; {----->
      IFEND;

      IF (entry_p^.file_hash <> p_dfl_entry^.file_hash)
{   } AND (gff$old_file_hash (entry_p^.file_hash) <> gff$old_file_hash (p_dfl_entry^.file_hash)) THEN

{Can the DFL Hash be newer than the DAT?

        osp$set_status_abnormal (dmc$device_manager_ident, dme$crossed_allocation,
              'dmp$retrieve_allocation_info - dau/dfl disagree on file hash', status);
        EXIT /dat_open/; {----->
      IFEND;

      allocated_length := allocated_length + bytes_per_au;

      IF entry_p^.data_status = dmc$dau_data_initialized THEN
        initialized_length := allocated_length;
      IFEND;

      IF (entry_p^.allocation_chain_position = dmc$first_and_last_allocation) OR
            (entry_p^.allocation_chain_position = dmc$last_allocation) THEN
        EXIT /dat_open/; {----->
      IFEND;

      current_allocation_unit_dau := entry_p^.next_allocation_unit_dau;

    WHILEND /dat_open/;

    IF status.normal THEN
      status_p := ^status;
    ELSE
      PUSH status_p;
    IFEND;

    dmp$close_file (p_dat, status_p^);

  PROCEND dmp$retrieve_allocation_info;
?? OLDTITLE ??
MODEND dmm$recover_volume;
