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

{
{ PURPOSE:
{  The purpose of this module is to recover the contents of a file, and
{  update Device Management structures to reflect the state of the recovered
{  file.
{ DESIGN:
{  A file is recovered by the following transitions:
{    1)  The file is attached causing a file table entry and other substructures
{        to be created.  These tables reflect the current state of the file
{        as it is known on the mass storage devices.
{    2)  The file table entry is retrieved.
{    3)  The file table entry and its substructures are updated to reflect the
{        more current information residing in the old memory image file table
{        entry.
{    4)  The file is opened.
{    5)  Pages belonging to the file in the old memory image are copied to the
{        file.
{    6)  The file is closed.
{    7)  The file table entry lock is cleared.
{    8)  The file is detached.  Detaching a file causes device tables to be
{        updated from the file table entry and its substructures.
{
?? NEWTITLE := '  Declarations' ??
?? PUSH (LISTEXT := ON) ??
*copyc oss$job_paged_literal
*copyc amt$file_limit
*copyc osd$virtual_address
*copyc amt$access_level
*copyc amt$attribute_source
*copyc amt$average_record_length
*copyc amt$block_type
*copyc amt$collate_table
*copyc amt$collation_value
*copyc amt$data_padding
*copyc amt$error_exit_procedure
*copyc amt$error_limit
*copyc amt$estimated_record_count
*copyc amt$file_access_selections
*copyc amt$file_attribute_keys
*copyc amt$file_attributes
*copyc amt$file_byte_address
*copyc amt$file_identifier
*copyc amt$file_length
*copyc amt$file_limit
*copyc amt$file_organization
*copyc amt$file_position
*copyc amt$forced_write
*copyc amt$global_file_position
*copyc amt$index_padding
*copyc amt$internal_code
*copyc amt$key_length
*copyc amt$key_position
*copyc amt$key_type
*copyc amt$label_exit_procedure
*copyc amt$label_options
*copyc amt$label_type
*copyc amt$local_file_name
*copyc amc$mau_length
*copyc amt$max_block_length
*copyc amt$max_record_length
*copyc amt$message_control
*copyc amt$min_block_length
*copyc amt$min_record_length
*copyc amt$padding_character
*copyc amt$record_limit
*copyc amt$record_type
*copyc amt$records_per_block
*copyc amt$return_option
*copyc amt$user_info
*copyc amt$vertical_print_density
*copyc ost$status
*copyc dmt$device_log_entries
*copyc dmt$directory_index
*copyc dmt$error_condition_codes
*copyc dmt$file_medium_descriptor
*copyc dmt$file_share_history
*copyc dmt$fmd_index
*copyc dmt$global_file_name
*copyc dmt$ms_device_allocation_table
*copyc dmt$ms_volume_directory
*copyc dmt$sparse_allocation
*copyc dmt$stored_fmd
*copyc dmt$stored_fmd_size
*copyc dmt$stored_ms_fmd_header
*copyc gft$file_desc_entry_p
*copyc gft$system_file_identifier
*copyc mmt$image_page_description
*copyc ost$name
*copyc ost$status
?? POP ??
*copyc i#ptr
*copyc dmp$allocate_file_space_r1
*copyc dmp$attach_device_file
*copyc dmp$attach_file
*copyc dmp$build_stored_fmd
*copyc dmp$change_dfl_damage
*copyc dmp$close_file
*copyc dmp$delete_file_descriptor
*copyc dmp$detach_device_file
*copyc dmp$detach_file
*copyc dmp$get_disk_file_descriptor_p
*copyc dmp$get_fmd_by_index
*copyc dmp$get_total_allocated_length
*copyc dmp$issue_message_to_ascii_log
*copyc dmp$open_directory
*copyc dmp$open_file
*copyc dmp$open_login_table
*copyc dmp$process_device_log_entry
*copyc dmp$search_avt_by_vsn
*copyc dmp$search_login_table
*copyc dmp$set_eoi
*copyc gfp$get_fde_p
*copyc lgp$add_entry_to_system_log
*copyc mmp$advise_in
*copyc mmp$advise_out
*copyc mmp$fetch_image_page_count
*copyc mmp$fetch_pvas_of_image_pages
*copyc mmp$write_modified_pages
*copyc osp$get_status_condition_string
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc pmp$binary_to_ascii_fit
*copyc pmp$get_legible_date_time
*copyc dmv$active_volume_table
*copyc dmv$display_recovery_messages
*copyc dmv$ds_msg_update_interval
*copyc osv$task_private_heap
*copyc i#move

  VAR
    dmv$file_recovery_version: [XREF] 0 .. 0ff(16);

  CONST
    c$number_logged_vsns = 4;

  TYPE
    dmt$recover_file_trace = record
      call_count: integer,
      data: dmt$recover_file_trace_data,
    recend,

    dmt$recover_file_trace_data = record
      file_entry_index: gft$file_descriptor_index,
      file_kind: gft$file_kind,
      global_file_name: ost$binary_unique_name,
      user_supplied_name: ost$name,
      p_fde_old: ^gft$file_descriptor_entry,
      p_fde_new: ^gft$file_descriptor_entry,
      p_dfd_old: ^dmt$disk_file_descriptor,
      eoi_updated: boolean,
      eoi_old: amt$file_byte_address,
      eoi_new: amt$file_byte_address,
      vsn_list_shorted: boolean,
      vsn_list: string (c$number_logged_vsns * rmc$recorded_vsn_size),
      image_page_count: integer,
      valid_desc_count: 0 .. 7ffffffff(16),
      pages_written: integer,
      highest_file_offset: ost$segment_offset,
      allocated_length_1: amt$file_byte_address,
      allocated_length_2: amt$file_byte_address,
      from_p: ^cell,
      to_p: ^cell,
    recend;

  VAR
    dmv$recover_file_trace_p: [XDCL, #GATE, oss$job_pageable] ^dmt$recover_file_trace := NIL,
    v$null_recover_file_trace_data: [READ, oss$job_paged_literal] dmt$recover_file_trace_data := [
{ FILE_ENTRY_INDEX           } 0,
{ FILE_KIND                  } gfc$fk_save_2,
{ GLOBAL_FILE_NAME           } [0, 0, 1980, 1, 1, 0, 0, 0, 0, 0],
{ USER_SUPPLIED_NAME         } '',
{ P_FDE_OLD                  } NIL,
{ P_FDE_NEW                  } NIL,
{ P_DFD_OLD                  } NIL,
{ EOI_UPDATED                } FALSE,
{ EOI_OLD                    } 0,
{ EOI_NEW                    } 0,
{ VSN_LIST_SHORTED           } FALSE,
{ VSN_LIST                   } '',
{ IMAGE_PAGE_COUNT           } 0,
{ VALID_DESC_COUNT           } 0,
{ PAGES_WRITTEN              } 0,
{ HIGHEST_FILE_OFFSET        } 0,
{ ALLOCATED_LENGTH_1         } 0,
{ ALLOCATED_LENGTH_2         } 0,
{ FROM_P                     } NIL,
{ TO_P                       } NIL];

?? TITLE := '  dmp$recover_file', EJECT ??

  PROCEDURE [XDCL, #GATE] dmp$recover_file
    (    p_old_fde: gft$file_desc_entry_p;
     VAR pages_written: integer;
     VAR status: ost$status);

    VAR
      file_entry_index: gft$file_descriptor_index,
      file_modified: boolean,
      fmd_modified: boolean,
      ignore_file_info: dmt$file_information,
      ijl_ordinal: jmt$ijl_ordinal,
      local_status: ost$status,
      p_dfd: ^dmt$disk_file_descriptor,
      p_fde: gft$file_desc_entry_p,
      p_file: ^cell,
      p_fmd: ^dmt$file_medium_descriptor,
      remove_dfl_damage: boolean,
      system_file_id: gft$system_file_identifier;

    status.normal := TRUE;
    pages_written := 0;

    IF dmv$recover_file_trace_p = NIL THEN
      ALLOCATE dmv$recover_file_trace_p IN osv$task_private_heap^;
      dmv$recover_file_trace_p^.call_count := 0;
    IFEND;

    dmv$recover_file_trace_p^.data := v$null_recover_file_trace_data;
    dmv$recover_file_trace_p^.call_count := dmv$recover_file_trace_p^.call_count + 1;
    dmv$recover_file_trace_p^.data.p_fde_old := p_old_fde;

{ !!!I believe the following calculation is OK--if the size or table base has changed the
{ system will not be recovering.

    file_entry_index := ((#OFFSET (p_old_fde)) - gfc$fde_table_base) DIV gfc$fde_size;
    dmv$recover_file_trace_p^.data.file_entry_index := file_entry_index;
    dmv$recover_file_trace_p^.data.file_kind := p_old_fde^.file_kind;

  /recover_file/
    BEGIN
      CASE p_old_fde^.file_kind OF
      = gfc$fk_job_permanent_file, gfc$fk_catalog =
        attach_pf_for_recovery (p_old_fde, file_entry_index, system_file_id, status);

      = gfc$fk_device_file =
        attach_df_for_recovery (p_old_fde, file_entry_index, system_file_id, status);
      ELSE
        EXIT /recover_file/; {----->
      CASEND;

      IF NOT status.normal THEN
        EXIT /recover_file/; {----->
      IFEND;

      gfp$get_fde_p (system_file_id, p_fde);
      dmv$recover_file_trace_p^.data.p_fde_new := p_fde;
      IF p_fde <> NIL THEN
        fixup_file_descriptor_entry (system_file_id, p_old_fde, p_fde, status);
      ELSE
        osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_locate_fde,
              'Bad SFID - dmp$recover_file.', status);
      IFEND;

      IF status.normal THEN
        open_file_for_recovery (system_file_id, p_file, status);

        IF status.normal THEN
          update_file_image (system_file_id, p_old_fde, p_fde, p_file, pages_written, status);

          dmp$close_file (p_file, local_status);
          IF (NOT local_status.normal) AND status.normal THEN
            status := local_status;
          IFEND;
        IFEND;
      IFEND;

      CASE p_old_fde^.file_kind OF

      = gfc$fk_job_permanent_file, gfc$fk_catalog =
        dmp$detach_file (system_file_id, {access_allowed} TRUE, {flush_pages} TRUE, dmc$df_ignore_file_info,
              file_modified, fmd_modified, ignore_file_info, local_status);
        IF local_status.normal THEN
          dmp$get_disk_file_descriptor_p (p_old_fde, p_dfd);
          IF p_dfd^.damaged_detection_enabled AND NOT p_dfd^.file_damaged AND (status.normal = TRUE) THEN
            remove_dfl_damage := p_old_fde^.queue_status = gfc$qs_global_shared;
            IF remove_dfl_damage THEN
              IF p_dfd <> NIL THEN
                dmp$get_fmd_by_index (p_dfd, 1, p_fmd);
                IF (p_fmd <> NIL) AND p_fmd^.in_use AND p_fmd^.volume_assigned THEN
                  dmp$change_dfl_damage (p_fmd^.avt_index, $dmt$file_damage [],
                        $dmt$file_damage [dmc$media_image_inconsistent], p_fmd^.dfl_index, FALSE,
                        p_fde^.global_file_name, local_status);
                IFEND;
              IFEND;
            IFEND;
          IFEND;
          dmp$delete_file_descriptor (system_file_id, local_status);
        IFEND;

      = gfc$fk_device_file =
        dmp$detach_device_file (system_file_id, file_modified, fmd_modified, local_status);
      ELSE
        EXIT /recover_file/; {----->
      CASEND;

      IF status.normal AND NOT local_status.normal THEN
        status := local_status;
      IFEND;

      IF status.normal THEN
        detach_old_file (p_old_fde, status);
      IFEND;
    END /recover_file/;

  PROCEND dmp$recover_file;
?? TITLE := '  attach_df_for_recovery', EJECT ??

  PROCEDURE attach_df_for_recovery
    (    p_old_fde: gft$file_desc_entry_p;
         file_entry_index: gft$file_descriptor_index;
     VAR system_file_id: gft$system_file_identifier;
     VAR status: ost$status);

    VAR
      avt_entry_found: boolean,
      avt_index: dmt$active_volume_table_index,
      close_status: ost$status,
      directory_entry_index: dmt$directory_index,
      directory_index: dmt$directory_index,
      directory_sfid: gft$system_file_identifier,
      fmd_index: dmt$fmd_index,
      p_old_dfd: ^dmt$disk_file_descriptor,
      p_directory: ^dmt$ms_volume_directory,
      p_fmd: ^dmt$file_medium_descriptor,
      global_file_name: dmt$global_file_name,
      user_supplied_name: ost$name,
      volume_assigned: boolean;

    status.normal := TRUE;

    dmp$get_disk_file_descriptor_p (p_old_fde, p_old_dfd);
    dmv$recover_file_trace_p^.data.p_dfd_old := p_old_dfd;

    fmd_index := 1;

    IF p_old_dfd <> NIL THEN
      dmp$get_fmd_by_index (p_old_dfd, fmd_index, p_fmd);
      volume_assigned := p_fmd^.in_use AND p_fmd^.volume_assigned;
    IFEND;

    IF (p_old_dfd = NIL) OR NOT volume_assigned THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$rf_no_vol_assigned_to_file, 'dmp$recover_file',
            status);
      RETURN; {----->
    IFEND;

    dmp$search_avt_by_vsn (p_fmd^.internal_vsn, avt_index, avt_entry_found);
    IF NOT avt_entry_found THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$volume_not_online, 'dmp$recover_file', status);
      RETURN; {----->
    IFEND;

    directory_sfid := dmv$active_volume_table.table_p^ [avt_index].mass_storage.p_directory;

    dmp$open_directory (directory_sfid, osc$os_ring_1, osc$tsrv_ring, mmc$sar_read, mmc$as_sequential,
          p_directory, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

  /directory_open/
    BEGIN

      global_file_name := p_old_fde^.global_file_name;
      dmv$recover_file_trace_p^.data.global_file_name := global_file_name;
      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_directory^.entries [directory_index].global_file_name = global_file_name THEN
            directory_entry_index := directory_index;
            EXIT /search_directory/; {----->
          IFEND;
        IFEND;
      FOREND /search_directory/;

      IF directory_entry_index = 0 THEN
        osp$set_status_abnormal (dmc$device_manager_ident, dme$unknown_device_file, 'dmp$recover_file',
              status);
        EXIT /directory_open/; {----->
      IFEND;

      user_supplied_name := p_directory^.entries [directory_entry_index].user_supplied_name;
      dmv$recover_file_trace_p^.data.user_supplied_name := user_supplied_name;

      dmv$recover_file_trace_p^.data.vsn_list := dmv$active_volume_table.table_p^ [avt_index].mass_storage.
            recorded_vsn;
      dmp$attach_device_file (dmv$active_volume_table.table_p^ [avt_index].mass_storage.recorded_vsn,
            user_supplied_name, system_file_id, status);
      IF NOT status.normal THEN
        osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_for_rec,
              'unable to attach file for rec - DMMRECF', status);
        EXIT /directory_open/; {----->
      IFEND;

    END /directory_open/;

    dmp$close_file (p_directory, close_status);
    IF status.normal THEN
      status := close_status;
    IFEND;

  PROCEND attach_df_for_recovery;
?? TITLE := '  attach_pf_for_recovery', EJECT ??

  PROCEDURE attach_pf_for_recovery
    (    p_old_fde: gft$file_desc_entry_p,
         file_entry_index: gft$file_descriptor_index;
     VAR system_file_id: gft$system_file_identifier;
     VAR status: ost$status);

    VAR
      avt_index: dmt$active_volume_table_index,
      existing_sft_entry: dmt$existing_sft_entry,
      fmd_index: dmt$fmd_index,
      file_damaged: boolean,
      file_share_selections: pft$share_selections,
      file_share_history: dmt$file_share_history,
      file_usage: pft$usage_selections,
      found: boolean,
      ignore_status: ost$status,
      length: integer,
      log_status: ost$status,
      log_time: ost$time,
      message: ost$string,
      number_of_fmds: dmt$fmd_index,
      old_system_file_id: gft$system_file_identifier,
      p_old_dfd: ^dmt$disk_file_descriptor,
      p_fmd: ^dmt$file_medium_descriptor,
      p_stored_fmd: ^dmt$stored_fmd,
      shared_queue: mmt$page_frame_queue_id,
      size_of_stored_fmd: dmt$stored_fmd_size,
      volume_assigned: boolean;

    status.normal := TRUE;

    dmp$get_disk_file_descriptor_p (p_old_fde, p_old_dfd);
    dmv$recover_file_trace_p^.data.p_dfd_old := p_old_dfd;

    volume_assigned := FALSE;

    IF p_old_dfd <> NIL THEN
      number_of_fmds := p_old_dfd^.number_of_fmds;
      dmv$recover_file_trace_p^.data.vsn_list_shorted := number_of_fmds > c$number_logged_vsns;

      FOR fmd_index := 1 TO number_of_fmds DO
        dmp$get_fmd_by_index (p_old_dfd, fmd_index, p_fmd);
        IF p_fmd^.in_use AND p_fmd^.volume_assigned THEN
          volume_assigned := TRUE;
          dmp$search_avt_by_vsn (p_fmd^.internal_vsn, avt_index, found);
          IF NOT found THEN
            osp$set_status_abnormal (dmc$device_manager_ident, dme$volume_not_online,
                  'volume not online - DMMRECF', status);
            RETURN; {----->
          ELSEIF fmd_index <= c$number_logged_vsns THEN
            dmv$recover_file_trace_p^.data.vsn_list (((fmd_index - 1) * rmc$recorded_vsn_size) + 1,
                  rmc$recorded_vsn_size) := dmv$active_volume_table.table_p^ [avt_index].mass_storage.
                  recorded_vsn;
          IFEND;
        IFEND;
      FOREND;
    IFEND;

    IF (p_old_dfd = NIL) OR (NOT volume_assigned) THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$rf_no_vol_assigned_to_file, 'dmp$recover_file',
            status);
      RETURN; {----->
    IFEND;

    old_system_file_id.file_hash := p_old_fde^.file_hash;
    old_system_file_id.file_entry_index := file_entry_index;
    old_system_file_id.residence := gfc$tr_system;

    size_of_stored_fmd := #SIZE (dmt$stored_ms_version_number) + #SIZE (dmt$stored_ms_fmd_header) +
          number_of_fmds * #SIZE (dmt$stored_ms_fmd_subfile);

    PUSH p_stored_fmd: [[REP size_of_stored_fmd OF cell]];

    dmp$build_stored_fmd (p_old_fde, p_stored_fmd, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    RESET p_stored_fmd;

    file_usage := $pft$usage_selections [pfc$read, pfc$modify, pfc$append, pfc$shorten];
    file_share_selections := $pft$share_selections [];
    file_share_history := dmc$minimum_file_share_his;

    IF p_old_fde^.queue_ordinal > mmc$pq_shared_last_sys THEN
      shared_queue := p_old_fde^.queue_ordinal - mmc$pq_shared_last_sys;
    ELSE
      shared_queue := mmc$null_shared_queue;
    IFEND;

    dmv$recover_file_trace_p^.data.global_file_name := p_old_fde^.global_file_name;
    dmp$attach_file (p_old_fde^.global_file_name, p_old_fde^.file_kind, p_stored_fmd^, file_usage,
          file_share_selections, file_share_history, p_old_fde^.file_limit, {restricted_attach} FALSE,
          {exit_on_unknown_file} FALSE, {server_file} FALSE, shared_queue, file_damaged, system_file_id,
          existing_sft_entry, status);
    IF NOT status.normal THEN
      lgp$add_entry_to_system_log (pmc$msg_origin_recovery, 'DM - ABNORMAL ATTACH DURING RECOVERY', log_time,
            log_status);
      osp$get_status_condition_string (status.condition, message, ignore_status);
      lgp$add_entry_to_system_log (pmc$msg_origin_recovery, message.value (1, message.size), log_time,
            log_status);
      osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_for_rec,
            'unable to attach file for rec - DMMRECF', status);
      RETURN; {----->
    IFEND;

  PROCEND attach_pf_for_recovery;
?? TITLE := '  detach_old_file', EJECT ??

  PROCEDURE detach_old_file
    (    p_fde: gft$file_desc_entry_p;
     VAR status: ost$status);

    VAR
      avt_index: dmt$active_volume_table_index,
      log_entry: dmt$dl_entry,
      mainframe_assigned: dmt$mainframe_assigned,
      p_dfd: ^dmt$disk_file_descriptor,
      p_fmd: ^dmt$file_medium_descriptor;

    status.normal := TRUE;

    dmp$get_disk_file_descriptor_p (p_fde, p_dfd);

    dmp$get_fmd_by_index (p_dfd, 1, p_fmd);

    IF (p_fmd <> NIL) THEN
      avt_index := p_fmd^.avt_index;

      IF dmv$active_volume_table.table_p^ [avt_index].mass_storage.logged_in_for_recovery THEN
        get_mainframe_assigned (avt_index, mainframe_assigned, status);

        IF status.normal THEN
          log_entry.kind := dmc$dl_detach_file;
          log_entry.attach_file_block.global_file_name := p_fde^.global_file_name;
          log_entry.attach_file_block.dfl_index := p_fmd^.dfl_index;
          log_entry.attach_file_block.mainframe_assigned := mainframe_assigned;

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

  PROCEND detach_old_file;
?? TITLE := '  fixup_file_descriptor_entry', EJECT ??

  PROCEDURE fixup_file_descriptor_entry
    (    system_file_id: gft$system_file_identifier;
         p_old_fde: gft$file_desc_entry_p;
         p_fde: gft$file_desc_entry_p;
     VAR status: ost$status);


    status.normal := TRUE;
    IF p_old_fde^.file_kind <> p_fde^.file_kind THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$rf_fde_file_type, '', status);
      RETURN; {----->
    IFEND;

    IF p_old_fde^.global_file_name <> p_fde^.global_file_name THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$rf_fde_gfn, '', status);
      RETURN; {----->
    IFEND;

    IF p_old_fde^.preset_value <> p_fde^.preset_value THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$rf_fde_preset, '', status);
      RETURN; {----->
    IFEND;

    dmv$recover_file_trace_p^.data.eoi_updated := p_old_fde^.eoi_byte_address <> p_fde^.eoi_byte_address;
    IF p_old_fde^.eoi_byte_address <> p_fde^.eoi_byte_address THEN
      dmv$recover_file_trace_p^.data.eoi_old := p_old_fde^.eoi_byte_address;
      dmv$recover_file_trace_p^.data.eoi_new := p_fde^.eoi_byte_address;
      dmp$set_eoi (system_file_id, p_old_fde^.eoi_byte_address, status);
    IFEND;

  PROCEND fixup_file_descriptor_entry;
?? TITLE := '  get_mainframe_assigned', EJECT ??

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

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


    status.normal := TRUE;

    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 THEN
        osp$set_status_abnormal (dmc$device_manager_ident, dme$mainframe_not_logged_in, 'dmp$recover_file',
              status);
        EXIT /login_table_open/; {----->
      IFEND;

      mainframe_assigned := p_login_table^.body [lt_entry_index].mainframe_assigned;

    END /login_table_open/;

    dmp$close_file (p_login_table, close_status);
    IF status.normal THEN
      status := close_status;
    IFEND;

  PROCEND get_mainframe_assigned;
?? TITLE := '  open_file_for_recovery', EJECT ??

  PROCEDURE open_file_for_recovery
    (    system_file_id: gft$system_file_identifier;
     VAR p_file: ^cell;
     VAR status: ost$status);

    VAR
      cell_pointer: mmt$segment_pointer;

    status.normal := TRUE;
    cell_pointer.kind := mmc$cell_pointer;

    dmp$open_file (system_file_id, osc$tsrv_ring, osc$tsrv_ring, mmc$sar_write_extend, mmc$as_sequential,
          cell_pointer, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    p_file := cell_pointer.cell_pointer;

  PROCEND open_file_for_recovery;
?? TITLE := '  update_file_image', EJECT ??

  PROCEDURE update_file_image
    (    system_file_id: gft$system_file_identifier;
         p_old_fde: gft$file_desc_entry_p;
         p_fde: gft$file_desc_entry_p;
         p_file: ^cell;
     VAR pages_written: integer;
     VAR status: ost$status);

    CONST
      second = 1000000;

    VAR
      allocated_length: amt$file_byte_address,
      base: integer,
      clock: integer,
      descriptor_list_index: 0 .. 7ffffffff(16),
      display_update_interval: integer,
      from_p: ^cell,
      highest_file_offset: ost$segment_offset,
      ignore_status: ost$status,
      image_page_count: 0 .. osc$max_page_frames,
      image_page_description_p: ^mmt$image_page_description,
      old_sfid: gft$system_file_identifier,
      to_p: ^cell;

?? NEWTITLE := 'p$display_periodic_ds_message', EJECT ??

    PROCEDURE p$display_periodic_ds_message;

      VAR
        date: ost$date,
        i: integer,
        local_status: ost$status,
        msg_string: string (80),
        pos: integer,
        time: ost$time;

      dmv$recover_file_trace_p^.data.pages_written := dmv$recover_file_trace_p^.data.pages_written + 1;
      clock := #FREE_RUNNING_CLOCK (0);

      IF clock > (base + display_update_interval) THEN
        base := clock;
        pmp$get_legible_date_time (osc$mdy_date, date, osc$hms_time, time, local_status);
        msg_string := '   ..                   of          Pages ( ) to ';
        msg_string (7, 8) := time.hms;

        pmp$binary_to_ascii_fit (dmv$recover_file_trace_p^.data.pages_written, 10, 8, 8, msg_string (16, 8));
        pmp$binary_to_ascii_fit (dmv$recover_file_trace_p^.data.valid_desc_count, 10, 8, 8,
              msg_string (28, 8));

        pos := 50;
        CASE dmv$recover_file_trace_p^.data.file_kind OF
        = gfc$fk_job_permanent_file =
          msg_string (44) := 'P';
        = gfc$fk_catalog =
          msg_string (44) := 'C';
        = gfc$fk_device_file =
          msg_string (44) := 'D';
        ELSE
        CASEND;

        FOR i := 0 TO c$number_logged_vsns - 1 DO
          IF dmv$recover_file_trace_p^.data.vsn_list ((i * rmc$recorded_vsn_size) + 1,
                rmc$recorded_vsn_size) <> '' THEN
            msg_string (pos, rmc$recorded_vsn_size) := dmv$recover_file_trace_p^.data.
                  vsn_list ((i * rmc$recorded_vsn_size) + 1, rmc$recorded_vsn_size);
            msg_string (pos + rmc$recorded_vsn_size) := ',';
            pos := pos + rmc$recorded_vsn_size + 2;
          IFEND;
        FOREND;
        IF dmv$recover_file_trace_p^.data.vsn_list_shorted THEN
          msg_string (pos - 2, 2) := '..';
        ELSE
          msg_string (pos - 2) := ' ';
        IFEND;

        dmp$issue_message_to_ascii_log (msg_string (1, 78), dmv$display_recovery_messages);
      IFEND;

    PROCEND p$display_periodic_ds_message;
?? OLDTITLE ??
?? EJECT ??

    status.normal := TRUE;
    IF p_old_fde^.asti = 0 THEN
      RETURN; {----->
    IFEND;

{we only want to emit a message when we say here longer than the interval!
    base := #FREE_RUNNING_CLOCK (0);
    display_update_interval := dmv$ds_msg_update_interval * second;

    mmp$fetch_image_page_count (image_page_count);
    IF image_page_count = 0 THEN
      RETURN; {----->
    IFEND;

    dmv$recover_file_trace_p^.data.image_page_count := image_page_count;
    ALLOCATE image_page_description_p: [1 .. image_page_count] IN osv$task_private_heap^;
    IF image_page_description_p = NIL THEN
      osp$system_error ('image_page_description_p NIL', NIL);
    IFEND;

    mmp$fetch_pvas_of_image_pages (p_old_fde, image_page_description_p, status);
    dmv$recover_file_trace_p^.data.valid_desc_count := image_page_description_p^.valid_desc_count;
    IF NOT status.normal OR (image_page_description_p^.valid_desc_count = 0) THEN
      IF image_page_description_p <> NIL THEN
        FREE image_page_description_p IN osv$task_private_heap^;
      IFEND;
      RETURN; {----->
    IFEND;

    highest_file_offset := 0;

    FOR descriptor_list_index := 1 TO image_page_description_p^.valid_desc_count DO
      IF image_page_description_p^.page_desc [descriptor_list_index].file_offset > highest_file_offset THEN
        highest_file_offset := image_page_description_p^.page_desc [descriptor_list_index].file_offset;
      IFEND;
    FOREND;

    dmv$recover_file_trace_p^.data.highest_file_offset := highest_file_offset;
    dmp$get_total_allocated_length (p_fde, allocated_length);
    dmv$recover_file_trace_p^.data.allocated_length_1 := allocated_length;

    IF allocated_length < (highest_file_offset + image_page_description_p^.pagesize) THEN
      dmp$allocate_file_space_r1 (system_file_id, highest_file_offset, image_page_description_p^.pagesize, 0,
            osc$nowait, sfc$no_limit, status);
      dmp$get_total_allocated_length (p_fde, allocated_length);
      dmv$recover_file_trace_p^.data.allocated_length_2 := allocated_length;
    IFEND;

    CASE dmv$file_recovery_version OF
    = 1 =
{ dmv$file_recovery_version does advise_in/out for every page

      FOR descriptor_list_index := 1 TO image_page_description_p^.valid_desc_count DO
        IF image_page_description_p^.page_desc [descriptor_list_index].file_offset +
              image_page_description_p^.pagesize <= allocated_length THEN
          from_p := image_page_description_p^.page_desc [descriptor_list_index].image_pva;
          to_p := i#ptr (image_page_description_p^.page_desc [descriptor_list_index].file_offset, p_file);

          dmv$recover_file_trace_p^.data.to_p := to_p;
          dmv$recover_file_trace_p^.data.from_p := from_p;

          mmp$advise_in (from_p, image_page_description_p^.pagesize, ignore_status);
          mmp$advise_in (to_p, image_page_description_p^.pagesize, ignore_status);

          i#move (from_p, to_p, image_page_description_p^.pagesize);

          mmp$advise_out (from_p, image_page_description_p^.pagesize, ignore_status);
          mmp$advise_out (to_p, image_page_description_p^.pagesize, ignore_status);

          p$display_periodic_ds_message;
        IFEND;
      FOREND;

    = 2 =
{ dmv$file_recovery_version does advise_out for every page

      FOR descriptor_list_index := 1 TO image_page_description_p^.valid_desc_count DO
        IF image_page_description_p^.page_desc [descriptor_list_index].file_offset +
              image_page_description_p^.pagesize <= allocated_length THEN
          from_p := image_page_description_p^.page_desc [descriptor_list_index].image_pva;
          to_p := i#ptr (image_page_description_p^.page_desc [descriptor_list_index].file_offset, p_file);

          dmv$recover_file_trace_p^.data.to_p := to_p;
          dmv$recover_file_trace_p^.data.from_p := from_p;

          i#move (from_p, to_p, image_page_description_p^.pagesize);

          mmp$advise_out (from_p, image_page_description_p^.pagesize, ignore_status);
          mmp$advise_out (to_p, image_page_description_p^.pagesize, ignore_status);

          p$display_periodic_ds_message;
        IFEND;
      FOREND;

    = 10 =
{ as 1, except that we write out via mmp$write_modified_pages with no-wait

      FOR descriptor_list_index := 1 TO image_page_description_p^.valid_desc_count DO
        IF image_page_description_p^.page_desc [descriptor_list_index].file_offset +
              image_page_description_p^.pagesize <= allocated_length THEN
          from_p := image_page_description_p^.page_desc [descriptor_list_index].image_pva;
          to_p := i#ptr (image_page_description_p^.page_desc [descriptor_list_index].file_offset, p_file);

          mmp$advise_in (from_p, image_page_description_p^.pagesize, ignore_status);
          mmp$advise_in (to_p, image_page_description_p^.pagesize, ignore_status);

          dmv$recover_file_trace_p^.data.to_p := to_p;
          dmv$recover_file_trace_p^.data.from_p := from_p;

          i#move (from_p, to_p, image_page_description_p^.pagesize);

          mmp$advise_out (from_p, image_page_description_p^.pagesize, ignore_status);
          mmp$write_modified_pages (to_p, image_page_description_p^.pagesize, osc$nowait, ignore_status);

          p$display_periodic_ds_message;
        IFEND;
      FOREND;

    = 11 =
{ as 10, except that we wait

      FOR descriptor_list_index := 1 TO image_page_description_p^.valid_desc_count DO
        IF image_page_description_p^.page_desc [descriptor_list_index].file_offset +
              image_page_description_p^.pagesize <= allocated_length THEN
          from_p := image_page_description_p^.page_desc [descriptor_list_index].image_pva;
          to_p := i#ptr (image_page_description_p^.page_desc [descriptor_list_index].file_offset, p_file);

          dmv$recover_file_trace_p^.data.to_p := to_p;
          dmv$recover_file_trace_p^.data.from_p := from_p;

          mmp$advise_in (from_p, image_page_description_p^.pagesize, ignore_status);
          mmp$advise_in (to_p, image_page_description_p^.pagesize, ignore_status);

          i#move (from_p, to_p, image_page_description_p^.pagesize);

          mmp$advise_out (from_p, image_page_description_p^.pagesize, ignore_status);
          mmp$write_modified_pages (to_p, image_page_description_p^.pagesize, osc$wait, ignore_status);

          p$display_periodic_ds_message;
        IFEND;
      FOREND;

    = 20 =
{ do a write_modified pages for every written page w. nowait

      FOR descriptor_list_index := 1 TO image_page_description_p^.valid_desc_count DO
        IF image_page_description_p^.page_desc [descriptor_list_index].file_offset +
              image_page_description_p^.pagesize <= allocated_length THEN
          from_p := image_page_description_p^.page_desc [descriptor_list_index].image_pva;
          to_p := i#ptr (image_page_description_p^.page_desc [descriptor_list_index].file_offset, p_file);

          dmv$recover_file_trace_p^.data.to_p := to_p;
          dmv$recover_file_trace_p^.data.from_p := from_p;

          i#move (from_p, to_p, image_page_description_p^.pagesize);

          mmp$write_modified_pages (to_p, image_page_description_p^.pagesize, osc$nowait, ignore_status);

          p$display_periodic_ds_message;
        IFEND;
      FOREND;

    = 21 =
{ do a write_modified pages for every written page w. wait

      FOR descriptor_list_index := 1 TO image_page_description_p^.valid_desc_count DO
        IF image_page_description_p^.page_desc [descriptor_list_index].file_offset +
              image_page_description_p^.pagesize <= allocated_length THEN
          from_p := image_page_description_p^.page_desc [descriptor_list_index].image_pva;
          to_p := i#ptr (image_page_description_p^.page_desc [descriptor_list_index].file_offset, p_file);

          dmv$recover_file_trace_p^.data.to_p := to_p;
          dmv$recover_file_trace_p^.data.from_p := from_p;

          i#move (from_p, to_p, image_page_description_p^.pagesize);

          mmp$write_modified_pages (to_p, image_page_description_p^.pagesize, osc$wait, ignore_status);

          p$display_periodic_ds_message;
        IFEND;
      FOREND;


    ELSE { Version 0 and all undefined ones
      FOR descriptor_list_index := 1 TO image_page_description_p^.valid_desc_count DO
        IF image_page_description_p^.page_desc [descriptor_list_index].file_offset +
              image_page_description_p^.pagesize <= allocated_length THEN
          from_p := image_page_description_p^.page_desc [descriptor_list_index].image_pva;
          to_p := i#ptr (image_page_description_p^.page_desc [descriptor_list_index].file_offset, p_file);

          dmv$recover_file_trace_p^.data.to_p := to_p;
          dmv$recover_file_trace_p^.data.from_p := from_p;

          i#move (from_p, to_p, image_page_description_p^.pagesize);

          p$display_periodic_ds_message;
        IFEND;
      FOREND;
    CASEND;

    pages_written := image_page_description_p^.valid_desc_count;

    IF image_page_description_p <> NIL THEN
      FREE image_page_description_p IN osv$task_private_heap^;
    IFEND;

  PROCEND update_file_image;
?? OLDTITLE ??
MODEND dmm$recover_file;

