Some notes BEFORE you read on:
This article talks about the Volume Header Block (VHB).  Since this article was written, a new version of the VHB was created.

Unisys refers to the versions as VHB (pre 3.0) and VHB.

VHB (pre 3.0) can only address "Physically Addressed Devices".

The new VHB is able to speak to logically addressed devices, such as SCSI disks.  The new VHB appeared with CTOS/XE 3.0 and CTOS II 3.3..

For further information about the new VHB, I refer you to
"CTOS Operating System Procedural Interface Reference
 Volume 4 - System Structures
 (part no 4586 3941-200)"

Anyway, this document is talking about VHB (pre 3.0):
 
 

Disk Structures Overview

The base element of the BTOS file system is a 512 byte sector which is defined either by logical file address (lfa), or by a cylinder, head and sector number. One or more contiguous sectors comprise a disk extent, which is definable by its starting lfa and its length. Files consist of one or more disk extents.

A lfa is used to locate a particular sector of a file. It specifies the byte position within a file; that is, it is the number (the offset) that would be assigned to a byte in a file if all the bytes were numbered consecutively starting with zero. A lfa is 32 bits in length. Bits 0-29 of the lfa define a disk address, bit 30 can be set to suppress retries, and bit 31 can be set to override normal system checks to access defective disks.

Files are grouped into user-defined sets called directories, such that a file may belong to only one directory. A disk, or volume contains at least one directory (sys), which minimally contains the files which describe the disk. The number of directories per volume, and the number of files per directory are finite numbers set at volume or directory creation time.

Hierarchy

The root structure of the file system is the Volume Home Block (VHB), which contains pointers to the other structures of the file system. Two VHB's are allocated at initialization time, one at lfa zero and one at mid-point on the volume. The VHB contains the lfa of the Master File Directory (MFD), which defines the volume's directories. Each active entry in the MFD contains the lfa of its directory, which is a hashed table of file names and their pointers into the File Headers.

Each file is allotted at least one entry in the File Headers. This entry defines up to 32 disk extents of which the file is comprised. In the rare case that a file requires more than 32 extents, file headers are chained. The base address of the File Headers is also found in the VHB.
 

There exists the option to write secondary, or backup file headers, to be used in the event that the primary is unreadable. (Secondary file headers is the default option in the standard volume initialization). The secondary headers are placed after every N primary headers, where N is defined in the VHB as AlternateFileHdrPageOffset. The VHB also keeps track of the next free file header, and the total number of free file headers.
 
 

+----------+                     +-----------+
|          |-------------------->|-----------|
|          |                     |-----------|
| Volume   |                     | File      |
| Home     |                     | Headers   |
| Block    |                     |-----------|
|          |                     |-----------|
|          |->+-----+  +-----+   |           |
|          |  |-----|->| DIR |-> |           |
|          |  |-----|  +-----+   |           |
|          |  | .   |  +-----+   |           |
|          |  |   . |  | DIR |   |           |
|          |  | MFD |  +-----+   |           |
+----------+  +-----+  +-----+   +-----------+
                       | DIR |
                       +-----+

Figure 1






Figure 1 illustrates that the Volume Home Block has a pointer to the first sector of the File Headers, as well as to the first sector of the Master File Directory. Each Directory has entries for all the files in the directory and a pointer to the File Header which describes the file. Each File Header has pointers to the extents of the file.

The other structures which comprise the file system are the Bad Block File (BadBlk.Sys), which keeps a count of bad spots by cylinder/head/sector address, and the Allocation Bit Map, which contains a bit for every disk sector. The bit is set if the sector is available.

The Volume Home Block

The VHB contains the locations and sizes of the other structures which comprise the BTOS file system as well as pointers to other system files such as the operating system (sysImage.sys), crash dump file (crashDump.sys), log file (log.sys), etc. BTOS initialization writes two Volume Home Blocks per disk, one at logical file address zero and one at a mid-point on the media.

The VHB is accessible by reading lfa zero, by calling the BTOS function GetVHB, or by accessing the pointer to the memory resident VHB found in the Device Control Block (DCB). The VHB itself has no entry in the File Headers.

Offset Size Field
0      2    Checksum
            See Appendix.

2      4    lfaSysImagebase
            Address of the first sector of the operating system.

6      2    cPagesSysImage
            Size in sectors of the operating system.

8      4    lfaBadBlkbase
            Address of the first sector of the bad sector file (badBlk.sys).

12     2    cPagesBadBlk
            Size in sectors of the bad block file .

14     4    lfaCrashDumpbase
            Address of the first sector of the crash dump file
            (crashDump.sys).

18     2    cPagesCrashDump
            Size in sectors of the crash dump file.

20    13    VolName
            Volume Name; first byte contains the count of bytes in
            the volume name.

33     13   volPasssword
            Volume Password; first byte contains the count of bytes
            in the Password.

46      4   lfaVHB
            Address of the first sector of the second (active) VHB.

50      4   lfaInitialVHB
            Address of the first sector of the first (backup) VHB.

54      4   creationDT
            The date of initialization in System Date/Time format.

58      4   modificationDT
            The date of last modification in System Date/Time format.

62      4   lfaMFDbase
            Address of the first sector of the Master File Directory.

66      2   cPagedMFD
            Size in sectors of the MFD.

68      2   lfaLogbase
            Address of the first sector of the system log file (log.sys).

72      2   cPagesLog
            Size in sectors of the system log file.

74      2   currentLogpage
            The sector offset from lfaLogbase where the current log
            entry is to be written.

76      2   currentLogbyte
            The byte offset within currentLogpage where the current
            log entry is to be written.

78      4   lfaFileHeadersbase
            Address of the first sector of the File Headers

82      2   cPagesFileHeaders
            Size in sectors of the File Headers.

84      2   altFileHeaderPageOffset
            The number of file headers that separate a primary file
            from its secondary file header.

86      2   iFreeFileHeader
            The offset from lfaFileHeadersbase to the next available
            file header.

88      2   cFreeFileHeaders
            The total number of unused file headers.

90      2   clusterFactor
            Not used. Contains a 1.

92      2   defaultExtend
            Not used. Contains a 1.

94      2   allocSkipCnt
            Not used. Contains a 1.

96      4   lfaAllocBitMapbase
            Address of the the first sector of the allocation bit map.

100     2   cPagesAllocBitMap
            Size in sectors of the allocation bit map.

102     2   lastAllocBitMapPage
            When combined with lastAllocWord and lastAllocBit, forms a
            pointer into the bit map for the next available sector.

104     2   lastAllocWord
            see lastAllocBitMapPage

106     2   lastAllocBit
            see lastAllocBitMapPage

108     4   cFreePages
            Total number of unallocated sectors.

112     2   iDev
            Offset into the array of device control blocks.

114   105   rgLruDirEntries
            An array of the three Last Recently Used MFDs
            (35 bytes each, see MFD structure).

219     2   magicWd
            Used to calculate the checksums for the Volume Home Block
            and the File Headers. Value is 7C39.

221     1   SysImageBaseSector

222     1   SysImageBaseHead

223     2   SysImageBaseCylinder

225     2   SysImageMaxPageCount

            (The above fields describe for the bootstrap ROM the location
             and file size of the program to be bootstrapped.)

227     1   BadBlkBaseSector

228     1   BadBlkBaseHead

229     2   BadBlkBaseCylinder

231     2   BadBlkBaseMaxPageCount

            (The above fields describe the location of the Bad Block map
             used by IVolume when reinitializing a volume.)

233     1   DumpBaseSector

234     1   DumpBaseHead

235     2   DumpBaseCylinder

237     2   DumpBaseMaxPageCount

            (The above fields describe the location and file size of the
             crashdump area to be used by the Bootstrap ROM when a memory
             dump is performed.)

239     2   BytesPerSector

241     2   SectorsPerTrack

243     2   TracksPerCylinder

245     2   CylindersPerDisk

            (The above fields describe the physical characteristics of the
             disk drive.)

247     1   InterleaveFactor
            see Sector interleaving

248     2   SectorSize

250     1   SpiralFactor
            see Sector spiraling

251     1   StartingSector
            The above four fields describe formatting parameters used
            by IVolume.

252     4   Reserved for expansion.
 

The Master File Directory.
The master file directory contains hashed entries for each directory on the volume. An entry for the MFD exists in the file headers under the file name "<sys>Mfd.Sys".

The sector address of the MFD is found in the Volume Home Block, as is its length in sectors. Up to fourteen entries can be stored in each sector of the MFD, and each sector has a one byte header before the MFD entries begin.

MFD entry:

Offset Size Field
0      13   DirectoryName
            Name of the directory; first byte contains the count of bytes
            in the directory name.

13     13   DirPassword
            Name of the password; first byte contains the count of bytes
            in the password name.

26      4   lfaDirbase
            Address of the the first sector of the directory.

30      2   cPages
            Size in sectors of the directory.

32      1   defaultAccessCode
            Password protection level of the directory.

33      2   lruCnt
            Last recently used count; the last used directory has a zero
            lruCnt. The other directories lruCnts are incremented when a
            file is accessed which does not belong to the directory.

MFD sector:

Offset Size Field
0       2   Header

2     490   rgMFDentries(14)
            an array of fourteen MFD entries described above.

The Directory
The Master File Directory contains the sector address and the sector size of all the directories on the volume. The Directory has no entry for itself in the File Headers and so may be accessed only through the Master File Directory itself.

File names are hashed into the list using the algorithm described in the appendix. All other bytes in the list are set to zero; a sequential search for files within the directory searches for the first non-zero byte, signifying start of entry.

Offset     Size Field
0            1   cbFileName
                 The count of bytes in the filename.

1                cbFileName
                 The File name.

1+cbFilename 2 FileHeaderOffset.

The offset from the start of the file headers to the entry for this file.
 

File Headers
The File Headers contain primary, and optionally, secondary file headers. Secondary file headers reside N sectors past the primary file header, where N is "AltFileHeaderPageOffset" as described in the VHB. Non-active file headers contain a zero length in the file name field. Since there is no notion of the "last" active file header, the file headers are rarely read sequentially - rather the Directory entries are searched for a pointer to the file headers.

The file headers reside in a file describing itself called "<sys>FileHeaders.Sys".
 

Offset Size Field
0       2   checksum
            see appendix

2       2   FileHeaderPageNumber.
            The sector offset from the start of the file headers of the
            primary file header.

4      51   sbFileName.
            File name, the first byte contains the length of the file name.
            If the first byte is zero the file header is inactive.

55     13   sbFileNamePassword.
            File password, first byte contains the length.

68     13   sbDirectoryName.
            Directory name the file is located in. The first byte contains
            the length of the name.

81      2   FileHeaderNumber.
            The sector offset from the start of the file headers of the
            primary file header.

83      2   ExtensionFileHeaderNumber.
            The sector offset from the start of the file headers of the
            extension file header in the case of a file with more than 32
            extents.

85      1   bHeaderSequenceNumber.
            Sequential number assigned to extension file headers.

86      1   bFileClass.
            Not Implemented.

87      1   bAccessProtection.
            File protection level.

88      4   lfaDirPage.
            Sector address of the master file directory entry for the
            file's directory.

92      4   CreationDate.

96      4   Modification Date.

100     4   AccessDate.

104     4   ExpirationDate.
            Not implemented.

108     1   fNoSave.
            Used by the BackUp Volume utility to determine whether to
            backup the file. Set to TRUE for FileHeaders.Sys, Mfd.Sys, etc.

109     1   fNoDirPrint.
            Not Implemented

110     1   fNoDelete
            Set to TRUE for system files which should not be deleted.

111     4   cbFile
            Size of the file in bytes.

115     4   defaultExpansion
            The sector size by which to expand the file if a new extend
            is to be created.

119     2   iFreeRun
            Index into the rgLfaExtent and rgoExtent tables below of the
            next available empty extent.

121   128   rgLfaExtents.
            Array of 32 sector addresses of the file extents.

249   128   rgcbExtents
            Array of 32 byte lengths of extents, where the length is a
            multiple of the sector size.

377    71   Reserved

448    64   application specific field
 

Bad Block File
The Bad Block file records up to 128 bad sectors on the volume. Its lfa is contained in the VHB, and its length is always one sector. For information on converting cylinder, head, and sector addresses into a lfa, see appendix.

Offset Size Field
0      128  rgbBadSector
            Array of 128 bytes defining the sector number of the first
            128 bad spots.

128    128  rgbBadHead
            Array of 128 bytes defining the head number of the first 128
            ad spots.

256    256  rgwBadCylinder
            Array of 128 words defining the track number of the first 128
            bad spots.
 

Allocation Bit Map
The lfa and size of the Allocation Bit Map is described in the VHB. Each bit in the map describes the availability of one sector on the volume; the bit is set to on if the sector is available. Since the bit map has no entry in the file headers, and hence no end of file description, it can be read successfully by determining the total number of sectors on the volume (this information can be extrapolated from the Device Control Block), and reading only the first nSectors/8 bytes of the bit map. (The DCB is described in the "Memory Structures", below).

Memory Structures Overview
Information about the disk devices available to the system is found in the Device Control Blocks which describe the characteristics of the device (eg, tracks, sectors, sector size, number of retries), as well as the name and password. The DCB also contains an offset to the memory resident VHB for the device, and offsets to the first and active IO Blocks for it. The pointer to the offset to an array of offsets to the system DCBs is located at 27Ch (in the System Common Address Table, described in the BTOS manual). The number of DCBs is a sysgen parameter.

When a request to open a file is issued, the OS allocates a File Control Block, does a search of the file header block for the filename, and loads the file header number of the file into the FCB (a new file header number is returned if the file did not previously exist). The lfa's and sizes of the file's disk extents are read from the file header and loaded into a linked list of File Access Blocks; the FCB remembering the first FAB. Also a File User Block is allocated which contains a pointer to the FCB and contains the user number. A file handle is assigned to the file and returned to the caller for subsequent operations as an offset to the correct FUB. The User Control Block associated with the task contains a pointer to an array of offsets which point to the FCB's of the task.
Pointers to the offset of an array of offsets to FCBs and UCBs are stored in the System Common Address Table (SCAT) at addresses 280h and 284h respectively. (Note: these offsets must be combined with the segment address of DGroup of the Operating System, which is found at 242h of the SCAT).

The number of FCBs and FABs are sysgen parameters: the number of FCBs should be equal to the number of files to be open at one time; the number of FABs is a factor of the number of FCBs and how fragmented the volume becomes (i.e., disk extents per open file).
 

Device Control Block
The DCB can be accessed via the BTOS call "QueryDCB", or through the pointer in the SCAT described above.

Offset Size Field
0       1   fMountable

1       1   fNonSharable

2       1   fDoubleDensity

3       1   fNoMultiTrack

4       1   fAttention

5       1   fTimeout

6      13   sbDeviceName

19     13   sbDevicePassword

32      1   controllerNum

33      1   iUnit

34      1   state

35      1   unitStatus

36      1   deviceClass

37      1   cUsers

38      2   oVHB

40      2   oIOBfirst

42      2   oIOBactive

44      4   lfaMax

48      4   lfaMask

50      2   verifyKey

52      2   ovlyProcOpen

54      2   ovlyProcClose

56      2   cRetryMax

58      2   cSoftSectors

60      2   cHardErrors

62      2   currentCylinder

64      1   sectorSizeCode

65      2   GapLength

67      1   DataLength

68      2   BytesPerSector

70      2   SectorsPerTrack

72      2   TracksPerCylinder

74      2   CylindersPerDisk
 

File Access Block
Offset Size Field
0       2   oChainFAB

2       4   lfaDiskExtent

6       4   sizeDiskExtent
 

File Control Block
Offset Size Field
0       2   oFAB

2       2   devNum

4       2   userCount

6       2   openMode

8       2   FileHeaderNum
 
 

File User Block
Offset Size Field
0       2   oFCB

2       2   userNum

4       1   fhStatus
 

Appendix

Sector Spiraling

Spiraling is a performance mechanism of the floppy device driver on the B20 Series used to reduce the wait time for a sector to come under the head when the head must switch tracks or diskette sides. During the time the head switches or seeks the next track approximately 3 sectors have spun past it; to avoid waiting for the next cSectorsPerTrack-3 to spin past the head, the first sector of the next track/head is assigned the sector number of nSectorPrevTrack+3. The first three tracks of a B20 16 sector diskette are shown below:

Physical Sector Position

1 1 1 1 1 1 1

1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6

1 1 1 1 1 1 1

Track 1 1 2 3 4 5 6 7 8 9 1 1 2 3 4 5 6

1 1 1 1 1 1 1 1

Track 2 4 5 6 1 2 3 4 5 6 7 8 9 1 1 2 3

1 1 1 1 1 1 1

Track 3 1 2 3 4 5 1 1 2 3 4 5 6 7 8 9 0
 
 

Sector Interleaving

Interleaving is a sector mapping technique used on B20 winchesters to match the spin speed of the disk to the disk controllers's ability to read sectors. Since 3 sectors spin by the head in the time it takes the controller to be ready to read the next sector. Sector numbers are assigned which are three past the last physical position read.

The sectors of a track of an interleaved disk are shown here:

Physical Sector Position

1 1 1 1 1 1 1

1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6

1 1 1 1 1 1 1

Track 1 1 4 7 0 3 6 3 6 9 2 5 2 5 8 1 4
 
 
 
 

Hashing Algorithm

The following hashing algorithm is used to place and locate entries within the Master File Directory and the Directories to determine the sector within which the entry resides.

The arguments the hasher takes are name (directory or file) and the size in sectors of the table (Mfd.cDirectory or Vhb.cMfd).

"Name" is an array[1..n] of bytes;

"Divisor" is the size in sectors of the

structure;

"n" is the length of Name;

"b" is a byte;

"x" is a word;

x := 0;
For i := 1 to n do
begin
    b := name[i];
    if b >= 'a' and b <= 'z'
        then b := b-#20;
        {make upper case}
    x := 73*x+b;
end;

hashSectorNumber := x Mod Divisor;
 

Checksum Algorithm

The Volume Home Block and the File Headers use this checksum, however the VHB checksums the first 128 words only, while the FHB checksums 256 words. The value of magicWord is 7C39h, and can be found in the VHB.

"w" is a word;

"nLastWdSector" is the number of words to

checksum;

"wBuffer" is the sector to be checksummed;

w := magicWord;

for i := 1 to nLastWdSector do
w := w - wBuffer[i];
if w <> 0 then erc := ercBadCheckSum;
 

Appendix

Computing Lfa's from head/cylinder/sector.

The algorithm for computing a logical file address (lfa) given the cylinder, head, and sector number (where sector number is zero based) is :

Lfa = ((cylinder * Dcb.TracksPerCylinder + Head) * (Dcb.SectorsPerTrack * Dcb.BytesPerSector)) + ((iSector-1) * Dcb.BytesPerSector).
 
 

Terminology

Prefix Meaning
lfa    logical file address, 4 bytes in length.

sb     character string where the first byte is the length of the string.

p      memory pointer, 4 bytes; most significant word is segment
        address, least is offset.

o      offset, the register address portion of a pointer rg array of.

c      count of, displaces a word.

b      byte.
       (note that words are in Intel format, most significant byte last)