21-Dec-2012

RMS hack

Occasionally, programmers ask us to convert RMS files to extend the record size or add additional indexes. On the occasion that's prompting this post, the programmers handed us an FDL file to add a new alternate key and extend the file at the same time. Obviously, there were a bunch of programs recompiled to reference the new record length to be installed along with the file convert.

We do changes like this on Sundays, where (theoretically) the system is unavailable. As the system is either being used 24 hours a day other than that, changes at other times are extremely problematic, and will generally impact users in some part of the country.

To cut a long story short, the programmer's testing had failed to uncover that the field that was turned into an alternate key had its value updated under certain conditions... and the FDL used to convert the file specified no changes for that key. So when a program attempted to change the value, a stack dump resulted.

The immediate problem became how to fix this without causing a major interruption to the users. The file in question is kept open 24 hours a day except for the 5 to 10 second quiet point where we split shadow sets for backup. And we couldn't wait for next Sunday.

After a little thinking, I realised that the only thing that RMS checks to see if an alternate key's value can be changed is a single bit in RMS metatdata defined by field key$v_chgkeys. If I could somehow set this bit in the file's metadata, future accessors would not encounter the error when trying to change the key's value.

After extensive testing, I ran the following code against the file. Please note, this is a complete hack, totally unsupported, if you don't understand the code, don't run it against a production file, etc, etc, etc. You have been warned.


#define __NEW_STARLET 1
/*
** Flip the "key changes" bit for krf 1 on TARGET_FILE.
*/

#include <stdio.h>
#include <stdlib.h>
#include <ssdef.h>
#include <stsdef.h>
#include <rms.h>
#include <descrip.h>
#include <string.h>

#include <bktdef.h>
#include <keydef.h>

#include <lib$routines.h>
#include <starlet.h>

#define errchk_sig(arg) if (!($VMS_STATUS_SUCCESS(arg))) (void)lib$signal(arg);


/******************************************************************************/
int main (void) {

static PROLOGUE_KEY *key_p;

static int r0_status;
static unsigned long int vbn;
static int i;

static unsigned short int noff;

static char bucket_buffer[512*64];

static long int checksum;
static short int *wp;

static struct FAB fab;
static struct RAB rab;

static char filename[] = "TARGET_FILE";


    /*
    ** Initialize RMS control blocks to open the file we will modify
    ** for block mode I/O for reading with full sharing.
    */
    fab = cc$rms_fab;
    fab.fab$b_fac = FAB$M_BIO|FAB$M_GET|FAB$M_PUT;
    fab.fab$l_fna = filename;
    fab.fab$b_fns = strlen (filename);
    fab.fab$b_shr = FAB$M_SHRGET|FAB$M_SHRPUT|FAB$M_SHRDEL|
                    FAB$M_SHRUPD|FAB$M_UPI;

    rab = cc$rms_rab;
    rab.rab$l_fab = &fab;
    rab.rab$l_rop = RAB$M_BIO;
    rab.rab$l_ubf = bucket_buffer;

    /*
    ** Open the file.
    */
    r0_status = sys$open (&fab);
    errchk_sig (r0_status);

    /*
    ** Connect a record stream.
    */
    r0_status = sys$connect (&rab);
    errchk_sig (r0_status);

    /*
    ** Set up the RAB to read the first 512 bytes of the file
    */
    rab.rab$w_usz = 512;
    vbn = 1;
    rab.rab$l_bkt = vbn;

    /*
    ** Read the indexed file's prolog table, contained in the first 512
    ** bytes of the file.
    */
    r0_status = sys$read (&rab);
    errchk_sig (r0_status);

    /*
    ** Cast the data as a prolog key structure. This is the primary key.
    */
    key_p = (PROLOGUE_KEY *)bucket_buffer;
    if ((key_p->key$b_flags & KEY$M_INITIDX) != 0) {
        /*
        ** Can't continue as index not initialized
        */
        (void)lib$stop (RMS$_IDX);
    }

    /*
    ** Get the vbn and offset for the first alternate key
    */
    vbn = key_p->key$l_idxfl;
    rab.rab$l_bkt = vbn;
    noff = key_p->key$w_noff;

    /*
    ** Read that block, and add the offset if necessary.
    */
    r0_status = sys$read (&rab);
    errchk_sig (r0_status);

    key_p = (PROLOGUE_KEY *)bucket_buffer;
    if (noff >= 1) {
        key_p += noff;
    }

    /*
    ** Set changes.
    */
    key_p->key$b_flags |= KEY$M_CHGKEYS;

    /*
    ** Recalculate checksum.
    */
    wp = (short int *)bucket_buffer;
    checksum = 0;
    for (i = 0; i < 255; i++) {
        checksum += *wp++;
    }
    bucket_buffer[510] = (unsigned short int)checksum;

    /*
    ** Rewrite the block
    */
    r0_status = sys$write (&rab);
    errchk_sig (r0_status);

    r0_status = sys$close (&fab);
    errchk_sig (r0_status);
}

Posted at December 21, 2012 9:00 AM
Tag Set:

Comments are closed