Notes on FileSwitch/Image Filing System interface


History:

  0.01  11-Jan-94  Created from info in PRM
  0.02  22-Feb-94  Added ImageEntry_Func 33 (almost missed out of PRM ...)
                   Corrected details of some of the more exotic Funcs.
  0.03  23-Feb-94  Minor correction to ImageEntry_File 0
  0.04  25-Feb-94  Add r0=code to ImageEntry_Open
        07-Mar-94  Further details added resulting from experience writing
                    an image filing system
        08-Mar-94  Note on preservation of R2 for ImageEntry_Func 14 added
        10-Mar-94  Section on image stamping added
        11-Mar-94  Sections on image identification and formatting added.
                   Some notes on other service calls, reentrancy requirements
                    and storage allocation added.



1  Initialisation and exit


When an image filing system (IFS) module initialises, it must:

  - register with FileSwitch (using OS_FSControl 35)

  - set the variable File$Type_<hex-image-type> = <image-type-string>
       (eg set File$Type_333 = "NewFS")


When the module exits, it must de-register from FileSwitch using
OS_FSControl 36; no other actions are necessary, since this call causes
FileSwitch to call ImageEntry_Func 22 (image file about to be closed) after
closing any files open in the image. Note, however, that any Filer viewers
displaying objects inside now-closed images remain on the screen.

Details of the registration and deregistration SWIs follow.


OS_FSControl 35

             In                      Out

    R0    35
    R1    IFS module base address
    R2    offset of IFSIB within IFS module
    R3    pointer to private word

The IFSIB - image filing system information block - is 9 words long, and
contains the following data:

  word    content

    0   IFSIW (see below)
    1   image filetype
    2   ImageEntry_Open entry point
    3   ImageEntry_GetBytes entry point
    4   ImageEntry_PutBytes entry point
    5   ImageEntry_Args entry point
    6   ImageEntry_Close entry point
    7   ImageEntry_File entry point
    8   ImageEntry_Func entry point

        where each entry point is given as an offset relative to the module
        base address.

The IFSIW - image filing system information word - has just one option:

   bit val  meaning

    27  1   if set, notify IFS when flushing by calling ImageEntry_Args 6
              [not 255, as documented on p.2-526, I think])

This bit needs to be set only if the IFS buffers data - in which case
FileSwitch will call ImageEntry_Args 6 whenever it needs to be sure that all
file data has been written to disc.

R3 should be set to the value to be passed in R12 whenever FileSwitch calls
one of the IFS's entry points.


OS_FSControl 36

             In                      Out

    R0    36
    R1    image filetype

This call instructs FileSwitch to deregister the IFS associated with the
given filetype. FileSwitch will, as a result, call ImageEntry_Close to close
any files open on any images, and then call ImageEntry_Func 22 - notification
that image is about to be closed - before closing any open image files.



2  Opening and closing the image file


  [These issues are relatively esoteric - it's not usual to open or close an
    image file explicitly.]


If an explicit request is made to open an image file that is already
registered with its IFS, FileSwitch behaves as follows:

 - If there are any files open in the image, the request is refused.

 - Otherwise, ImageEntry_Func 22 is called to de-register the image file.

 - The image file is then closed before being reopened as requested.


Any attempt to access the contents of an image when the image file has been
opened for some other reason is faulted.



3  Image stamping


A particular (version of a) disc image is recognised by a combination of its
image-name and image-stamp; this makes it possible for the host filing system
(ie FileCore) to keep track of removeable media, and to determine whether
the contents are the same when remounted.


The image-stamp value itself may be an explicit value, or may be calculated
(as, say, a check-sum of the root directory contents). We assume here that
an explicit "sequence number" is used.


The IFS's responsibilities are as follows:

  When a disc image is identified as belonging to the IFS (see the section on
  image identification below), the value of the image-stamp is inserted into
  the disc record.

  When an image is registered with the IFS (by calling ImageEntry_Func 21 -
  notification of new image), that image is put into the "stamp on next
  update" state.

  When explicitly requested to do so by a call of ImageEntry_Func 32 - stamp
  image - with R2 = 0, the specified image is put into the "stamp on next
  update" state.

  When explicitly requested to do so by a call of ImageEntry_Func 32 - stamp
  image - with R2 = 1, the specified image is "stamped" immediately.

where:

  The IFS "stamps" an image by:

    - incrementing the image-stamp value in the image
    - flushing the image to its storage medium by calling OS_Args 255
    - informing FileSwitch of the new image-stamp value by calling OS_Args 8

  When an image is in "stamp on next update" state and any data in the image
  is updated, the IFS must:

    - stamp the image
    - reset the "stamp on next update" state



4  Image identification


FileSwitch recognises a disc file as being an image file by means of its file
type - if this matches a filetype registered by an IFS, then FileSwitch calls
ImageEntry_Func 21 - notification of new image - to register the image file
with the relevant IFS.


Determining who is responsible for an entire disc image is not so easy, and
the process is summarised as follows:

  FileCore asks ADFS to mount the disc, and ADFS identifies various physical
  characteristics of the disc which are noted in a "disc record" associated
  with the drive.

  FileCore now issues the service call Service_IdentifyDisc.

  Each IFS in turn examines both the disc record and the disc itself if
  necessary to determine whether the disc is one that the IFS knows how to
  handle.

  If the IFS recognises the disc as one consisting of an image that it is
  able to process, it fills in further details in the disc record, including:

      - the image-name          (in the disc_name field)
      - the image-stamp value   (in the disc_id field)

  and then claims the service call.

Other details of Service_IdentifyDisc follow.


Service_IdentifyDisc  (&69)

             In                      Out

    R0
    R1    &69                     0  (to claim)
    R2    ->buff                  image filetype
    R3    length
    R4
    R5    ->disc record           ->disc record (modified)    
    R6    sector cache handle     new sector cache handle
    R7
    R8    ->FileCore private word

If the IFS needs to read data from the disc, it should use a special form of
FileCore_DiscOp which maintains an RMA cache of sectors that have been read:
this avoids repetitive reads of, say, sector 0 by each registered IFS
whenever a floppy disc is inserted into a drive. A handle for this cache is
passed in R6, and may be updated by FileCore_DiscOp when more data is read;
if so, the new value should be passed on (or returned if the service call is
claimed). Details of this special form of FileCore_DiscOp follow.

If the disc is not recognised, the service call should be passed on (with
only R6 possibly updated).

If the disc is recognised:

  - If R2 is non-zero, a textual description of the disc's current format
     is copied to the buffer addressed by R2 whose length is given by R3;
     this is used to respond to the DeskTop "Format=>Current format=>" menu
     selection.

  - R2 is set to the image filetype - passed to FileSwitch when the IFS
     registered.

  - Additional information about the disc is noted in the disc record.

  - R1 is set to 0 to claim the call.


FileCore_DiscOp - as used to read sectors during identification

             In                      Out

    R0
    R1    9+64+((drec>>2)<<8)
    R2    disc address            updated disc address
    R3    ->buff                  updated buffer pointer
    R4    length                  updated length
    R5    
    R6    sector cache handle     new sector cache handle
    R7
    R8    ->FileCore private word

On entry:

  - R1 contains 9 (reason code = "read sectors via cache"), plus 64 (ignore
     escape), plus - in the top three bytes - the address in words of the
     disc record supplied with the service call.

  - R2 contains the disc address from which data is to be read. The top three
     bits contain the drive number, and the bottom 29 bits a byte offset
     which must be a multiple of the sector size. The drive number can be
     obtained as the top three bits of the "root directory address" field
     of the disc record supplied with the service call.

  - R3 addresses a buffer to hold the data, and R4 specifies how much data
     is to be read.

  - R6 and R8 should be set to the corresponding values passed in with the
     service call.



5  Formatting


When formatting from the command line, the command:

  *help format

causes ADFS to issue service call Service_DisplayFormatHelp; each IFS
responds by writing information about the formats it supports to the active
output stream.

The command:

  *format <drive> <format-ident> [<disc name>] [Y]

causes ADFS to issue service call Service_IdentifyFormat, passing the
<format-ident> as a parameter. Any IFS which recognises the format claims
the service, and returns its "formatting package" to ADFS (my terminology -
see below). ADFS then uses information in the formatting package to prepare
the disc for use by the IFS.


When a user passes across the "Format=>" menu item, ADFS issues the service
call Service_EnumerateFormats. Each IFS responds by supplying menu and help
text for any formats it supports, together with its formatting package.

When a user chooses an IFS format by choosing:

  Format=>Other formats=><IFS format>

ADFS uses the corresponding formatting package to prepare the disc for use by
the IFS.*


A disc is prepared for use in two stages:

  - physical formatting
  - logical structure layout

Physical formatting is undertaken by the HFS (ie ADFS) after discussion with
the IFS to agree on suitable formatting parameters. The disc is then opened
as a file, and the IFS is called to layout the logical structures required.

The "formatting package" contains the numbers of two SWIs supplied by the IFS
to assist in these two stages - together with parameters with which they
should be called:

  IFS_DiscFormat, format_param       -  to be called during format parameter
                                        negotiations

  IFS_LayoutStructure, layout_param  -  to be called to write out appropriate
                                        structures to the blank image file


Physical formatting proceeds as follows:

  - ADFS calls IFS_DiscFormat passing across:
       - format_param (as supplied in the formatting package)
       - a disc format structure
       - HFS_VetFormat
       - vetformat_param

  - IFS_DiscFormat fills in details of the formatting parameter values that
    it would, ideally, like to use in the disc format structure, and then
    calls the SWI HFS_VetFormat passing across:
       - vetformat_param
       - the disc format structure

  - HFS_VetFormat checks to see if the formatting parameter values are
    achievable; if not, it modifies them if possible, and returns to the IFS.

  - IFS_DiscFormat performs a final check on the (possibly modified)
    formatting parameters; if these are satisfactory, this SWI returns
    without error to ADFS.

  - ADFS now proceeds to format the disc according to these parameters -
    known to be both achievable and acceptable to the IFS - using either the
    *format command or the DeskTop formatter.

If either of IFS_DiscFormat or HFS_VetFormat find the formatting parameters
suggested by the other to be unacceptable, they can return errors, in which
case ADFS will abandon the formatting attempt.


Logical structure layout is much simpler:

  - ADFS opens the newly-formatted disc as a file.

  - ADFS calls IFS_LayoutStructure passing across:
      - layout_param (as supplied in the formatting package)
      - disc name

  - IFS_LayoutStructure constructs an appropriate image with the disc name as
    image-name and a suitable initial image-stamp value.


 [* In fact, the order of operations for desktop formatting appears to be as
    follows:

      - Service_EnumerateFormats is issued as the user crosses the Format=>
         sub-menu arrow - presumably to discover whether there are "ADFS
         native formats" for which main menu entries must be created.

      - IFS_DiscFormat is immediately called to negotiate physical formatting
         parameters (although this appears unnecessary).

      - When the user chooses a format from the menu, these two actions are
         repeated before loading the DeskTop formatter and displaying its
         main window - presumably to discover whether there are further
         ("foreign") formats for which sub-menu entries must be created.

      - When the user clicks the "Format" button, a final call of the SWI
         IFS_DiscFormat is made before physical formatting commences; this
         call must be the one which determines the physical formatting
         parameters to be used.

      - When formatting is complete, IFS_LayoutStructure is called, and,
         finally, the disc is verified. ]


More details of these various service calls and SWIs follow.


Service_DisplayFormatHelp  (&6c)

             In                      Out

    R0    0
    R1    &6c

Use, for example, OS_Write0 to write out a description of the format(s)
supported by the IFS.

This service call should only be claimed in the event of an error arising
while displaying the help information, in which case R1 should be set to 0
and R0 should point to the error block on return.


Service_IdentifyFormat  (&6b)

             In                      Out

    R0    ->format-ident
    R1    &6b                     0 (to claim)
    R2                            IFS_DiscFormat
    R3                            format_param
    R4                            IFS_LayoutStructure
    R5                            layout_param

On entry, R0 addresses the string that identifies the format requested by the
user in the *format command.

If the IFS recognises the format, the service call must be claimed by setting
R1 = 0, and a "formatting package" is returned in registers R2 to R5:

  IFS_DiscFormat is the number of the SWI to call to determine the IFS's
  preferred physical formatting parameters; when it is called, R3 must be
  set equal to format_param.

  IFS_LayoutStructure is the number of the SWI to call to layout the logical
  structure onto the formatted disc; when it is called, R0 must be set equal
  to layout_param.


Service_EnumerateFormats  (&6a)

             In                      Out

    R1    &6a
    R2    ->format-spec-list      ->format-spec-list

R2 contains a pointer to a list of format specifications blocks. Each IFS
which supports formatting should add one or more further format specification
blocks to the list. Note that R2 will be 0 if no format specification blocks
have yet been defined.

Each format specification block contains:

  word    meaning

    0   pointer to next format specification block, or zero if none
    1   pointer to RMA block containing menu text
    2   pointer to RMA block containing help text
    3   IFS_DiscFormat
    4   format_param
    5   IFS_LayoutStructure
    6   layout_param
    7   flags:            bit     meaning

                          0     This is a "native" (ADFS) format
                         1-31   Reserved - must be set to zero

Note that words 3 to 6 inclusive of a format specification block contain a
"formatting package" as described above.

This service call should be claimed only if an error arises, in which case
set R1 = 0 and R0 to the address of an error block.

The purpose of flag bit 0 is to determine where in the Format menu structure
the corresponding format entry appears: if the bit is set, the format will
appear in the "Format=>" sub-menu; if it is not set, the format appears in
the "Format=>Other formats=>" sub-menu.

Both the format specification block itself and the blocks to which it points
must be individually allocated RMA blocks. The order in which these blocks
are allocated and linked together is prescribed as follows:

  - allocate format specification block, and zero words 1 and 2
  - link this block to the format-spec-list
  - allocate the block for menu text, and store a pointer to it in word 1
  - allocate the block for help text, and store a pointer to it in word 2

This makes it possible for the issuer of the service call to tidy up if an
error is reported part way through the process of filling in a format
specification block.


IFS_DiscFormat

             In                      Out

    R0    ->disc format structure
    R1    HFS_VetFormat
    R2    vetformat_param
    R3    format_param

The disc format structure is as follows:

  offset   size   meaning

    0       4     Sector size in bytes
    4       4     Gap 1 Side 0
    8       4     Gap 1 Side 1
   12       4     Gap 3
   16       1     Sectors per track
   17       1     Density:
                   1 - single density
                   2 - double density
                   3 - double+ density
                   4 - quad density
                   8 - octal density
   18       1     Options:
                   bit   0    1   index mark required
                   bit   1    1   double step
                   bits 2-3   0   interleave sides
                              1   format side 1 only
                              2   format side 2 only
                              3   sequence sides
                   bits 4-7       reserved (must be 0)
   19       1     Start sector number on track
   20       1     Sector interleave
   21       1     Side/side sector skew (signed)
   22       1     Track/track sector skew (signed)
   23       1     Sector fill value
   24       4     Number of tracks to format
   28      36     Reserved (must be 0)

Some further information on the meaning of these values and how they should
be set and vetted can be found in the PRM around pages 2-197 and 2-264.
   

HFS_VetFormat

             In                      Out

    R0    ->disc format structure
    R1    vetformat_param


IFS_LayoutStructure

             In                      Out

    R0    layout_param
    R1    ->bad block list
    R2    ->disc name
    R3    fswh



6  Other service calls


This is a list of other service calls to which DOSFS responds:

  number  meaning

   &11    Service_Memory
   &12    Service_StartUpFS
   &27    Service_Reset
   &40    Service_FSRedeclare
   &42    Service_LookupFileType
   &5c    Service_WimpSaveDesktop
   &68    Service_CloseFile



7  Reentrancy requirements


In general, IFS entry points must be re-entrant - since an image file may
itself be stored as a file inside another image - possibly of the same image
type.

In practice, the "downwards" interface between an IFS and FileSwitch is
usually quite narrow, which simplifies considerations. For example, the
demonstration NewFS uses only the following FileSwitch entry points:

  During initialisation/finalisation:

     OS_FSControl 35 -  register image filing system
     OS_FSControl 36 -  de-register image filing system

  In response to Service_StartUpFS:

     OS_FSControl 14 -  select filing system 

  In response to entry points:

     OS_GBPB 3       -  read bytes from open file
     OS_GBPB 1       -  write bytes to open file
     OS_Args 255     -  flush file to storage medium
     OS_Args 8       -  notify new image-stamp value
     OS_Args 2       -  read open file's extent

  During execution of the *crimage command:

     OS_File 11      -  create empty file
     OS_Find &cc     -  open file
     OS_GBPB 1       -  write bytes to open file
     OS_Find 0       -  close file



8  Storage allocation


In my demonstration NewFS filing system, all storage allocation is done by
calling malloc(..) - which allocates RMA.

What are optimum techniques for RMA usage?



9  IFS entry points - summary


The entry points - and sub-entry points where appropriate - are as follows:

  ImageEntry_Open
  ImageEntry_Close

  ImageEntry_GetBytes
  ImageEntry_PutBytes

  ImageEntry_Args

       3  Write file extent
       4  Read size allocated to file
       6  Notify of a flush
       7  Ensure file size
       8  Write zeros
       9  Read file datestamp

  ImageEntry_File

       0  Save file
       1  Write catalogue information
       5  Read catalogue information
       6  Delete object
       7  Create file
       8  Create directory
      10  Read block size

  ImageEntry_Func

       8  Rename object
      14  Read directory entries
      15  Read directory entries and information
      21  Notification of new image
      22  Notification that image is about to be closed
      25  Read defect list
      26  Add a defect
      27  Read boot option
      28  Write boot option
      29  Read used space map
      30  Read free space
      31  Name image
      32  Stamp image
      33  Get usage of offset


Note that a null path name indicates the root directory of an image.



10 Parameters


  name      )
  file-name )
  dir-name  )
    All object names are presented as full path names relative to the root
    directory of the image; thus a null path name indicates the root
    directory itself. Names are zero-terminated.

  file-handle
    This is the value which identifies an open file to the IFS. It is
    returned to FileSwitch as a result of an ImageEntry_Open call, and
    FileSwitch then passes it back to the IFS in future calls relating to
    the open file.

  image-handle
    This is the value which identifies a registered image file to the IFS.
    It is returned to FileSwitch as a result of an ImageEntry_Func 21 call,
    and FileSwitch then passes it back to the IFS in future calls relating
    to that registered image or objects within it.

  FSw-handle
    This is the value which identifies an open file to FileSwitch; it is the
    value seen by all FileSwitch clients.

  extent
    A file's extent defines the size of the file.
    When a file is opened, just bytes 0 to extent-1 are available to read.
    While a file is open, its extent can be changed in two ways:
      - explicitly: by a call of OS_Args 3
      - implicitly: by an OS_GBPB call which writes data beyond the current
        extent of the file; the new extent is equal to one greater than the
        largest offset written to.
    While a file is open, its extent is not generally known to the IFS, but
    FileSwitch guarantees that no requests to read from or write to an open
    file (ImageEntry_GetBytes, ImageEntry_PutBytes) will result in accesses
    outside of the file's allocation.
    Just before a file is closed, FileSwitch calls ImageEntry_Args 3 to write
    its new extent.

  allocation
    A file's allocation defines the number of bytes immediately available for
    data transfer. It is always a multiple of the file's block-size.

  block-size
    This defines the size of the "natural" unit of data transfer for a file
    in the IFS. It must be an exact power of 2 in the inclusive range 64 to
    1024. Data transfers requested by FileSwitch (ImageEntry_GetBytes,
    ImageEntry_PutBytes) are always for multiples of the block-size, based
    on block-size boundaries in the file.

  file-offset
    A byte offset within an open file; this is always a multiple of the
    file's block-size.

  load )
  exec )
    These two values normally define the timestamp and filetype values for
    an object; format is &FFFtttdd &dddddddd where ttt is the 12-bit filetype
    and dd...dd is the 40-bit timestamp.      

  attributes
    The least significant byte of this value contains an object's access
    bits as follows:

        bit     meaning
         0     owner read
         1     owner write
         3     locked
         4     public read
         5     public write

  image-name
    This is the name of the image itself, which, in conjunction with the
    value of the image-stamp, is used to identify removeable media. It is
    also known as the "disc name". It is limited to a maximum size of 10
    characters because of space allocated in ADFS disc records.

  image-stamp
    This is a value which is used to distinguish between different "versions"
    of an image which have the same image-name. It is also known as a "cycle
    id", "disc id" or "disc stamp". It is limited to a maximum size of 16\       bits because of space allocated in ADFS disc records.



11 Register usage


Register entry values common to all IFS entry points are:

  R12 - value of R3 passed to OS_FSControl 35 during registration
  R13 - supervisor stack
  R14 - return address


The PRM - page 2-529 - says that no registers other than R13 need to be
preserved by image filing system routines.

There is one known exception to this:

   ***   R2 must be preserved across calls to ImageEntry_Func 14.   *** 

This call - read directory entries - may be made as a result of an OS_GBPB 8
call or an OS_GBPB 9 call. In the former case, the object names are returned
as <length><name> pairs, and in the latter case as <name><null> pairs.
ImageEntry_Func 14 always returns with the object names stored as
<name><null> pairs, so in the OS_GBPB 8 case FileSwitch must reformat the
buffer before returning to the caller. The buffer address is held in R2, and
it seems that FileSwitch expects this still to be the case on return from
ImageEntry_Func 14.

Incidentally, this is a bug in DOSFS - OS_GBPB 8 calls do not return with
correctly formatted buffer contents (and, presumably, some area of memory
somewhere is corrupted instead).



12 The entry points


*****************************************************************************

Open  - open a file

             In                      Out

    R0    code                    file information word
    R1    ->file-name             file-handle
    R2                            block-size  (2^n st 6<=n<=10)
    R3    FSw-handle              extent
    R4                            allocation
    R5
    R6    image-handle

  file information word:

     bit  meaning
      31  write permitted
      30  read permitted
    29-0  0

  code = 0 for read access
         2 for update access

  The object is known to exist and is not a directory.

  Access rights must be checked: for example, if read access is requested,
  then the "owner read" bit must be set; if update access is requested, then
  the "owner write" bit must be set.

  Presumably the two access bits in the "file information word" should be set
  according to the owner read/write bits. [??]

  Checks must be made to ensure that this open request is compatible with
  the current state of the file:

    - If the file is already open for update, then no further opens are
      allowed.

    - If the file is already open for read, then only further opens for
      read are allowed.

  The block-size is a power of 2 ranging from 64 to 1024; it is most
  likely to be a property of the image, rather than of individual files
  within the image.

  The allocation must be a multiple of the block-size, and defines an upper
  bound on the extent; if the file's size needs to increase beyond this, a
  call of ImageEntry_Args 7 (ensure file size) will be made.

*****************************************************************************

Close  - close an open file

             In                      Out

    R0
    R1    file-handle
    R2    load
    R3    exec

  Close the file, storing new load and/or exec values if R2 and/or R3 are
  non-zero.

  FileSwitch ensures that both R2 and R3 are equal to zero when the file to
  be closed has not been modified - that is, if it was opened for reading
  only, or opened for update but not modified.

  After closing, OS_Args 255 must be called to flush the image file itself to
  disc to ensure that any data written to the closed file is secure. The PRM
  implies that this call should always be made, but presumably it can be
  skipped if the file was opened for read only. [??]

  [ Indeed, for any IFS which is capable of stamping open files the call can
    be skipped whenever both R2 and R3 equal zero. ] [??]

*****************************************************************************
  
GetBytes  - get bytes from a buffered file

             In                      Out

    R0    
    R1    file-handle
    R2    ->buffer
    R3    number of bytes
    R4    file-offset

  The file-offset and number of bytes to read are guaranteed to be multiples
  of the file's block size.

  The file-offset is guaranteed to be less than the file's extent, but the
  (file-offset + number of bytes) may be greater - but will still lie within
  the file's allocation.

  There is no guarantee as to the alignment of the buffer, but transfers
  should be optimised for word alignment.

  Note that this call may be made even if the file has been opened with
  "write-only" access. Consider the case where a client writes just one byte
  to the start of the first block of the file: then FileSwitch must read in
  the entire first block prior to updating the first byte and writing it out
  again.

*****************************************************************************

PutBytes  - put bytes to a buffered file

             In                      Out

    R0    
    R1    file-handle
    R2    ->buffer
    R3    number of bytes
    R4    file-offset

  The file-offset and number of bytes to write are guaranteed to be multiples
  of the file's block size, and the file is guaranteed to have been granted
  write access when opened.

  The (file-offset + number of bytes) is guaranteed to be less than the
  file's allocation - the true extent is written explicitly before closing.

  There is no guarantee as to the alignment of the buffer, but transfers
  should be optimised for word alignment.

*****************************************************************************

Args  - controlling open files

*****************************************************************************

Args  3  - write file extent

             In                      Out

    R0    3
    R1    file-handle
    R2    extent

  This call is only made just before closing an open file after it has been
  modified; the corresponding directory entry should be updated.

  FileSwitch guarantees that the file was granted write access at opening
  time.

  Note that this call may be made even if the extent has not changed.

  A client may call OS_Args 3 to write an open file's extent at any time,
  but - as mentioned above - the IFS is only informed of any changes just
  before the file is closed. So when this call is made, it is possible that
  the file's allocation is much greater than its extent, and so may need to
  be reduced before the file is closed.

*****************************************************************************

Args  4  - read size allocated to file

             In                      Out

    R0    4
    R1    file-handle
    R2                            allocation

  Returns the amount of space allocated to the file. This will be a multiple
  of the file's block size.

*****************************************************************************

Args  6  - notification of a flush

             In                      Out

    R0    6
    R1    file-handle
    R2                            load
    R3                            exec

  Ensure that all data written to this file is secure on disc - in other
  words, flush any buffered data to the image, and then flush the image
  itself to its storage medium by calling OS_Args 255.

  The file's filetype and timestamp information is returned in R2 and R3.

  This call need only be supported if bit 27 = 1 in the IFSIW - which
  indicates that the IFS buffers data (write-behind, for example); FileSwitch
  will never call this entry point if bit 27 = 0.

*****************************************************************************

Args  7  - ensure file size

             In                      Out

    R0    7
    R1    file-handle
    R2    allocation              allocation

  Ensure that the space allocated to the file is at least as great as R2;
  the space actually allocated is returned as the new value of R2.

  An error should be raised if there is insufficient space to meet the
  allocation request.

  Note that it is possible for the requested value to be less than the
  current allocation - although FileSwitch does not call this function each
  time a file's extent is decreased by a call of OS_Args 3.

*****************************************************************************

Args  8  - write zeros to file

             In                      Out

    R0    8
    R1    file-handle
    R2    file-offset
    R3    number of bytes

  The file-offset and number of zero bytes to write are guaranteed to be
  multiples of the file's block size.

  The (file-offset + number of bytes) is guaranteed to be less than or equal
  to the file's allocation.

  The file-handle is guaranteed to describe a file for which write access was
  granted at open time.

*****************************************************************************

Args  9  - read file datestamp

             In                      Out

    R0    9
    R1    file-handle
    R2                            load
    R3                            exec

  Read an open file's timestamp and filetype information.

*****************************************************************************

File  - whole file operations

*****************************************************************************

File   0  - save file

             In                      Out

    R0    0
    R1    ->file-name
    R2    load
    R3    exec
    R4    ->buff
    R5    ->(byte following buff)
    R6    image-handle            ->leaf-name

  This call is used to create a new file with the given filetype and 
  timestamp containing as data the contents of the buffer.

  On entry, FileSwitch guarantees that the buffer exists (that is, that the
  addresses R4 to R5-1 inclusive are all valid).

  The file-name is specified as a full path name, but there is no guarantee
  that the directory to contain the file exists: if it does not, an error
  should be raised.

  An error should also be raised if there is insufficient space to create
  the new file.

  If an object with the same name exists, the following conditions should be
  reported as errors:

    - the object is a directory
    - the object is a locked file
    - the object is an open file

  Otherwise, any pre-existing file should be discarded.

  The new file should inherit access rights from its predecessor, if any -
  otherwise use suitable defaults.

  The leaf-name returned in R6 is used by the "*OPT 1 string" which is
  described on page 2-176 of the PRM.

*****************************************************************************

File   1  - write catalogue information

             In                      Out

    R0    1
    R1    ->name
    R2    load
    R3    exec
    R4
    R5    attributes
    R6    image-handle

  The given filetype, timestamp and attributes are written to the named
  object's directory entry; the object may be a file or a directory.

  No error should be raised if the object does not exist.

*****************************************************************************

File   5  - read catalogue information

             In                      Out

    R0    5                       object-type
    R1    ->name
    R2                            load
    R3                            exec
    R4                            extent
    R5                            attributes
    R6    image-handle

  Directory information about the named object is returned, including the
  object's type:

      object-type     meaning

         0           not found
         1           file
         2           directory

*****************************************************************************

File   6  - delete object

             In                      Out

    R0    6                       object-type
    R1    ->name
    R2                            load
    R3                            exec
    R4                            extent
    R5                            attributes
    R6    image-handle

  If possible, the named object is deleted after returning information from
  its directory entry.

  No error should be raised if the object does not exist; instead, an object-
  type of zero is returned.

  However, errors should be raised under the following circumstances:

    - The object is locked.
    - The object is an open file.
    - The object is a non-empty directory.

*****************************************************************************

File   7  - create file

             In                      Out

    R0    7
    R1    ->file-name
    R2    load
    R3    exec
    R4    addr1
    R5    addr2
    R6    image-handle

  This function behaves exactly as ImageEntry_File 0 (save file) except as
  follows:

    - Create file does not write data from R4 to R5-1 inclusive to the file;
       instead, a file of extent addr2-addr1 is created, whose content is
       undefined.

    - Create file does not return a leaf-name in R6.

*****************************************************************************

File   8  - create directory

             In                      Out

    R0    8
    R1    ->dir-name
    R2    load
    R3    exec
    R4    number of entries
    R5
    R6    image-handle

  Create a new directory with the given name; the number of entries is a
  hint, where 0 means choose some suitable default.

  No action need be taken if a directory with that name already exists, but
  other conditions should raise errors as follows:

    - The directory in which the new directory is to be created does not
       exist.
    - A file with the same name exists.
    - There is insufficient space to create the new directory.

*****************************************************************************

File  10  - read block size

             In                      Out

    R0    10
    R1    ->file-name
    R2                            block-size
    R3
    R4
    R5
    R6    image-handle

  The file's natural block size is returned.

  It seems that this entry point is never called by FileSwitch. [??]

*****************************************************************************

Func  - miscellaneous filing system operations

*****************************************************************************

Func  8  - rename object

             In                      Out

    R0    8
    R1    ->old-name              0 iff rename achieved
    R2    ->new-name
    R3
    R4
    R5
    R6    image-handle

  This call is a request to change the name of an object from old-name to
  new-name.

  FileSwitch will make this call only if the object specified by old-name is
  known to exist, and when new-name specifies an object in the same image.
  Nevertheless, the IFS is entitled to refuse the rename request by leaving
  R1 untouched on exit.

  Conditions for which errors should be raised are:

    - The directory to contain the renamed object does not exist.

  Note that FileSwitch ensures that no object with name new_name exists
  before making this call.

*****************************************************************************

Func 14  - read directory entries

             In                      Out

    R0    14
    R1    ->dir-name
    R2    ->buff
    R3    number to read          number read
    R4    first item to read      next item to read
    R5    buffer length
    R6    image-handle

  This function is called to read the names of objects in the specified
  directory.

  Object names will be copied to the buffer until there is no room for any
  more, or until the "number to read" have been copied; they are returned as
  a contiguous sequence of null-terminated strings.

  On exit, R3 is set to the number of names copied, and R4 is set to the
  index of the next object to examine. If there are none left, this value is
  -1; otherwise it can be used as the "first item to read" parameter in a
  further call. "first item to read" is set to zero if object names are to
  be read from the beginning of the directory.

  Conditions for which errors should be raised are:

    - No object with the specified name exists.
    - An object exists, but it is a file.

  This entry point may be called as a result of a (deprecated) OS_GBPB 8 call
  in which case the length of the buffer is not known, and so R5 cannot be
  relied upon. This means that in general this value cannot be trusted, and
  so the IFS should continue to add names to the buffer while:

    - there is space to do so according to R5, and:
    - the addresses of the locations to be used are valid

  Furthermore, OS_GBPB 8 expects its object names to be stored as <length>
  followed by <name>, and so FileSwitch has to reformat the buffer returned
  by this call before returning to the client. FileSwitch optimistically
  assumes that R2 still addresses the buffer upon return from this IFS call,
  and so, exceptionally, ImageEntry_Func 14 must preserve R2.

*****************************************************************************

Func 15  - read directory entries and information

             In                      Out

    R0    15
    R1    ->dir-name
    R2    ->buff
    R3    number to read          number read
    R4    first item to read      next item to read
    R5    buffer length
    R6    image-handle

  The behaviour of ImageEntry_Func 15 is entirely analogous to that of
  ImageEntry_Func 14, except that word-aligned directory information records
  are copied to the buffer instead of byte-aligned object names.

  Each directory information record has the following format:

      0   load
      4   exec
      8   extent
     12   attributes
     16   object-type  (1 or 2)
     20   name  (zero-terminated string)

  In this case, FileSwitch validates the buffer correctly prior to the call,
  and it is not necessary to preserve R2 across the call.

*****************************************************************************

Func 21  - notification of a new image

             In                      Out

    R0    21
    R1    FSw-handle              image-handle
    R2    buffer size (or 0)

  This call is made by FileSwitch to register a file as a new image for the
  image filing system to handle.

  On entry, R1 gives FileSwitch's file handle for the image file, and R2
  gives the buffer size (or 0 if not known).

  On exit, R1 holds the image filing system's handle for the image - which
  will be used in future calls from FileSwitch to the image filing system.

  The image filing system should mark the image as "stamp image on next
  update" so that its image stamp will be updated as soon as any change is
  made to the image.

  Furthermore, whenever the image stamp changes, the image filing system
  should call OS_Args 8 to inform the host filing system of the new value.

*****************************************************************************

Func 22  - notification that image is about to be closed

             In                      Out

    R0    22
    R1    image-handle

  Before closing a registered image file, FileSwitch first makes sure that
  all files in the image have been closed, and then calls this entry point
  to allow the image filing system to write out any buffered data.

  The image filing system should also free any memory occupied by data
  cached from the image.

*****************************************************************************

Func 25  - read defect list

             In                      Out

    R0    25
    R1
    R2    ->buff
    R3
    R4
    R5    length
    R6    image-handle

  The buffer should be filled with the byte offsets of any defects in the
  image, terminated by &20000000.

  These byte offsets are the addresses of defective sectors on the disc.

  An error should be raised if defect mapping is not supported.

*****************************************************************************

Func 26  - add a defect

             In                      Out

    R0    26
    R1
    R2    offset
    R3
    R4
    R5
    R6    image-handle

  "offset" is a byte offset within the image.

  The sector identified by the offset should be marked as unusable - in other
  words "mapped out" - unless it is already in use, in which case an error
  should be returned.

  An error should also be raised if defect mapping is not supported.

*****************************************************************************

Func 27  - read boot option

             In                      Out

    R0    27
    R1
    R2                            boot-option
    R3
    R4
    R5
    R6    image-handle

  Returns the boot option (ie the "n" in *OPT 4,n) for the image.

*****************************************************************************

Func 28  - write boot option

             In                      Out

    R0    28
    R1
    R2    boot-option
    R3
    R4
    R5
    R6    image-handle

  Sets the boot option for the image.

*****************************************************************************

Func 29  - read used space map

             In                      Out

    R0    29
    R1
    R2    ->buff
    R3
    R4
    R5    length
    R6    image-handle

  The used space map - one bit per block - must be copied to the buffer,
  which is prefilled with zeros.

  Note that the size of buffer required can be calculated from the image's
  size and its block-size - this latter being returned by ImageEntry_Open.

*****************************************************************************

Func 30  - read free space

             In                      Out

    R0    30                      free space
    R1                            max object size
    R2                            image size
    R3
    R4
    R5
    R6    image-handle

  Information about free space and its availability on the image is returned.

*****************************************************************************

Func 31  - name image

             In                      Out

    R0    31
    R1
    R2    ->image-name
    R3
    R4
    R5
    R6    image-handle

  The image is given the image-name specified in R2.

*****************************************************************************

Func 32  - stamp image

             In                      Out

    R0    32
    R1
    R2    code
    R3
    R4
    R5
    R6    image-handle

  This is a request to "stamp the image" - in other words, to change the
  value of the image-stamp.

      code = 0  => stamp image on next update
      code = 1  => stamp image now

  This is used to determine when removeable media is updated, or when two
  discs with the same name are the same.

*****************************************************************************

Func 33  - get usage of offset

             In                      Out

    R0    33
    R1
    R2    offset                  kind of object found
    R3    ->buff
    R4    buffer length
    R5
    R6    image-handle

  Information about the kind of object at the given offset in the image is
  returned:

    0  -  no object found; offset is free, or a defect, or beyond the end
           of the image

    1  -  no object found; offset is allocated, but not free, nor a defect,
           nor beyond the end of the image (eg a mapping table)

    2  -  object found; this offset cannot be shared with other objects (and
           so deleting this object will certainly free this offset)

    3  -  object found; this offset can be shared with other objects

  If an object is found, its name is returned in the buffer provided.

*****************************************************************************
