MODULE bam$exit_fap_on_condition;
?? RIGHT := 110 ??

{ PURPOSE:
{   This module contains the procedure that returns control to BAM when a
{   volume down or disk full condition occurs during a record_access operation.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc oss$task_private
?? POP ??
*copyc bav$last_tft_entry
*copyc bav$task_file_table
*copyc bav$tft_entry_assignment
*copyc osp$enforce_exception_policies
*copyc osp$establish_block_exit_hndlr
*copyc osp$get_access_condition_entry
*copyc osp$get_file_criteria
*copyc osp$set_status_condition
*copyc osp$verify_system_privilege
*copyc osp$wait_on_condition
*copyc osv$initial_exception_context
*copyc pmp$exit
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] bap$exit_fap_on_condition', EJECT ??

{ PURPOSE:
{   To process the VOLUME_UNAVAILABLE and SPACE_UNAVAILABLE conditions that
{   arise during record or segment access.
{
{ DESIGN:
{   Search the task_file_table for an instance of open that has a pointer to a
{   rollback_procedure.  If a non-NIL pointer is found then invoke a call to
{   the rollback_procedure with the status that was passed in.  There should be
{   only one ^rollback_procedure in the TFT at any time except in the case of
{   a byte_move copy for which there are two pointers, one each for the input
{   and output files.  Since both files are involved in the copy process it's
{   okay to exit the copy if access to either of the files encounters volume
{   down.

{   If the preceding search fails, we repeat the scan of the TFT.  This time
{   we look for instances of open for segment access that reside on a missing
{   volume.  We then exit the task.

{   If neither preceding scan finds a file to blame the condition on, we
{   assume the problem is related to a scratch file.  This is not always true.
{   The BACPF and RESPF utilities open permanent files for segment access
{   using a low-level call to Segment Management.  This results in no
{   instance of open in the TFT and imprecision in our analysis of which
{   file is causing the condition.  We pass a NIL file name and call
{   OSP$ENFORCE_EXCEPTION_POLICIES in case the site wants to EXIT on
{   disk full or volume unavailable on a temporary device.

{   The possibility of recursion exists when we try to PUSH and we are processing
{   a temporary space unavailable condition.  In this situation, we detect recursion
{   by using the ISOLATING_SPACE_UNAVAILABLE boolean.  We stop trying to analyze the
{   specific file when we detect recursion and simply wait for space to become
{   available.

  PROCEDURE [XDCL, #GATE] bap$exit_fap_on_condition
    (    condition: ost$status_condition_code);

    CONST
      max_volumes_per_file = 500;

    VAR
      isolating_space_unavailable: [oss$task_private] boolean := FALSE;

    VAR
      access_condition_entry: fst$access_condition_entry,
      context: ost$ecp_exception_context,
      criteria: ost$ecp_criteria,
      debug_status: ost$status,
      file_id: ost$ecp_file_identification,
      found: boolean,
      index: bat$tft_limit,
      path: ^fst$path,
      seq_size: ost$positive_integers,
      status: ost$status,
      work_area: ^SEQ ( * );

?? NEWTITLE := '  exit_fap_handler', EJECT ??

    PROCEDURE exit_fap_handler
      (    ignore_condition: pmt$condition;
           ignore_information: ^pmt$condition_information;
           ignore_save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      isolating_space_unavailable := FALSE;

    PROCEND exit_fap_handler;
?? OLDTITLE ??
?? NEWTITLE := 'process_space_unavailable', EJECT ??

    PROCEDURE process_space_unavailable;

      VAR
        index: bat$tft_limit,
        number_found: 0 .. bac$maximum_tft_size,
        volume_condition_list: ^fst$volume_condition_list;

      number_found := 0;

      FOR index := 1 TO bav$last_tft_entry DO
        IF (bav$tft_entry_assignment^ (index, 1) = fmc$entry_assigned)
{     } AND (bav$task_file_table^ [index].device_class = rmc$mass_storage_device)
{     } AND (bav$task_file_table^ [index].access_level = amc$segment)
{     } AND ((bav$task_file_table^ [index].instance_attributes.dynamic_label.access_mode *
              $pft$usage_selections [pfc$append, pfc$modify, pfc$shorten]) <> $pft$usage_selections []) THEN

          file_id.selector := osc$ecp_file_identifier;
          file_id.file_identifier.ordinal := index;
          file_id.file_identifier.sequence := bav$task_file_table^ [index].sequence_number;

          seq_size := #SIZE (fst$goi_object_information) + fsc$max_path_size + #SIZE (fst$goi_object) +
                #SIZE (fst$device_information) + (max_volumes_per_file *
                (#SIZE (rmt$volume_descriptor) + #SIZE (fst$file_access_condition)));
          PUSH work_area: [[REP seq_size OF cell]];

          osp$get_file_criteria (file_id, {catalog_object} FALSE, {catalog_space_unavailable} FALSE,
                {password} osc$null_name, work_area, criteria, volume_condition_list, status);

          IF status.normal THEN
            IF (criteria.mass_storage_class = rmc$msc_system_temporary_files) OR
                  (criteria.mass_storage_class = rmc$msc_user_temporary_files) THEN
              IF temp_space_unavailable (volume_condition_list) THEN
                number_found := number_found + 1;
              IFEND;
            ELSEIF perm_space_unavailable (volume_condition_list) THEN
              number_found := number_found + 1;
            IFEND;
          IFEND;
        IFEND;
      FOREND;

      IF number_found = 1 THEN
        context := osv$initial_exception_context;
        context.externalized_info.file_segment_isolated := TRUE;
        context.externalized_info.file_segment := #SEGMENT (bav$task_file_table^
              [file_id.file_identifier.ordinal].file_pva);
        context.file := file_id;
        context.force_wait := TRUE;
        context.logging_allowed := FALSE;

        osp$set_status_condition (condition, context.condition_status);

        osp$enforce_exception_policies (context);
        IF (context.elapsed_wait_time = 0) THEN
          osp$wait_on_condition (condition);
        IFEND;

        EXIT bap$exit_fap_on_condition; {----->

      IFEND;

    PROCEND process_space_unavailable;
?? OLDTITLE ??
?? NEWTITLE := 'process_volume_unavailable', EJECT ??

    PROCEDURE process_volume_unavailable;

      VAR
        index: bat$tft_limit,
        volume_condition_list: ^fst$volume_condition_list;

      FOR index := 1 TO bav$last_tft_entry DO
        IF (bav$tft_entry_assignment^ (index, 1) = fmc$entry_assigned)
{     } AND (bav$task_file_table^ [index].device_class = rmc$mass_storage_device)
{     } AND (bav$task_file_table^ [index].access_level = amc$segment) THEN

          file_id.selector := osc$ecp_file_identifier;
          file_id.file_identifier.ordinal := index;
          file_id.file_identifier.sequence := bav$task_file_table^ [index].sequence_number;

          seq_size := #SIZE (fst$goi_object_information) + fsc$max_path_size + #SIZE (fst$goi_object) +
                #SIZE (fst$device_information) + (max_volumes_per_file *
                (#SIZE (rmt$volume_descriptor) + #SIZE (fst$file_access_condition)));
          PUSH work_area: [[REP seq_size OF cell]];

          osp$get_file_criteria (file_id, {catalog_object} FALSE, {catalog_space_unavailable} FALSE,
                {password} osc$null_name, work_area, criteria, volume_condition_list, status);

          IF status.normal AND volume_unavailable (volume_condition_list) THEN
            context := osv$initial_exception_context;
            context.externalized_info.file_segment_isolated := TRUE;
            context.externalized_info.file_segment := #SEGMENT (bav$task_file_table^ [index].file_pva);
            context.file := file_id;
            context.force_wait := TRUE;
            context.logging_allowed := FALSE;

            osp$set_status_condition (condition, context.condition_status);

            osp$enforce_exception_policies (context);
            IF (context.elapsed_wait_time = 0) THEN
              osp$wait_on_condition (condition);
            IFEND;

            EXIT bap$exit_fap_on_condition; {----->

          IFEND;
        IFEND;
      FOREND;

    PROCEND process_volume_unavailable;
?? OLDTITLE ??
?? NEWTITLE := 'perm_space_unavailable', EJECT ??

    FUNCTION [INLINE] perm_space_unavailable
      (    volume_condition_list: ^fst$volume_condition_list): boolean;

      perm_space_unavailable := FALSE;
      IF volume_condition_list <> NIL THEN
        IF volume_condition_list^ [UPPERBOUND (volume_condition_list^)] = fsc$space_unavailable THEN
          perm_space_unavailable := TRUE;
        IFEND;
      IFEND;

    FUNCEND perm_space_unavailable;
?? OLDTITLE ??
?? NEWTITLE := 'temp_space_unavailable', EJECT ??

    FUNCTION [INLINE] temp_space_unavailable
      (    volume_condition_list: ^fst$volume_condition_list): boolean;

      VAR
        i: ost$positive_integers;

      temp_space_unavailable := FALSE;
      IF volume_condition_list <> NIL THEN
        FOR i := 1 TO UPPERBOUND (volume_condition_list^) DO
          IF volume_condition_list^ [i] = fsc$space_unavailable THEN
            temp_space_unavailable := TRUE;
            RETURN; {----->
          IFEND;
        FOREND;
      IFEND;

    FUNCEND temp_space_unavailable;
?? OLDTITLE ??
?? NEWTITLE := 'volume_unavailable', EJECT ??

    FUNCTION [INLINE] volume_unavailable
      (    volume_condition_list: ^fst$volume_condition_list): boolean;

      VAR
        i: ost$positive_integers;

      volume_unavailable := FALSE;
      IF volume_condition_list <> NIL THEN
        FOR i := 1 TO UPPERBOUND (volume_condition_list^) DO
          IF volume_condition_list^ [i] = fsc$volume_unavailable THEN
            volume_unavailable := TRUE;
            RETURN; {----->
          IFEND;
        FOREND;
      IFEND;

    FUNCEND volume_unavailable;
?? OLDTITLE ??
?? EJECT ??

    osp$verify_system_privilege;
    osp$establish_block_exit_hndlr (^exit_fap_handler);

    osp$set_status_condition (condition, context.condition_status);
    osp$get_access_condition_entry (context.condition_status, access_condition_entry, found);

    IF found AND (bav$task_file_table <> NIL) THEN

      {Search for an instance of record access and rollback to GET/PUT interface

      FOR index := 1 TO bav$last_tft_entry DO
        IF (bav$tft_entry_assignment^ (index, 1) = fmc$entry_assigned)
{     } AND (bav$task_file_table^ [index].device_class = rmc$mass_storage_device)
{     } AND (access_condition_entry.file_access_condition = fsc$space_unavailable)
{     } AND ((bav$task_file_table^ [index].instance_attributes.dynamic_label.access_mode *
              $pft$usage_selections [pfc$append, pfc$modify, pfc$shorten]) <> $pft$usage_selections []) OR
              (access_condition_entry.file_access_condition = fsc$volume_unavailable)
{     } AND (bav$task_file_table^ [index].rollback_procedure <> NIL) THEN
          status := context.condition_status;
          bav$task_file_table^ [index].rollback_procedure^ (status);
          RETURN; {BAM does a block exit so this statement is for safety only}
        IFEND;
      FOREND;

      IF access_condition_entry.file_access_condition = fsc$volume_unavailable THEN
        process_volume_unavailable;
      ELSEIF access_condition_entry.file_access_condition = fsc$space_unavailable THEN
        IF isolating_space_unavailable THEN
          osp$wait_on_condition (condition);
          RETURN; {----->
        ELSE
          isolating_space_unavailable := TRUE;
          #SPOIL (isolating_space_unavailable);
          process_space_unavailable;
        IFEND;
      IFEND;

{Unable to isolate file involved.
      context := osv$initial_exception_context;
      context.file.selector := osc$ecp_file_reference;
      context.file.file_reference := NIL;
      context.force_wait := TRUE;
      context.logging_allowed := FALSE;

      osp$set_status_condition (condition, context.condition_status);

      osp$enforce_exception_policies (context);

      IF (context.elapsed_wait_time = 0) THEN
        osp$wait_on_condition (condition);
      IFEND;
    IFEND;

  PROCEND bap$exit_fap_on_condition;
?? OLDTITLE ??
MODEND bam$exit_fap_on_condition;

