Sunday, June 12, 2016

Examining The Anatomy of a Logical Unit (LU)

This page is a part of the "Understanding IRIS" collection.  Many thanks to David Takle, for figuring this out, and sharing this with us:

------------------------------------------------

I used an octal file reader, rather than a hex one, since I am more familiar with how IRIS looks in octal formats.

This octal file reader is actually a PHP program I wrote and run on my pc. (I use WAMP for a local server).
( I could not find a good octal program to download)

another approach:
If you have access to a website that you can upload to, you can upload the PHP program and your binary file and use it that way.
They just need to be on the same file system, since PHP runs as a process on the server, not in the browser. And nearly every web server supports PHP.

To begin, I examined block 0 of the LU3.bin file.



It is obviously some kind of filler block. This went on for a few blocks.
Then, in block 010 (8) something changed.


There's no way to be sure at this point, but it might be a boot block of some kind.
Try the next block:

Now this looks like an IRIS INDEX header, but with all of the bytes swapped in the words.
If you swap the pairs of letters beginning at the top, you get "INDEX."
Position 177 should be the header block address (which is '1' for INDEX), and 0400 happens to be an integer 01 with the bytes swapped (i.e. lower byte times 0400)
Just to be sure, let's skip a block and check the next one, which would correspond to an IRIS block 03, which is always an ACCOUNTS file header.



Sure enough, CAOCNUST is ACCOUNTS with pairs of letters swapped.

At this point, we can be sure that this LU3.bin file is reverse endian for what we need, and the first 8 blocks should be dropped off.
A short utility does the trick. See conv-endian.php.

After the conversion, let's look at block 1. It should look like an INDEX header.



Success!
From displ 011, we can see that the index is 0161 blocks long, and all the block#'s are listed
beginning with displ 0177 through displ 0357.
Notice that the allocations are mostly sequential, but with 2 gaps.
Block 5 is missing, because it usually belongs to ACCOUNTS.
Blocks 020-026 are also missing, which means they probably belong to DMAP.

Before going much further, let's look at the accounts file.




The header block 3 shows 2 blocks in the file (displ 011) which are 3 and 5 (displ 0177, 0200).
Block 5 shows there are 3 accounts on the LU, SYSTEM, MANAGER, and COMP.

Now let's go back and look at block 4, which is the first data block of the index (header displ 0200).

It only shows 3 files so far, INDEX, DMAP, and ACCOUNTS.
We do not know for sure if that's everything, because the INDEX can scatter the filenames around (I think there's a hashing algorithm to make it faster to find files that looking through all the index blocks in order).
But we have verified that DMAP begins on block 020.
Most of the other INDEX blocks look empty, like this one.



So now I'm curious about DMAP. It can tell us a lot about the Logical Unit.
Here's the header.



Sure enough, it uses blocks 21-26 for its bit-maps of what blocks are in use and which ones are available.
Two more words are helpful here as well.

Displ 014 is the length of each dmap 'record', and it shows 014, which is 12 decimal.
Given the nature of a dmap record, that means there are 10 decimal tracks per cylinder.

Displ 015 is the number of records per block, and it shows 025, or 21 decimal. 
That means there are 21 cylinders mapped in each block.

Calculating in decimal, these numbers make sense, since 21 records times 12 words each yields 252 words used in the block.
Since a block contains 256 words, that's all that fit.

What we still do not know: How many sectors per track are there, and how many cylinders are there?
We should be able to tell by looking at the last block of DMAP.



Notice that each record takes a row and a half (12 words).
The first word in each record in the RDA of a cylinder, and the next word is how many blocks are till available in that cylinder.
The other 10 words are bit maps, one word per track.
What we see here is that no blocks have been allocated from the end of the drive, leaving 0240 (160) blocks per cylinder available for use.
Decimal 160 blocks for 10 tracks means 16 sectors per track.
We also see there are 7 cylinders defined here (the 177777 defines the end of LU) and we know there are 21 cylinders defined in each of blocks 12-15.
That comes out to 21 *4 + 7 or 91 cylinders, or 0133 octal.

Now let's verify whether the only files on the disk are Index, Accounts, and Dmap.
Picking up the first block of DMAP = 021, we see a lot of blocks in use.



In fact, every block has been allocated from Block 0 to the first 32 (dec) blocks of the cylinder that starts on 05240.
So there must be at least 1 more file somewhere in the INDEX.
And chances are, it begins at block 0173, since the last block of INDEX is 0172.
We find it in block 0153 of the index.



And it begins at block 0173. Let's take a look at the header.



We see something different here. Displ 011 tells us there are 05105 blocks in the file, but there is nothing in the block list from 0200 on.
Checking dipl 010, we see 077032. The 77 is a protection level, and 032 reveals this to be a contiguous file. That's why there's no block list.
Adding 0173 and 05104 (not including the header block) we get 05277 as the ending block for the file.
Checking back on the 1st DMAP block, that matches up perfectly. We have accounted for all the file use on the LU.

But why a big contiguous file with the name "AS.EL.11.OLD" ?
Poking around inside the file space, revealed this:



Notice the patter of sequential numbers every 11 (dec) words... 213, 214, 215, 216 ....
This is looking a lot like an index from an indexed data file, pointing to RDA's 213+ for finer detail.
Looking back at the header for AS.EL.11.OLD there are words in the FMAP area (displ 070+)
which in contiguous files is only used for Indexed Contiguous Files.

This appears to be an attempt to save old documents that used to be contained in an indexed file.
This might have been how they held them prior to obtaining StartWriter, or else a later version of Starwriter.
Because the version we are seeing in the CC tapes appears to use Formatted Data Files instead of an Indexed Contiguous.

That's about as far as we can go with this.
I'm guessing the tape said "Do Not Use" because they wanted to preserve the documents prior to converting to a different file structure.

~David

--------------------------------------------------------------

Octal File Reader PHP Program

<?php
$path = "f:/www/nova/ajs-tapes/AA-donotuse/";  // this should be edited as needed

$RDA = (isset($_POST["irda"])) ? $_POST["irda"] : '01';
$file = (isset($_POST["ifile"]) and $_POST["ifile"]>'' ) ? $_POST["ifile"] : '';
// $RDA = '01';
// $file = "tapec-lu1.dkp";
if (substr($RDA,0,1) != '0') $RDA = '0' . $RDA;
if (isset($_POST['next']) ) {
$RDA = decoct( octdec($RDA) + 1 );
}
if (isset($_POST['prior']) ) {
$RDA = decoct( octdec($RDA) - 1 );
}
$decRDA = octdec($RDA);
$rowSize = 8;
$LUFile = $path . $file;

$alpha = '................................'
. '^!"#$%&' . "'" . '()*+,-./0123456789:;<=>?'
. '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_'
. '`abcdefghijklmnopqrstuvwxyz{|}~.';

if ($file) {
$LU = fopen($LUFile, "rb");

$BLOCK = readBlock( $decRDA );  // file BLOCK
if (!$BLOCK or strlen($BLOCK) != 512 ) {
var_dump ($BLOCK);
die ("<BR>block not read");
}
$data = unpack('v*', $BLOCK);  // 256 words
$chars = unpack('C*', $BLOCK); // 512 bytes
if (count($data) != 256 or count($chars) != 512) die ("<BR>unpack failed");
}

function convertChar($start) {
global $chars;
$name = '';
for ($x=1; $x<=15; $x += 2 ) {
$byte = $start + $x;
$c = $chars[$byte+1];
// if ($c == 0) break;
$name .= showbyte($c);
$c = $chars[$byte];
// if ($c == 0) break;
$name .= showbyte($c);
}
return $name;
}

function showbyte($dc) {
global $alpha;
if ($dc >=128) $dc = $dc - 128;
$byte = substr($alpha, $dc, 1);
switch ($byte) {
  case '<':
    $byte = '&lt';
break;
  case '>':
    $byte = '&gt';
break;
  case "'" :
    $byte = '&#39';
}
// $byte .= '(' . $dc . ')';
return $byte;
}

function readBlock ($rda) {
global $LU;
$seek = $rda * 512;  // displk of block on LU
fseek($LU, $seek); // position drive
$block = fread($LU, 512); // read block
return $block;
}

function formatOctal($value) {
$octal = decoct($value);
$display = substr('       ' . $octal, -7);
return $display;
}

?>
<html>
<head>
<title>Octal View</title>
</head>
<body>
<p>
You are currently viewing file:<b> <?php echo $file;?> </b>..... Block: <b><?php echo $RDA;?> ( <?php echo $decRDA;?> )</b>
</p>
<div>
<pre>
<?php
// loop thru file
if ($file) {
$fileAddr = 0 - $rowSize;
for ($row=0; $row <= 256 - $rowSize; $row=$row+$rowSize) {
$fileAddr += $rowSize;   // word displacement
echo "<BR>" . formatOctal($fileAddr) . ": ";
for ($x=1; $x<=$rowSize; $x++) {
echo formatOctal( $data[$row + $x] );
}
$linechars = convertChar($fileAddr * 2);
echo '    ' . $linechars;
}
// wrap up
fclose($LU);
}
?>
</pre>
</div>
<br>
<form action="block1view.php" method="post">
  Block# to view (in octal): <input type="text" name="irda" value="<?php echo $RDA; ?>">
  <br><BR>
  Filename to view: <input type="text" name="ifile" value="<?php echo $file; ?>">
  <br><BR>
  <input type="hidden" name="RDA" value="<?php echo $RDA; ?>">
  <input type="hidden" name="file" value="<?php echo $file; ?>">
  <input type="submit" name="submit" value="Submit">
  <input type="submit" name="prior" value="Prior">
  <input type="submit" name="next" value="Next">
</form>
</body>
</html>

--------------------------------------------------------------

Convert Endian PHP Program

conv-endian.php

<?php
//
$filename = "f:/www/nova/AJs-Tapes/AA-donotuse/AAdnu-LU3.bin";
$outputFile = "f:/www/nova/AJs-Tapes/AA-donotuse/AAdnu-LU3.dkp";

$input = fopen($filename, "rb");
$output = fopen($outputFile, "wb");

// loop thru file
fseek($input, 8*512);
while (true) {
$contents = fread($input, 512);
if (feof($input)) break;
$data = unpack( 'n*', $contents);
$endian = '';
foreach ($data as $word) {
$endian .= pack('v',$word);
}
fwrite($output,$endian);
} // end while
fclose($input);
fclose($output);


?>

--------------------------------------------------------------

This page is a part of the "Understanding IRIS" collection.  

No comments:

Post a Comment