MODULE pfm$attached_pf_table;
?? RIGHT := 110 ??
{
{ PURPOSE:
{   This module manages the attached pf table.  The pointer to the attached
{   pf table as well as the table lock are static variables in this module.
{   This module must reside in ring 2.
{ DESIGN:
{   This module assumes that locking of the requests is accomplished by
{   use of the file manager locking, and is done outside of this module.

?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc oss$task_shared
*copyc ose$job_recovery_exceptions
*copyc pfe$error_condition_codes
*copyc pfe$internal_error_conditions
*copyc ost$heap
*copyc ost$status
*copyc pft$attached_permanent_file_id
*copyc pft$attached_pf_table_index
*copyc pft$p_attached_pf_entry
*copyc pft$p_table_info
*copyc pft$record_id
*copyc pft$return_files_option
*copyc pft$table_name
*copyc pmt$binary_mainframe_id
?? POP ??
*copyc ofp$display_status_message
*copyc osp$append_status_integer
*copyc osp$enforce_exception_policies
*copyc osp$file_access_condition
*copyc osp$log_job_recovery_message
*copyc osp$log_job_recovery_status
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc pfp$catalog_access_retry_wait
*copyc pfp$convert_cycle_path_to_strng
*copyc pfp$internal_return_file
*copyc pfp$process_unexpected_status
*copyc pfp$reattach_permanent_file
*copyc pmp$log_ascii
*copyc dfv$file_server_debug_enabled
*copyc osv$initial_exception_context
*copyc pfv$p_p_attached_pf_table
*copyc pfv$p_p_job_heap
*copyc dfi$display
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared By This Module', EJECT ??

  VAR
    pfv$p_attached_pf_table: [XDCL, #GATE, oss$task_shared] pft$p_attached_pf_table := [0, NIL];

?? TITLE := 'F$VALID_APFID', EJECT ??

{ PURPOSE:
{   This procedure validates that the input apfid is  valid.

  FUNCTION [INLINE] f$valid_apfid
    (    apfid: pft$attached_pf_table_index): boolean;

    f$valid_apfid := (pfv$p_p_attached_pf_table^.table_p <> NIL) AND
          (apfid <= UPPERBOUND (pfv$p_p_attached_pf_table^.table_p^)) AND
          (apfid >= LOWERBOUND (pfv$p_p_attached_pf_table^.table_p^)) AND
          (pfv$p_p_attached_pf_table^.table_p^ [apfid].entry_type = pfc$attached_pf_entry_valid);

  FUNCEND f$valid_apfid;
?? TITLE := '*** PFP$ASSIGN_LOCKED_APFID *** ', EJECT ??

{ PURPOSE:
{   This procedure gets an unused entry in the attached pf table,
{   and stores a NIL for the pointer to the attached pf entry.

  PROCEDURE [XDCL] pfp$assign_locked_apfid
    (VAR apfid: pft$attached_pf_table_index;
     VAR status: ost$status);

    get_unused_apft_entry (apfid, status);
    IF status.normal THEN
      IF (apfid <= pfv$p_p_attached_pf_table^.lowest_possible_free_entry) AND (apfid < 0ffff(16)) THEN
        pfv$p_p_attached_pf_table^.lowest_possible_free_entry := apfid + 1;
      IFEND;
      pfv$p_p_attached_pf_table^.table_p^ [apfid].entry_type := pfc$attached_pf_entry_valid;
      pfv$p_p_attached_pf_table^.table_p^ [apfid].p_attached_pf_entry := NIL;
    IFEND;

  PROCEND pfp$assign_locked_apfid;
?? TITLE := '*** PFP$COMPLETE_JOB_RECOVERY ***', EJECT ??

{  This procedure verifies that all files that permanent files knows about
{  as being attached have been reattached by the file manager.
{  Possible states for the file at this time are:
{  - normal cycle
{    The file is attached in the catalog and file manager cycle description
{    knows about it.
{ - attached_pf_in_job_recovery
{    The file has no cycle description, is not attached in the catalog, but
{    an entry exists in the attached pf table, that must be removed.
{ - attached_pf_awaiting_client
{   The file has no cycle description, is attached in the catalog and in the
{   attached pf table.  The file must be detached from the catalog.

  PROCEDURE [XDCL, #GATE] pfp$complete_job_recovery
    (    mainframe_id: pmt$binary_mainframe_id;
     VAR status: ost$status);

    VAR
      apft_index: pft$attached_pf_table_index,
      authority: pft$authority,
      bytes_allocated_change: sft$counter,
      log_status: ost$status,
      p_complete_path: ^pft$complete_path,
      path_string: ost$string,
      return_status: ost$status,
      unrecovered_pf_count: integer;

    status.normal := TRUE;
    unrecovered_pf_count := 0;
    IF pfv$p_p_attached_pf_table^.table_p <> NIL THEN

    /verify_all_files/
      FOR apft_index := LOWERBOUND (pfv$p_p_attached_pf_table^.table_p^)
            TO UPPERBOUND (pfv$p_p_attached_pf_table^.table_p^) DO
        IF pfv$p_p_attached_pf_table^.table_p^ [apft_index].entry_type = pfc$attached_pf_entry_valid THEN
          CASE pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.sfid_status.
                recovery_state OF
          = pfc$attached_pf_in_job_recovery =
            unrecovered_pf_count := unrecovered_pf_count + 1;
            { The permanent file has NOT been reattached.
            { The attachement will be broken when:
            { this particular attach causes a cycle busy, OR
            { on the next pf recovery.
            { Alternately we could reaccess the catalog and decrement the access
            { count. This may be preferable since this would allow the current
            { job to continue, and re-attach the file, without making
            { the file busy itself.
            osp$log_job_recovery_message (' Permanent file not reattached', log_status);
            p_complete_path := pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.
                  p_external_path;
            pfp$convert_cycle_path_to_strng (p_complete_path^, pfv$p_p_attached_pf_table^.
                  table_p^ [apft_index].p_attached_pf_entry^.cycle_number, path_string);
            osp$log_job_recovery_message (path_string.value (1, path_string.size), log_status);
            FREE pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.p_external_path IN
                  pfv$p_p_job_heap^^;
            FREE pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry IN pfv$p_p_job_heap^^;
            pfv$p_p_attached_pf_table^.table_p^ [apft_index].entry_type := pfc$attached_pf_entry_unused;
            IF apft_index < pfv$p_p_attached_pf_table^.lowest_possible_free_entry THEN
              pfv$p_p_attached_pf_table^.lowest_possible_free_entry := apft_index;
            IFEND;

          = pfc$attached_pf_awaiting_client =
            unrecovered_pf_count := unrecovered_pf_count + 1;
            { The file is actually attached in the device manager and catalog manager sense.
            IF dfv$file_server_debug_enabled THEN
              p_complete_path := pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.
                    p_external_path;
              pfp$convert_cycle_path_to_strng (p_complete_path^, pfv$p_p_attached_pf_table^.
                    table_p^ [apft_index].p_attached_pf_entry^.cycle_number, path_string);
              display (' File not found on client ');
              display (path_string.value (1, path_string.size));
            IFEND;
            { Set the sfid status to 'normal' to allow the standard return to work.
            pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.sfid_status.
                  recovery_state := pfc$attached_pf_normal;
            pfp$internal_return_file (apft_index, mainframe_id, authority, bytes_allocated_change,
                  return_status);
            IF NOT return_status.normal AND dfv$file_server_debug_enabled THEN
              display_status (return_status);
            IFEND;
          ELSE { Normal cycle - Its been recovered.
          CASEND;
        IFEND;
      FOREND /verify_all_files/;
      IF unrecovered_pf_count > 0 THEN
        osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$not_all_pfs_recovered,
              'complete job recovery', status);
        osp$append_status_integer (osc$status_parameter_delimiter, unrecovered_pf_count, 10, FALSE, status);
      IFEND;
    IFEND;
  PROCEND pfp$complete_job_recovery;
?? TITLE := '*** PFP$DETACH_ALL_FILES ***', EJECT ??

  PROCEDURE [XDCL] pfp$detach_all_files
    (    files_binary_mainframe_id: pmt$binary_mainframe_id;
     VAR return_files_option: pft$return_files_option);

    VAR
      apft_index: pft$attached_pf_table_index,
      authority: pft$authority,
      bytes_allocated_change: sft$counter,
      context: ^ost$ecp_exception_context,
      display_status: ost$status,
      entry_p: ^pft$attached_pf_header,
      p_complete_path: ^pft$complete_path,
      path_string: ost$string,
      status: ost$status;

    IF return_files_option.wait_for_down_volume THEN
      PUSH context;
    ELSE
      return_files_option.files_on_down_device := 0;
    IFEND;

    return_files_option.files_returned := 0;

    IF pfv$p_p_attached_pf_table^.table_p <> NIL THEN
      IF return_files_option.log_returned_files THEN
        pmp$log_ascii ('Returning permanent files', $pmt$ascii_logset [pmc$system_log, pmc$job_log],
              pmc$msg_origin_system, display_status);
      IFEND;

    /detach_all_files/
      FOR apft_index := LOWERBOUND (pfv$p_p_attached_pf_table^.table_p^)
            TO UPPERBOUND (pfv$p_p_attached_pf_table^.table_p^) DO
        entry_p := ^pfv$p_p_attached_pf_table^.table_p^ [apft_index];
        IF (entry_p^.entry_type = pfc$attached_pf_entry_valid) AND
              ((entry_p^.p_attached_pf_entry^.sfid_status.recovery_state = pfc$attached_pf_normal) OR
              (entry_p^.p_attached_pf_entry^.sfid_status.recovery_state = pfc$attached_pf_awaiting_client))
              THEN
          IF return_files_option.log_returned_files THEN
            p_complete_path := entry_p^.p_attached_pf_entry^.p_external_path;
            pfp$convert_cycle_path_to_strng (p_complete_path^, entry_p^.p_attached_pf_entry^.cycle_number,
                  path_string);
            pmp$log_ascii (path_string.value (1, path_string.size), $pmt$ascii_logset
                  [pmc$system_log, pmc$job_log], pmc$msg_origin_system, display_status);
          IFEND;

          pfp$internal_return_file (apft_index, files_binary_mainframe_id, authority, bytes_allocated_change,
                status);
          IF status.normal THEN
            return_files_option.files_returned := return_files_option.files_returned + 1;
          ELSEIF osp$file_access_condition (status) THEN
            IF return_files_option.wait_for_down_volume THEN

              context^ := osv$initial_exception_context;

            /wait_for_unavailable_volume/
              REPEAT
                context^.condition_status := status;
                osp$enforce_exception_policies (context^);
                IF context^.wait THEN
                  pfp$internal_return_file (apft_index, files_binary_mainframe_id, authority,
                        bytes_allocated_change, status);
                ELSE
                  status := context^.condition_status;
                IFEND;
              UNTIL status.normal OR (NOT osp$file_access_condition (status)) OR (NOT context^.wait);

              IF status.normal THEN
                return_files_option.files_returned := return_files_option.files_returned + 1;
              ELSE
                pfp$process_unexpected_status (status);
              IFEND;
              ofp$display_status_message ('Continuing: detach_all_files ', display_status);
            ELSE
              return_files_option.files_on_down_device := return_files_option.files_on_down_device + 1;
            IFEND;
          ELSEIF (status.condition <> mme$io_write_error) THEN
            pfp$report_unexpected_status (status);
          IFEND;
        IFEND;
      FOREND /detach_all_files/;
    IFEND;
  PROCEND pfp$detach_all_files;
?? TITLE := '*** PFP$LOCATE_ATTACHED_FILE ***', EJECT ??

{ Note: This procedure assumes that there are no
{ asynchronous attaches, defines, opens (attach_or_create), or
{ returns, occuring in the job during the search.

  PROCEDURE [XDCL] pfp$locate_attached_file
    (    internal_cycle_name: pft$internal_name;
     VAR apfid: pft$attached_pf_table_index;
     VAR p_attached_pf_entry: pft$p_attached_pf_entry;
     VAR cycle_found: boolean);

    VAR
      entry_p: ^pft$attached_pf_header;

    IF pfv$p_p_attached_pf_table^.table_p <> NIL THEN
      FOR apfid := LOWERBOUND (pfv$p_p_attached_pf_table^.table_p^)
            TO UPPERBOUND (pfv$p_p_attached_pf_table^.table_p^) DO
        entry_p := ^pfv$p_p_attached_pf_table^.table_p^ [apfid];
        IF (entry_p^.entry_type = pfc$attached_pf_entry_valid)
{     } AND (entry_p^.p_attached_pf_entry <> NIL)
{     } AND (entry_p^.p_attached_pf_entry^.internal_cycle_path.cycle_name = internal_cycle_name) THEN
          p_attached_pf_entry := entry_p^.p_attached_pf_entry;
          cycle_found := TRUE;
          RETURN; {----->
        IFEND;
      FOREND;
    IFEND;

    cycle_found := FALSE;

  PROCEND pfp$locate_attached_file;

?? TITLE := '*** PFP$LOCK_APFID ***', EJECT ??

{ PURPOSE:
{   This procedure locks an attached pf entry (waits if necessary)
{   This also retrieves the p_attached_pf_entry stored in the table.

  PROCEDURE [XDCL] pfp$lock_apfid
    (    apfid: pft$attached_pf_table_index;
     VAR p_attached_pf_entry: pft$p_attached_pf_entry;
     VAR status: ost$status);

    status.normal := TRUE;
    IF f$valid_apfid (apfid) THEN
      p_attached_pf_entry := pfv$p_p_attached_pf_table^.table_p^ [apfid].p_attached_pf_entry;
    ELSE
      osp$set_status_condition (pfe$invalid_apfid, status);
    IFEND;

  PROCEND pfp$lock_apfid;
?? TITLE := '*** PFP$R2_GET_ATTACHED_PF_TABLE ***', EJECT ??

  PROCEDURE [XDCL, #GATE] pfp$r2_get_attached_pf_table
    (VAR p_info: pft$p_table_info;
     VAR status: ost$status);

    VAR
      afpt_entry: integer,
      p_attached_pf_entry: pft$p_attached_pf_entry,
      p_entry_number: ^integer,
      p_entry_size: ^integer,
      p_external_path: ^pft$path,
      p_record_id: ^pft$record_id,
      p_table: ^pft$attached_pf_table,
      p_table_name: ^pft$table_name,
      p_table_size: ^integer;

    status.normal := TRUE;
    NEXT p_table_name IN p_info;
    IF p_table_name = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'table name', status);
      RETURN; {----->
    IFEND;
    p_table_name^ := 'ATTACHED_PF_TABLE';

    NEXT p_record_id IN p_info;
    IF p_record_id = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
      RETURN; {----->
    IFEND;
    NEXT p_record_id IN p_info;
    IF p_record_id = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
      RETURN; {----->
    IFEND;
    p_record_id^ := 'TABLSIZE';

    NEXT p_table_size IN p_info;
    IF p_table_size = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'table size', status);
      RETURN; {----->
    IFEND;
    IF pfv$p_p_attached_pf_table^.table_p = NIL THEN
      p_table_size^ := 0;
    ELSE
      p_table_size^ := UPPERBOUND (pfv$p_p_attached_pf_table^.table_p^);

      NEXT p_record_id IN p_info;
      IF p_record_id = NIL THEN
        osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
        RETURN; {----->
      IFEND;
      p_record_id^ := 'APFTABLE';

      NEXT p_table: [1 .. p_table_size^] IN p_info;
      IF p_table = NIL THEN
        osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'table', status);
        RETURN; {----->
      IFEND;
      p_table^ := pfv$p_p_attached_pf_table^.table_p^;

    /get_all_entries/
      FOR afpt_entry := 1 TO UPPERBOUND (pfv$p_p_attached_pf_table^.table_p^) DO
        NEXT p_record_id IN p_info;
        IF p_record_id = NIL THEN
          osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
          RETURN; {----->
        IFEND;
        p_record_id^ := 'APFIDNUM';

        NEXT p_entry_number IN p_info;
        IF p_entry_number = NIL THEN
          osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'entry number', status);
          RETURN; {----->
        IFEND;
        p_entry_number^ := afpt_entry;

        NEXT p_record_id IN p_info;
        IF p_record_id = NIL THEN
          osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
          RETURN; {----->
        IFEND;
        p_record_id^ := 'ENTRYSIZ';

        NEXT p_entry_size IN p_info;
        IF p_entry_size = NIL THEN
          osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'entry szie', status);
          RETURN; {----->
        IFEND;
        IF pfv$p_p_attached_pf_table^.table_p^ [afpt_entry].entry_type = pfc$attached_pf_entry_unused THEN
          p_entry_size^ := 0;
        ELSE { valid entry
          p_entry_size^ := UPPERBOUND (pfv$p_p_attached_pf_table^.table_p^ [afpt_entry].p_attached_pf_entry^.
                internal_cycle_path.path);

          NEXT p_attached_pf_entry: [1 .. p_entry_size^] IN p_info;
          IF p_attached_pf_entry = NIL THEN
            osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'attached pf entry',
                  status);
            RETURN; {----->
          IFEND;
          p_attached_pf_entry^ := pfv$p_p_attached_pf_table^.table_p^ [afpt_entry].p_attached_pf_entry^;

          NEXT p_record_id IN p_info;
          IF p_record_id = NIL THEN
            osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'record id', status);
            RETURN; {----->
          IFEND;
          p_record_id^ := 'EXTERCAT';

          NEXT p_external_path: [1 .. p_entry_size^] IN p_info;
          IF p_external_path = NIL THEN
            osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$info_full, 'external', status);
            RETURN; {----->
          IFEND;
          p_external_path^ := pfv$p_p_attached_pf_table^.table_p^ [afpt_entry].p_attached_pf_entry^.
                p_external_path^;
        IFEND;
      FOREND /get_all_entries/;
    IFEND;
  PROCEND pfp$r2_get_attached_pf_table;
?? TITLE := '*** PFP$REATTACH_FILES_FOR_CLIENT ***', EJECT ??
*copyc pfh$reattach_files_for_client

  PROCEDURE [XDCL, #GATE] pfp$reattach_files_for_client
    (    client_mainframe_id: pmt$binary_mainframe_id;
         p_old_attached_pf_table: ^pft$attached_pf_table;
     VAR files_reattached: ost$non_negative_integers;
     VAR files_not_reattached: ost$non_negative_integers;
     VAR status: ost$status);

    VAR
      apfid: pft$attached_permanent_file_id,
      apft_index: pft$attached_pf_table_index,
      new_sfid: gft$system_file_identifier,
      p_complete_path: ^pft$complete_path,
      path_string: ost$string,
      reattach_status: ost$status;

    status.normal := TRUE;
    files_reattached := 0;
    files_not_reattached := 0;
    IF p_old_attached_pf_table = NIL THEN
      RETURN; {----->
    IFEND;
    { Allocate and copy the full table.
    ALLOCATE pfv$p_p_attached_pf_table^.table_p: [1 .. UPPERBOUND (p_old_attached_pf_table^)] IN
          pfv$p_p_job_heap^^;
    IF pfv$p_p_attached_pf_table^.table_p = NIL THEN
      osp$set_status_abnormal (pfc$permanent_file_manager_id, ose$job_severely_damaged,
            'Too many attached files - Allocate expanded apft', status);
      RETURN; {----->
    IFEND;
    pfv$p_p_attached_pf_table^.table_p^ := p_old_attached_pf_table^;
    apfid.family_location := pfc$local_mainframe;

  /reattach_all_files/
    FOR apft_index := LOWERBOUND (p_old_attached_pf_table^) TO UPPERBOUND (p_old_attached_pf_table^) DO
      IF p_old_attached_pf_table^ [apft_index].entry_type = pfc$attached_pf_entry_valid THEN
        IF p_old_attached_pf_table^ [apft_index].p_attached_pf_entry <> NIL THEN
          { Copy the old attached pf table entry to the new attached pf table entry.
          { pfp$reattach_permanent_file will use the new entry
          { by virtue of the fact that the task pointers (pfv$p_p_attached_pf_table^) are
          { pointing to the NEW table.
          ALLOCATE pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry:
                [1 .. UPPERBOUND (p_old_attached_pf_table^ [apft_index].p_attached_pf_entry^.
                internal_cycle_path.path)] IN pfv$p_p_job_heap^^;
          IF pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry = NIL THEN
            osp$set_status_abnormal (pfc$permanent_file_manager_id, ose$job_severely_damaged,
                  '  Cant allocate attached pf entry', status);
            RETURN; {----->
          IFEND;
          pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^ :=
                p_old_attached_pf_table^ [apft_index].p_attached_pf_entry^;
          ALLOCATE pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.p_external_path:
                [1 .. UPPERBOUND (p_old_attached_pf_table^ [apft_index].p_attached_pf_entry^.
                p_external_path^)] IN pfv$p_p_job_heap^^;
          IF pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.p_external_path = NIL THEN
            osp$set_status_abnormal (pfc$permanent_file_manager_id, ose$job_severely_damaged,
                  '  Cant allocate attached external path', status);
            RETURN; {----->
          IFEND;
          pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.p_external_path^ :=
                p_old_attached_pf_table^ [apft_index].p_attached_pf_entry^.p_external_path^;
          apfid.attached_pf_table_index := apft_index;
          IF dfv$file_server_debug_enabled THEN
            p_complete_path := pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.
                  p_external_path;
            pfp$convert_cycle_path_to_strng (p_complete_path^, pfv$p_p_attached_pf_table^.
                  table_p^ [apft_index].p_attached_pf_entry^.cycle_number, path_string);
          IFEND;

        /reattach_permanent_file/
          WHILE TRUE DO
            pfp$reattach_permanent_file (apfid, pfv$p_p_attached_pf_table^.table_p^ [apft_index].
                  p_attached_pf_entry^.device_class, pfv$p_p_attached_pf_table^.table_p^ [apft_index].
                  p_attached_pf_entry^.internal_cycle_path.cycle_name, client_mainframe_id,
                  pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.usage_selections,
                  pfv$p_p_attached_pf_table^.table_p^ [apft_index].p_attached_pf_entry^.share_selections,
                  new_sfid, reattach_status);
            IF reattach_status.normal OR (reattach_status.condition <> pfe$catalog_access_retry) THEN
              EXIT /reattach_permanent_file/; {----->
            ELSE
              pfp$catalog_access_retry_wait ('PFP$REATTACH_PERMANENT_FILE');
            IFEND;
          WHILEND /reattach_permanent_file/;
          IF reattach_status.normal THEN
            files_reattached := files_reattached + 1;
          ELSE
            { Dont issue an error.  The client will discover this file
            {   when it reconciles the file manager table with the attached file table.
            files_not_reattached := files_not_reattached + 1;
            IF dfv$file_server_debug_enabled THEN
              display (path_string.value (1, path_string.size));
              display_status (reattach_status);
            IFEND;
            { The ALLOCATED pieces have been FREED by pfp$reattach_permanent_file
            pfv$p_p_attached_pf_table^.table_p^ [apft_index].entry_type := pfc$attached_pf_entry_unused;
            IF apft_index < pfv$p_p_attached_pf_table^.lowest_possible_free_entry THEN
              pfv$p_p_attached_pf_table^.lowest_possible_free_entry := apft_index;
            IFEND;
          IFEND;
        IFEND;
      IFEND;
    FOREND /reattach_all_files/;

  PROCEND pfp$reattach_files_for_client;
?? TITLE := '*** PFP$RELEASE_LOCKED_APFID *** ', EJECT ??

{ PURPOSE:
{   This procedure releases a previosly assigned attached pf table
{   entry from the attached pf table.
{   This does NOT free the pointer to the attached pf entry.

  PROCEDURE [XDCL] pfp$release_locked_apfid
    (    apfid: pft$attached_pf_table_index;
     VAR status: ost$status);

    status.normal := TRUE;
    IF f$valid_apfid (apfid) THEN
      pfv$p_p_attached_pf_table^.table_p^ [apfid].entry_type := pfc$attached_pf_entry_unused;
      IF apfid < pfv$p_p_attached_pf_table^.lowest_possible_free_entry THEN
        pfv$p_p_attached_pf_table^.lowest_possible_free_entry := apfid;
      IFEND;
    ELSE
      osp$set_status_condition (pfe$invalid_apfid, status);
    IFEND;

  PROCEND pfp$release_locked_apfid;
?? TITLE := '*** PFP$SETUP_ATTACHED_PF_RECOVERY ***', EJECT ??

{ This procedure initiated the permanent file part of recovery of
{ files within a job. This verifies that the attached permanent
{ file table may be read, and marks each attached file, as being in
{ the process of being recovered. The pfp$reattach_permanent_file
{ interface will mark the file as being normal again, and the
{ pfp$complete_job_recovery processing check that all files have been
{ re-attached.

  PROCEDURE [XDCL, #GATE] pfp$setup_attached_pf_recovery
    (    file_recovery_state: pft$attached_pf_recovery_state;
     VAR status: ost$status);

    VAR
      apft_index: pft$attached_pf_table_index,
      display_status: ost$status,
      entry_p: ^pft$attached_pf_header;

    IF pfv$p_p_attached_pf_table^.table_p <> NIL THEN
      FOR apft_index := LOWERBOUND (pfv$p_p_attached_pf_table^.table_p^)
            TO UPPERBOUND (pfv$p_p_attached_pf_table^.table_p^) DO
        entry_p := ^pfv$p_p_attached_pf_table^.table_p^ [apft_index];
        IF (entry_p^.entry_type = pfc$attached_pf_entry_valid) AND (entry_p^.p_attached_pf_entry <> NIL) THEN
          entry_p^.p_attached_pf_entry^.sfid_status.recovery_state := file_recovery_state;
        IFEND;
      FOREND;
    IFEND;

  PROCEND pfp$setup_attached_pf_recovery;
?? TITLE := '*** PFP$UNLOCK_APFID ***', EJECT ??

{ PURPOSE:
{   This procedure stores the pointer to the attached pf entry in the
{   attached pf table, and unlocks the entry.

  PROCEDURE [XDCL] pfp$unlock_apfid
    (    apfid: pft$attached_pf_table_index;
         p_attached_pf_entry: pft$p_attached_pf_entry;
     VAR status: ost$status);

    status.normal := TRUE;
    IF f$valid_apfid (apfid) THEN
      pfv$p_p_attached_pf_table^.table_p^ [apfid].p_attached_pf_entry := p_attached_pf_entry;
    ELSE
      osp$set_status_condition (pfe$invalid_apfid, status);
    IFEND;

  PROCEND pfp$unlock_apfid;
?? TITLE := '     *** GET_UNUSED_APFT_ENTRY ***', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is
{   to get an unused entry in the attached pf table, this includes initially
{   creating the table, expanding the table if necessary, and
{   searching for an unused entry.
{   The only possible error condition is if the job pageable heap
{   is full and the table cannot be expanded.

  PROCEDURE get_unused_apft_entry
    (VAR apfid: pft$attached_pf_table_index;
     VAR status: ost$status);

    CONST
      pfc$expand_apft_amount = 8,
      pfc$initial_apft_size = 30;

    VAR
      p_new_attached_pf_table: pft$p_attached_pf_table,
      p_old_attached_pf_table: pft$p_attached_pf_table,
      space_found: boolean;

?? NEWTITLE := '*** SEARCH_APFT_FOR_UNUSED ***', EJECT ??

{ PURPOSE:
{   This procedure searches the attached pf table looking for an unused
{   table entry.  The unused index number (APFID) is returned.

    PROCEDURE [INLINE] search_apft_for_unused
      (    p_attached_pf_table: pft$p_attached_pf_table;
       VAR apfid: pft$attached_pf_table_index;
       VAR unused_found: boolean);

      IF p_attached_pf_table.table_p <> NIL THEN

      /search_for_unused/
        FOR apfid := p_attached_pf_table.lowest_possible_free_entry TO
              UPPERBOUND (p_attached_pf_table.table_p^) DO
          IF p_attached_pf_table.table_p^ [apfid].entry_type = pfc$attached_pf_entry_unused THEN
            unused_found := TRUE;
            RETURN; {----->
          IFEND;
        FOREND /search_for_unused/;
      IFEND;
      unused_found := FALSE;
    PROCEND search_apft_for_unused;
?? OLDTITLE ??
?? NEWTITLE := '*** INITIALIZE_APFT_TO_UNUSED ***', EJECT ??

{ PURPOSE:
{   This procedure initializes an attached pf table to indicate that
{   all entries are unused.

    PROCEDURE [INLINE] initialize_apft_to_unused
      (VAR p_attached_pf_table: pft$p_attached_pf_table);

      VAR
        apfid: pft$attached_pf_table_index;

      IF p_attached_pf_table.table_p <> NIL THEN
        p_attached_pf_table.lowest_possible_free_entry := 1;

      /initialize_all_entries/
        FOR apfid := LOWERBOUND (p_attached_pf_table.table_p^) TO UPPERBOUND (p_attached_pf_table.table_p^) DO
          p_attached_pf_table.table_p^ [apfid].entry_type := pfc$attached_pf_entry_unused;
        FOREND /initialize_all_entries/;
      IFEND;

    PROCEND initialize_apft_to_unused;
?? OLDTITLE ??
?? NEWTITLE := '*** TRANSFER_OLD_TO_NEW_APFT ***', EJECT ??

{ PURPOSE:
{   This procedure transfers an old attached pf table, to a new one.  The
{   new table must be as large as or larger than the old table.  If the new table
{   is larger, additional entries are initialized to indicate they are unused.

    PROCEDURE [INLINE] transfer_old_to_new_apft
      (    p_old_attached_pf_table: pft$p_attached_pf_table;
       VAR p_new_attached_pf_table: pft$p_attached_pf_table);

      VAR
        apfid: pft$attached_pf_table_index,
        apfid_free: 0 .. 0ffff(16);

      apfid_free := 0;

    /copy_old_entries/
      FOR apfid := LOWERBOUND (p_old_attached_pf_table.table_p^)
            TO UPPERBOUND (p_old_attached_pf_table.table_p^) DO
        p_new_attached_pf_table.table_p^ [apfid] := p_old_attached_pf_table.table_p^ [apfid];
        IF p_new_attached_pf_table.table_p^ [apfid].entry_type = pfc$attached_pf_entry_unused THEN
          apfid_free := apfid;
        IFEND;
      FOREND /copy_old_entries/;

      IF apfid_free = 0 THEN { If we do it right, then it's always 0!
        apfid_free := UPPERBOUND (p_old_attached_pf_table.table_p^) + 1;
      IFEND;

      p_new_attached_pf_table.lowest_possible_free_entry := apfid_free;

    /initialize_new_entries/
      FOR apfid := (UPPERBOUND (p_old_attached_pf_table.table_p^) + 1)
            TO UPPERBOUND (p_new_attached_pf_table.table_p^) DO
        p_new_attached_pf_table.table_p^ [apfid].entry_type := pfc$attached_pf_entry_unused;
      FOREND /initialize_new_entries/;

    PROCEND transfer_old_to_new_apft;
?? OLDTITLE ??
?? EJECT ??

    status.normal := TRUE;
    space_found := FALSE;
    IF pfv$p_p_attached_pf_table^.table_p = NIL THEN
{     ALLOCATE A NEW ATTACHED PF TABLE
      ALLOCATE pfv$p_p_attached_pf_table^.table_p: [1 .. pfc$initial_apft_size] IN pfv$p_p_job_heap^^;
      IF pfv$p_p_attached_pf_table^.table_p = NIL THEN
        osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$pf_system_error,
              'Too many attached files - Allocate initial apft', status);
      ELSE
        initialize_apft_to_unused (pfv$p_p_attached_pf_table^);
        apfid := 1;
      IFEND;
    ELSE
      search_apft_for_unused (pfv$p_p_attached_pf_table^, apfid, space_found);
      IF NOT space_found THEN {expand the old table }
        ALLOCATE p_new_attached_pf_table.table_p: [1 .. (UPPERBOUND (pfv$p_p_attached_pf_table^.table_p^) +
              pfc$expand_apft_amount)] IN pfv$p_p_job_heap^^;
        IF p_new_attached_pf_table.table_p = NIL THEN
          osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$pf_system_error,
                'Too many attached files - Allocate expanded apft', status);
        ELSE
          p_old_attached_pf_table := pfv$p_p_attached_pf_table^;
          apfid := UPPERBOUND (p_old_attached_pf_table.table_p^) + 1;
          transfer_old_to_new_apft (p_old_attached_pf_table, p_new_attached_pf_table);
          pfv$p_p_attached_pf_table^ := p_new_attached_pf_table;
          FREE p_old_attached_pf_table.table_p IN pfv$p_p_job_heap^^;
        IFEND;
      IFEND;
    IFEND;

  PROCEND get_unused_apft_entry;
?? OLDTITLE ??
MODEND pfm$attached_pf_table;
