?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Deadstart : Deadstart File Management' ??
MODULE dsm$deadstart_file_management;

{ PURPOSE:
{   This module contains the interfaces that manage device files for the
{   deadstart process.  The following files are currently being managed.
{     1. The image file that is written by the VCB program.
{     2. A primary copy of the NOS/VE deadstart file, saved after deadstart.
{     3. A secondary/upgrade copy of the deadstart file, saved after deadstart.
{     4. A spare entry reserved for future use.
{ DESIGN:
{   Each of the files listed above is a pair of device files; one containing
{   the raw data and a partner containing an array of disk MAUs (minimum
{   addressable units) that are used to address the device file.  The starting MAU
{   of the mau file is stored in the device label of the system device.  The mass
{   storage space allocated by the procedures contained herein occur at different
{   times with respect to the state of device management.  The deadstart files are
{   installed after the deadstart is complete and a more fault tolerant approach to
{   errors is taken whereas the image file creation occurs at the front end of the
{   installation deadstart and errors are fatal.
{
{   Following is a picture of the two device files and their relationship to each
{   other.  Each MAU of the data device file contains either a deadstart file or
{   the image file.  Each MAU of the mau device file contains a header followed by
{   a list of allocation units of the data device file.  The mau file is the map
{   of the data device file, it shows where and the order of the data in the data
{   device file.  The header in the mau file contains information necessary to perform
{   IO efficiently and the allocation unit of the next MAU in the mau file.  This
{   allows the mau file to exceed one allocation unit.  The mau file, by its nature
{   and content, is a much smaller file than the data device file.
{
{      Data device file                               Mau device file
{     __________________                             __________________
{     |                |                             |             ---|---:
{     |  MAU - 1       |                             |  MAU - 1       |   :
{     |                |                             |                |   :
{     |----------------|                             |----------------|   :
{     |                |                             |            <---|---:
{     |  MAU - 2       |                             |  MAU - 2       |
{     |                |                             |                |
{     |----------------|                             |----------------|
{     |                |                             |                |
{     |  MAU - 3       |                            //\\             //\\
{     |                |                            \\//             \\//
{     |----------------|                             |                |
{     |                |                             |----------------|
{     |                |                             |                |
{     |                |                             |  MAU - n-1     |
{     |                |                             |                |
{     |                |                             ------------------
{
{   //\\             //\\
{   \\//             \\//
{
{     |                |
{     |                |
{     |                |
{     |----------------|
{     |                |
{     |  MAU - n-1     |
{     |                |
{     |                |
{     ------------------


?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dmc$deadstart_file_alloc_size
*copyc dmt$error_condition_codes
*copyc dmt$mau_list
*copyc dse$estdbs_errors
*copyc dst$image_file
?? POP ??
*copyc dmp$allocate_file_space_r1
*copyc dmp$attach_device_file
*copyc dmp$close_file
*copyc dmp$create_device_file
*copyc dmp$detach_device_file
*copyc dmp$evacuate_active_device_log
*copyc dmp$get_fau_entry
*copyc dmp$get_fmd_by_index
*copyc dmp$get_disk_file_descriptor_p
*copyc dmp$open_file
*copyc dmp$open_label
*copyc dmp$set_eoi
*copyc dsp$get_integer_from_rdf
*copyc dsp$system_committed
*copyc gfp$get_locked_fde_p
*copyc gfp$unlock_fde_p
*copyc i#move
*copyc mmp$free_pages
*copyc mmp$write_modified_pages
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc pmp$delay
*copyc pmp$zero_out_table
?? EJECT ??
*copyc gfv$null_sfid
*copyc dmv$system_device_information
*copyc osv$page_size
?? TITLE := 'Global Declarations Declared by This Module', EJECT ??

  CONST
    c$image_mau_file_name = 'dsc$image_file_mau_list';

  VAR
    current_installed_file_length: amt$file_byte_address,
    system_device_label_sfid: gft$system_file_identifier;

?? TITLE := 'attach_or_create_the_mau_file', EJECT ??

{ PURPOSE:
{   This procedure either attaches or creates a MAU file and stores the MAUs for
{   a given file in the MAU file.  It returns the address of the first MAU.

  PROCEDURE attach_or_create_the_mau_file
    (    file_name: ost$name;
         file_mau_count: dmt$mau_count;
         file_mau_list_p: ^dmt$mau_address_list;
         file_transfer_unit_size: dmt$allocation_size;
         mau_file_name: ost$name;
     VAR first_mau: dmt$mau_address_entry;
     VAR status: ost$status);

    VAR
      allocate_pass_count: 0 .. 5,
      block_count: dmt$mau_count,
      block_header_p: ^dmt$mau_list_header,
      block_mau_entry_p: ^dmt$mau_address_entry,
      close_status: ost$status,
      detach_status: ost$status,
      file_mau_index: dmt$mau_count,
      file_segment_pointer: mmt$segment_pointer,
      file_sfid: gft$system_file_identifier,
      ignore: boolean,
      ignore_status: ost$status,
      mau_addresses_per_block: dmt$allocation_size,
      mau_count: dmt$allocation_size,
      mau_file_avt_index: dmt$active_volume_table_index,
      mau_file_length: amt$file_byte_address,
      mau_file_mau_count: dmt$mau_count,
      mau_file_mau_list_p: ^dmt$mau_address_list,
      mau_file_transfer_unit_size: dmt$allocation_size,
      physical_file_attributes: ARRAY [1 .. 3] OF dmt$new_device_file_attribute;

    status.normal := TRUE;

    { Attach or create the MAU file.

    mau_addresses_per_block := (file_transfer_unit_size - #SIZE (dmt$mau_list_header)) DIV 8;
    mau_file_length := ((file_mau_count DIV mau_addresses_per_block) + 1) * file_transfer_unit_size;
    dmp$attach_device_file (dmv$system_device_recorded_vsn, mau_file_name, file_sfid, status);
    IF NOT status.normal AND (status.condition = dme$unknown_device_file) THEN
      physical_file_attributes [1].keyword := dmc$clear_space;
      physical_file_attributes [1].required := FALSE;
      physical_file_attributes [2].keyword := dmc$file_limit;
      physical_file_attributes [2].limit := UPPERVALUE (amt$file_limit);
      physical_file_attributes [3].keyword := dmc$requested_allocation_size;
      physical_file_attributes [3].requested_allocation_size := dmc$deadstart_file_alloc_size;
      dmp$create_device_file (mau_file_name, dmv$system_device_recorded_vsn, ^physical_file_attributes,
            mau_file_length, file_sfid, status);
    IFEND;
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  /attach_mau_file/
    BEGIN
      file_segment_pointer.kind := mmc$sequence_pointer;
      dmp$open_file (file_sfid, 1, 1, mmc$sar_write_extend, mmc$as_sequential,
            file_segment_pointer, status);
      IF NOT status.normal THEN
        EXIT /attach_mau_file/;
      IFEND;

    /open_mau_file/
      BEGIN

        {  Extend the mau file if necessary.

        allocate_pass_count := 5;
        REPEAT
          dmp$allocate_file_space_r1 (file_sfid, mau_file_length, 0, 0, osc$nowait, sfc$no_limit, status);
          IF NOT status.normal THEN
            pmp$delay (1000, ignore_status);
          IFEND;
          allocate_pass_count := allocate_pass_count - 1;
        UNTIL status.normal OR (allocate_pass_count = 0);
        IF NOT status.normal THEN
          EXIT /open_mau_file/;
        IFEND;

        { Build the MAU list for the MAU file.

        PUSH mau_file_mau_list_p: [1 .. dmc$max_mau_addresses];
        build_mau_list (file_sfid, mau_file_mau_list_p, mau_file_mau_count,
              mau_file_transfer_unit_size, mau_file_avt_index, status);
        IF NOT status.normal THEN
          EXIT /open_mau_file/;
        IFEND;

        { Store the MAUs for the file in the MAU file.  Each allocation unit of the mau file contains a
        { header followed by the list of allocation units of the device file.  The header contains the
        { allocation unit of the next mau file MAU.

        RESET file_segment_pointer.seq_pointer;
        mau_count := mau_addresses_per_block + 1;
        block_count := 0;
        block_header_p := NIL;

        FOR file_mau_index := 1 TO file_mau_count DO
          IF mau_count > mau_addresses_per_block THEN
            block_count := block_count + 1;
            IF block_header_p <> NIL THEN
              IF block_count <= mau_file_mau_count THEN
                block_header_p^.next_mau_list_address := mau_file_mau_list_p^ [block_count];
              ELSE
                osp$system_error ('Not enough space in MAU file.', NIL);
              IFEND;
            IFEND;
            NEXT block_header_p IN file_segment_pointer.seq_pointer;
            block_header_p^.valid_data := 1;
            block_header_p^.first_mau_of_maufile := mau_file_mau_list_p^ [block_count];
            block_header_p^.dsfile_name := file_name;
            block_header_p^.maufile_name := mau_file_name;
            block_header_p^.block_size := file_transfer_unit_size;
            block_header_p^.total_mau_addresses := file_mau_count;
            block_header_p^.next_mau_list_address := 0;
            mau_count := 1;
          IFEND;
          NEXT block_mau_entry_p IN file_segment_pointer.seq_pointer;
          block_mau_entry_p^ := file_mau_list_p^ [file_mau_index];
          mau_count := mau_count + 1;
        FOREND;

        { Return the first MAU of the mau file.

        first_mau := mau_file_mau_list_p^ [1];

        mmp$write_modified_pages (file_segment_pointer.seq_pointer, mau_file_length, osc$wait, status);
      END /open_mau_file/;
      dmp$close_file (file_segment_pointer.seq_pointer, close_status);

    END /attach_mau_file/;
    dmp$detach_device_file (file_sfid, ignore, ignore, detach_status);
    IF status.normal THEN
      IF NOT close_status.normal THEN
        status := close_status;
      ELSEIF NOT detach_status.normal THEN
        status := detach_status;
      IFEND;
    IFEND;

  PROCEND attach_or_create_the_mau_file;
?? TITLE := 'build_mau_list', EJECT ??

{ PURPOSE:
{   This procedure builds the mau list for the given system file.

  PROCEDURE build_mau_list
    (    system_file_id: gft$system_file_identifier;
     VAR mau_list_p: ^dmt$mau_address_list;
     VAR mau_count: dmt$mau_count;
     VAR transfer_unit_size: dmt$allocation_size;
     VAR device_file_avt_index: dmt$active_volume_table_index;
     VAR status: ost$status);

    VAR
      byte_address: amt$file_byte_address,
      fau_entry_p: ^dmt$file_allocation_unit,
      disk_file_descriptor_p: ^dmt$disk_file_descriptor,
      file_descriptor_entry_p: gft$locked_file_desc_entry_p,
      file_medium_descriptor_p: ^dmt$file_medium_descriptor,
      maus_per_dau: dmt$maus_per_dau;

    status.normal := TRUE;
    mau_count := 0;
    transfer_unit_size := 0;

    gfp$get_locked_fde_p (system_file_id, file_descriptor_entry_p);
    IF file_descriptor_entry_p = NIL THEN
      osp$set_status_abnormal ('DM', dme$unable_to_locate_fde,
            'Unable to locate FDE in DM file descriptor', status);
      RETURN;
    IFEND;

    dmp$get_disk_file_descriptor_p (file_descriptor_entry_p, disk_file_descriptor_p);

    dmp$get_fmd_by_index (disk_file_descriptor_p, 1, file_medium_descriptor_p);
    device_file_avt_index := file_medium_descriptor_p^.avt_index;
    byte_address := 0;
    transfer_unit_size := file_descriptor_entry_p^.allocation_unit_size;
    WHILE byte_address < disk_file_descriptor_p^.highest_offset_allocated DO
      dmp$get_fau_entry (disk_file_descriptor_p, byte_address, fau_entry_p);
      IF (fau_entry_p <> NIL) AND (fau_entry_p^.state >= dmc$fau_invalid_data) THEN
        dmp$get_fmd_by_index (disk_file_descriptor_p, fau_entry_p^.fmd_index, file_medium_descriptor_p);
        maus_per_dau := file_medium_descriptor_p^.maus_per_dau;
        IF mau_count = UPPERBOUND (mau_list_p^) THEN
          mau_count := 0;
          gfp$unlock_fde_p (file_descriptor_entry_p);
          osp$set_status_abnormal ('DM', dme$maulist_too_small,
                'Maulist too small to represent file in mau units', status);
          RETURN;
        IFEND;
        mau_count := mau_count + 1;
        mau_list_p^ [mau_count] := fau_entry_p^.dau_address * maus_per_dau;
      ELSE
        osp$system_error ('Cannot create mau file', NIL);
      IFEND;
      byte_address := byte_address + transfer_unit_size;
    WHILEND;

    gfp$unlock_fde_p (file_descriptor_entry_p);

    IF mau_count = 0 THEN
      osp$set_status_abnormal ('DM', dme$unable_to_locate_fde,
            'Unable to locate FDE in DM file descriptor', status);
    IFEND;

  PROCEND build_mau_list;
?? TITLE := 'install_file', EJECT ??

{ PURPOSE:
{   This procedure creates a device file and a corresponding file (mau file) that contains the allocation
{   units of the device file.

  PROCEDURE install_file
    (    file_length: amt$file_byte_address;
         current_file_length: amt$file_byte_address;  { Used only if extending the image file.
         file_name: ost$name;
         mau_file_name: ost$name;
     VAR first_mau: dmt$mau_address_entry;
     VAR status: ost$status);


     TYPE
       trick_pointer = record
         case 0 .. 2 of
           = 0 =
             cell_pointer: ^cell,
           = 1 =
             pva: ost$pva,
           = 2 =
             byte_pointer: ^0 .. 0ff(16),
         casend,
       recend;

    VAR
      allocate_pass_count: 0 .. 5,
      byte: 0 .. 0ff(16),
      call_evacuate_device_log: boolean,
      close_status: ost$status,
      detach_status: ost$status,
      device_file_avt_index: dmt$active_volume_table_index,
      device_file_created: boolean,
      device_file_pointer: trick_pointer,
      fde_p: gft$locked_file_desc_entry_p,
      file_mau_count: dmt$mau_count,
      file_mau_list_p: ^dmt$mau_address_list,
      file_segment_pointer: mmt$segment_pointer,
      file_sfid: gft$system_file_identifier,
      file_transfer_unit_size: dmt$allocation_size,
      ignore: boolean,
      ignore_status: ost$status,
      physical_file_attributes: ARRAY [1 .. 3] OF dmt$new_device_file_attribute;

    status.normal := TRUE;

    { Attach or create the file.

    device_file_created := FALSE;
    dmp$attach_device_file (dmv$system_device_recorded_vsn, file_name, file_sfid, status);
    IF NOT status.normal AND (status.condition = dme$unknown_device_file) THEN
      physical_file_attributes [1].keyword := dmc$clear_space;
      physical_file_attributes [1].required := FALSE;
      physical_file_attributes [2].keyword := dmc$file_limit;
      physical_file_attributes [2].limit := UPPERVALUE (amt$file_limit);
      physical_file_attributes [3].keyword := dmc$requested_allocation_size;
      physical_file_attributes [3].requested_allocation_size := dmc$max_bytes_per_allocation;
      dmp$create_device_file (file_name, dmv$system_device_recorded_vsn, ^physical_file_attributes,
            file_length, file_sfid, status);
      device_file_created := TRUE;
    IFEND;
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    call_evacuate_device_log := FALSE;

  /attach_file/
    BEGIN
      file_segment_pointer.kind := mmc$cell_pointer;
      dmp$open_file (file_sfid, 1, 1, mmc$sar_write_extend, mmc$as_sequential,
            file_segment_pointer, status);
      IF NOT status.normal THEN
        EXIT /attach_file/;
      IFEND;

    /open_file/
      BEGIN

        { Extend the device file if necessary.

        allocate_pass_count := 5;
        REPEAT
          dmp$allocate_file_space_r1 (file_sfid, file_length, 0, 0, osc$nowait, sfc$no_limit, status);
          IF NOT status.normal THEN
            pmp$delay (1000, ignore_status);
          IFEND;
          allocate_pass_count := allocate_pass_count - 1;
        UNTIL status.normal OR (allocate_pass_count = 0);
        IF NOT status.normal THEN
          EXIT /open_file/;
        IFEND;

        IF NOT device_file_created THEN

          { The device file was extended and not created.  No data moved to file, make explicit calls to
          { device management to set new EOI and touch each page beyond current file length to new file
          { length to initialize all of mass storage space allocated.

          gfp$get_locked_fde_p (file_sfid, fde_p);
          fde_p^.file_limit := file_length;
          fde_p^.eoi_byte_address := file_length;
          gfp$unlock_fde_p (fde_p);

          { Write each page of file extension, free them and read them back in to verify no IO errors.

          device_file_pointer.cell_pointer := file_segment_pointer.cell_pointer;
          device_file_pointer.pva.offset := current_file_length;
          pmp$zero_out_table (device_file_pointer.cell_pointer, (file_length - current_file_length));
          mmp$write_modified_pages (file_segment_pointer.cell_pointer, file_length, osc$wait, status);
          IF NOT status.normal THEN
            EXIT /open_file/;
          IFEND;

          mmp$free_pages (file_segment_pointer.cell_pointer, file_length, osc$wait, status);
          IF NOT status.normal THEN
            EXIT /open_file/;
          IFEND;

          /read_each_new_page/
          WHILE device_file_pointer.pva.offset < file_length DO
            byte := device_file_pointer.byte_pointer^;
            device_file_pointer.pva.offset := device_file_pointer.pva.offset + osv$page_size;
          WHILEND /read_each_new_page/;

          { Image file extended, evacuate device log after device file detached.

          call_evacuate_device_log := TRUE;
        IFEND;

        { Build the MAU list for the file.

        PUSH file_mau_list_p: [1 .. dmc$max_mau_addresses];
        build_mau_list (file_sfid, file_mau_list_p, file_mau_count, file_transfer_unit_size,
              device_file_avt_index, status);
        IF NOT status.normal THEN
          EXIT /open_file/;
        IFEND;
        mmp$write_modified_pages (file_segment_pointer.cell_pointer, file_length, osc$wait, status);
      END /open_file/;
      dmp$close_file (file_segment_pointer.cell_pointer, close_status);

    END /attach_file/;
    dmp$detach_device_file (file_sfid, ignore, ignore, detach_status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    IF NOT close_status.normal THEN
      status := close_status;
      RETURN;
    IFEND;
    IF NOT detach_status.normal THEN
      status := detach_status;
      RETURN;
    IFEND;

    attach_or_create_the_mau_file (file_name, file_mau_count, file_mau_list_p, file_transfer_unit_size,
          mau_file_name, first_mau, status);

    IF (call_evacuate_device_log) and (status.normal) THEN

      { Flush the device log to disk to prevent recovery failure if system should abort before log normally
      { flushed to disk.

      dmp$evacuate_active_device_log (device_file_avt_index, status);
    IFEND;

  PROCEND install_file;
?? TITLE := 'dsp$append_file_to_ds_file', EJECT ??

{ PURPOSE:
{   This procedure appends a file to the device deadstart file.
{ DESIGN:
{   Create the device deadstart file if it does not already exist.  Allocate the
{   space and append the file.

  PROCEDURE [XDCL, #GATE] dsp$append_file_to_ds_file
    (    file_length: amt$file_byte_address;
         deadstart_file: ost$name;
     VAR file_p: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      allocate_pass_count: 0 .. 5,
      close_status: ost$status,
      data_p: ^SEQ ( * ),
      detach_status: ost$status,
      file_data_p: ^SEQ ( * ),
      file_segment_pointer: mmt$segment_pointer,
      file_sfid: gft$system_file_identifier,
      ignore: boolean,
      ignore_status: ost$status,
      physical_file_attributes: ARRAY [1..3] OF dmt$new_device_file_attribute;

    status.normal := TRUE;

    {  Attach or create the device file.

    dmp$attach_device_file (dmv$system_device_recorded_vsn, deadstart_file, file_sfid, status);

    IF NOT status.normal AND (status.condition = dme$unknown_device_file) THEN
      physical_file_attributes [1].keyword := dmc$clear_space;
      physical_file_attributes [1].required := FALSE;
      physical_file_attributes [2].keyword := dmc$file_limit;
      physical_file_attributes [2].limit := UPPERVALUE (amt$file_limit);
      physical_file_attributes [3].keyword := dmc$requested_allocation_size;
      physical_file_attributes [3].requested_allocation_size := dmc$deadstart_file_alloc_size;
      dmp$create_device_file (deadstart_file, dmv$system_device_recorded_vsn,
            ^physical_file_attributes, file_length, file_sfid, status);
    IFEND;
    IF NOT status.normal THEN
      RETURN;
    IFEND;

   /attach_file/
    BEGIN
      file_segment_pointer.kind := mmc$sequence_pointer;
      dmp$open_file (file_sfid, 1, 1, mmc$sar_write_extend, mmc$as_sequential,
            file_segment_pointer, status);
      IF NOT status.normal THEN
        EXIT /attach_file/;
      IFEND;

      { Allocate the space needed for the new file to be added to the system device file.
      { Move the data from the new file to the system device file.  Then write the
      { modified pages.

     /open_file/
      BEGIN

        allocate_pass_count := 5;
        REPEAT
          dmp$allocate_file_space_r1 (file_sfid, current_installed_file_length, file_length,
                0, osc$nowait, sfc$no_limit, status);
          IF NOT status.normal THEN
            pmp$delay (1000, ignore_status);
          IFEND;
          allocate_pass_count := allocate_pass_count - 1;
        UNTIL status.normal OR (allocate_pass_count = 0);
        IF NOT status.normal THEN
          EXIT /open_file/;
        IFEND;

        { Skip the data that has already been written.

        IF current_installed_file_length <> 0 THEN
          NEXT data_p: [[REP current_installed_file_length OF CELL]] IN file_segment_pointer.seq_pointer;
        IFEND;

        { Copy the data from the new file to the device file.

        NEXT data_p: [[REP file_length OF CELL]] IN file_segment_pointer.seq_pointer;
        RESET file_p;
        NEXT file_data_p: [[REP file_length OF cell]] IN file_p;
        data_p^ := file_data_p^;
        mmp$write_modified_pages (file_segment_pointer.seq_pointer, file_length, osc$wait, status);
        IF NOT status.normal THEN
          EXIT /open_file/;
        IFEND;
        current_installed_file_length := current_installed_file_length + file_length;
        dmp$set_eoi (file_sfid, current_installed_file_length, status);
        IF NOT status.normal THEN
          EXIT /open_file/;
        IFEND;
      END /open_file/;
      dmp$close_file (file_segment_pointer.seq_pointer, close_status);

    END /attach_file/;
    dmp$detach_device_file (file_sfid, ignore, ignore, detach_status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    IF NOT close_status.normal THEN
      status := close_status;
      RETURN;
    IFEND;
    IF NOT detach_status.normal THEN
      status := detach_status;
      RETURN;
    IFEND;

  PROCEND dsp$append_file_to_ds_file;
?? TITLE := 'dsp$attach_label_for_upgrade', EJECT ??

{ PURPOSE:
{   When the system is terminated, device management idles and then
{   deadstart idles.  If a commit new system has been requested then
{   data in the label file on the system device must be changed.  It
{   cannot be attached once device management has idled.  This procedure
{   attaches the label file to get the SFID so that it can be opened
{   after device management has been idled.

  PROCEDURE [XDCL, #GATE] dsp$attach_label_for_upgrade;

    VAR
      commit_new_dsfile: integer,
      ignore: boolean,
      label_header_p: ^dmt$volume_label_header,
      label_p: ^dmt$ms_volume_label,
      local_status: ost$status,
      user_supplied_name: ost$name;

    { Determine if an upgrade to a new disk deadstart file has been requested.

    system_device_label_sfid := gfv$null_sfid;
    dsp$get_integer_from_rdf (dsc$rdf_commit_new_dsfile_flag, dsc$rdf_production, commit_new_dsfile);
    IF commit_new_dsfile = $INTEGER (FALSE) THEN
      RETURN;
    IFEND;

    { Attempt to attach the system device file and open the label.

    user_supplied_name := 'LABEL';
    user_supplied_name (6, rmc$recorded_vsn_size) := dmv$system_device_recorded_vsn;
    dmp$attach_device_file (dmv$system_device_recorded_vsn, user_supplied_name,
          system_device_label_sfid, local_status);
    IF NOT local_status.normal THEN
      system_device_label_sfid := gfv$null_sfid;
      RETURN;
    IFEND;
    dmp$open_label (system_device_label_sfid, osc$os_ring_1, osc$tsrv_ring, mmc$sar_modify,
          mmc$as_random, label_p, local_status);
    IF NOT local_status.normal THEN
      dmp$detach_device_file (system_device_label_sfid, ignore, ignore, local_status);
      system_device_label_sfid := gfv$null_sfid;
      RETURN;
    IFEND;

    { Check the system device label for the presence of a deadstart file to upgrade.

    RESET label_p;
    NEXT label_header_p IN label_p;

    IF label_header_p^.secondary_deadstart_file = 0 THEN
      dmp$close_file (label_p, local_status);
      dmp$detach_device_file (system_device_label_sfid, ignore, ignore, local_status);
      system_device_label_sfid := gfv$null_sfid;
      RETURN;
    IFEND;

    { An upgrade can occur, the label file SFID is known and can be used to open the label
    { so the label file can now be closed.

    dmp$close_file (label_p, local_status);

  PROCEND dsp$attach_label_for_upgrade;
?? TITLE := 'dsp$check_system_available', EJECT ??

{ PURPOSE:
{   This procedure checks the Secondary Deadstart File MAU in the system device
{   label to see if there is a system to commit.

  PROCEDURE [XDCL, #GATE] dsp$check_system_available
    (VAR status: ost$status);

    VAR
      close_status: ost$status,
      detach_status: ost$status,
      file_sfid: gft$system_file_identifier,
      ignore: boolean,
      label_header_p: ^dmt$volume_label_header,
      label_p: ^dmt$ms_volume_label,
      user_supplied_name: ost$name;

    status.normal := TRUE;

    { Attach the system device file and open the label.

    user_supplied_name := 'LABEL';
    user_supplied_name (6, rmc$recorded_vsn_size) := dmv$system_device_recorded_vsn;
    dmp$attach_device_file (dmv$system_device_recorded_vsn, user_supplied_name, file_sfid, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  /attach_system_file/
    BEGIN
      dmp$open_label (file_sfid, osc$os_ring_1, osc$tsrv_ring, mmc$sar_modify, mmc$as_random,
            label_p, status);
      IF NOT status.normal THEN
        EXIT /attach_system_file/;
      IFEND;

      { Retrieve the label from the system file.

      RESET label_p;
      NEXT label_header_p IN label_p;

      { Check if there is a Secondary Deadstart File MAU.  This will determine whether there is a system
      { to commit.

      IF label_header_p^.secondary_deadstart_file = 0 THEN
        osp$set_status_abnormal (dsc$display_processor_id, dse$no_deadstart_file_to_commit, '', status);
      IFEND;

      dmp$close_file (label_p, close_status);

    END /attach_system_file/;
    dmp$detach_device_file (file_sfid, ignore, ignore, detach_status);
    IF status.normal THEN
      IF NOT close_status.normal THEN
        status := close_status;
      ELSEIF NOT detach_status.normal THEN
        status := detach_status;
      IFEND;
    IFEND;

  PROCEND dsp$check_system_available;
?? TITLE := 'dsp$complete_deadstart_file', EJECT ??

{ PURPOSE:
{   This procedure creates the MAU file and builds the MAU list.

  PROCEDURE [XDCL, #GATE] dsp$complete_deadstart_file
    (    deadstart_file: ost$name;
         mau_file_name: ost$name;
     VAR status: ost$status);

    VAR
      close_status: ost$status,
      detach_status: ost$status,
      file_mau_count: dmt$mau_count,
      file_mau_list_p: ^dmt$mau_address_list,
      file_sfid: gft$system_file_identifier,
      file_transfer_unit_size: dmt$allocation_size,
      first_mau: dmt$mau_address_entry,
      ignore: boolean,
      label_header_p: ^dmt$volume_label_header,
      label_p: ^dmt$ms_volume_label,
      mau_file_avt_index: dmt$active_volume_table_index,
      user_supplied_name: ost$name;

    status.normal := TRUE;

    {  Attach the device deadstart file.

    dmp$attach_device_file (dmv$system_device_recorded_vsn, deadstart_file, file_sfid, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Build the MAU list for the file.

    PUSH file_mau_list_p: [1 .. dmc$max_mau_addresses];
    build_mau_list (file_sfid, file_mau_list_p, file_mau_count, file_transfer_unit_size,
          mau_file_avt_index, status);

    dmp$detach_device_file (file_sfid, ignore, ignore, detach_status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    IF NOT detach_status.normal THEN
      status := detach_status;
      RETURN;
    IFEND;

    attach_or_create_the_mau_file (deadstart_file, file_mau_count, file_mau_list_p,
          file_transfer_unit_size, mau_file_name, first_mau, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Set the first MAU of the deadstart file in the Secondary Deadstart file MAU in the system
    { device label.

    { Attach the system device file and open the label.

    user_supplied_name := 'LABEL';
    user_supplied_name (6, rmc$recorded_vsn_size) := dmv$system_device_recorded_vsn;
    dmp$attach_device_file (dmv$system_device_recorded_vsn, user_supplied_name, file_sfid, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  /attach_system_file/
    BEGIN
      dmp$open_label (file_sfid, osc$os_ring_1, osc$tsrv_ring, mmc$sar_modify, mmc$as_random,
            label_p, status);
      IF NOT status.normal THEN
        EXIT /attach_system_file/;
      IFEND;

    /open_system_label/
      BEGIN

        { Retrieve the label from the system file.

        RESET label_p;
        NEXT label_header_p IN label_p;

        { Store the first MAU of the deadstart file in the Secondary Deadstart File MAU
        { in the system device label.

        label_header_p^.secondary_deadstart_file := first_mau;
        mmp$write_modified_pages (label_header_p, #SIZE (label_header_p^), osc$wait, status);

      END /open_system_label/;
      dmp$close_file (label_p, close_status);

    END /attach_system_file/;
    dmp$detach_device_file (file_sfid, ignore, ignore, detach_status);
    IF status.normal THEN
      IF NOT close_status.normal THEN
        status := close_status;
      ELSEIF NOT detach_status.normal THEN
        status := detach_status;
      IFEND;
    IFEND;

  PROCEND dsp$complete_deadstart_file;
?? TITLE := 'dsp$create_image_file', EJECT ??

{ PURPOSE:
{   This procedure creates the image file and stores the first MAU of
{   the image file mau file in the system device label which VCB will use
{   to locate where to write the image file.

  PROCEDURE [XDCL] dsp$create_image_file
    (    file_length: amt$file_byte_address;
     VAR status: ost$status);

    VAR
      close_status: ost$status,
      detach_status: ost$status,
      file_name: ost$name,
      file_sfid: gft$system_file_identifier,
      first_mau: dmt$mau_address_entry,
      ignore: boolean,
      label_header_p: ^dmt$volume_label_header,
      label_p: ^dmt$ms_volume_label,
      mau_file_name: ost$name,
      user_supplied_name: ost$name;

    status.normal := TRUE;

    { The image file must be created before the system is committed.

    IF dsp$system_committed () THEN
       osp$system_error ('Must create image file before system committed', NIL);
    IFEND;

    { Install the image file.

    file_name := dsc$image_file_name;
    mau_file_name := c$image_mau_file_name;
    install_file (file_length, 0, file_name, mau_file_name, first_mau, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Attach the system device file and open the label.

    user_supplied_name := 'LABEL';
    user_supplied_name (6, rmc$recorded_vsn_size) := dmv$system_device_recorded_vsn;
    dmp$attach_device_file (dmv$system_device_recorded_vsn, user_supplied_name, file_sfid, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  /attach_system_file/
    BEGIN
      dmp$open_label (file_sfid, osc$os_ring_1, osc$tsrv_ring, mmc$sar_modify, mmc$as_random,
            label_p, status);
      IF NOT status.normal THEN
        EXIT /attach_system_file/;
      IFEND;

      { Retrieve the label from the system file.

      RESET label_p;
      NEXT label_header_p IN label_p;

      { Store the first MAU of the image file mau file in the system device label.

      label_header_p^.image_file := first_mau;
      mmp$write_modified_pages (label_header_p, #SIZE (label_header_p^), osc$wait, status);

      dmp$close_file (label_p, close_status);

    END /attach_system_file/;
    dmp$detach_device_file (file_sfid, ignore, ignore, detach_status);
    IF status.normal THEN
      IF NOT close_status.normal THEN
        status := close_status;
      ELSEIF NOT detach_status.normal THEN
        status := detach_status;
      IFEND;
    IFEND;

  PROCEND dsp$create_image_file;
?? TITLE := 'dsp$extend_image_file', EJECT ??

{ PURPOSE:
{   This procedure extends the image file if its size has increased since the last system deadstart.  This
{   implies that a different system is running than was previously running.  This is intended to occur right
{   before the system is committed.
{
{ DESIGN:
{   The same process is used as creating the image file but because the file is being extended there is no
{   need to update the allocation unit of the first MAU of the image file mau file in the system label.
{   Extending the image file does not change any data on the file although the mau file is rewritten.

  PROCEDURE [XDCL] dsp$extend_image_file
    (    file_length: amt$file_byte_address;
         current_file_length: amt$file_byte_address;
     VAR status: ost$status);

    VAR
      file_name: ost$name,
      first_mau: dmt$mau_address_entry,
      mau_file_name: ost$name;

    status.normal := TRUE;

    file_name := dsc$image_file_name;
    mau_file_name := c$image_mau_file_name;
    install_file (file_length, current_file_length, file_name, mau_file_name, first_mau, status);

  PROCEND dsp$extend_image_file;
?? TITLE := 'dsp$prep_ds_file_installation', EJECT ??

{ PURPOSE:
{   This procedure determines which deadstart file slot to use.
{ DESIGN:
{   If the primary deadstart file is stored in one of the slots then the other
{   slot is used.

  PROCEDURE [XDCL, #GATE] dsp$prep_ds_file_installation
    (VAR deadstart_file_name: ost$name;
     VAR mau_file_name: ost$name;
     VAR status: ost$status);

    CONST
      slot_1_file_name = 'dsfile_1',
      slot_1_mau_file_name = 'maufile_1',
      slot_2_file_name = 'dsfile_2',
      slot_2_mau_file_name = 'maufile_2';

    VAR
      close_status: ost$status,
      detach_status: ost$status,
      file_segment_pointer: mmt$segment_pointer,
      file_sfid: gft$system_file_identifier,
      first_mau_slot_1: dmt$mau_address_entry,
      ignore: boolean,
      label_header_p: ^dmt$volume_label_header,
      label_p: ^dmt$ms_volume_label,
      mau_file_header_p: ^dmt$mau_list_header,
      user_supplied_name: ost$name;

    status.normal := TRUE;

    current_installed_file_length := 0;

    { Attempt to retrieve the first MAU from the file in SLOT 1.
    { If the file in SLOT 1 does not exist then the first MAU is zero.

  /retrieve_first_mau/
    BEGIN
      mau_file_name := slot_1_mau_file_name;
      dmp$attach_device_file (dmv$system_device_recorded_vsn, mau_file_name, file_sfid, status);
      IF NOT status.normal THEN
        first_mau_slot_1 := 0;
        EXIT /retrieve_first_mau/;
      IFEND;

    /attach_file/
      BEGIN
        file_segment_pointer.kind := mmc$sequence_pointer;
        dmp$open_file (file_sfid, 1, 1, mmc$sar_write_extend, mmc$as_sequential,
              file_segment_pointer, status);
        IF NOT status.normal THEN
          first_mau_slot_1 := 0;
          EXIT /attach_file/;
        IFEND;

        RESET file_segment_pointer.seq_pointer;
        NEXT mau_file_header_p IN file_segment_pointer.seq_pointer;
        first_mau_slot_1 := mau_file_header_p^.first_mau_of_maufile;

        { Okay to ignore the status because at this point the file may not exist.

        dmp$close_file (file_segment_pointer.seq_pointer, close_status);

      END /attach_file/;
      dmp$detach_device_file (file_sfid, ignore, ignore, detach_status);
    END /retrieve_first_mau/;

    { Attach the system device file and open the label.

    user_supplied_name := 'LABEL';
    user_supplied_name (6, rmc$recorded_vsn_size) := dmv$system_device_recorded_vsn;
    dmp$attach_device_file (dmv$system_device_recorded_vsn, user_supplied_name, file_sfid, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  /attach_system_file/
    BEGIN
      dmp$open_label (file_sfid, osc$os_ring_1, osc$tsrv_ring, mmc$sar_modify, mmc$as_random,
            label_p, status);
      IF NOT status.normal THEN
        EXIT /attach_system_file/;
      IFEND;

      { Retrieve the label from the system file.

      RESET label_p;
      NEXT label_header_p IN label_p;

      { Determine in which SLOT to install the deadstart file.

      IF label_header_p^.primary_deadstart_file = first_mau_slot_1 THEN
        deadstart_file_name := slot_2_file_name;
        mau_file_name := slot_2_mau_file_name;
      ELSE
        deadstart_file_name := slot_1_file_name;
        mau_file_name := slot_1_mau_file_name;
      IFEND;

      dmp$close_file (label_p, close_status);

    END /attach_system_file/;
    dmp$detach_device_file (file_sfid, ignore, ignore, detach_status);
    IF status.normal THEN
      IF NOT close_status.normal THEN
        status := close_status;
      ELSEIF NOT detach_status.normal THEN
        status := detach_status;
      IFEND;
    IFEND;

  PROCEND dsp$prep_ds_file_installation;
?? TITLE := 'dsp$upgrade_primary_dsfile_mau', EJECT ??

{ PURPOSE:
{   This procedure upgrades the Primary Deadstart File MAU in the system device
{   label with the Secondary Deadstart File MAU.
{ DESIGN:
{   This procedure can not use the device management "attach" and "detach"
{   because this procedure will be executed after device management idles.
{   The system device SFID is saved prior to the device management idle.

  PROCEDURE [XDCL] dsp$upgrade_primary_dsfile_mau
    (VAR status: ost$status);

    VAR
      ignore_status: ost$status,
      label_header_p: ^dmt$volume_label_header,
      label_p: ^dmt$ms_volume_label;

    status.normal := TRUE;

    { Check for a saved SFID.  If none exists then there is no deadstart file to upgrade.

    IF system_device_label_sfid = gfv$null_sfid THEN
      osp$set_status_abnormal (dsc$display_processor_id, dse$no_deadstart_file_to_commit, '', status);
      RETURN;
    IFEND;

    dmp$open_label (system_device_label_sfid, osc$os_ring_1, osc$tsrv_ring,
          mmc$sar_modify, mmc$as_random, label_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Move the Secondary Deadstart File MAU to the Primary Deadstart File MAU.

    RESET label_p;
    NEXT label_header_p IN label_p;
    label_header_p^.primary_deadstart_file := label_header_p^.secondary_deadstart_file;
    label_header_p^.secondary_deadstart_file := 0;

    mmp$write_modified_pages (label_header_p, #SIZE (label_header_p^), osc$wait, status);

    dmp$close_file (label_p, ignore_status);

  PROCEND dsp$upgrade_primary_dsfile_mau;
MODEND dsm$deadstart_file_management;
