?? RIGHT := 110 ??
?? TITLE := 'NOS/VE: Device Management - Recover Job Temporary File Space' ??
MODULE dmm$recover_job_temp_file_space;
?? RIGHT := 110 ??
?? NEWTITLE := '  External declarations referenced in this module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc osd$virtual_address
*copyc mme$condition_codes
*copyc dmt$error_condition_codes
*copyc gft$file_desc_entry_p
?? POP ??
*copyc i#build_adaptable_array_ptr
*copyc dmp$close_file
*copyc dmp$get_disk_file_descriptor_p
*copyc dmp$get_fmd_by_index
*copyc dmp$get_level_1_ptr
*copyc dmp$get_level_2_ptr
*copyc dmp$open_dat
*copyc dmp$return_dat_entries
*copyc dmp$search_avt_by_vsn
*copyc dmp$volume_space_manager
*copyc gfp$scan_all_fdes
*copyc gfp$scan_all_fdes_in_image
*copyc lgp$add_entry_to_system_log
*copyc osp$clear_mainframe_sig_lock
*copyc osp$fatal_system_error
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_status_abnormal
*copyc pmp$cycle
*copyc syp$disestablish_cond_handler
*copyc syp$establish_condition_handler
*copyc dmv$active_volume_table
?? TITLE := '  Global Declarations declared in this module', EJECT ??

  TYPE
    t$dat_pointers = array [ * ] of ^dmt$ms_device_allocation_table,

    t$fmd_info = array [ * ] of record
      daus_per_allocation: dmt$daus_per_allocation,
      new_mainframe_id: dmt$mainframe_assigned,
      p_dat: ^dmt$ms_device_allocation_table,
    recend;

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

  PROCEDURE [XDCL] dmp$recover_job_temp_file_space
    (VAR status: ost$status);

    VAR
      avt_index: dmt$active_volume_table_index,
      p_dat_pointers: ^t$dat_pointers,
      p_fde: gft$file_desc_entry_p,
      scan_control: gft$scan_all_fdes_state;

    status.normal := TRUE;

    PUSH p_dat_pointers: [LOWERBOUND (dmv$active_volume_table.table_p^) ..
          UPPERBOUND (dmv$active_volume_table.table_p^)];
    FOR avt_index := LOWERBOUND (p_dat_pointers^) TO UPPERBOUND (p_dat_pointers^) DO
      p_dat_pointers^ [avt_index] := NIL;
    FOREND;

    gfp$scan_all_fdes (gfc$tr_job, scan_control, p_fde);
    WHILE status.normal AND (p_fde <> NIL) DO
      IF (p_fde^.media = gfc$fm_mass_storage_file) THEN
        recover_job_file (p_fde, p_dat_pointers^, status);
      IFEND;
      gfp$scan_all_fdes (gfc$tr_null_residence, scan_control, p_fde);
    WHILEND;

    close_all_dats (p_dat_pointers^);

  PROCEND dmp$recover_job_temp_file_space;
?? TITLE := '  dmp$return_temp_file_space', EJECT ??

  PROCEDURE [XDCL] dmp$return_temp_file_space;

    CONST
      c$full_update = TRUE;

    VAR
      avt_index: dmt$active_volume_table_index,
      entry_p: ^dmt$ms_active_vol_table_entry,
      mainframe_assigned: dmt$mainframe_assigned,
      status: ost$status,
      str: string (70),
      strl: integer,
      ignore_status: ost$status;

  /avt_loop/
    FOR avt_index := LOWERBOUND (dmv$active_volume_table.table_p^) TO
          UPPERBOUND (dmv$active_volume_table.table_p^) DO
      entry_p := ^dmv$active_volume_table.table_p^ [avt_index].mass_storage;
      IF NOT dmv$active_volume_table.table_p^ [avt_index].entry_available AND NOT entry_p^.
            volume_unavailable AND (entry_p^.mainframe_assigned.log_in_sequence <> 0) THEN
        REPEAT
          mainframe_assigned := entry_p^.mainframe_assigned;
          dmp$return_dat_entries (mainframe_assigned, avt_index, dmc$return_all_except_entry, status);
          IF NOT status.normal THEN
            IF (status.condition = dme$unable_to_lock_dat) THEN
              pmp$cycle (ignore_status);
            ELSEIF status.condition = mme$io_read_error THEN
              STRINGREP (str, strl, 'Unable to return temp file space on ', entry_p^.recorded_vsn,
                    ' - I/O read error');
              log (str);
              CYCLE /avt_loop/; {----->
            ELSEIF status.condition = dme$volume_unavailable THEN
              STRINGREP (str, strl, 'Unable to return temp file space on ', entry_p^.recorded_vsn,
                    ' - volume unavailable');
              log (str);
              CYCLE /avt_loop/; {----->
            ELSE
              osp$fatal_system_error ('Bad status in dmp$return_temp_file_space.', ^status);
            IFEND;
          IFEND;
        UNTIL status.normal;

        {Bring partial cylinders (created in the DAT by returning old
        {temporary space) back into the MAT.

        REPEAT
          dmp$volume_space_manager (avt_index, c$full_update, status);
          IF NOT status.normal THEN
            IF (status.condition = dme$unable_to_lock_dat) OR (status.condition = dme$unable_to_lock_dflt) OR
                  (status.condition = dme$unable_to_lock_avt_entry) THEN
              pmp$cycle (ignore_status);
            ELSE
              osp$fatal_system_error ('Bad status in dmp$return_temp_file_space.', ^status);
            IFEND;
          IFEND;
        UNTIL status.normal;
      IFEND;
    FOREND /avt_loop/;
  PROCEND dmp$return_temp_file_space;
?? TITLE := '  dmp$verify_job_volumes', EJECT ??

  PROCEDURE [XDCL] dmp$verify_job_volumes
    (    job_fixed_segment: ost$segment;
     VAR volume_missing: boolean;
     VAR unrecoverable_file: boolean);

    VAR
      avt_index: dmt$active_volume_table_index,
      p_dat_pointers: ^t$dat_pointers,
      p_fde: gft$file_desc_entry_p,
      scan_control: gft$scan_all_fdes_state;

    volume_missing := FALSE;
    unrecoverable_file := FALSE;

    PUSH p_dat_pointers: [LOWERBOUND (dmv$active_volume_table.table_p^) ..
          UPPERBOUND (dmv$active_volume_table.table_p^)];
    FOR avt_index := LOWERBOUND (p_dat_pointers^) TO UPPERBOUND (p_dat_pointers^) DO
      p_dat_pointers^ [avt_index] := NIL;
    FOREND;

    gfp$scan_all_fdes_in_image (job_fixed_segment, scan_control, p_fde);
    WHILE (p_fde <> NIL) AND NOT volume_missing AND NOT unrecoverable_file DO
      IF p_fde^.media = gfc$fm_mass_storage_file THEN
        validate_job_file (p_fde, p_dat_pointers^, volume_missing, unrecoverable_file);
      IFEND;
      gfp$scan_all_fdes (gfc$tr_null_residence, scan_control, p_fde);
    WHILEND;

    close_all_dats (p_dat_pointers^);
  PROCEND dmp$verify_job_volumes;
?? TITLE := '  close_all_dats', EJECT ??

  PROCEDURE close_all_dats
    (    dat_pointers: t$dat_pointers);

    VAR
      index: integer,
      status: ost$status;

    FOR index := LOWERBOUND (dat_pointers) TO UPPERBOUND (dat_pointers) DO
      IF (dat_pointers [index] <> NIL) THEN
        dmp$close_file (dat_pointers [index], status);
        IF NOT status.normal THEN
          IF (status.condition <> mme$io_read_error) AND (status.condition <> dme$volume_unavailable) THEN
            osp$fatal_system_error ('Close DAT failed - close_all_dats.', ^status);
          IFEND;
        IFEND;
      IFEND;
    FOREND;
  PROCEND close_all_dats;
?? TITLE := '  get_dat_pointer', EJECT ??

  PROCEDURE get_dat_pointer
    (    avt_index: dmt$active_volume_table_index;
     VAR dat_pointers: {input/output} t$dat_pointers;
     VAR p_dat: ^dmt$ms_device_allocation_table;
     VAR status: ost$status);

    status.normal := TRUE;
    p_dat := dat_pointers [avt_index];

    IF p_dat = NIL THEN
      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_write_extend, mmc$as_sequential, p_dat, status);
      IF NOT status.normal THEN
        p_dat := NIL;
        IF (status.condition <> mme$io_read_error) AND (status.condition <> dme$volume_unavailable) THEN
          osp$fatal_system_error ('Open DAT failed - get_dat_pointer.', ^status);
        IFEND;
      IFEND;
      dat_pointers [avt_index] := p_dat;
    IFEND;

  PROCEND get_dat_pointer;
?? TITLE := '  log', EJECT ??

  PROCEDURE log
    (    text: string ( * ));

    VAR
      log_time: ost$time,
      status: ost$status;

    lgp$add_entry_to_system_log (pmc$msg_origin_system, text, log_time, status);
  PROCEND log;
?? TITLE := '  recover_allocation_unit', EJECT ??

  PROCEDURE recover_allocation_unit
    (    avt_index: dmt$active_volume_table_index;
         p_dat: ^dmt$ms_device_allocation_table;
         first_dau: dmt$dau_address;
         daus_per_allocation: dmt$daus_per_allocation;
         new_mainframe_id: dmt$mainframe_assigned);

    VAR
      dau: dmt$dau_address,
      lock_cleared_by_condition_hdler: boolean;

?? NEWTITLE := 'UNLOCK_HANDLER', EJECT ??

    PROCEDURE unlock_handler
      (    mf: ost$monitor_fault;
           psa: ^ost$minimum_save_area;
       VAR continue: syt$continue_option);

      osp$clear_mainframe_sig_lock (dmv$active_volume_table.table_p^ [avt_index].mass_storage.update_lock);
      lock_cleared_by_condition_hdler := TRUE;
      #SPOIL (lock_cleared_by_condition_hdler);
      continue := syc$condition_ignored;

    PROCEND unlock_handler;
?? OLDTITLE ??
?? EJECT ??
    lock_cleared_by_condition_hdler := FALSE;
    osp$set_mainframe_sig_lock (dmv$active_volume_table.table_p^ [avt_index].mass_storage.update_lock);

    syp$establish_condition_handler (^unlock_handler);

    IF valid_allocation_unit (p_dat, first_dau, daus_per_allocation, new_mainframe_id) THEN
      FOR dau := first_dau TO (first_dau + daus_per_allocation - 1) DO
        p_dat^.body [dau].mainframe_id := new_mainframe_id;
      FOREND;
    ELSE
      osp$fatal_system_error ('Bad allocation unit - recover_allocation_unit', NIL);
    IFEND;

    syp$disestablish_cond_handler;

    #SPOIL(lock_cleared_by_condition_hdler);
    IF NOT lock_cleared_by_condition_hdler THEN
      osp$clear_mainframe_sig_lock (dmv$active_volume_table.table_p^ [avt_index].mass_storage.update_lock);
    IFEND;

  PROCEND recover_allocation_unit;
?? TITLE := '  recover_job_file', EJECT ??

  PROCEDURE recover_job_file
    (    p_fde: gft$file_desc_entry_p;
     VAR dat_pointers: {input/output} t$dat_pointers;
     VAR status: ost$status);

    VAR
      avt_index: dmt$active_volume_table_index,
      bytes_per_allocation: dmt$bytes_per_allocation,
      dau: dmt$dau_address,
      daus_per_allocation: dmt$daus_per_allocation,
      fmd_count: dmt$fmd_index,
      fmd_index: dmt$fmd_index,
      found: boolean,
      level_1_index: dmt$level_1_index,
      level_2_index: dmt$level_2_index,
      new_mainframe_id: dmt$mainframe_assigned,
      p_dat: ^dmt$ms_device_allocation_table,
      p_dfd: ^dmt$disk_file_descriptor,
      p_fmd: ^dmt$file_medium_descriptor,
      p_fmd_info: ^t$fmd_info,
      p_level_1: ^dmt$level_1_table,
      p_level_2: ^dmt$level_2_table;

    status.normal := TRUE;
    dmp$get_disk_file_descriptor_p (p_fde, p_dfd);

    IF (p_dfd = NIL) OR (p_dfd^.number_of_fmds = 0) THEN
      RETURN; {----->
    IFEND;

    fmd_count := p_dfd^.number_of_fmds;
    PUSH p_fmd_info: [1 .. fmd_count];

    FOR fmd_index := 1 TO fmd_count DO
      dmp$get_fmd_by_index (p_dfd, fmd_index, p_fmd);
      IF (p_fmd <> NIL) AND p_fmd^.in_use AND p_fmd^.volume_assigned THEN
        dmp$search_avt_by_vsn (p_fmd^.internal_vsn, avt_index, found);
        IF (NOT found) OR (dmv$active_volume_table.table_p^ [avt_index].mass_storage.volume_unavailable) THEN
          osp$set_status_abnormal (dmc$device_manager_ident, dme$volume_unavailable,
                'Volume unavailable - recover_job_file.', status);
          RETURN; {----->
        IFEND;
        p_fmd_info^ [fmd_index].daus_per_allocation := p_fmd^.daus_per_allocation_unit;
        p_fmd_info^ [fmd_index].new_mainframe_id := dmv$active_volume_table.table_p^ [avt_index].
              mass_storage.mainframe_assigned;
        get_dat_pointer (avt_index, dat_pointers, p_fmd_info^ [fmd_index].p_dat, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
        p_fmd^.avt_index := avt_index;
      ELSE
        p_fmd_info^ [fmd_index].daus_per_allocation := 1;
        p_fmd_info^ [fmd_index].new_mainframe_id.log_in_sequence := 0;
        p_fmd_info^ [fmd_index].new_mainframe_id.log_in_index := 1;
        p_fmd_info^ [fmd_index].p_dat := NIL;
      IFEND;
    FOREND;

    bytes_per_allocation := p_dfd^.bytes_per_allocation;
    dmp$get_level_1_ptr (p_dfd, p_level_1);
    IF p_level_1 <> NIL THEN
      FOR level_1_index := 0 TO p_dfd^.fat_upper_bound DO
        dmp$get_level_2_ptr (^p_level_1^ [level_1_index], p_level_2);
        IF p_level_2 <> NIL THEN
          FOR level_2_index := 0 TO (p_dfd^.bytes_per_level_2 DIV bytes_per_allocation) - 1 DO
            IF (p_level_2^ [level_2_index].state <> dmc$fau_free) THEN
              fmd_index := p_level_2^ [level_2_index].fmd_index;
              dau := p_level_2^ [level_2_index].dau_address;
              IF (fmd_index = 0) OR (fmd_index > fmd_count) THEN
                osp$fatal_system_error ('Bad FAT entry - recover_job_file', NIL);
                RETURN; {----->
              IFEND;
              daus_per_allocation := p_fmd_info^ [fmd_index].daus_per_allocation;
              new_mainframe_id := p_fmd_info^ [fmd_index].new_mainframe_id;
              p_dat := p_fmd_info^ [fmd_index].p_dat;
              recover_allocation_unit (avt_index, p_dat, dau, daus_per_allocation, new_mainframe_id);
            IFEND;
          FOREND;
        IFEND;
      FOREND;
    IFEND;
  PROCEND recover_job_file;
?? TITLE := '  valid_allocation_unit', EJECT ??

  FUNCTION valid_allocation_unit
    (    p_dat: ^dmt$ms_device_allocation_table;
         first_dau: dmt$dau_address;
         daus_per_allocation: dmt$daus_per_allocation;
         new_mainframe_id: dmt$mainframe_assigned): boolean;

    VAR
      dau: dmt$dau_address,
      last_dau: dmt$dau_address,
      state: dmt$dau_status;

    valid_allocation_unit := FALSE;
    last_dau := first_dau + daus_per_allocation - 1;

    IF (p_dat = NIL) OR (last_dau >= p_dat^.header.number_of_entries) THEN
      RETURN; {----->
    IFEND;

    FOR dau := first_dau TO last_dau DO
      state := p_dat^.body [dau].dau_status;
      IF ((state <> dmc$dau_assigned_to_mainframe) AND (state <> dmc$dau_ass_to_mf_swr_flawed)) OR
            (p_dat^.body [dau].mainframe_id = new_mainframe_id) THEN
        RETURN; {----->
      IFEND;
    FOREND;

    valid_allocation_unit := TRUE;
  FUNCEND valid_allocation_unit;
?? TITLE := '  validate_job_file', EJECT ??

  PROCEDURE validate_job_file
    (    p_fde: gft$file_desc_entry_p;
     VAR dat_pointers: {input/output} t$dat_pointers;
     VAR volume_missing: boolean;
     VAR unrecoverable_file: boolean);

    VAR
      avt_index: dmt$active_volume_table_index,
      bytes_per_allocation: dmt$bytes_per_allocation,
      dau: dmt$dau_address,
      daus_per_allocation: dmt$daus_per_allocation,
      found: boolean,
      fmd_count: dmt$fmd_index,
      fmd_index: dmt$fmd_index,
      level_1_index: dmt$level_1_index,
      level_2_index: dmt$level_2_index,
      new_mainframe_id: dmt$mainframe_assigned,
      p_dat: ^dmt$ms_device_allocation_table,
      p_dfd: ^dmt$disk_file_descriptor,
      p_fmd: ^dmt$file_medium_descriptor,
      p_fmd_info: ^t$fmd_info,
      p_level_1: ^dmt$level_1_table,
      p_level_2: ^dmt$level_2_table,
      status: ost$status;

    volume_missing := FALSE;
    unrecoverable_file := FALSE;

    dmp$get_disk_file_descriptor_p (p_fde, p_dfd);

    IF (p_dfd = NIL) OR (p_dfd^.number_of_fmds = 0) THEN
      RETURN; {----->
    IFEND;

    fmd_count := p_dfd^.number_of_fmds;
    PUSH p_fmd_info: [1 .. fmd_count];

    FOR fmd_index := 1 TO fmd_count DO
      dmp$get_fmd_by_index (p_dfd, fmd_index, p_fmd);
      IF (p_fmd <> NIL) AND p_fmd^.in_use AND p_fmd^.volume_assigned THEN
        dmp$search_avt_by_vsn (p_fmd^.internal_vsn, avt_index, found);
        IF (NOT found) OR (dmv$active_volume_table.table_p^ [avt_index].mass_storage.volume_unavailable) THEN
          volume_missing := TRUE;
          RETURN; {----->
        IFEND;
        p_fmd_info^ [fmd_index].daus_per_allocation := p_fmd^.daus_per_allocation_unit;
        p_fmd_info^ [fmd_index].new_mainframe_id := dmv$active_volume_table.table_p^ [avt_index].
              mass_storage.mainframe_assigned;
        get_dat_pointer (avt_index, dat_pointers, p_fmd_info^ [fmd_index].p_dat, status);
        IF NOT status.normal THEN
          volume_missing := TRUE;
          RETURN; {----->
        IFEND;
      ELSE
        p_fmd_info^ [fmd_index].daus_per_allocation := 1;
        p_fmd_info^ [fmd_index].new_mainframe_id.log_in_sequence := 0;
        p_fmd_info^ [fmd_index].new_mainframe_id.log_in_index := 1;
        p_fmd_info^ [fmd_index].p_dat := NIL;
      IFEND;
    FOREND;

    bytes_per_allocation := p_dfd^.bytes_per_allocation;
    dmp$get_level_1_ptr (p_dfd, p_level_1);
    IF p_level_1 <> NIL THEN
      FOR level_1_index := 0 TO p_dfd^.fat_upper_bound DO
        dmp$get_level_2_ptr (^p_level_1^ [level_1_index], p_level_2);
        IF p_level_2 <> NIL THEN
          FOR level_2_index := 0 TO (p_dfd^.bytes_per_level_2 DIV bytes_per_allocation) - 1 DO
            IF (p_level_2^ [level_2_index].state <> dmc$fau_free) THEN
              fmd_index := p_level_2^ [level_2_index].fmd_index;
              dau := p_level_2^ [level_2_index].dau_address;
              IF (fmd_index = 0) OR (fmd_index > fmd_count) THEN
                unrecoverable_file := TRUE;
                RETURN; {----->
              IFEND;
              daus_per_allocation := p_fmd_info^ [fmd_index].daus_per_allocation;
              new_mainframe_id := p_fmd_info^ [fmd_index].new_mainframe_id;
              p_dat := p_fmd_info^ [fmd_index].p_dat;
              IF NOT valid_allocation_unit (p_dat, dau, daus_per_allocation, new_mainframe_id) THEN
                unrecoverable_file := TRUE;
                RETURN; {----->
              IFEND;
            IFEND;
          FOREND;
        IFEND;
      FOREND;
    IFEND;
  PROCEND validate_job_file;
?? OLDTITLE, OLDTITLE ??
MODEND dmm$recover_job_temp_file_space;
