?? LEFT := 1, RIGHT := 110 ??
MODULE dmm$df_client_requests;
?? TITLE := ' NOS/VE File Server: Client: df_client_requests ', EJECT ??
{  This module contains the device manager code to support the client side of the
{  file server.
{  This includes creating the system file table entry on the client for the server file.
{
?? NEWTITLE := '  Global Declarations ', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc oss$mainframe_pageable
*copyc oss$mainframe_paged_literal
*copyc amc$mau_length
*copyc dfc$partially_rebuilt_fde_eoi
*copyc mmc$null_shared_queue
*copyc osd$integer_limits
*copyc osd$virtual_address
*copyc rmd$volume_declarations
*copyc dfe$error_condition_codes
*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 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 dft$served_family_table
*copyc dft$server_descriptor
*copyc dft$server_file_output
*copyc dmt$create_client_sft_operation
*copyc dmt$disk_file_descriptor
*copyc dmt$error_condition_codes
*copyc dmt$file_attributes
*copyc dmt$file_location
*copyc dmt$file_share_history
*copyc dmt$global_file_name
*copyc dmt$segment_file_information
*copyc gft$file_desc_entry_p
*copyc gft$system_file_identifier
*copyc mmt$rb_ring1_segment_request
*copyc ost$status
*copyc pft$share_selections
*copyc pft$usage_selections
?? TITLE := '  XREF/INLINE Procedures', EJECT ??
*copyc dfp$assign_terminated_gfn
*copyc dfp$fetch_served_family_entry
*copyc dfp$get_served_file_desc_p
*copyc dmp$allocate_file_space_r1
*copyc dmp$clear_master_attach_lock
*copyc dmp$create_fd_entry
*copyc dmp$determine_queue_status
*copyc dmp$generate_gfn_hash
*copyc dmp$get_disk_file_descriptor_p
*copyc dmp$search_fdt_by_gfn
*copyc dmp$set_file_residence
*copyc dmp$set_file_table_locator
*copyc dmp$set_master_attach_lock
*copyc dpp$put_critical_message
*copyc gfp$get_locked_fde_p
*copyc gfp$unlock_fde_p
*copyc i#call_monitor
*copyc i#current_sequence_position
*copyc mmp$preset_conversion
*copyc mmp$issue_ring1_segment_request
*copyc osp$append_status_parameter
*copyc osp$set_status_abnormal
*copyc pmp$cycle
*copyc pmp$delay
*copyc pmp$get_executing_task_gtid
*copyc syp$set_status_from_mtr_status
?? POP ??
?? TITLE := '  Global Variables ', EJECT ??
*copyc dfv$file_server_debug_enabled
*copyc dfv$served_family_table_root

*copyc dmv$idle_system
*copyc osv$page_size
*copyc osv$mainframe_wired_heap

  VAR
    dmv$modify_options: [STATIC, READ, oss$mainframe_paged_literal] pft$usage_selections :=
          $pft$usage_selections [pfc$shorten, pfc$append, pfc$modify];

?? TITLE := ' [XDCL, #GATE] dmp$create_client_sft ', EJECT ??
*copyc dmh$create_client_sft
  PROCEDURE [XDCL, #GATE] dmp$create_client_sft
    (    global_file_name: dmt$global_file_name;
         file_usage: pft$usage_selections;
         file_share_selections: pft$share_selections;
         operation: dmt$create_client_sft_operation;
         dm_parameters: dft$server_file_output;
         served_family_table_index: dft$served_family_table_index;
         server_mainframe_id: pmt$binary_mainframe_id;
     VAR system_file_id: gft$system_file_identifier;
     VAR status: ost$status);

    VAR
      dfd_p: ^dmt$disk_file_descriptor,
      existing_fde_entry_found: boolean,
      file_attributes_p: ^array [1 .. * ] of dmt$file_attribute,
      file_descriptor_entry_p: gft$file_desc_entry_p,
      file_locator: dmt$file_location,
      file_table_residence: gft$table_residence,
      local_status: ost$status,
      queue_status: gft$queue_status,
      rb: mmt$rb_ring1_segment_request,
      served_family_table_entry_p: ^dft$served_family_table_entry,
      server_descriptor_p: dft$server_descriptor_p,
      server_descriptor_r_pointer: ost$relative_pointer,
      transfer_unit_size: amt$file_byte_address;

    IF dmv$idle_system THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_file,
            'system is idle - dmp$create_client_sft ', status);
      RETURN;
    IFEND;

    status.normal := TRUE;

  /process_request/
    BEGIN
      file_table_residence := gfc$tr_system;
      dmp$set_file_table_locator (file_table_residence, file_locator, status);
      IF NOT status.normal THEN
        EXIT /process_request/;
      IFEND;
      system_file_id.residence := file_table_residence;

      dmp$determine_queue_status (gfc$fk_job_permanent_file, file_usage, file_share_selections,
            queue_status, status);
      IF NOT status.normal THEN
        EXIT /process_request/;
      IFEND;

      dmp$generate_gfn_hash (global_file_name, system_file_id.file_hash);

      dmp$set_master_attach_lock (system_file_id);

    /master_attach_lock_set/
      BEGIN

        dmp$search_fdt_by_gfn (file_table_residence, global_file_name, system_file_id.file_entry_index,
              existing_fde_entry_found);
        IF existing_fde_entry_found THEN
          gfp$get_locked_fde_p (system_file_id, file_descriptor_entry_p);

        /sft_entry_locked/
          BEGIN
            IF file_descriptor_entry_p^.global_file_name = global_file_name THEN
              IF file_descriptor_entry_p^.media <> gfc$fm_served_file THEN
                existing_fde_entry_found := FALSE;
                osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_file,
                      'expecting SERVER FILE in dmp$create_client_sft ', status);
                EXIT /sft_entry_locked/;
              IFEND;

              dfp$get_served_file_desc_p (file_descriptor_entry_p, server_descriptor_p);
              IF server_descriptor_p^.header.purged THEN
                existing_fde_entry_found := FALSE;
                osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_file,
                      'SERVER FILE purged/exists in dmp$create_client_sft ', status);
                EXIT /sft_entry_locked/;
              IFEND;

              existing_fde_entry_found := TRUE;
              dfp$fetch_served_family_entry (served_family_table_index,
                   served_family_table_entry_p, status);
              IF NOT status.normal THEN
                EXIT /sft_entry_locked/;
              IFEND;

              CASE server_descriptor_p^.header.file_state OF
              = dfc$terminated =
                { The lifetime has changed since the original attach,
                {  because the server has terminated and become re-active.
                { Change the global_file_name of this entry to an arbirtary
                { value, and create a new sft entry under the correct
                { global file name and lifetime.
                { This assumes that all old references
                { are done via sfid and not global file name.
                { Advance usage count so that this old system file table entry
                { is not removed.  This covers end cases in return.
                { The only negative impact is the pages for this file will
                { never be deleted by return.
                file_descriptor_entry_p^.attach_count := file_descriptor_entry_p^.attach_count + 10;
                IF operation = dmc$complete_job_recovery THEN
                    osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_file,
                      ' Terminated server file - Recovery not possible ', status);
                   EXIT /sft_entry_locked/;
                IFEND;
                dfp$assign_terminated_gfn (
                    file_descriptor_entry_p^.global_file_name,
                    file_descriptor_entry_p^.global_file_name);
                existing_fde_entry_found := FALSE;
                EXIT /sft_entry_locked/;

              = dfc$awaiting_recovery =
                { Despite the fact that the system file table entry was found
                { it is probably not initialized. Complete initialization of
                { the entry, and advance the state to active.
                IF (operation = dmc$complete_job_recovery) OR
                  (operation = dmc$attach_or_create)  THEN
                  { Complete initialization of the file descriptor entry.
                  mmp$preset_conversion (dm_parameters.preset_value, file_descriptor_entry_p^.preset_value);
                  file_descriptor_entry_p^.file_limit := dm_parameters.file_limit;
                  IF file_descriptor_entry_p^.eoi_byte_address = dfc$partially_rebuilt_fde_eoi THEN
                    { Kludge value specified in pfm$r2_df_client_requests
                    file_descriptor_entry_p^.eoi_byte_address := dm_parameters.eoi_byte_address;
                  IFEND;
                  server_descriptor_p^.header.bytes_per_allocation := dm_parameters.bytes_per_allocation;
                  file_descriptor_entry_p^.queue_status := queue_status;

                 { Complete initialization of the server descriptor
                  server_descriptor_p^.header.server_lifetime :=
                      served_family_table_entry_p^.server_lifetime;
                  server_descriptor_p^.header.file_state := dfc$active;
                  server_descriptor_p^.header.total_allocated_length := dm_parameters.total_allocated_length;
                  server_descriptor_p^.header.remote_sfid := dm_parameters.remote_sfid;
                  {  server_descriptor_p^.header.allowed_other_mainframe_writer :=
                  {        dm_parameters.allowed_other_mainframe_writer;
                  server_descriptor_p^.header.allocation_info.allocation_needed_on_server := FALSE;
                  server_descriptor_p^.header.allocation_info.invalid_data := 0;
                  server_descriptor_p^.header.requested_transfer_size :=
                       dm_parameters.requested_transfer_size;
                  server_descriptor_p^.header.read_write_count := 0;
{?}               server_descriptor_p^.header.highest_offset_allocated := dm_parameters.
                       total_allocated_length;

{ Complete adjustment of the file descriptor entry for this served file

                  IF served_family_table_entry_p^.p_queue_interface_table^.maximum_data_bytes <
                        file_descriptor_entry_p^.allocation_unit_size THEN
                    file_descriptor_entry_p^.allocation_unit_size := served_family_table_entry_p^.
                          p_queue_interface_table^.maximum_data_bytes;
                  IFEND;
                IFEND;
              ELSE
                { Active.
              CASEND;

              IF server_descriptor_p^.header.remote_sfid <> dm_parameters.remote_sfid THEN
                { The client and server mainframes got out of sync.
                { This could happen as a result of a terminated server file
                { because the usage count on the client is updated, but the count
                { on the server was not, and in fact the file on the server may have
                { been removed from the system file table and re-attached.
                server_descriptor_p^.header.remote_sfid := dm_parameters.remote_sfid;
              IFEND;
              IF file_descriptor_entry_p^.attach_count = 0 THEN
                file_descriptor_entry_p^.queue_status := queue_status;
                IF dm_parameters.shared_queue <> mmc$null_shared_queue THEN
                  file_descriptor_entry_p^.queue_ordinal :=
                        mmc$pq_shared_last_sys + dm_parameters.shared_queue;
                ELSE
                  file_descriptor_entry_p^.queue_ordinal := mmc$null_shared_queue;
                IFEND;
              IFEND;
              IF (operation <> dmc$complete_job_recovery) OR
                   (file_descriptor_entry_p^.attach_count = 0) THEN
                 file_descriptor_entry_p^.attach_count := file_descriptor_entry_p^.attach_count + 1;
              IFEND;
              IF (file_descriptor_entry_p^.queue_status = gfc$qs_job_shared) AND
                    (file_descriptor_entry_p^.attach_count = 2) THEN
                rb.reqcode := syc$rc_ring1_segment_request;
                rb.request := mmc$sr1_remove_job_shared_pages;
                rb.system_file_id := system_file_id;
                rb.server_file := TRUE;
                rb.status.normal := TRUE;
                mmp$issue_ring1_segment_request (rb);
              IFEND;
              IF operation <> dmc$begin_job_recovery THEN
                IF ((dmv$modify_options * file_usage) <> $pft$usage_selections []) THEN
                  file_descriptor_entry_p^.attached_in_write_count := file_descriptor_entry_p^.
                        attached_in_write_count + 1;
                IFEND;
              IFEND;
            ELSE
              existing_fde_entry_found := FALSE;
            IFEND;

          END /sft_entry_locked/;
          gfp$unlock_fde_p (file_descriptor_entry_p);

          IF NOT status.normal THEN
            EXIT /master_attach_lock_set/;
          IFEND;

        IFEND;

        IF NOT existing_fde_entry_found THEN
           IF operation = dmc$complete_job_recovery THEN
              osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_file,
                    'Expecting file to already exist when completing job recovery ' CAT
                    ' in dmp$create_client_sft ', status);
            EXIT /master_attach_lock_set/;
          IFEND;

{         create new file table entries
          PUSH file_attributes_p: [1 .. 10];
          file_attributes_p^ [1].keyword := dmc$file_hash;
          file_attributes_p^ [1].file_hash := system_file_id.file_hash;
          file_attributes_p^ [2].keyword := dmc$file_kind;
          file_attributes_p^ [2].file_kind := gfc$fk_job_permanent_file;
          file_attributes_p^ [3].keyword := dmc$preset_value;
          file_attributes_p^ [3].preset_value := dm_parameters.preset_value;
          file_attributes_p^ [4].keyword := dmc$clear_space;
{!}       file_attributes_p^ [4].required := FALSE;
          file_attributes_p^ [5].keyword := dmc$file_limit;
          file_attributes_p^ [5].limit := dm_parameters.file_limit;
          file_attributes_p^ [6].keyword := dmc$global_file_name;
          file_attributes_p^ [6].global_file_name := global_file_name;
          file_attributes_p^ [7].keyword := dmc$locked_file;
          file_attributes_p^ [7].file_lock.required := FALSE;
          file_attributes_p^ [7].file_lock.read_lock_count := 0;
          file_attributes_p^ [7].file_lock.write_lock := dmc$no_write_lock;
          file_attributes_p^ [8].keyword := dmc$write_mode;
          file_attributes_p^ [8].attached_in_write_mode := ((dmv$modify_options * file_usage) <>
                $pft$usage_selections []);
          file_attributes_p^ [9].keyword := dmc$queue_status;
          file_attributes_p^ [9].queue_status := queue_status;
          file_attributes_p^ [10].keyword := dmc$eoi_byte_address;
          file_attributes_p^ [10].eoi_address := dm_parameters.eoi_byte_address;

          dmp$create_fd_entry (file_attributes_p, system_file_id, status);
          IF NOT status.normal THEN
            EXIT /master_attach_lock_set/;
          IFEND;

        /file_descriptor_created/
          BEGIN
            gfp$get_locked_fde_p (system_file_id, file_descriptor_entry_p);

            file_descriptor_entry_p^.attach_count := 1;
            IF dm_parameters.shared_queue <> mmc$null_shared_queue THEN
              file_descriptor_entry_p^.queue_ordinal := mmc$pq_shared_last_sys + dm_parameters.shared_queue;
            ELSE
              file_descriptor_entry_p^.queue_ordinal := mmc$null_shared_queue;
            IFEND;

            dmp$clear_master_attach_lock (system_file_id);

            build_server_descriptor (operation,
                  dm_parameters, served_family_table_index, server_mainframe_id,
                  dfv$served_family_table_root.p_family_list_pointer_array^ [
                  served_family_table_index.pointers_index].p_served_family_list^ [
                  served_family_table_index.family_list_index].server_lifetime,
                  server_descriptor_r_pointer);

            dmp$get_disk_file_descriptor_p (file_descriptor_entry_p, dfd_p);
            file_descriptor_entry_p^.served_file_descriptor_p := server_descriptor_r_pointer;
            file_descriptor_entry_p^.media := gfc$fm_served_file;
            dmp$set_file_table_locator (system_file_id.residence, file_locator, status);
            FREE dfd_p IN file_locator^;
            dfp$get_served_file_desc_p (file_descriptor_entry_p, server_descriptor_p);
            server_descriptor_p^.header.bytes_per_allocation := dm_parameters.bytes_per_allocation;

{ Complete adjustment of the file descriptor entry for this served file IF the server is active.

            IF dfv$served_family_table_root.p_family_list_pointer_array^ [
                  served_family_table_index.pointers_index].p_served_family_list^ [
                  served_family_table_index.family_list_index].server_state = dfc$active THEN
              transfer_unit_size := dfv$served_family_table_root.p_family_list_pointer_array^ [
                    served_family_table_index.pointers_index].p_served_family_list^ [
                    served_family_table_index.family_list_index].p_queue_interface_table^.maximum_data_bytes;
              IF transfer_unit_size < file_descriptor_entry_p^.allocation_unit_size THEN
                file_descriptor_entry_p^.allocation_unit_size := transfer_unit_size;
              IFEND;
            IFEND;
            gfp$unlock_fde_p (file_descriptor_entry_p);
            EXIT /process_request/; { <-------------------<<< NORMAL EXIT <---------------<<< }

          END /file_descriptor_created/;

        IFEND;

      END /master_attach_lock_set/;

      dmp$clear_master_attach_lock (system_file_id);

    END /process_request/;

  PROCEND dmp$create_client_sft;

?? TITLE := ' [XDCL, #GATE] dmp$fixup_client_file_length ', EJECT ??
{
{   The purpose of this routine is to fixup a file on the client following
{ a failure of the server mainframe.  This means that the eoi is updated
{ on the client with any pages written on the client but that did not make
{ it to the server.  Any pages on the client not yet on the server are
{ allocated.
  PROCEDURE [XDCL, #GATE] dmp$fixup_client_file_length
    (    sfid: gft$system_file_identifier;
     VAR status: ost$status);

    VAR
      bytes_to_allocate: amt$file_byte_address,
      current_allocated_length: amt$file_byte_address,
      fde_p: gft$file_desc_entry_p,
      message: string (80),
      message_length: integer,
      next_page: amt$file_byte_address,
      server_descriptor_p: dft$server_descriptor_p,
      rb: mmt$rb_ring1_segment_request;

    gfp$get_locked_fde_p (sfid, fde_p);
    IF fde_p^.attached_in_write_count = 0 THEN
      gfp$unlock_fde_p (fde_p);
      RETURN;
    IFEND;
    dfp$get_served_file_desc_p (fde_p, server_descriptor_p);

    rb.status.normal := TRUE;
    rb.reqcode := syc$rc_ring1_segment_request;
    rb.request := mmc$sr1_get_highest_offset;
    rb.file_sfid := sfid;
    i#call_monitor (#LOC (rb), #SIZE (rb));
    syp$set_status_from_mtr_status (rb.status, status);
    IF NOT status.normal THEN
      gfp$unlock_fde_p (fde_p);
      RETURN;
    IFEND;

    current_allocated_length := server_descriptor_p^.header.total_allocated_length;
    IF dfv$file_server_debug_enabled THEN
      STRINGREP (message, message_length, 'SFID', sfid.file_entry_index:#(16), '(16): eoi',
            fde_p^.eoi_byte_address:#(16), '(16), high', rb.highest_offset:#(16), '(16), length',
            current_allocated_length:#(16), '(16)');
      dpp$put_critical_message (message (1, message_length), status);
    IFEND;

    next_page := rb.highest_offset + osv$page_size;
    IF (rb.highest_offset >= fde_p^.eoi_byte_address) THEN
      fde_p^.eoi_byte_address := next_page;
    IFEND;

    gfp$unlock_fde_p (fde_p);

  /allocate_required_bytes/
    WHILE (next_page > current_allocated_length) DO
      bytes_to_allocate := next_page - current_allocated_length;
      IF dfv$file_server_debug_enabled THEN
        STRINGREP (message, message_length, ' Fixup allocate cal, bytes ', sfid.file_entry_index,
              current_allocated_length, bytes_to_allocate);
        dpp$put_critical_message (message (1, message_length), status);
      IFEND;
      dmp$allocate_file_space_r1 (sfid, current_allocated_length, bytes_to_allocate, { chapter_number } 0,
            osc$wait, sfc$no_limit, status);
      #SPOIL (server_descriptor_p^.header.total_allocated_length);
      current_allocated_length := server_descriptor_p^.header.total_allocated_length;
      IF (next_page > current_allocated_length) THEN
        pmp$delay (1000, status);
      IFEND;
    WHILEND /allocate_required_bytes/;

  PROCEND dmp$fixup_client_file_length;

?? TITLE := ' [XDCL, #GATE] dmp$replace_client_sft ', EJECT ??
{
{   The purpose of this routine is to replace the old client SFT entry
{ with a new client SFT entry when the data for an attached served file
{ has been recreated as the result of an exception condition.

  PROCEDURE [XDCL, #GATE] dmp$replace_client_sft
    (    old_global_file_name: dmt$global_file_name;
         new_global_file_name: dmt$global_file_name;
         new_remote_sfid: gft$system_file_identifier;
     VAR new_client_sfid: gft$system_file_identifier;
     VAR status: ost$status);

    VAR
      file_attributes_p: ^array [1 .. * ] of dmt$file_attribute,
      file_locator: dmt$file_location,
      new_fde_p: gft$file_desc_entry_p,
      new_server_descriptor_p: dft$server_descriptor_p,
      old_client_sfid: gft$system_file_identifier,
      old_fde_entry_found: boolean,
      old_fde_p: gft$file_desc_entry_p;

    dmp$set_file_table_locator (gfc$tr_system, file_locator, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    old_client_sfid.residence := gfc$tr_system;
    dmp$generate_gfn_hash (old_global_file_name, old_client_sfid.file_hash);

    dmp$search_fdt_by_gfn (gfc$tr_system, old_global_file_name, old_client_sfid.file_entry_index,
          old_fde_entry_found);
    IF NOT old_fde_entry_found THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_file,
            'expecting client file to exist in dmp$replace_client_sft', status);
      RETURN;
    IFEND;

    gfp$get_locked_fde_p (old_client_sfid, old_fde_p);
  /old_fde_locked/
    BEGIN
      IF old_fde_p^.media <> gfc$fm_served_file THEN
        osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_file,
              'expecting SERVER FILE in dmp$replace_client_sft', status);
        EXIT /old_fde_locked/;
      IFEND;
      new_client_sfid.residence := gfc$tr_system;
      dmp$generate_gfn_hash (new_global_file_name, new_client_sfid.file_hash);

      dmp$set_master_attach_lock (new_client_sfid);
    /master_attach_lock_set/
      BEGIN
        PUSH file_attributes_p: [1 .. 9];
        file_attributes_p^ [1].keyword := dmc$file_hash;
        file_attributes_p^ [1].file_hash := new_client_sfid.file_hash;
        file_attributes_p^ [2].keyword := dmc$file_kind;
        file_attributes_p^ [2].file_kind := gfc$fk_job_permanent_file;
        file_attributes_p^ [3].keyword := dmc$clear_space;
        file_attributes_p^ [3].required := FALSE;
        file_attributes_p^ [4].keyword := dmc$file_limit;
        file_attributes_p^ [4].limit := old_fde_p^.file_limit;
        file_attributes_p^ [5].keyword := dmc$global_file_name;
        file_attributes_p^ [5].global_file_name := new_global_file_name;
        file_attributes_p^ [6].keyword := dmc$locked_file;
        file_attributes_p^ [6].file_lock.required := FALSE;
        file_attributes_p^ [6].file_lock.read_lock_count := 0;
        file_attributes_p^ [6].file_lock.write_lock := dmc$no_write_lock;
        file_attributes_p^ [7].keyword := dmc$write_mode;
        file_attributes_p^ [7].attached_in_write_mode := TRUE;
        file_attributes_p^ [8].keyword := dmc$queue_status;
        file_attributes_p^ [8].queue_status := old_fde_p^.queue_status;
        file_attributes_p^ [9].keyword := dmc$eoi_byte_address;
        file_attributes_p^ [9].eoi_address := old_fde_p^.eoi_byte_address;

        dmp$create_fd_entry (file_attributes_p, new_client_sfid, status);
        IF NOT status.normal THEN
          EXIT /master_attach_lock_set/;
        IFEND;

        gfp$get_locked_fde_p (new_client_sfid, new_fde_p);

        new_fde_p^.job_lock := old_fde_p^.job_lock;
        new_fde_p^.monitor_lock := old_fde_p^.monitor_lock;
        new_fde_p^.flags := old_fde_p^.flags;
        new_fde_p^.attach_count := old_fde_p^.attach_count;
        new_fde_p^.open_count := old_fde_p^.open_count;
        new_fde_p^.segment_lock := old_fde_p^.segment_lock;
        new_fde_p^.asti := old_fde_p^.asti;
        new_fde_p^.eoi_state := old_fde_p^.eoi_state;
        new_fde_p^.allocation_unit_size := old_fde_p^.allocation_unit_size;
        new_fde_p^.transfer_unit_size := old_fde_p^.transfer_unit_size;
        new_fde_p^.queue_ordinal := old_fde_p^.queue_ordinal;
        new_fde_p^.preset_value := old_fde_p^.preset_value;
        new_fde_p^.time_last_modified := old_fde_p^.time_last_modified;
        new_fde_p^.last_segment_number := old_fde_p^.last_segment_number;
        new_fde_p^.global_task_id := old_fde_p^.global_task_id;
        new_fde_p^.stack_for_ring := old_fde_p^.stack_for_ring;
        new_fde_p^.media := old_fde_p^.media;
        new_fde_p^.served_file_descriptor_p := old_fde_p^.served_file_descriptor_p;

        dfp$get_served_file_desc_p (new_fde_p, new_server_descriptor_p);
        new_server_descriptor_p^.header.remote_sfid := new_remote_sfid;

        gfp$unlock_fde_p (new_fde_p);

      END /master_attach_lock_set/;
      dmp$clear_master_attach_lock (new_client_sfid);

    END /old_fde_locked/;
    gfp$unlock_fde_p (old_fde_p);

  PROCEND dmp$replace_client_sft;

?? TITLE := ' [XDCL, #GATE] dmp$set_file_state ', EJECT ??
{ Warning.
{ This only sets the state to terminated if there is only one user.
{  This procedure executes on the client mainframe and is used to mark
{  a server file as terminated.

  PROCEDURE [XDCL, #GATE] dmp$set_file_state
    (    global_file_name: dmt$global_file_name;
         file_state: dft$server_state;
     VAR status: ost$status);

    VAR
      existing_fde_entry_found: boolean,
      file_descriptor_entry_p: gft$file_desc_entry_p,
      file_locator: dmt$file_location,
      file_table_residence: gft$table_residence,
      local_status: ost$status,
      server_descriptor_p: dft$server_descriptor_p,
      system_file_id: gft$system_file_identifier;

    status.normal := TRUE;
    file_table_residence := gfc$tr_system;
    dmp$set_file_table_locator (file_table_residence, file_locator, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    system_file_id.residence := file_table_residence;

    dmp$generate_gfn_hash (global_file_name, system_file_id.file_hash);

    dmp$search_fdt_by_gfn (file_table_residence, global_file_name, system_file_id.file_entry_index,
          existing_fde_entry_found);
    IF existing_fde_entry_found THEN
      gfp$get_locked_fde_p (system_file_id, file_descriptor_entry_p);

      IF file_descriptor_entry_p^.global_file_name = global_file_name THEN
        IF file_descriptor_entry_p^.media = gfc$fm_served_file THEN
          dfp$get_served_file_desc_p (file_descriptor_entry_p, server_descriptor_p);
          IF NOT server_descriptor_p^.header.purged THEN
            IF (file_state = dfc$terminated) THEN
              IF (file_descriptor_entry_p^.attach_count <= 1) THEN

{ We do not want to change the state to terminated if there are other users of the file.  Depend upon the job
{ setting the sdtx access state to terminate access, and also to set the lifetime in the
{ attached_permanent_file_id to indicate that the file is terminated.

                server_descriptor_p^.header.file_state := file_state;
              IFEND;

{ Advance usage count so that this old system file table entry is not removed.  This covers end cases in
{ RETURN.

              file_descriptor_entry_p^.attach_count := file_descriptor_entry_p^.attach_count + 10;
            ELSE { State other than terminated
              server_descriptor_p^.header.file_state := file_state;
            IFEND;
          ELSE
            osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_file,
                  'SERVER FILE purged/exists in dmp$set_file_state ', status);
          IFEND;
        ELSE
          osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_attach_file,
                'expecting SERVER FILE in dmp$set_file_state ', status);
        IFEND;
      IFEND;
      gfp$unlock_fde_p (file_descriptor_entry_p);
    IFEND;

  PROCEND dmp$set_file_state;
?? TITLE := ' [XDCL, #GATE] dmp$terminate_server_file_list ', EJECT ??
{
{  This  client procedure marks a list of server files as terminated.
{ This procedure returns a count of the number of files found that were
{ terminated as well as a count of the number of files that could not be
{ terminated.
{ There should be no pages to remove when this request is called.
{
  PROCEDURE [XDCL, #GATE] dmp$terminate_server_file_list
    (    global_file_name_list: array [1 .. * ] of dmt$global_file_name;
     VAR files_terminated: ost$non_negative_integers;
     VAR files_not_terminated: ost$non_negative_integers);

    VAR
      existing_fde_entry_found: boolean,
      file: gft$file_descriptor_index,
      p_file_descriptor_entry: gft$file_desc_entry_p,
      p_server_descriptor: dmt$p_server_descriptor,
      status: ost$status,
      system_file_id: gft$system_file_identifier;

    status.normal := TRUE;
    files_terminated := 0;
    files_not_terminated := 0;
    system_file_id.residence := gfc$tr_system;

  /terminate_all_files/
    FOR file := 1 TO UPPERBOUND (global_file_name_list) DO

      dmp$generate_gfn_hash (global_file_name_list [file], system_file_id.file_hash);

      dmp$search_fdt_by_gfn (gfc$tr_system, global_file_name_list [file],
            system_file_id.file_entry_index, existing_fde_entry_found);
      IF existing_fde_entry_found THEN
        gfp$get_locked_fde_p (system_file_id, p_file_descriptor_entry);
        IF (p_file_descriptor_entry^.global_file_name =
              global_file_name_list [file]) AND (p_file_descriptor_entry^.media = gfc$fm_served_file) THEN
          dfp$get_served_file_desc_p (p_file_descriptor_entry, p_server_descriptor);
          IF NOT p_server_descriptor^.header.purged THEN
            p_server_descriptor^.header.file_state := dfc$terminated;
            files_terminated := files_terminated + 1;
          ELSE
            files_not_terminated := files_not_terminated + 1;
          IFEND;
        ELSE
          files_not_terminated := files_not_terminated + 1;
        IFEND;
        gfp$unlock_fde_p (p_file_descriptor_entry);
      ELSE
        files_not_terminated := files_not_terminated + 1;
      IFEND;
    FOREND /terminate_all_files/;
  PROCEND dmp$terminate_server_file_list;
?? TITLE := ' [INLINE] build_server_descriptor ', EJECT ??

  PROCEDURE [INLINE] build_server_descriptor
    (    operation: dmt$create_client_sft_operation;
         dm_parameters: dft$server_file_output;
         served_family_table_index: dft$served_family_table_index;
         server_mainframe_id: pmt$binary_mainframe_id;
         server_lifetime: dft$server_lifetime;
     VAR server_descriptor_r_pointer: ost$relative_pointer);

    VAR
      server_descriptor_p: dft$server_descriptor_p;

    ALLOCATE server_descriptor_p IN osv$mainframe_wired_heap^;
    IF operation = dmc$begin_job_recovery THEN
      server_descriptor_p^.header.file_state := dfc$awaiting_recovery;
    ELSE
      server_descriptor_p^.header.file_state := dfc$active;
      server_descriptor_p^.header.total_allocated_length := dm_parameters.total_allocated_length;
      server_descriptor_p^.header.remote_sfid := dm_parameters.remote_sfid;
      {  server_descriptor_p^.header.allowed_other_mainframe_writer :=
      {        dm_parameters.allowed_other_mainframe_writer;
      server_descriptor_p^.header.allocation_info.allocation_needed_on_server := FALSE;
      server_descriptor_p^.header.allocation_info.invalid_data := 0;
      server_descriptor_p^.header.requested_transfer_size := dm_parameters.requested_transfer_size;
    IFEND;
    server_descriptor_p^.header.read_write_count := 0;
    server_descriptor_p^.header.purged := FALSE;
{?} server_descriptor_p^.header.highest_offset_allocated := dm_parameters.total_allocated_length;
    server_descriptor_p^.header.server_mainframe_id := server_mainframe_id;
    server_descriptor_p^.header.server_lifetime := server_lifetime;
    server_descriptor_p^.header.served_family_table_index := served_family_table_index;

    server_descriptor_r_pointer := #OFFSET (server_descriptor_p);

  PROCEND build_server_descriptor;
?? TITLE := ' remove_file_pages ', EJECT ??
  PROCEDURE remove_files_pages
    (    system_file_id: gft$system_file_identifier;
     VAR status: ost$status);

    VAR
      p_rb_ring1_segment_request: ^mmt$rb_ring1_segment_request;

    PUSH p_rb_ring1_segment_request;
    p_rb_ring1_segment_request^.reqcode := syc$rc_ring1_segment_request;
    p_rb_ring1_segment_request^.sfid := system_file_id;
    p_rb_ring1_segment_request^.wait_for_io_complete := FALSE;
    p_rb_ring1_segment_request^.status.normal := TRUE;
    p_rb_ring1_segment_request^.request := mmc$sr1_delete_job_seg_by_sfid;
    mmp$issue_ring1_segment_request (p_rb_ring1_segment_request^);
    syp$set_status_from_mtr_status (p_rb_ring1_segment_request^.status, status);
  PROCEND remove_files_pages;
?? OLDTITLE, OLDTITLE ??
MODEND dmm$df_client_requests;
