?? RIGHT := 110 ??
MODULE fmm$pf_utility_label;
{
{   This module contains those interfaces that build and restore the label as
{ passed to the permanent file utilities and the permanent file transfer
{ facility.  This is different than the label passed to the permanent
{ file manager, as it contains checksums to validate the data.
{   The label is an adaptable sequence and the format for the sequence is:
{       checksum of label
{       fmt$static_label_header
{       if static_label_size > 0 then
{         fmt$static_label_item
{       if job_label_size > 0 then
{         bat$route_descriptor
{
?? PUSH (LISTEXT := ON) ??
*copyc ame$attribute_validation_errors
*copyc ame$ring_validation_errors
*copyc fmc$current_revision_level
*copyc fmc$unique_label_id
*copyc fme$file_management_errors
*copyc fmt$basic_file_label
*copyc fmt$file_attribute_keys
*copyc fmt$label_headers
*copyc osd$virtual_address
*copyc ose$heap_full_exceptions
?? POP ??
*copyc avp$system_administrator
*copyc bap$validate_compatibility
*copyc clp$validate_name
*copyc fmi$put_job_routing_label
*copyc fmi$put_label_in_lnt
*copyc fmp$catalog_system_file_label
*copyc fmp$expand_v1_label
*copyc fmp$get_cycle_description
*copyc fmp$get_path_string
*copyc fmp$put_label_attributes
*copyc fmp$unlock_path_table
*copyc fmp$verify_attribute_limits
*copyc fmv$system_file_attributes
*copyc fmv$static_label_header
*copyc i#move
*copyc jmp$system_job
*copyc osp$append_status_file
*copyc osp$append_status_parameter
*copyc osp$enforce_exception_policies
*copyc osp$file_access_condition
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osv$initial_exception_context
*copyc osv$job_pageable_heap
*copyc pfp$compute_checksum

?? TITLE := 'fmp$fetch_system_label', EJECT ??

  PROCEDURE [XDCL, #GATE] fmp$fetch_system_label (local_file_name:
    amt$local_file_name;
    VAR label: SEQ ( * );
    VAR status: ost$status);

    VAR
      cycle_description: ^fmt$cycle_description,
      i: integer,
      job_label: ^SEQ ( * ),
      job_label_size: jmt$system_label_info_length,
      p_checksum: ^integer,
      p_label: ^SEQ ( * ),
      start_of_label: ^cell,
      static_label: ^SEQ ( * ),
      static_label_header: ^fmt$static_label_header,
      static_label_size: integer,
      total_label_size: 0 .. 7fffffff(16);

    fmp$get_cycle_description (local_file_name, cycle_description, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  /begin_end/
    BEGIN
      total_label_size := 0;
      get_label_size (cycle_description, job_label_size, total_label_size,
            status);
      IF NOT status.normal THEN
        EXIT /begin_end/;
      IFEND;

      p_label := ^label;
      RESET p_label;
      NEXT p_checksum IN p_label;
      IF cycle_description^.system_file_label.static_label <> NIL THEN
        static_label_size := total_label_size - #SIZE (integer) -
              job_label_size;
        NEXT static_label: [[REP static_label_size OF cell]] IN p_label;
        IF static_label = NIL THEN
          osp$set_status_abnormal (amc$access_method_id, fme$system_error,
                ' - the label container passed to fmp$fetch_system_label is too small to hold the file label',
                status);
          EXIT /begin_end/;
        IFEND;
        static_label^ := cycle_description^.system_file_label.
              static_label^;
      ELSE
        NEXT static_label_header IN p_label;
        static_label_header^ := fmv$static_label_header;
        static_label_header^.job_routing_label_size := job_label_size;
        static_label_header^.file_previously_opened := cycle_description^.
              system_file_label.file_previously_opened;
      IFEND;

      IF job_label_size > 0 THEN
        NEXT job_label: [[REP job_label_size OF cell]] IN p_label;
        job_label^ := cycle_description^.job_routing_label^;
      IFEND;

      RESET p_label;
      NEXT p_checksum IN p_label;
      NEXT start_of_label IN p_label;
      pfp$compute_checksum (start_of_label, total_label_size - #SIZE (integer),
            p_checksum^);

    END /begin_end/;
    fmp$unlock_path_table;

  PROCEND fmp$fetch_system_label;

?? TITLE := 'fmp$fetch_system_label_size', EJECT ??

  PROCEDURE [XDCL, #GATE] fmp$fetch_system_label_size
    (    local_file_name: amt$local_file_name;
     VAR label_size: 0 .. 7fffffff(16);
     VAR status: ost$status);

{ This procedure fetches the size of the label as given to the pf backup
{ utility and permanent file transfer facility.

    VAR
      cycle_description: ^fmt$cycle_description,
      job_label_size: jmt$system_label_info_length;

    label_size := 0;
    fmp$get_cycle_description (local_file_name, cycle_description, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    IF cycle_description = NIL THEN
      osp$set_status_abnormal (amc$access_method_id, fme$no_preserved_attributes,
            ' file is not attached - fmp$fetch_system_label_size', status);
      RETURN; {----->
    IFEND;

    get_label_size (cycle_description, job_label_size, label_size, status);
    fmp$unlock_path_table;

  PROCEND fmp$fetch_system_label_size;

?? TITLE := 'fmp$store_system_label', EJECT ??

  PROCEDURE [XDCL, #GATE] fmp$store_system_label
    (   local_file_name: amt$local_file_name;
        validation_ring: ost$valid_ring;
        label: SEQ ( * );
    VAR status: ost$status);

    CONST
      checksum_present = TRUE;

    VAR
      context: ^ost$ecp_exception_context,
      cycle_description: ^fmt$cycle_description,
      expanded_static_label: bat$static_label_attributes,
      file_path: fst$path,
      file_path_size: fst$path_size,
      file_returned: boolean,
      ignore_status: ost$status,
      job_label_header: fmt$job_label_header,
      job_label_size: jmt$system_label_info_length,
      p_file_label_header: ^fmt$static_label_header,
      p_job_label: ^SEQ ( * ),
      p_label: ^SEQ ( * ),
      p_stored_checksum: ^integer,
      static_label_header: ^fmt$static_label_header,
      valid_checksum: boolean,
      v1_header: fmt$static_bam_label_header,
      v1_file_previously_opened: boolean;

    context := NIL;

  /begin_end/
    BEGIN
      fmp$get_cycle_description (local_file_name, cycle_description, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      IF cycle_description = NIL THEN
        osp$set_status_abnormal (amc$access_method_id, fme$system_error,
              ' file is not attached in fmp$store_system_label', status);
        RETURN;
      IFEND;

      fmp$validate_system_label (label, validation_ring, valid_checksum, status);
      IF NOT status.normal THEN
        EXIT /begin_end/;
      IFEND;

      p_label := ^label;
      RESET p_label;
      IF valid_checksum THEN
        NEXT p_stored_checksum IN p_label;
        NEXT p_file_label_header IN p_label;
        job_label_size := p_file_label_header^.job_routing_label_size;
        IF p_file_label_header^.highest_attribute_present > 0 THEN
          IF p_file_label_header^.revision_level <> fmc$current_revision_level THEN
            bap$validate_compatibility (p_label, p_file_label_header, cycle_description^.path_handle,
                  checksum_present, status);
            IF NOT status.normal THEN
              EXIT /begin_end/;
            IFEND;
          IFEND;

          RESET p_label;
          NEXT p_stored_checksum IN p_label;
          fmi$put_label_in_lnt (checksum_present, p_file_label_header, p_label, cycle_description,
                status);
          IF NOT status.normal THEN
            EXIT /begin_end/;
          IFEND;

          fmp$verify_attribute_limits (cycle_description^. system_file_label.static_label, status);
          IF NOT status.normal THEN
            EXIT /begin_end/;
          IFEND;

          IF job_label_size > 0 THEN
            NEXT p_job_label: [[REP job_label_size OF cell]] IN p_label;
          IFEND;
        ELSE { highest attribute present = 0 }
          cycle_description^.system_file_label.static_label := NIL;
        IFEND;

        cycle_description^.system_file_label.file_previously_opened :=
              p_file_label_header^.file_previously_opened;
      ELSE { possible v1 label }
        RESET p_label;
        get_static_label_header (p_label, v1_header, status);
        IF NOT status.normal THEN
          EXIT /begin_end/;
        IFEND;

        IF v1_header.size > 0 THEN
          get_expanded_static_label (p_label, expanded_static_label,
                v1_file_previously_opened, status);
          IF NOT status.normal THEN
            EXIT /begin_end/;
          IFEND;

          cycle_description^.system_file_label.file_previously_opened :=
                v1_file_previously_opened;
          fmp$put_label_attributes (expanded_static_label,
                cycle_description^.system_file_label);
        IFEND;

        get_job_label_header (p_label, job_label_header, status);
        IF NOT status.normal THEN
          EXIT /begin_end/;
        IFEND;

        job_label_size := job_label_header.size;
        IF job_label_size > 0 THEN
          get_job_label (p_label, job_label_header.size, p_job_label, status);
          IF NOT status.normal THEN
            EXIT /begin_end/;
          IFEND;
        IFEND;

        RESET cycle_description^.system_file_label.static_label;
        NEXT p_file_label_header IN cycle_description^.system_file_label.
              static_label;
        p_file_label_header^.job_routing_label_size := job_label_size;
      IFEND;

      IF cycle_description^.attached_file AND (cycle_description^.device_class =
            rmc$mass_storage_device) THEN
        IF cycle_description^.job_routing_label <> NIL THEN
          FREE cycle_description^.job_routing_label IN
                osv$job_pageable_heap^;
          cycle_description^.job_routing_label := NIL;
        IFEND;
        cycle_description^.job_routing_label_length := 0;

        IF job_label_size > 0 THEN
          fmi$put_job_routing_label (job_label_size, p_job_label,
                cycle_description, status);
          IF NOT status.normal THEN
            EXIT /begin_end/;
          IFEND;
        IFEND;

        IF cycle_description^.permanent_file THEN
          IF (cycle_description^.system_file_label.static_label <> NIL) OR
                cycle_description^.system_file_label.file_previously_opened OR
                (cycle_description^.job_routing_label <> NIL) THEN
            REPEAT
              fmp$catalog_system_file_label (^cycle_description^.
                    system_file_label, cycle_description^.job_routing_label,
                    cycle_description^.job_routing_label_length,
                    cycle_description^.apfid, pfc$append, status);
              IF NOT status.normal THEN
                IF context = NIL THEN
                  PUSH context;
                  context^ := osv$initial_exception_context;
                IFEND;
                context^.condition_status := status;
                osp$enforce_exception_policies (context^);
                status := context^.condition_status;
              IFEND;
            UNTIL status.normal OR (NOT osp$file_access_condition (status)) OR (NOT context^.wait);
            IF status.normal THEN
              cycle_description^.system_file_label_catalogued := TRUE;
            IFEND;
          IFEND;
        IFEND;
      IFEND;
    END /begin_end/;

    fmp$unlock_path_table;
  PROCEND fmp$store_system_label;

?? TITLE := 'fmp$validate_system_label', EJECT ??

  PROCEDURE [XDCL] fmp$validate_system_label
    (    label: SEQ ( * );
         validation_ring: ost$valid_ring;
     VAR valid_checksum: boolean;
     VAR status: ost$status);

    VAR
      computed_checksum: pft$checksum,
      p_file_label_header: ^fmt$static_label_header,
      p_local_label: ^fmt$file_label,
      p_stored_checksum: ^pft$checksum;

    status.normal := TRUE;
    valid_checksum := FALSE;

    p_local_label := ^label;
    RESET p_local_label;
    NEXT p_stored_checksum IN p_local_label;
    IF p_stored_checksum = NIL THEN
      osp$set_status_abnormal (amc$access_method_id, ame$damaged_file_attributes, 'static label checksum',
            status);
      RETURN;
    IFEND;

    NEXT p_file_label_header IN p_local_label;
    IF p_file_label_header = NIL THEN
      osp$set_status_abnormal (amc$access_method_id, ame$damaged_file_attributes, 'p_file_label_header',
            status);
      RETURN;
    IFEND;

    pfp$compute_checksum (p_file_label_header, #SIZE (label) - #SIZE (pft$checksum), computed_checksum);
    IF computed_checksum = p_stored_checksum^ THEN
      valid_checksum := TRUE;
      IF p_file_label_header^.unique_character = fmc$unique_label_id THEN

{ Make sure the job has sufficient privilege to restore the file.  The system
{ job or the system administrator can restore all files.  Other jobs must
{ have minimum ring privilege equal to or less than R1 of the file to be
{ restored.

        IF p_file_label_header^.file_previously_opened AND
              (validation_ring > p_file_label_header^.ring_attributes.r1) THEN
          osp$set_status_condition (ame$ring_validation_error, status);
          RETURN;
        IFEND;

      ELSE { unique character <> fmc$unique_label_id }
        osp$set_status_abnormal (amc$access_method_id, ame$damaged_file_attributes, 'static label', status);
        RETURN;
      IFEND;
    IFEND;
  PROCEND fmp$validate_system_label;

?? TITLE := 'get_expanded_static_label', EJECT ??

  PROCEDURE [INLINE] get_expanded_static_label (VAR p_label: ^SEQ ( * );
    VAR expanded_static_label: bat$static_label_attributes;
    VAR file_previously_opened: boolean;
    VAR status: ost$status);

    VAR
      computed_checksum: integer,
      p_stored_checksum: ^integer,
      v1_label: ^fmt$basic_file_label;

    NEXT v1_label IN p_label;
    IF v1_label = NIL THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' static label ', status);
      RETURN;
    IFEND;

    fmp$expand_v1_label (v1_label, expanded_static_label,
          file_previously_opened, status);

    NEXT p_stored_checksum IN p_label;
    IF p_stored_checksum = NIL THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' static label checksum ', status);
      RETURN;
    IFEND;

    pfp$compute_checksum (#LOC (v1_label^), #SIZE (v1_label^),
          computed_checksum);
    IF computed_checksum <> p_stored_checksum^ THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' static label ', status);
      RETURN;
    IFEND;

    status.normal := TRUE;
  PROCEND get_expanded_static_label;

?? TITLE := 'get_job_label', EJECT ??

  PROCEDURE [INLINE] get_job_label (VAR p_label: ^SEQ ( * );
        job_label_size: fmt$label_section_size;
    VAR p_job_label: ^SEQ ( * );
    VAR status: ost$status);

    VAR
      computed_checksum: integer,
      p_stored_checksum: ^integer;

    NEXT p_job_label: [[REP job_label_size OF cell]] IN p_label;
    IF p_job_label = NIL THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' job label ', status);
      RETURN;
    IFEND;

    NEXT p_stored_checksum IN p_label;
    IF p_stored_checksum = NIL THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' job label checksum ', status);
      RETURN;
    IFEND;

    pfp$compute_checksum (#LOC (p_job_label^), #SIZE (p_job_label^),
          computed_checksum);
    IF computed_checksum <> p_stored_checksum^ THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' job label ', status);
      RETURN;
    IFEND;

    status.normal := TRUE;
  PROCEND get_job_label;

?? TITLE := 'get_job_label_header', EJECT ??

  PROCEDURE [INLINE] get_job_label_header (VAR p_label: ^SEQ ( * );
    VAR job_label_header: fmt$job_label_header;
    VAR status: ost$status);

    CONST
      current_version = 1;

    VAR
      computed_checksum: integer,
      p_job_label_header: ^fmt$job_label_header,
      p_stored_checksum: ^integer;

    NEXT p_job_label_header IN p_label;
    IF p_job_label_header = NIL THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' job label header', status);
      RETURN;
    IFEND;

    NEXT p_stored_checksum IN p_label;
    IF p_stored_checksum = NIL THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' job label header checksum', status);
      RETURN;
    IFEND;

    pfp$compute_checksum (#LOC (p_job_label_header^), #SIZE
          (p_job_label_header^), computed_checksum);
    IF computed_checksum <> p_stored_checksum^ THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' job label header', status);
      RETURN;
    IFEND;

    IF p_job_label_header^.name <> 'BAM_JOB_LABEL' THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' job label header', status);
      RETURN;
    IFEND;

    IF p_job_label_header^.version <> current_version THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' job label header', status);
      RETURN;
    IFEND;

    status.normal := TRUE;
    job_label_header := p_job_label_header^;
  PROCEND get_job_label_header;

?? TITLE := 'get_label_size', EJECT ??

  PROCEDURE [INLINE] get_label_size (cycle_description:
    ^fmt$cycle_description;
    VAR job_label_size: jmt$system_label_info_length;
    VAR label_size: 0 .. 7fffffff(16);
    VAR status: ost$status);

{ Given an lnt entry this routine returns the size of the label as returned to
{ the caller.

    CONST
      checksum_size = 8;

    VAR
      permanent_file: boolean;

    status.normal := TRUE;
    label_size := 0;
    IF NOT cycle_description^.attached_file THEN
      osp$set_status_abnormal (amc$access_method_id,
            fme$no_preserved_attributes, ' file not attached ', status);
      RETURN;
    IFEND;

    IF (cycle_description^.permanent_file) AND
          (NOT cycle_description^.system_file_label_catalogued) THEN
      osp$set_status_abnormal (amc$access_method_id,
            fme$no_preserved_attributes, ' system file label NOT catalogued ',
            status);
      RETURN;
    ELSE
      IF cycle_description^.system_file_label.static_label <> NIL THEN
        label_size := #SIZE (cycle_description^.system_file_label.
              static_label^) + checksum_size;
      ELSE
        label_size := #SIZE (fmt$static_label_header) + checksum_size;
      IFEND;
    IFEND;

    IF (cycle_description^.device_class <> rmc$mass_storage_device) OR
          (cycle_description^.job_routing_label = NIL) THEN
      job_label_size := 0;
    ELSE
      job_label_size := #SIZE (cycle_description^.job_routing_label^);
      label_size := label_size + job_label_size;
    IFEND;
  PROCEND get_label_size;

?? TITLE := 'get_static_label_header', EJECT ??

  PROCEDURE [INLINE] get_static_label_header (VAR p_label: ^SEQ ( * );
    VAR static_label_header: fmt$static_bam_label_header;
    VAR status: ost$status);

    CONST
      current_version = 1;

    VAR
      computed_checksum: integer,
      p_stored_checksum: ^integer,
      p_static_label_header: ^fmt$static_bam_label_header;

    NEXT p_static_label_header IN p_label;
    IF p_static_label_header = NIL THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' p_static_label_header', status);
      RETURN;
    IFEND;

    NEXT p_stored_checksum IN p_label;
    IF p_stored_checksum = NIL THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, 'p_stored_checksum label header ',
            status);
      RETURN;
    IFEND;

    pfp$compute_checksum (#LOC (p_static_label_header^), #SIZE
          (p_static_label_header^), computed_checksum);
    IF computed_checksum <> p_stored_checksum^ THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' static label header', status);
      RETURN;
    IFEND;

    IF p_static_label_header^.name <> 'BAM_STATIC_LABEL' THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' bam_static_label', status);
      RETURN;
    IFEND;

    IF p_static_label_header^.version <> current_version THEN
      osp$set_status_abnormal (amc$access_method_id,
            ame$damaged_file_attributes, ' static label header', status);
      RETURN;
    IFEND;

    status.normal := TRUE;
    static_label_header := p_static_label_header^;

  PROCEND get_static_label_header;
MODEND fmm$pf_utility_label;
