DataMuseum.dk

Presents historical artifacts from the history of:

Bogika Butler

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Bogika Butler

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download

⟦130ed934d⟧ TextFile

    Length: 110976 (0x1b180)
    Types: TextFile
    Names: »UNIXFS.SA«

Derivation

└─⟦311ba069f⟧ Bits:30009789/_.ft.Ibm2.50006625.imd Mogens Pelles Zilog 80,000 / EOS projekt
    └─⟦this⟧ »UNIXFS.SA« 
└─⟦49237ce80⟧ Bits:30009789/_.ft.Ibm2.50006627.imd Mogens Pelles Zilog 80,000 / EOS projekt
    └─⟦this⟧ »UNIXFS.SA« 
└─⟦714bbb381⟧ Bits:30009789/_.ft.Ibm2.50006595.imd Mogens Pelles Zilog 80,000 / EOS projekt
    └─⟦this⟧ »UNIXFS.SA« 

TextFile

 
æ*****************************************************************
                        Copyright 1984 by
                        NCR Corporation
                        Dayton, Ohio  U.S.A.
                        All Rights Reserved
******************************************************************
                        EOS Software produced by:
                        NCR Systems Engineering - Copenhagen
                        Copenhagen
                        DENMARK
*****************************************************************å
 
æ$H=0å æ no heap space å
 
OBJECT PROGRAM UnixFs;
 
 
æ
 
 
                         U N I X - F S
 
 
 
Change history: 83-09-09, EAR  first version
                83-09-22, EAR  change after inspection
                83-09-26, EAR  i-node handling
                83-10-03, EAR  pascal error, fillInoList
                83-10-10, EAR  sbGate
                83-10-14, EAR  update super block after getFreeInode
                83-10-27, EAR  reject -> status on error upd.super block
                83-10-28, EAR  abort before terminateFao
                83-10-31, EAR  new proc 'cutFile'
                83-11-02, EAR  testprint off
                83-11-03, EAR  re-entrant fao
    vers.01.01  83-11-09, EAR  allocBlock: VAR fd param (stack overflow)
    vers.01.02  83-11-10, EAR  automatic call of InitUnixSys
    vers.01.03  83-11-21, EAR  illegal const value of nicinod
    vers.01.04  83-11-23, EAR  new entry FStat
    vers.01.05  83-11-24, EAR  allocBlock, get next free block problem
    vers.01.07  83-11-25, EAR  releaseBlock, same problem
    vers.01.08  83-11-25, ERN  write assign to dir allowed, w.o. writeright
    vers.01.09  83-11-26, ERN  clear fd.iTabÆ*Å.tabÆ*Å in new or emptied fao
                               also free to allocsize instead of eospos.
    vers.01.10  83-11-30, EAR  assign: don't create file when readMode
    vers.01.11  83-12-01, EAR  exclude: moveMan in terminate loop
    vers.01.12  83-12-02, EAR  in-/exclude: check for space in device name
    vers.01.13  83-12-02, EAR  assign: create all files with all rights
    vers.01.14  83-12-09, EAR  fileType, testprint on
    vers.01.15  83-12-09, EAR  change fileType algorithm
    vers.01.16  83-12-21, EAR  createLink, error in change filetype
 
å
CONST
  ProcId   = 'unixfs  vers.01.16  83-12-21 ';
 
  ResultId = 'UNIXFS:  sNo OrgSy Au Ar OrgNo Fa Ma';
  OrgSys   = 4005;
 
 
æ
The UNIX FS Module is a low level file system for the UNIX disc format.
 
UnixFs contains the following entries:
 
 
OBJECT UnixFs;
 
  ENTRY Include ( ; devName, rootName  Æ, readOnlyÅ );
 
        This procedure includes the disc volume which is currently
        placed on the device specified by 'devName'.
 
        The disc volume must contain a UNIX file system, i.e. the
        second 512-byte block (block 1) of the disc volume must
        contain the so-called "super-block" with a format as
        described in "UNIX for the 68000".
 
        The 'rootName' (max 30 chars) is inserted in the device table to
        be used as shortcut for the pathName of all files on this disc.
        'rootName' need neither be a legal unix path name, nor a filename
        on a currently mounted disc, as in a normal unix system. But the
        name must be used as prefix of all filenames on that disc (see
        also Assign).
 
 
  ENTRY Assign (ownedFao ; pathName, ioMode Æ, createModeÅ );
 
        Creates a File Access Object (FAO) to the file named by
        'pathName'. The disc must have been included previously.
 
        'pathName' is always a full path name, i.e. job handler or
        run-time system supplies directory names from root to current
        directory:
                     root / dir1 /.../ dir-n / file
 
        UnixFs maintains a list of root names corresponding to the
        devices currently included (see Include). Each name in this
        device table is compared to the 'pathName', and the longest
        matching name is used as shortcut to the disc. This will
        save access to the root device(s), and it will be possible
        to remove an "intermediate" disc without destroying the path.
 
        This implies however, that a removable disc can only be accessed
        through the path name specified at include, but not through an
        arbitrary path ending in the root of the disc as in a normal
        unix system.
 
        When a file is to be created, all directories in the path name
        must exist. An empty datafile is automatically changed into a
        directory file when a file name is linked into it.
 
        The file is reserved according to 'ioMode':
 
        Write reservation demands that no othed FAO have any access to
        the same file; read reservation demands only that no other FAO
        have write access to the file.
 
 
  ENTRY Exclude ( ; devName Æ, abortAllowedÅ );
 
        This procedure removes the disc specified by 'devName' from the
        system.
 
        The value of the optional parameter 'abortAllowed' determines
        the reaction on any open FAO on the disc: If 'abortAllowed'
        = 1 all open FAOs on the disc are aborted, and the termination
        procedure is called. Otherwise Exclude is rejected if any
        open FAO is found. Default is 0.
 
        The Disc Access Object (DAO) corresponding to 'devName' is
        deallocated, if the necessary resources are available.
 
 
  ENTRY CreateLink ( ; pathName, newPathName );
 
  ENTRY DeleteLink ( ; pathName );
 
 
 
  OBJECT Fao;
 
    ENTRY ReadSeq ( segment ; byteCount );
 
          This procedure reads one or more bytes from the file starting
          in the file position defined by current position. The bytes
          are read into the buffer specified by 'segment'. The size of
          the buffer defines how many bytes are read. Current position
          is updated corresponding to the number of bytes read, and the
          parameter 'byteCount' returns this number.
 
          Current position need not point to a page boundary. So the
          first and/or last physical blocks read may be shorter than
          the page size.
 
          The procedure minimizes the calls to the driver by reading
          possibly consecutive physical blocks in one call.
 
          When reaching the logical end-of-file a block shorter than
          the buffer may be read, and a correspondingly smaller value
          of 'byteCount' is returned. Reading beyond end-of-file but
          within the allocation limit returns a 'byteCount' of zero.
 
          An attempt to read beyond the allocation limit is rejected.
          This situation can only occur after a seek operation.
 
          The FAO must have read or readWrite reservation, otherwise
          the call is rejected.
 
 
    ENTRY WriteSeq ( segment ; byteCount );
 
          This procedure writes one or more bytes from the buffer
          specified by 'segment', into the file from current position
          and on. The size of the buffer defines how many bytes are
          written. Current position is updated correspondingly.
 
          If the new bytes written exceed the current allocation limit
          new physical blocks are allocated automatically, and the
          filesize (logical end-of-file) is changed according to the
          last byte written.
 
          Current position need not point to a page boundary, so the
          first and/or last blocks written may be shorter than the
          page size.
 
          This first version of the procedure does NOT minimize the
          calls to the driver by writing consecutive physical blocks
          in one call.
 
          The FAO must have write or readWrite reservation, otherwise
          the call is rejected.
 
 
    ENTRY ReadRandom ( segment ; byteCount, pos, actualPos );
 
 
    ENTRY WriteRandom ( segment ; byteCount, pos, actualPos );
 
 
    ENTRY Seek ( ; baseMode, offset, pos );
 
 
    ENTRY DataSize ( ; dataBytes );
 
          This procedure cuts the size of the file. In this first
          version 'dataBytes' = 0 is the only value allowed.
 
  END  Fao;
 
END UnixFs;
 
å
 
æ$Eå
 
 
æ$L-å
 
æ**********************************************************å
æ*****     I M P O R T    D E S C R I P T I O N S     *****å
æ**********************************************************å
 
æ$F=FAMILY.UNIV.IDå
æ$Eå
 
æ$F=FAMILY.KNEL.IDå
æ$Eå
 
æ$F=FAMILY.ALLOC.IDå
æ$Eå
 
æ$F=FAMILY.SCHED.IDå
æ$Eå
 
æ$F=FAMILY.OBJDIR.IDå
æ$Eå
 
æ$F=FAMILY.CLOCK.IDå
æ$Eå
 
æ$L+å
 
æ********************************************************å
æ*****     E X P O R T    D E S C R I P T I O N     *****å
æ********************************************************å
 
æ$F=PRIVATE.UNIXFS.IDå
 
æ$Eå
 
æ**********************************************************å
æ*****     S T A N D A R D    P R O C E D U R E S     *****å
æ**********************************************************å
 
æ$F=PASINCLU.CHKPROCS.SAå
 
 
FUNCTION equal (s1, s2 : fullId) : boolean;
  VAR
    i : integer;
    b : boolean;
BEGIN
æ*b*   printvar ('equal, s1 = ', s1);
       printvar ('s2 = ', s2);            *e*å
  b := elements(s1) = elements(s2);
  i := 1;
  WHILE b AND (i <= elements(s1)) DO
  BEGIN
    b := s1ÆiÅ = s2ÆiÅ;
    i := i+1;
  END;
  equal := b;
END;  æequalå
 
 
FUNCTION vEqual (s1, s2 : fullId;
                 ls2    : integer ) : boolean;
  VAR
    i : integer;
    b : boolean;
 
æ vEqual becomes true if 'ls2' characters of 's2' are included in 's1' å
 
BEGIN
  b := elements(s1) >= ls2;
  i := 1;
  WHILE b AND (i <= ls2) DO
  BEGIN
    b := s1ÆiÅ = s2ÆiÅ;
    i := i+1;
  END;
  vEqual := b;
END;  ævEqualå
 
 
 
PROCEDURE zeroFill (UNIV a : blockPtr;
                    f, t   : integer );
  VAR
    i : integer;
 
æ inserts zero in all bytes of array 'a' from byteindex 'f' to
  byteindex 't' å
 
BEGIN
  FOR i := f TO t DO aÆiÅ := 0;
END;  æzeroFillå
 
æ$Eå
 
æ******************************************************å
æ*****     U N I X    D E S C R I P T I O N S     *****å
æ******************************************************å
 
CONST
  maxIdLength  = 48;              æmax length of device nameå
  maxPathLength= 30;              æmax length of root nameå
  nameSize     = 14;              æ# of chars in file name partå
  dirSize      = 16;              æsize of directory entryå
  inoSize      = 64;              æsize of i-node entryå
  inoLSize     =  8;
  inoFSize     = 56;
  unixPageSize = 512;
 
  nicFree      = 50;
  freeBfSize   = 202;             ænicFree*4+2å
  freeLstSize  = 49;              ænicFree-1å
  nicInod      = 100;
  inoLstSize   = 99;              ænicInod-1å
  rootIno      = 2;
  egoDir       = 1;
  parentDir    = 2;
  maxDisc      = 5;
  superAddr    = 512;             æbytePos of super blockå
  space        = ' ';
  p12          = 16#1000;
  p16          = 16#10000;
  dirType      = 16#4000;
  dirMode      = 16#41ff;
  dataType     = 16#8000;
  dataMode     = 16#81ff;
 
 
TYPE
  name6        = packed array Æ1..6Å of char;
  name14       = packed array Æ1..14Å of char;
  name30       = packed array Æ1..maxPathLengthÅ of char;
  fullName     = packed array Æ1..maxIdLengthÅ of char;
 
  ino          = word;
  time         = long;
  blockNo      = long;
 
  choice       = (a, b);
 
æ$Eå
 
  freeRec = RECORD
    CASE choice OF
      a: ( freeBuf : array Æ1..freeBfSizeÅ of byte );
      b: ( nFree   : word;
           freeList: array Æ0..freeLstSizeÅ of blockNo );
    END;
 
  superBlock = RECORD
      inodSize   : word;           æblockno of first block after i-listå
      volSize    : blockNo;        ætotal no.of blocks in volumeå
      freeBlock  : freeRec;        æfree listå
      nIno       : word;           æ# used entries in ino listå
      inoList    : array Æ0..inoLstSizeÅ of ino;   æino listå
      dummy1     : long;           æunix flags not used by eoså
      sTime      : time;           ælast super block updateå
      tFreeBl    : long;           ætotal # free blockså
      tInode     : word;           æ  -   # i-nodeså
      sM         : word;           æinterleave factorå
      sN         : word;           æ    -      moduloå
      volName    : name6;          ævolume nameå
      packName   : name6;          ædisc pack nameå
      dummy2     : arrayÆ1..72Å of byte;  æfill to 512 byteså
    END;
 
 
  iLnk = RECORD
      iMode      : word;           æmode, type of fileå
      iNLink     : word;           æ# links to fileå
      iUserId    : word;           æowners userIdå
      iGroupId   : word;           æ  -    groupIdå
    END;
 
  iFl = RECORD
      iSize      : long;           æ logical eof: # bytes in fileå
      iAddr      : array Æ1..40Å of byte;   æ13 disc block addr, each 3 byteså
      iAccTime   : time;           ælast accessedå
      iModTime   : time;           ælast modifiedå
      iCreTime   : time;           ælast createdå
    END;
 
  iNode = RECORD
      iLink      : iLnk;
      iFil       : iFl;
    END;
 
  iNodeArray = array Æ1..8Å of iNode;
 
æ$Eå
 
  bAddress = RECORD
    CASE choice OF
      a: ( bAddr  : array Æ0..3Å of byte );
      b: ( blockAddr : blockNo );
    END;
 
  b13Array  = array Æ0..12Å  of bAddress;
  b128Array = array Æ0..127Å of blockNo;
 
  dirEntry = RECORD
      dIno   : ino;             æi-numberå
      dName  : name14;          æfileNameå
    END;
 
  dirEntArray = array Æ1..32Å of dirEntry;
 
 
æ$Eå
 
æ*********************************************************å
æ*****     F A O    L O C A L    P O I N T E R S     *****å
æ*********************************************************å
 
TYPE
  pFaoLocals  = ^^ faoLocals;
  pUnixLocals = ^^ unixLocals;
  pUxDatType  = ^^ unixData;
  pGateType   = ^^ Gate;
 
  faoLocals = RECORD
      code      : ^^;
      pFaoData  : ^^ faoData;
      unixEnv   : pUnixLocals;
      daoRef    : faoRefType;
      sbGateRef : pGateType;
      faoGate   : pGateType;
    END;
 
  indirect = RECORD
      tab       : b128Array;
      curIb     : blockNo;
      update    : boolean;
    END;
 
  faoData = RECORD
      iNumber   : ino;
      blockTab  : b13Array;         æ10 direct, 3 indirect blockså
      iTab  : array Æ1..3Å of indirect;
      inx       : array Æ0..3Å of word;
      level     : word;
      curPos    : long;       ælast transferred byte positionå
      eofPos    : long;       ælast data        byte positionå
      allocSize : long;       ælast allocated   byte positionå
      daoIndex  : word;
      localId   : word;
      rwMode    : ioType;
      devReadOnly : byte;
      terminated: boolean;    ætrue when terminateFao has been calledå
      readMark  : boolean;    ætrue when ReadSeq/ReadRandom   has been calledå
      writeMark : boolean;    ætrue when WriteSeq/WriteRandom has been calledå
      inoUpdate : boolean;    ætrue when i-node changed, but not on discå
      inoLink   : iLnk;
      inoFile   : iFl;
    END;
 
æ$Eå
 
æ*************************************************************å
æ*****     I O S Y S    L O C A L    P O I N T E R S     *****å
æ*************************************************************å
 
TYPE
  unixLocals = RECORD
      code       : ^^;
      ioGate     : ^^Gate;
      dao        : array Æ1..maxDiscÅ of faoRefType;
      faoMan     : array Æ1..maxDiscÅ of pFaoLocals;
      sbGate     : array Æ1..maxDiscÅ of pGateType;
      nextFao    : pFaoLocals;     æused only as work.ptr.in countOpenFaoså
      pUnixData  : ^^unixData;
      pUnixDir   : bufRef;         æ^^dirBlock, subsegment of unixDataå
      objDirRef  : ^^ObjDir;
      allocRef   : ^^Allocate;
      schedRef   : ^^Scheduler;
      clockRef   : ^^Clock;
      egoEnv     : pUnixLocals;
    END;
 
  deviceData = RECORD
      devName    : fullName;
      pathLength : word;       æ# chars used in pathNameå
      pathName   : name30;
      devReadOnly: word;
      super      : superBlock;
    END;
 
  unixData = RECORD
      dirBlock     : dirEntArray;
      faoWork      : faoData;         æfor use by create/deleteLinkå
      lastLocalId  : integer;
      devTable     : array Æ1..maxDiscÅ of deviceData;
    END;
 
 
æ$Eå
 
æ************************************************************å
æ*****     F O R W A R D    D E C L A R A T I O N S     *****å
æ************************************************************å
 
 
FUNCTION allocBlock (VAR dao       : faoRefType;
                     VAR sbGate    : pGateType;
                     VAR pUnixData : pUxDatType;
                     VAR fd        : faoData
                    ) : blockNo;                   FORWARD;
 
 
FUNCTION checkFileName (VAR pathName : fullId;
                        charno       : integer
                       ) : integer;                FORWARD;
 
 
FUNCTION countDirEntries (VAR dao       : faoRefType;
                          VAR sbGate    : pGateType;
                          VAR pUnixData : pUxDatType;
                          VAR pUnixDir  : bufRef;
                          VAR fd        : faoData
                         ) : integer;              FORWARD;
 
 
PROCEDURE cutFile (VAR dao       : faoRefType;
                   VAR dummyGate : pGateType;
                   VAR pUnixData : pUxDatType;
                   VAR fd        : faoData;
                   VAR sb        : superBlock );   FORWARD;
 
 
FUNCTION fileType (modeWord : word) : integer;     FORWARD;
 
 
FUNCTION fillInoList (VAR dao : faoRefType;
                      VAR sb  : superBlock
                     ) : integer;                  FORWARD;
 
 
FUNCTION getBlockNo (VAR dao       : faoRefType;
                     VAR sbGate    : pGateType;
                     VAR pUnixData : pUxDatType;
                     VAR fd        : faoData;
                     relBlock      : long
                    ) : BlockNo;                   FORWARD;
 
 
FUNCTION getFreeInum (VAR dao       : faoRefType;
                      VAR sbGate    : pGateType;
                      VAR pUnixData : pUxDatType;
                      VAR fd        : faoData
                     ) : ino;                     FORWARD;
 
 
PROCEDURE getInoFile (VAR dao : faoRefType;
                      VAR fd  : faoData;
                      iNum    : ino );            FORWARD;
 
 
PROCEDURE getInoLink (VAR dao : faoRefType;
                      VAR fd  : faoData;
                      iNum    : ino );            FORWARD;
 
 
FUNCTION getNamePart ( VAR pathName : fullId;
                       VAR fName    : name14;
                       VAR cNo      : integer
                     ) : boolean;                 FORWARD;
 
 
PROCEDURE getOneBlock (VAR dao  : faoRefType;
                       UNIV buf : blockPtr;
                       first, last : integer;
                       block    : blockNo;
                       relByte  : integer    );    FORWARD;
 
 
PROCEDURE getOneSegment (VAR dao : faoRefType;
                         VAR segment : bufRef;
                         first, last : integer;
                         block : blockNo;
                         relByte : integer  );    FORWARD;
 
 
PROCEDURE newDirEntry ( VAR dao  : faoRefType;
                        VAR sbGate    : pGateType;
                        VAR pUnixData : pUxDatType;
                        VAR pUnixDir  : bufRef;
                        VAR fd        : faoData;
                        dirIno   : ino;
                        dirPos   : integer;
                        fileIno  : ino;
                        fName    : name14 );      FORWARD;
 
 
PROCEDURE putInoFile (VAR dao : faoRefType;
                      VAR fd : faoData );         FORWARD;
 
 
PROCEDURE putInoLink (VAR dao : faoRefType;
                      VAR fd  : faoData );        FORWARD;
 
 
FUNCTION putOneBlock (VAR dao : faoRefType;
                      VAR sbGate    : pGateType;
                      VAR pUnixData : pUxDatType;
                      buf     : blockPtr;
                      VAR fd  : faoData;
                      first, last : integer;
                      block   : blockNo;
                      relByte : integer
                     ) : blockNo;                 FORWARD;
 
 
FUNCTION putOneSegment (VAR dao : faoRefType;
                        VAR sbGate    : pGateType;
                        VAR pUnixData : pUxDatType;
                        VAR segment : bufRef;
                        VAR fd      : faoData;
                        first, last : integer;
                        block       : blockNo;
                        relByte     : integer
                       ) : blockNo;               FORWARD;
 
 
PROCEDURE readSegment (VAR dao : faoRefType;
                       VAR sbGate    : pGateType;
                       VAR pUnixData : pUxDatType;
                       VAR segment : bufRef;
                       VAR fd : faoData;
                       VAR byteCount : integer ); FORWARD;
 
 
PROCEDURE releaseBlock (VAR dao : faoRefType;
                        VAR sbGate    : pGateType;
                        VAR pUnixData : pUxDatType;
                        VAR fd  : faoData ;
                        VAR sb  : superBlock ;
                        absBlock : long );        FORWARD;
 
 
PROCEDURE remDirEntry ( VAR dao : faoRefType;
                        VAR sbGate    : pGateType;
                        VAR pUnixData : pUxDatType;
                        VAR pUnixDir  : bufRef;
                        VAR fd        : faoData;
                        dirIno  : ino;
                        dirPos  : integer );      FORWARD;
 
 
PROCEDURE removeFile ( VAR dao : faoRefType;
                       VAR sbGate    : pGateType;
                       VAR dummyGate : pGateType;
                       VAR pUNixData : pUxDatType;
                       VAR fd  : faoData);        FORWARD;
 
 
FUNCTION searchDevTab ( VAR ux     : unixData;
                        pathName   : fullId;
                        VAR charNo : integer
                      ) : integer;                FORWARD;
 
 
FUNCTION searchDirectory (VAR dao       : faoRefType;
                          VAR sbGate    : pGateType;
                          VAR pUnixData : pUxDatType;
                          VAR pUnixDir  : bufRef;
                          VAR fd        : faoData;
                          VAR fName     : name14;
                          VAR dirPos    : integer
                         ) : ino;                 FORWARD;
 
 
FUNCTION segLength (VAR segment : bufRef) : integer;  FORWARD;
 
 
PROCEDURE writeSegment (VAR dao       : faoRefType;
                        VAR sbGate    : pGateType;
                        VAR pUnixData : pUxDatType;
                        VAR segment   : bufRef;
                        VAR fd        : faoData;
                        VAR byteCount : integer); FORWARD;
 
 
æ$Eå
 
æ******************************************************å
æ*****     G L O B A L    P R O C E D U R E S     *****å
æ******************************************************å
 
FUNCTION allocBlock; æ( VAR dao       : faoRefType;
                        VAR sbGate    : pGateType;
                        VAR pUnixData : pUxDatType;
                        VAR fd        : faoData
                      ) : blockNo;            å
  VAR
    i, ix : integer;
    bl : blockNo;
    res : resultType;
 
BEGIN
æ*b*   printText ('allocBlock ');   *e*å
  ix := fd.daoIndex;
  res.main := ok;
 
  æ critical region for update super block å
  xCheck ( sbGate.Lock );
  IN
    WITH ux = pUnixData^^, sb = ux.devTableÆixÅ.super DO
    BEGIN
      WITH xb = sb.freeBlock DO
      BEGIN
        xb.nFree := xb.nFree - 1;
        bl := xb.freeListÆxb.nFreeÅ;
        IF bl = 0 THEN BEGIN
          xb.nFree := xb.nFree + 1;
          Exception ( makeRes ( Reject*NoVolumeSpace, IoFamily, entryA, 0));
        END;
        sb.tFreeBl := sb.tFreeBl - 1;
        IF xb.nFree = 0 THEN
        BEGIN
          æ the allocated block was a free-chain block, which must be
            read into the super block å
          IN
æ#ern#  printVar ('Alloc: get freeBuf from ', bl );  &ern&å
            getOneBlock (dao, xb.freeBuf, 1, freeBfSize, bl, 0);
æ#ern#  printVar ('Updated superblock ', sb );  &ern&å
          DO
            BEGIN  æif input no ok, the block has not been allocated,
                    so increase free count å
              xb.nFree := xb.nFree + 1;
              sb.tFreeBl := sb.tFreeBl + 1;
              exception (getException);
            END;
        END;
 
        æ put super blockå
        xCheck ( dao.WriteRandom
                   (VAR IN OUT pUnixData^^.devTableÆixÅ.super ;
                    OUT i,  IN superAddr, OUT i));
 
        allocBlock := bl;
æ!b!   printVar ('allocBlock: super block = ', sb);   !e!å
      END;  æwith xbå
    END;  æwith sbå
  DO
    BEGIN
      res := GetException;
      IF res.main < 0 THEN res.main := -res.main;
    END;
 
  æ leave critical region å
  sCheck ( sbGate.Open );
  IF res.main <> ok THEN Exception (res);
æ*b*   printVar ('end allocBlock, blockNo = ', bl);   *e*å
END;  æallocBlockå
 
 
 
FUNCTION checkFileName; æ (VAR pathName : fullId;
                           charno : integer
                          ) : integer;             å
  VAR
    i, k, parts, tLength : integer;
    ch    : char;
    error : boolean;
 
æ This function analyses the 'pathName', which may contain any number of
  name parts separated by '/'. Each name part may contain up to 14 chars.
  An empty name part (//) is not allowed. A '/' after the last name part is
  not allowed. The file name may be terminated by a null char.
  'charNo' points to the base of the file name.
  The return value of the function is the number of name parts if name ok,
  -1 if an error is found.
å
 
BEGIN
æ*b*   printVar ('checkFileName : pathName = ', pathName);
       printVar ('charNo = ', charNo);                       *e*å
  k := charNo;
  i := 0;
  parts := 0;
  error := false;
  tLength := elements (pathName);
 
  WHILE k < tLength DO
  BEGIN
    k := k + 1;
    ch := pathNameÆkÅ;
    IF (ch <> '/') AND (ch <> CHR(0)) THEN
    BEGIN
      parts := parts + 1;
      REPEAT
        i := i + 1;
        k := k + 1;
        IF k <= tLength THEN ch := pathNameÆkÅ;
      UNTIL (ch = '/') OR (ch = CHR(0)) OR (k>=tLength);
    END;
 
    æ check name partå
    IF (i > 14) OR                    ætoo longå
       (i = 0) AND (k > charNo+1)     æemptyå
    THEN  error := true;
 
    IF ch = CHR(0) THEN k := tLength
    ELSE
    IF ch = '/' THEN i := 0;
  END;  æWHILEå
 
  IF (i=0) AND (parts>0) THEN error := true;  æ'/' after last partå
  IF error THEN checkFileName := -1
           ELSE checkFileName := parts;
æ*b*   printVar ('end checkFileName, parts = ', parts);   *e*å
END;  æcheckFileNameå
 
 
 
 
FUNCTION countDirEntries; æ (VAR dao : faoRefType;
                             VAR sbGate    : pGateType;
                             VAR pUnixData : pUxDatType;
                             VAR pUnixDir  : bufRef;
                             VAR fd        : faoData
                            ) : integer;                å
  VAR
    bytesRead, count, i, ix, lastIx : integer;
 
æ This function reads through the entries of the directory file pointed to
  by 'fd', to count the number of used entries. 'lastPos' returns the
  position after the last used directory entry.
å
 
BEGIN
  WITH dir = pUnixData^^.dirBlock DO
  BEGIN
æ*b*   printVar ('countDirEntries : eofPos = ', fd.eofPos);   *e*å
    fd.curPos := 0;
    count := 0;
    WHILE fd.curPos < fd.eofPos DO
    BEGIN
      readSegment (dao, sbGate, pUnixData, pUnixDir, fd, bytesRead);
æ*b*   printVar ('dir block = ', dir);   *e*å
      lastIx := bytesRead DIV dirSize;
      FOR ix := 1 TO lastIx DO
      BEGIN
        IF dirÆixÅ.dIno <> 0 THEN count := count + 1;
      END;  æfor ixå
    END;  æwhileå
 
    countDirEntries := count;
  END;  æwith dirå
æ*b*   printVar ('end countDirEntries, count = ', count);   *e*å
END;  æcountDirEntrieså
 
 
 
 
PROCEDURE cutFile; æ (VAR dao : faoRefType;
                      VAR dummyGate : pGateType;
                      VAR pUnixData : pUxDatType;
                      VAR fd        : faoData;
                      VAR sb        : superBlock );  å
  VAR
    absBlock, lastBlock, relBlock, bl : integer;
 
æ This procedure releases all data blocks and index blocks of the file å
 
BEGIN
  WITH fd DO
  BEGIN
    lastBlock := fd.allocSize DIV unixPageSize - 1;
    FOR relBlock := lastBlock DOWNTO 0 DO
    BEGIN
      absBlock := getBlockNo (dao, dummyGate, pUnixData, fd, relBlock);
      IF absBlock <> 0 THEN
        releaseBlock (dao, dummyGate, pUnixData, fd, sb, absBlock);
      IF level > 0 THEN
      BEGIN   ælevel and iTab.curIb have been assigned by getBlockNoå
        IF (relBlock - 10) MOD 128 = 0 THEN
        BEGIN  ærelease 1st level indexBlockå
          bl := iTabÆ1Å.curIb;
          IF bl <> 0 THEN
            releaseBlock (dao, dummyGate, pUnixData, fd, sb, bl);
          IF level > 1 THEN
          BEGIN
            IF (relBlock - 138) MOD 16384 = 0 THEN
            BEGIN  ærelease 2nd level index blockå
              bl := iTabÆ2Å.curIb;
              IF bl <> 0 THEN
                releaseBlock (dao, dummyGate, pUnixData, fd, sb, bl);
              IF level > 2 THEN
              BEGIN
                IF relBlock = 16522 THEN
                BEGIN
                  bl := iTabÆ3Å.curIb;
                  IF bl <> 0 THEN
                    releaseBlock (dao, dummyGate, pUnixData, fd, sb, bl);
                END;  ærelease 3rd levelå
              END;  ælevel > 2å
            END;  ærelease 2nd levelå
          END;  ælevel > 1å
        END;  ærelease 1st levelå
      END;  ælevel > 0å
    END;  æfor relBlockå
  END;  æwith fdå
END;  æcutFileå
 
 
 
 
FUNCTION fileType; æ (modeWord : word) : integer; å
  VAR
    t : integer;
BEGIN
  t := ((modeWord + p16) DIV p12 * p12) MOD p16;
  fileType := t;
æ*b*   printVar ('unixfs.fileType = ', t);    *e*å
END;
 
 
 
 
FUNCTION fillInoList; æ (VAR dao : faoRefType;
                         VAR sb  : superBlock
                        ) : integer;             å
  LABEL
    100;   æ listFull; å
  VAR
    inoBuf : inodeArray;
    i : ino;
    bl, k, n : integer;
 
æ This function fills the inode-list in the super buffer with free
  i-numbers.
  The return value is a count of i-numbers filled into the super buffer.
å
 
BEGIN
æ*b*   printText ('fillInoList ');   *e*å
  k := 0;
  i := 0;
  FOR bl := 2 TO sb.inodSize - 1 DO
  BEGIN
    getOneBlock (dao, inoBuf, 1, unixPageSize, bl, 0);
    FOR n := 1 TO 8 DO
    BEGIN
      i := i + 1;
      IF inoBufÆnÅ.iLink.iNLink = 0 THEN
      BEGIN
        IF k <= nicInod THEN sb.inoListÆkÅ := i
                        ELSE GOTO 100;  æ listFull; å
        k := k + 1;
      END;  æfreeå
    END;  æfor nå
  END;  æfor blå
 
100:   æ listFull: å
  fillInoList := k;
  sb.nIno     := k;
 
æ*b*   printVar ('end fillInoList, free nodes = ', k);   *e*å
END;  æfillInoListå
 
 
 
 
 
FUNCTION getBlockNo; æ (VAR dao : faoRefType;
                        VAR sbGate    : pGateType;
                        VAR pUnixData : pUxDatType;
                        VAR fd : faoData;
                        relBlock : long
                       ) : blockNo;                å
  CONST
    bLimit1 = 10;
    bLimit2 = 138;    æ bLimit1 + 128 å
    bLimit3 = 16522;  æ bLimit2 + 128*128 å
  VAR
    i, l, x : integer;
    bl      : blockNo;
 
  æ This function finds the actual block number on disc corresponding
    to the relative block number in the file, specified by 'relBlock'.
    If 'relBlock' < 10 then the block table in 'fd' gives directly
    the actual block number, otherwise the block number is found
    through 1, 2, or 3 indirect block references å
 
BEGIN
  WITH fd DO
  BEGIN
æ+b+   printVar ('getBlockNo : relBlock = ', relBlock);   +e+å
    IF relBlock < bLimit1 THEN
    BEGHN
      bl := blockTabÆrelBlockÅ.blockAddr;
      inxÆ0Å := relBlock;
      level := 0;
    END  ædirectå
    ELSE
    BEGIN  æindirectå
      IF relBlock < bLimit2 THEN
      BEGIN
        level := 1;
        inxÆ1Å := relBlock - bLimit1;
      END  æ1st level, singly indirectå
      ELSE
      IF relBlock < bLimit3 THEN
      BEGIN
        level := 2;
        i := relBlock - bLimit2;
        inxÆ2Å := i DIV 128;
        inxÆ1Å := i MOD 128;
      END  æ2nd level, doubly indirectå
      ELSE
      BEGIN
        level := 3;
        i := relBlock - bLimit3;
        inxÆ3Å := i DIV 16384;
        i      := i MOD 16384;
        inxÆ2Å := i DIV 128;
        inxÆ1Å := i MOD 128;
      END;  æ3rd level, triply indirectå
      bl := blockTab ÆbLimit1 + level - 1Å.blockAddr;
      inxÆ0Å := bLimit1 + level - 1;
 
æ+b+   printVar ('abs block = ', bl);
       printVar ('level     = ', level);
       printVar ('inx array = ', inx);
       printVar ('blockTab  = ', blockTab);   +e+å
 
      FOR l := level DOWNTO 1 DO
      BEGIN
        IF iTabÆlÅ.curIb <> bl THEN
        BEGIN
          IF iTabÆlÅ.update THEN
          BEGIN
            æ all i-tabs from lower levels up to this level, with
              update mark, must be written to disc before new readå
            FOR x := 1 TO l DO
            BEGIN
              IF iTabÆxÅ.update THEN
              BEGIN
æ+b+   printVar ('indirect table index ÆxÅ = ', x);
       printVar ('indirect table put = ', iTabÆxÅ);   +e+å
                i := putOneBlock (dao, sbGate, pUnixData, iTabÆxÅ.tab, fd,
                                  1, unixPageSize, iTabÆxÅ.curIb, 0);
 
                iTabÆxÅ.update := false;
              END;  æifå
            END;  æfor xå
          END;  æupdateå
          getOneBlock (dao, iTabÆlÅ.tab, 1, unixPageSize, bl, 0);
          iTabÆlÅ.curIb := bl;
æ+b+   printVar ('indirect table index ÆlÅ = ', l);
       printVar ('indirect table read = ', iTabÆlÅ);   +e+å
        END;  æcurIb <> blå
        bl := iTabÆlÅ.tabÆinxÆlÅÅ;
      END;  æfor lå
    END;  æindirectå
  END;  æwith fdå
 
  getBlockNo := bl;
æ+b+   printVar ('end getBlockNo, blockNo = ', bl);   +e+å
END;  ægetBlockNoå
 
 
 
 
FUNCTION getFreeInum; æ (VAR dao : faoRefType;
                         VAR sbGate    : pGateType;
                         VAR pUnixData : pUxDatType;
                         VAR fd : faoData
                        ) : ino;              å
  VAR
    ix, k, n : integer;
    res : resultType;
 
æ This function returns the i-number of the next free i-node å
 
BEGIN
æ*b*   printText ('getFreeInum ');   *e*å
  ix := fd.daoIndex;
  res.main := ok;
 
  æ critical region for update super block å
  xCheck ( sbGate.Lock );
  IN
    WITH ux = pUnixData^^, sb = ux.devTableÆixÅ.super DO
    BEGIN
      k := sb.nIno;
      IF k = 0 THEN k := fillInoList (dao, sb);
      IF k > 0 THEN
      BEGIN
        sb.nIno := k - 1;
        k := sb.inoListÆsb.nInoÅ;
        sb.tInode := sb.tInode - 1;
      END;
      getFreeInum  := k;
      fd.inoUpdate := true;
 
      æ put super block å
      xCheck ( dao.WriteRandom (VAR IN OUT pUnixData^^.devTableÆixÅ.super ;
                                OUT n, IN superAddr, OUT n));
æ!b!   printVar ('getFreeInum, super block = ', sb);   !e!å
    END;  æwith sbå
  DO
    BEGIN
      res := GetException;
      IF res.main < 0 THEN res.main := -res.main;
    END;
 
  æ leave critical region å
  sCheck ( sbGate.Open );
  IF res.main <> ok THEN Exception (res);
æ*b*   printVar ('end getFreeInum = ', k);   *e*å
END;  ægetFreeInumå
 
 
 
 
 
PROCEDURE getInoFile; æ (VAR dao : faoRefType;
                         VAR fd : faoData;
                         iNum : ino );        å
  VAR
    i, j, block, relByte : integer;
 
æ This procedure reads the file description part of the i-node
  specified by 'iNum' å
 
BEGIN
æ*b*   printVar ('getInoFile, iNum = ', iNum);   *e*å
  WITH fd DO
  BEGIN
    iNumber := iNum;
    block := (iNumber - 1) DIV 8 + 2;
    relByte := ((iNumber - 1) MOD 8) * inoSize + inoLSize;
    getOneBlock (dao, iNoFile, 1, inoFSize, block, relByte);
æ*b*   printVar ('inoFile = ', inoFile);   *e*å
 
    FOR i := 0 TO 12 DO
    BEGIN
      blockTabÆiÅ.bAddrÆ0Å := 0;
      FOR j := 1 TO 3 DO
        blockTabÆiÅ.bAddrÆjÅ := inoFile.iAddrÆi*3+jÅ;
    END;  æfor iå
æ+b+   printVar ('blockTab = ', blockTab);   +e+å
 
    FOR i := 0 TO 3 DO inxÆiÅ := 0;
 
    eofPos := inoFile.iSize;
    allocSize := (eofPos + unixPageSize - 1) DIV unixPageSize * unixPageSize;
    curPos    := 0;
    inoUpdate := false;
 
  END;  æwith fdå
æ+b+   printText ('end getInoFile ');   +e+å
END;  ægetInoFileå
 
 
 
 
 
PROCEDURE getInoLink; æ (VAR dao : faoRefType;
                         VAR fd  : faoData;
                         iNum    : ino );       å
  VAR
    block, relByte : integer;
 
æ This procedure reads the link description part of the i-node
  specified by 'iNum' å
 
BEGIN
æ*b*   printVar ('getInoLink, iNum = ', iNum);   *e*å
  WITH fd DO
  BEGIN
    iNumber := iNum;
    block := (iNumber - 1) DIV 8 + 2;
    relByte := ((iNumber - 1) MOD 8) * inoSize;
    getOneBlock (dao, inoLink, 1, inoLSize, block, relByte);
æ*b*   printVar ('end getInoLink = ', inoLink);   *e*å
  END;  æwith fdå
END;  ægetInoLinkå
 
 
 
 
FUNCTION getNamePart; æ ( VAR pathName : fullId;
                          VAR fName    : name14;
                          VAR cNo      : integer
                         ) : boolean;             å
  VAR
    totL, i : integer;
    last    : boolean;
    ch      : char;
 
æ This function returns in 'fName' the name part of 'pathName' pointed
  to by 'cNo', which as return value is a pointer to the next name part.
  The return value of the function is true when the last name part is
  returned, otherwise false.
å
 
BEGIN
  totL := elements (pathName);
æ*b*   printVar ('getNamePart, totL = ', totL);   *e*å
 
  i := 1;
  IF cNo < totL THEN
  REPEAT
    cNo := cNo + 1;
    ch := pathNameÆcNoÅ;
    IF ch <> '/' THEN
    BEGIN
      fNameÆiÅ := ch;
      i := i + 1;
    END;
  UNTIL ((ch = '/') AND (i > 1)) OR
        (ch = CHR(0)) OR
        (cNo >= totL);
  FOR i := i TO nameSize DO fNameÆiÅ := CHR(0);
  last := (cNo >= totL) OR (ch = CHR(0));
  getNamePart := last;
æ*b*   printVar ('end getNamePart : fName = ', fName);
       printVar (' new charNo = ', cNo);
       IF last THEN printText ('last ');               *e*å
END;  ægetNamePartå
 
 
 
 
PROCEDURE getOneBlock; æ (VAR dao  : faoRefType;
                          UNIV buf : blockPtr;
                          first, last : integer;
                          block    : blockNo;
                          relByte  : integer      ); å
  VAR
    i, j : integer;
 
æ This procedure reads a number of consecutive bytes (max one page)
  from disc to the array defined by 'buf'.
  'block' = 0 designates an un-allocated block, otherwise 'block' and
  'relByte' define the absolute disc position for start reading.
å
 
BEGIN
æ*b*   printVar ('getOneBlock : blockNo = ', block);   *e*å
æ+b+   printVar ('relByte = ', relByte);
       printVar ('first   = ', first);
       printVar ('last    = ', last);     *e*å
 
  IF block = 0 THEN   æunallocated blockå
    FOR i := first TO last DO
      bufÆiÅ := 0
  ELSE
    xCheck ( dao.ReadRandom (VAR IN OUT bufÆfirst..lastÅ ; OUT i,
                             IN block*unixPageSize + relByte, OUT j));
æ*b*   printVar ('end getOneBlock, bytecount = ', i);   *e*å
END;  ægetOneBlockå
 
 
 
 
 
PROCEDURE getOneSegment; æ (VAR dao : faoRefType;
                            VAR segment : bufRef;
                            first, last : integer;
                            block : blockNo;
                            relByte : integer  );   å
  VAR
    i, j : integer;
 
æ This procedure reads a number of consecutive bytes (max one page)
  from disc in the same way as getOneBlock, but data are read into
  a sub segment (instead of into an array).
å
 
BEGIN
æ*b*   printVar ('getOneSegment : blockNo = ', block);    *e*å
æ+b+   printVar ('relByte = ', relByte);
       printVar ('first   = ', first);
       printVar ('last    = ', last);        +e+å
 
  IF block = 0 THEN   æunallocated blockå
    WITH buf = segment^^ DO
    BEGIN
      FOR i := first TO last DO
        bufÆiÅ := 0;
    END  æwithå
  ELSE
    xCheck ( dao.ReadRandom (VAR IN OUT segment^^Æfirst..lastÅ ; OUT i,
                             IN block*unixPageSize + relByte, OUT j));
æ*b*   printVar ('end getOneSegment, byteCount = ', i);   *e*å
END;  ægetOneSegmentå
 
 
 
 
 
PROCEDURE newDirEntry; æ (VAR dao : faoRefType;
                          VAR sbGate    : pGateType;
                          VAR pUnixData : pUxDatType;
                          VAR pUnixDir  : bufRef;
                          VAR fd        : faoData;
                          dirIno   : ino;
                          dirPos   : integer;
                          fileIno  : ino;
                          fName    : name14 );   å
  VAR
    bytes, dirIx, i, saveEof, savePos : integer;
 
æ This procedure inserts the file specified by 'fileIno' and 'fName'
  in the directory specified by 'dirIno' in the position specified by
  'dirPos'.
å
 
BEGIN
æ*b*   printVar ('newDirEntry, dirPos = ', dirPos);   *e*å
æ+b+   printVar ('fileIno = ', fileIno);
       printVar ('fName   = ', fName);       +e+å
 
  getInoFile (dao, fd, dirIno);
  getInoLink (dao, fd, dirIno);
  IF fd.inoLink.iNLink < 1 THEN
    Exception ( makeRes (Reject * EntryIllegal, Universal,
                         entryA, dirNotPermanent ));
 
  savePos   := dirPos DIV unixPageSize * unixPageSize;
  fd.curPos := savePos;
  saveEof   := fd.eofPos;
  dirIx     := (dirPos - fd.curPos) DIV dirSize + 1;
  readSegment (dao, sbGate, pUnixData, pUnixDir, fd, æoutå bytes);
 
  WITH dir = pUnixData^^.dirBlock DO
  BEGIN
    IF bytes < unixPageSize THEN  zeroFill (dir, bytes+1, unixPageSize);
    dirÆdirIxÅ.dIno  := fileIno;
    dirÆdirIxÅ.dName := fName;
æ*b*   printVar ('dirBlock after insert = ', dir);   *e*å
  END;
  fd.curPos := savePos;
  writeSegment (dao, sbGate, pUnixData, pUnixDir, fd, i);
 
  æset new eofPoså
  IF fd.eofPos > saveEof THEN
  BEGIN
    IF saveEof = dirPos THEN fd.eofPos := dirPos + dirSize
                        ELSE fd.eofPos := saveEof;
    putInoFile (dao, fd);
  END;
 
æ+b+   printText ('end newDirEntry ');   +e+å
END;  ænewDirEntryå
 
 
 
 
 
PROCEDURE putInoFile; æ (VAR dao : faoRefType;
                         VAR fd : faoData );   å
  VAR
    i, j, block, relByte : integer;
 
BEGIN
  WITH fd DO
  BEGIN
æ*b*   printVar ('putInoFile : iNum = ', iNumber);   *e*å
    FOR i := 0 TO 12 DO
      FOR j := 1 TO 3 DO
        inoFile.iAddrÆi*3+jÅ := blockTabÆiÅ.bAddrÆjÅ;
 
    inoFile.iSize := eofPos;
    block := (iNumber-1) DIV 8 + 2;
    relByte := ((iNumber-1) MOD 8) * inoSize + inoLSize;
æ+b+   printVar ('block = ', block);
       printVar ('relByte = ', relByte);   +e+å
 
    inoUpdate := true;
    xCheck ( dao.WriteRandom ( VAR IN OUT inoFile ; OUT i,
                               IN block * unixPageSize + relByte, OUT i));
    inoUpdate := false;
 
æ*b*   printVar ('end putInoFile, i-node = ', inoFile);   *e*å
  END;  æwith fdå
END;  æputInoFileå
 
 
 
 
PROCEDURE putInoLink; æ (VAR dao : faoRefType;
                         VAR fd  : faoData );  å
  VAR
    block, i, relByte : integer;
 
æ This procedure writes to disc the link description part of the i-node
  specified by fd.iNumber å
 
BEGIN
  WITH fd DO
  BEGIN
æ*b*   printVar ('putInoLink, iNum = ', fd.iNumber);   *e*å
    block := (iNumber - 1) DIV 8 + 2;
    relByte := ((iNumber - 1) MOD 8) * inoSize;
æ+b+   printVar ('block = ', block);
       printVar ('relByte = ', relByte);   +e+å
 
    xCheck ( dao.WriteRandom (VAR IN OUT inoLink ; OUT i,
                              IN block * unixPageSize + relByte, OUT i));
æ*b*   printVar ('end putInoLink = ', inoLink);   *e*å
  END;  æwith fdå
END;  æputInoLinkå
 
 
 
 
 
FUNCTION putOneBlock; æ (VAR dao : faoRefType;
                         VAR sbGate    : pGateType;
                         VAR pUnixData : pUxDatType;
                         buf     : blockPtr;
                         VAR fd  : faoData;
                         first, last : integer;
                         block   : blockNo;
                         relByte : integer
                        ) : blockNo;             å
  VAR
    new : boolean;
    i, j, count, pos : integer;
    helpBuf : array Æ1..unixPageSizeÅ of byte;
 
æ This procedure writes a number of consecutive bytes (max
  one page) to the disc, at the absolute block number defined by
  'block'.
  If 'block' = 0 a new block must be allocated from the pool of
  free blocks. If 'buf' does not start and/or end at a page
  boundary, all other bytes of that page are left unchanged if
  existing block, or zeroed if new-allocated block.
å
 
BEGIN
æ*b*   printVar ('putOneBlock : blockNo = ', block);   *e*å
 
æ+b+   printVar ('relByte = ', relByte);
       printVar ('first   = ', first);
       printVar ('last    = ', last);      +e+å
 
  IF block = 0 THEN
  BEGIN
    block := allocBlock (dao, sbGate, pUnixData, fd);
    new := true;
  END
  ELSE
    new := false;
 
  IF new AND
     ((relByte > 0) OR
      (last - first + 1 < unixPageSize)) THEN
  BEGIN
    FOR i := 1 TO unixPageSize DO helpBufÆiÅ := 0;
    j := relByte + 1;
    FOR i := first TO last DO
    BEGIN
      helpBufÆjÅ := bufÆiÅ;
      j := j + 1;
    END;
æ*b*   printVar ('helpBuf = ', helpBuf);   *e*å
    xCheck ( dao.WriteRandom (VAR IN OUT helpBuf ; OUT count,
                              IN block * unixPageSize, OUT pos));
  END   ænot full pageå
  ELSE
æ*b*   printVar ('buf = ', buf);   *e*å
    xCheck ( dao.WriteRandom (VAR IN OUT bufÆfirst..lastÅ ; OUT count,
                              IN block * unixPageSize + relByte, OUT pos));
 
  putOneBlock := block;  æused block numberå
æ*b*   printVar ('end putOneBlock, used blockNo = ', block);   *e*å
END;  æputOneBlockå
 
 
 
 
FUNCTION putOneSegment; æ (VAR dao : faoRefType;
                           VAR sbGate    : pGateType;
                           VAR pUnixData : pUxDatType;
                           VAR segment : bufRef;
                           VAR fd      : faoData;
                           first, last : integer;
                           block       : blockNo;
                           relByte     : integer
                         ) : blockNo;              å
  VAR
    new : boolean;
    i, j, count, pos : integer;
    helpBuf : array Æ1..unixPageSizeÅ of byte;
 
æ This procedure writes a number of consecutive bytes (max one page)
  to disc in the same way as putOneBlock, but the data are written
  from a sub segment (instead of from an array).
å
 
BEGIN
æ*b*   printVar ('putOneSegment : blockNo = ', block);   *e*å
 
æ+b+   printVar ('relByte = ', relByte);
       printVar ('first   = ', first);
       printVar ('last    = ', last);        +e+å
 
  IF block = 0 THEN
  BEGIN
    block := allocBlock (dao, sbGate, pUnixData, fd);
    new := true;
  END
  ELSE
    new := false;
 
  IF new AND
     ((relByte > 0) OR
      (last - first + 1 < unixPageSize)) THEN
  BEGIN
    FOR i := 1 TO unixPageSize DO helpBufÆiÅ := 0;
    j := relByte + 1;
    WITH buf = segment^^ DO
    BEGIN
      FOR i := first TO last DO
      BEGIN
        helpBufÆjÅ := bufÆiÅ;
        j := j + 1;
      END;
    END;  æwithå
æ*b*   printVar ('helpBuf = ', helpBuf);   *e*å
    xCheck ( dao.WriteRandom (VAR IN OUT helpBuf ; OUT count,
                              IN block * unixPageSize, OUT pos));
  END   ænot full pageå
  ELSE
    xCheck ( dao.WriteRandom (VAR IN OUT segment^^Æfirst..lastÅ ; OUT count,
                              IN block * unixPageSize + relByte, OUT pos));
 
  putOneSegment := block;  æused block numberå
æ*b*   printVar ('end putOneSegment, used blockNo = ', block);   *e*å
END;  æputOneSegmentå
 
 
 
 
 
PROCEDURE readSegment; æ (VAR dao : faoRefType;
                          VAR sbGate    : pGateType;
                          VAR pUnixData : pUxDatType;
                          VAR segment : bufRef;
                          VAR fd : faoData;
                          VAR byteCount : integer ); å
  VAR
    bufFirst, bufLast,bs, firstBlock, lastBlock, nextActual, pos,
    relBlock, relPos, savedActual                     : integer;
 
æ This procedure reads into 'segment' from the file, one page (or less)
  at a time. If two or more pages are placed consecutively on the
  disc, they are read in one operation.
å
 
BEGIN
æ*b*   printVar ('readSegment : fd.curPos = ', fd.curPos);
       printVar ('fd.eofPos = ', fd.eofPos);                 *e*å
  byteCount := 0;
 
  æ check read reservation å
  IF (fd.rwMode = NoRights) OR
     (fd.rwMode = WriteMode) THEN
       Exception ( makeRes ( Reject * EntryIllegal, Universal,
                             entryA, noReadReservation ));
 
  æ check current position against eof position å
  IF fd.curPos > fd.allocSize THEN
    Exception ( makeRes ( Reject * PosOutsideRange, IoFamily,
                          0, 0));
 
  fd.readMark := true;
  IF fd.curPos >= fd.eofPos THEN
    byteCount := 0
  ELSE
  BEGIN
    pos := fd.curPos;
    bs  := segLength (segment);
æ*b*   printVar ('segLength = ', bs);   *e*å
    IF pos + bs > fd.eofPos THEN bs := fd.eofPos - pos;
    firstBlock := pos DIV unixPageSize;
    relPos     := pos MOD unixPageSize;
    lastBlock  := (pos + bs - 1) DIV unixPageSize;
 
    bufFirst := 1;
    bufLast  := unixPageSize - relPos;
    if bufLast >= bs THEN bufLast := bs;
 
    savedActual := getBlockNo (dao, sbGate, pUnixData, fd, firstBlock);
    FOR relBlock := firstBlock + 1 TO lastBlock DO
    BEGIN
      nextActual := getBlockNo (dao, sbGate, pUnixData, fd, relBlock);
      IF nextActual - savedActual <> relBlock - firstBlock THEN
      BEGIN
        æ non consecutive blocks, read old block now å
        getOneSegment (dao, segment, bufFirst, bufLast, savedActual, relPos);
        fd.curPos := fd.curPos + bufLast - bufFirst + 1;
        byteCount := byteCount + bufLast - bufFirst + 1;
        savedActual := nextActual;
        firstBlock := relBlock;
        bufFirst   := bufLast + 1;
        relPos     := 0;
      END;
      bufLast := bufLast + unixPageSize;
      IF bufLast > bs THEN bufLast := bs;
    END;  æfor relBlockå
 
    æ read final block å
    getOneSegment (dao, segment, bufFirst, bufLast, savedActual, relPos);
    fd.curPos := fd.curPos + bufLast - bufFirst + 1;
    byteCount := byteCount + bufLast - bufFirst + 1;
  END;  ænot eofå
æ!b!   printVar ('end readSegment, byteCount = ', byteCount);   !e!å
END;  æreadSegmentå
 
 
 
 
PROCEDURE releaseBlock; æ (VAR dao : faoRefType;
                           VAR sbGate    : pGateType;
                           VAR pUnixData : pUxDatType;
                           VAR fd  : faoData ;
                           VAR sb  : superBlock;
                           absBlock : long );     å
  VAR
    i  : integer;
 
æ This procedure releases a block, inserting the block number in the
  free list in the super buffer å
 
BEGIN
æ*b*   printVar ('releaseBlock : absBlock = ', absBlock);   *e*å
  WITH xb = sb.freeBlock DO
  BEGIN
æ*b*   printVar ('freeList before release = ', xb);   *e*å
    IF xb.nFree = nicFree THEN
    BEGIN
      æ Free list in super block full, use the freed block as
        new free chain å
æ#ern#  printVar ('Release block: superblock: ', sb );  &ern&å
      i := putOneBlock (dao, sbGate, pUnixData, xb.freeBuf, fd,
                        1, freeBfSize, absBlock, 0);
æ#ern#  printVar ('to block ', absBlock );  &ern&å
      xb.nFree := 0;
    END;
    xb.freeListÆxb.nFreeÅ := absBlock;
    xb.nFree := xb.nFree + 1;
    sb.tFreeBl := sb.tFreeBl + 1;
æ*b*   printVar ('freeList after release = ', xb);   *e*å
  END;  æwith xbå
æ*b*   printText ('end releaseBlock ');   *e*å
 
END;  æreleaseBlockå
 
 
 
 
 
PROCEDURE remDirEntry; æ (VAR dao : faoRefType;
                          VAR sbGate    : pGateType;
                          VAR pUnixData : pUxDatType;
                          VAR pUnixDir  : bufRef;
                          VAR fd        : faoData;
                          dirIno  : ino;
                          dirPos  : integer );  å
  VAR
    dirIx, i, saveEof : integer;
 
æ This procedure clears the directory entry defined by 'dirPos' from
  the directory defined by 'dirIno'.
  Note! The sub-segment pUnixDir is supposed to contain the directory
        block from which the entry is to be removed, and the total
        block is written to the file.
å
 
BEGIN
æ*b*   printVar ('remDirEntry : dirPos = ', dirPos);   *e*å
  getInoFile (dao, fd, dirIno);
  saveEof := fd.eofPos;
  fd.curPos := dirPos DIV unixPageSize * unixPageSize;
  dirIx     := (dirPos - fd.curPos) DIV dirSize + 1;
æ*b*   printVar ('dirIx = ', dirIx);   *e*å
 
  WITH dir = pUnixData^^.dirBlock DO
  BEGIN
æ*b*   printVar ('dir block before remove ', dir);   *e*å
    dirÆdirIxÅ.dIno := 0;
    FOR i := 1 TO nameSize DO dirÆdirIxÅ.dNameÆiÅ := CHR(0);
æ*b*   printVar ('dirBlock after remove ', dir);   *e*å
  END;
 
  writeSegment (dao, sbGate, pUnixData, pUnixDir, fd, i);
 
  æ restore eofPos å
  IF fd.eofPos > saveEof THEN
  BEGIN
    fd.eofPos := saveEof;
    putInoFile (dao, fd);
  END;
æ*b*   printText ('end remDirEntry');   *e*å
END;  æremDirEntryå
 
 
 
 
PROCEDURE removeFile; æ (VAR dao : faoRefType;
                         VAR sbGate    : pGateType;
                         VAR dummyGate : pGateType;
                         VAR pUnixData : pUxDatType;
                         VAR fd : faoData );         å
  VAR
    empty : array Æ1..inoSizeÅ of byte;
    block, i, ix, relByte : integer;
    res : resultType;
 
æ This procedure clears the i-node defined by 'fd.iNumber' and calls
  'cutFile' to release all data blocks belonging to this file.
å
 
BEGIN
æ*b*   printVar ('removeFile : iNum = ', fd.iNumber);   *e*å
 
  æ put empty i-node å
  FOR i := 1 TO inoSize DO emptyÆiÅ := 0;
  block := (fd.iNumber - 1) DIV 8 + 2;
  relByte := ((fd.iNumber - 1) MOD 8) * inoSize;
æ*b*   printVar ('i-node position: blockNo = ', block);
       printVar ('       relByte = ', relByte);          *e*å
  xCheck ( dao.WriteRandom (VAR IN OUT empty ;
                            OUT i, IN block * UnixPageSize + relByte, OUT i));
 
  æ critical region for update super block å
  res.main := ok;
  xCheck ( sbGate.Lock );
  IN
    æ release data blocks å
    ix := fd.daoIndex;
    WITH ux = pUnixData^^, sb = ux.devTableÆixÅ.super DO
    BEGIN
      æ release data blocks and index blocks å
      cutFile (dao, dummyGate, pUnixData, fd, sb);
 
      æ increase free inode count å
      IF sb.nIno < nicInod THEN
      BEGIN
        sb.inoListÆsb.nInoÅ := fd.iNumber;
        sb.nIno := sb.nIno + 1;
      END;
      sb.tInode := sb.tInode + 1;
 
      æ put super block å
æ!b!   printVar ('removeFile: super block = ', sb);   !e!å
      xCheck (dao.WriteRandom
               (VAR IN OUT pUnixData^^.devTableÆixÅ.super ;
                OUT i, IN superAddr, OUT i));
    END;  æwith sbå
  DO
    BEGIN
      res := GetException;
      IF res.main < 0 THEN res.main := -res.main;
    END;
 
  æ leave critical region å
  sCheck ( sbGate.Open );
  IF res.main <> ok THEN Exception (res);
æ*b*   printText ('end removeFile ');   *e*å
END;  æremoveFileå
 
 
 
 
 
FUNCTION searchDevTab; æ (VAR ux : unixData;
                          VAR pathName : fullId;
                          VAR charNo : integer
                         ) : integer;             å
  VAR
    i, k, n : integer;
 
æ This function searches through the device table (list of mounted
  devices) in 'ux' for the longest path name prefix matching 'pathName'.
  The return value of 'charNo' is the position of the last char of
  the prefix.
å
 
BEGIN
æ*b*   printText ('searchDevTab ');   *e*å
  charNo := 0;
  n := 0;
  FOR i := 1 TO maxDisc DO
  BEGIN
    WITH  dev = ux.devTableÆiÅ DO
    BEGIN
      k := dev.pathLength;
      IF vEqual (pathName, dev.pathName, k) THEN
        IF k > charNo THEN
        BEGIN
          charNo := k;
          n := i;
        END;
    END;  æwith devå
  END;  æfor iå
  searchDevTab := n;
æ*b*   printVar ('end searchDevTab, daoIndex = ', n);
       printVar (' matching length = ', charNo);        *e*å
END;  æsearchDevTabå
 
 
 
 
FUNCTION searchDirectory; æ (VAR dao : faoRefType;
                             VAR sbGate    : pGateType;
                             VAR pUnixData : pUxDatType;
                             VAR pUnixDir  : bufRef;
                             VAR fd : faoData;
                             VAR fName : name14;
                             VAR dirPos : integer
                            ) : ino;                 å
  VAR
    byteCount, ix, lastIx, pos : integer;
    found : boolean;
    iNum  : ino;
 
æ This procedure reads through the entries of the directory file
  pointed to by 'fd', to find the file with name 'fName'.
  The return value of searchDirectory is the i-number of this file
  if found, otherwise zero. The parameter 'dirPos' returns the
  position of the entry found, or of the first free entry if not found.
å
 
BEGIN
æ*b*   printVar ('searchDirectory : fName = ', fName);   *e*å
  WITH fd , dir = pUnixData^^.dirBlock DO
  BEGIN
    IF (fileType (inoLink.iMode) <> dirType) AND
       (fd.eofPos > 0)                       THEN
          exception ( makeRes ( Reject * DataValueIllegal, Universal,
                                fNameArg, notDirFile));
 
    pos := 0;
    dirPos := 0;
    fd.curPos := 0;
    found := false;
    iNum  := 0;
    WHILE (pos < fd.eofPos) AND NOT found DO
    BEGIN
      readSegment (dao, sbGate, pUnixData, pUnixDir, fd, byteCount);
      lastIx := byteCount DIV dirSize;
æ*b*   printVar ('next dir block read, lastIx = ', lastIx);
       printVar (' dir block = ', dir);                       *e*å
 
      ix := 1;
      REPEAT
        IF equal (dirÆixÅ.dName, fName) THEN
        BEGIN
          iNum := dirÆixÅ.dIno;
          found := true;
          dirPos  := pos + dirSize*(ix-1);
        END
        ELSE
        IF dirPos = 0 THEN
          IF dirÆixÅ.dIno = 0 THEN
            dirPos  := pos + dirSize*(ix-1);
        ix := ix + 1;
      UNTIL (ix > lastIx) OR found;
      pos := pos + unixPageSize;
    END;  æwhileå
 
    searchDirectory := iNum;
    IF dirPos = 0 THEN dirPos := fd.eofPos;
æ*b*   printVar ('end searchDirectory, i-number = ', iNum);
       printVar (' dirPos = ', dirPos);                        *e*å
  END;  æwith fd, dirå
END;  æsearchDirectoryå
 
 
 
 
FUNCTION segLength; æ (VAR segment : bufRef) : integer; å
  VAR
    addr, length, use : integer;
 
æ This function returns the length (in bytes) of 'segment' å
 
BEGIN
  sCheck ( IoAddr (segment, addr, length, use));
  segLength := length;
END;  æsegLengthå
 
 
 
 
PROCEDURE writeSegment; æ (VAR dao : faoRefType;
                           VAR sbGate    : pGateType;
                           VAR pUnixData : pUxDatType;
                           VAR segment : bufRef;
                           VAR fd : faoData;
                           VAR byteCount : integer ); å
  VAR
    actual, bl, bs, bufFirst, bufLast, firstBlock, l, lastBlock,
    newPos, pos, relBlock, relPos, used : integer;
    res : resultType;
 
æ This procedure writes the contents of 'buf' to the file, one page
  at a time. (In this first version consecutive physical blocks are
  NOT written in a single operation, because data blocks must be
  written before possibly updated indirect blocks).
 
  If a block has not been allocated before, a block from the free
  pool is allocated, and if the block is placed beyond eof, the eof
  position and the allocation limit are updated.
å
 
BEGIN
æ*b*   printVar ('writeSegment : fd.curPos = ', fd.curPos);
       printVar ('fd.eofPos = ', fd.eofPos);                  *e*å
  res.main := ok;
  byteCount := 0;
 
  æ check write reservation å
  IF (fd.rwMode < WriteMode) OR
     (fd.devReadOnly > 0) THEN
       exception ( makeRes ( Reject * EntryIllegal, Universal,
                             entryA, noWriteReservation ));
 
  fd.writeMark := true;
  pos := fd.curPos;
  bs := segLength (segment);
  firstBlock := pos DIV unixPageSize;
  relPos     := pos MOD unixPageSize;
  lastBlock  := (pos + bs - 1) DIV unixPageSize;
 
  bufFirst := 1;
  bufLast  := unixPageSize - relPos;
  IF bufLast > bs THEN bufLast := bs;
 
  IN
    FOR relBlock := firstBlock TO lastBlock DO
    BEGIN
      actual := getBlockNo (dao, sbGate, pUnixData, fd, relBlock);
      used := putOneSegment (dao, sbGate, pUnixData, segment, fd,
                             bufFirst, bufLast, actual, relPos);
      æ data block written, now update indirect blocks, if
        new data block allocated å
      IF actual = 0 THEN
      BEGIN
        WITH fd DO
        BEGIN
          FOR l :=1 TO level DO
          BEGIN
            WITH iTabÆlÅ DO
            BEGIN
              IF tabÆinxÆlÅÅ = 0 THEN
              BEGIN
                tabÆinxÆlÅÅ := used;
                bl := curIb;
                IF bl = 0 THEN
                BEGIN
                  used := putOneBlock (dao, sbGate, pUnixData, tab, fd,
                                       1, unixPageSize, bl, 0);
                  curIb := used;
                END
                ELSE
                  iTabÆlÅ.update := true;
              END;  ænew tab elementå
            END;  æwith iTabå
          END;  æfor lå
 
          IF blockTabÆinxÆ0ÅÅ.blockAddr = 0 THEN
          BEGIN
            blockTabÆinxÆ0ÅÅ.blockAddr := used;
            inoUpdate := true;
          END;
æ+b+   printVar ('updated fd = ', fd);   +e+å
        END;  æwith fdå
      END;  æactual = 0å
 
      fd.curPos := fd.curPos + bufLast - bufFirst + 1;
      byteCount := byteCount + bufLast - bufFirst + 1;
      relPos := 0;
      bufFirst := bufLast + 1;
      bufLast := bufLast + unixPageSize;
      IF bufLast > bs THEN bufLast := bs;
    END;  æfor relBlockå
  DO
    BEGIN
      res := GetException;
      IF res.main < 0 THEN res.main := -res.main;
    END;
 
  æ update positionså
  IF fd.curPos > fd.eofPos THEN
  BEGIN
    fd.inoUpdate := true;
    fd.eofPos := fd.curPos;
    fd.allocSize := (fd.eofPos + unixPageSize -1)
                       DIV unixPageSize * unixPageSize;
  END;
 
  IF fd.inoUpdate THEN
  BEGIN
    putInoFile (dao, fd);
    fd.inoUpdate := false;
  END;
 
  IF res.main <> ok THEN exception (res);
æ*b*   printVar ('end writeSegment, fd.eofPos = ', fd.eofPos);   *e*å
END;  æwriteSegmentå
 
 
 
 
æ$Eå
 
æ*********************************************************å
æ*****     I M P L E M E N T    U N I X    F A O     *****å
æ*********************************************************å
 
PROGRAM unixFaoImplement  OBJECT Fao  WITH faoLocals;
 
 
æ****************************å
æ*****     READ SEQ     *****å
æ****************************å
 
ENTRY ReadSeq
  æ segment ; OUT byteCount å
  WITH RECORD
    t : ^^;
  END;
 
  VAR
    res : resultType;
 
BEGIN
æ!b!   printText ('----- unixFs.ReadSeq ----- ');   !e!å
 
  sCheck ( faoGate.Lock );
 
  IN
    WITH fd = pFaoData^^ DO
    BEGIN
      readSegment (daoRef, sbGateRef, unixEnv^^.pUnixData,
                   segment, fd, byteCount);
æ!b!   printVar ('after readSegment: byteCount = ', byteCount);
       with buf = segment^^ do printVar ('buffer segment = ', buf);
       printVar ('curPos = ', fd.curPos);
       printVar ('eofPos = ', fd.eofPos);
       printVar ('fd = ', fd);                                   !e!å
    END;
    res := makeRes (ok, ok, ok, ok);
  DO
    res := GetException;
 
  sCheck ( faoGate.Open );
 
æ!b!   printVar ('----- end unixFs.ReadSeq -----, res = ', res);   !e!å
  ObjReturn (res);
END;  æReadSeqå
 
 
æ$Eå
 
æ*****************************å
æ*****     WRITE SEQ     *****å
æ*****************************å
 
ENTRY WriteSeq
  æ segment ; OUT byteCount å
  WITH RECORD
    t : ^^;
  END;
 
  VAR
    res : resultType;
 
BEGIN
æ!b!   printText ('----- unixFs.WriteSeq ----- ');   !e!å
 
  sCheck ( faoGate.Lock );
 
  IN
    WITH fd = pFaoData^^ DO
    BEGIN
      writeSegment (daoRef, sbGateRef, unixEnv^^.pUnixData,
                    segment, fd, byteCount);
æ!b!   printVar ('after writeSeq, byteCount = ', byteCount);
       with buf = segment^^ do printVar ('buffer segment = ', buf);
       printVar ('curPos = ', fd.curPos);
       printVar ('eofPos = ', fd.eofPos);
       printVar ('fd = ', fd);                                       !e!å
    END;
    res := makeRes (ok, ok, ok, ok);
  DO
    res := GetException;
 
  sCheck ( faoGate.Open );
 
æ!b!   printVar ('----- end unixFs.WriteSeq -----, res = ', res);   !e!å
  ObjReturn (res);
END;
 
æ$Eå
 
æ*******************************å
æ*****     READ RANDOM     *****å
æ*******************************å
 
ENTRY ReadRandom
  æ segment ; OUT byteCount, IN pos, OUT actualPos å
  WITH RECORD
    t : ^^;
  END;
 
  VAR
    res : resultType;
 
BEGIN
æ!b!   printText ('----- unixFs.ReadRandom ----- ');   !e!å
 
  sCheck ( faoGate.Lock );
 
  IN
    WITH fd = pFaoData^^ DO
    BEGIN
æ!b!   printVar ('pos = ', pos);   !e!å
      æ Check position and assign current pos å
      IF (pos < 0)            OR
         (pos > fd.allocSize) THEN
           Exception ( makeRes (Reject * PosOutsideRange, IoFamily,
                                posArg, 0 ));
      fd.curPos := pos;
 
      æ Read data block å
      readSegment (daoRef, sbGateRef, unixEnv^^.pUnixData,
                   segment, fd, bytecount);
æ!b!   printVar ('after readRandom, byteCount = ', byteCount);
       with buf = segment ^^ do printVar ('buffer segment = ', buf);
       printRar ('curPos = ', fd.curPos);
       printVar ('eofPos = ', fd.eofPos);
       printVar ('fd = ', fd);                                        !e!å
 
      res := makeRes (ok, ok, ok, ok);
    END;  æwith fdå
  DO
    res := GetException;
 
  sCheck ( faoGate.Open );
 
æ!b!   printVar ('----- end unixFs.ReadRandom -----, res = ', res);   !e!å
  ObjReturn (res);
END;  æReadRandomå
 
æ$Eå
 
æ********************************å
æ*****     WRITE RANDOM     *****å
æ********************************å
 
ENTRY WriteRandom
  æ segment ; OUT byteCount, IN pos, OUT actualPos å
  WITH RECORD
    t : ^^;
  END;
 
  VAR
    res : resultType;
 
BEGIN
æ!b!   printText ('----- unixFs.WriteRandom ----- ');   !e!å
 
  sCheck ( faoGate.Lock );
 
  IN
    WITH fd = pFaoData^^ DO
    BEGIN
æ!b!   printVar ('pos = ', pos);   !e!å
      æ Check position and assign current pos å
      IF pos < 0 THEN
        Exception ( makeRes (Reject * PosOutsideRange, IoFamily,
                             posArg, 0 ));
      fd.curPos := pos;
 
      æ Write data block å
      writeSegment (daoRef, sbGateRef, unixEnv^^.pUnixData,
                    segment, fd, byteCount);
æ!b!   printVar ('after writeRandom, byteCount = ', byteCount);
       with buf = segment^^ do printVar ('buffer segment = ', buf);
       printVar ('curPos = ', fd.curPos);
       printVar ('eofPos = ', fd.eofPos);
       printVar ('fd = ', fd);                                       !e!å
      res := makeRes (ok, ok, ok, ok);
    END;  æwith fdå
  DO
    res := GetException;
 
  sCheck ( faoGate.Open );
 
æ!b!   printVar ('----- end unixFs.WriteRandom -----, res= ', res);   !e!å
  ObjReturn (res);
END;  æWriteRandomå
 
 
æ$Eå
 
æ************************å
æ*****     SEEK     *****å
æ************************å
 
ENTRY Seek
  æ ; IN baseMode, IN offset, OUT pos å
  WITH RECORD
    t : ^^;
  END;
 
  VAR
    res : resultType;
    tPos : integer;
 
BEGIN
æ!b!   printText ('----- unixFs.Seek ----- ');   !e!å
 
  sCheck ( faoGate.Lock );
 
  IN
    WITH fd = pFaoData^^ DO
    BEGIN
      CASE baseMode OF
        FromStart:
          tPos := offSet;
        FromCurrent:
          tPos := fd.curPos + offset;
        FromEnd:
          tPos := fd.eofPos + offset;
        otherwise
          Exception ( makeRes (Reject * DataValueIllegal, IoFamily,
                               baseArg, illBaseMode ));
      END;  æcaseå
æ!b!   printVar ('baseMode = ', baseMode);
       printVar ('offset   = ', offset);
       printVar ('tPos     = ', tPos);       !e!å
 
      IF tPos < 0 THEN
        Exception ( makeRes (Reject * PosOutsideRange, IoFamily,
                             offsetArg, posNegative ));
      fd.curPos := tPos;
      pos := tPos;
    END;  æwith fdå
    res := makeRes (ok, ok, ok, ok);
  DO
    res := GetException;
 
  sCheck ( faoGate.Open );
 
æ!b!   printVar ('----- end unixFs.Seek -----, res = ', res);   !e!å
  ObjReturn (res);
END;  æseekå
 
 
æ$Eå
 
æ*****************************å
æ*****     DATA SIZE     *****å
æ*****************************å
 
ENTRY DataSize
  æ ; IN dataBytes å
  WITH RECORD
    t : ^^;
    dummyGate : ^^;
  END;
 
  VAR
    i, k, ix, block, relByte : integer;
    empty : array Æ1..inoFSizeÅ of byte;
    res : resultType;
 
BEGIN
æ!b!   printText ('----- unixFs.DataSize ----- ');   !e!å
 
  sCheck ( faoGate.Lock );
 
  IN
    WITH fd = pFaoData^^ DO
    BEGIN
      IF fileType (fd.inoLink.iMode) <> dataType THEN
        Exception ( makeRes (Reject * EntryIllegal, Universal,
                             entryA, notDataFile ));
      IF (fd.rwMode < WriteMode) THEN
        exception ( makeRes ( Reject * EntryIllegal, Universal,
                             entryA, noWriteReservation ));
 
æ!b!   printVar ('dataBytes = ', dataBytes);
       printVar ('eofPos = ', fd.eofPos);      !e!å
      IF dataBytes <> 0 THEN
        Exception ( makeRes (Reject * DataValueIllegal, Universal,
                             sizeArg, cutSizeIllegal ));
 
      IF fd.eofPos > 0 THEN
      BEGIN
        ix := fd.daoIndex;
        res.main := ok;
 
        æ put empty inoFile å
        FOR i := 1 TO inoFSize DO emptyÆiÅ := 0;
        block := (fd.iNumber - 1) DIV 8 + 2;
        relByte := ((fd.iNumber - 1) MOD 8) * inoSize + inoLSize;
        xCheck ( daoref.WriteRandom (VAR IN OUT empty ;
                          OUT i, IN block * unixPageSize + relByte, OUT i));
 
        æ critical region for update super block å
        xCheck ( sbGateRef.Lock );
 
        IN
          WITH ux = unixEnv^^.pUnixData^^,
               sb = ux.devTableÆixÅ.super DO
          BEGIN
            cutFile (daoRef, dummyGate, unixEnv^^.pUnixData, fd, sb);
 
            æ init empty fd å
            WITH fd DO
            BEGIN
              FOR i := 0 TO 12 DO blockTabÆiÅ.blockAddr := 0;
              FOR i := 1 TO 3 DO
              BEGIN
                WITH iTabÆiÅ DO BEGIN
                  curIb := 0;
                  update := false;
                  FOR k := 0 TO 127 DO tabÆkÅ := 0;
                END;
                inxÆiÅ := 0;
              END;
              level     := 0;
              curPos    := 0;
              eofPos    := 0;
              allocSize := 0;
            END;  æwith fdå
 
            æ Put super block å
æ!b!   printVar ('super block = ', sb);   !e!å
            xCheck (daoRef.WriteRandom
                      (VAR IN OUT unixEnv^^.pUnixData^^.devTableÆixÅ.super ;
                       OUT i, IN superAddr, OUT i ));
          END;  æwith sbå
        DO
          BEGIN
            res := GetException;
            IF res.main < 0 THEN res.main := -res.main;
          END;
 
        æ leave critical region å
        sCheck ( sbGateRef.Open );
        IF res.main <> ok THEN Exception (res);
      END;  æeofPos > 0å
    END;  æwith fd=å
    res := makeRes (ok, ok, ok, ok);
  DO
    res := GetException;
 
  sCheck ( faoGate.Open );
 
æ!b!   printVar ('----- end unixFs.DataSize -----, res= ', res);   !e!å
  ObjReturn (res);
END;  ædataSizeå
 
 
æ$Eå
 
æ*************************å
æ*****     FSTAT     *****å
æ*************************å
 
ENTRY FStat   æ IN stBuf å
  WITH RECORD
    t : ^^;
  END;
 
  VAR
    res : resultType;
 
BEGIN
æ!b!   ps ('----- unixFs.FStat ----- ');   !e!å
 
  sCheck ( faoGate.Lock );
 
  IN
    WITH fd = pFaoData^^, bf = stBuf^^ DO
    BEGIN
      bf.stDev   := fd.daoIndex;
      bf.stIno   := fd.iNumber;
      bf.stMode  := fd.inoLink.iMode;
      bf.stNLink := fd.inoLink.iNLink;
      bf.stUId   := fd.inoLink.iUserId;
      bf.stGId   := fd.inoLink.iGroupId;
      bf.stRDev  := 0;
      bf.stSize  := fd.inoFile.iSize;
      bf.stATime := fd.inoFile.iAccTime;
      bf.stMTime := fd.inoFile.iModTime;
      bf.stCTime := fd.inoFile.iCreTime;
    END;  æwith fd, bfå
    res := makeRes (ok, ok, ok, ok);
  DO
    res := GetException;
 
  sCheck ( faoGate.Open );
 
æ!b!   printVar ('----- end unixFs.FStat -----, res= ', res);   !e!å
  ObjReturn (res);
END;  æFStatå
 
æ$Eå
 
 
OTHERWISE faoOther
  WITH RECORD  t : ^^;  END;
BEGIN
  Exception ( makeRes (Reject * EntryIllegal, Universal, entryA, 0 ));
END;  æotherwiseå
 
 
 
END;  æUnixFaoImplementå
 
 
æ$Eå
 
æ***********************************************************************å
æ*****     I M P L E M E N T   U N I X   F I L E   S Y S T E M     *****å
æ***********************************************************************å
 
PROGRAM unixFilImplement OBJECT UnixFs WITH unixLocals;
 
 
PRIVATE Close
  (IN   fao    : ownSet;
   IN   faoEnv : pFaoLocals
   æoptional IN deleteMode : deleteType  (not used in unixFs)å
  );
 
 
 
 
FUNCTION countOpenFao (daoIndex : integer;
                       curIno   : ino
                      ) : integer;
  VAR
    fileCount : integer;
    res : resultType;
 
æ This function searches through the fao manager set of the disc
  specified by 'daoIndex' counting all faos opened to the file
  specified by 'curIno'
å
 
BEGIN
æ#b#   printVar ('countOpenFaos, curIno = ', curIno);   *e*å
  fileCount := 0;
  res := FirstInSet (faoManÆdaoIndexÅ, nextFao);
  IF res.main = ok THEN
  BEGIN
    REPEAT
      WITH xfd = nextFao^^.pFaoData^^ DO
      BEGIN
æ#b#   printVar ('xfd.iNumber = ', xfd.iNumber);   #e#å
        IF xfd.iNumber = curIno THEN   æ same file å
          fileCount := fileCount + 1;
      END;  æwith xfdå
      res := NextInSet (faoManÆdaoIndexÅ, nextFao);
    UNTIL res.main <> ok;
  END;  æset not emptyå
 
  countOpenFaos := fileCount;
æ#b#   printVar ('fileCount = ', fileCount);   #e#å
END;  æcountOpenFaoså
 
æ$Eå
 
PROCEDURE creatFile  (VAR dao : faoRefType;
                      VAR  fd : faoData;
                      fType   : integer );
  VAR
    i, k: integer;
BEGIN
æ*b*   printVar ('creatFile : iNum = ', fd.iNumber);   *e*å
  WITH fd DO
  BEGIN
    FOR i := 0 TO 12 DO
      blockTabÆiÅ.blockAddr := 0;
    FOR i := 1 TO 3 DO
    BEGIN
      WITH iTabÆiÅ DO BEGIN
        curIb := 0;
        update := false;
        FOR k := 0 TO 127 DO tabÆkÅ := 0;
      END;
      inxÆiÅ := 0;
    END;
    level := 0;
    curPos := 0;
    eofPos := 0;
    allocSize := 0;
 
    æ initialize i-node å
    inoLink.iMode    := fType;   ædirMode/dataModeå
    inoLink.iNLink   := 1;
    inoLink.iUserId  := 0;
    inoLink.iGroupId := 0;
 
    sCheck ( clockRef.GetClock ( ; OUT i));
    inoFile.iCreTime := i;
    inoFile.iAccTime := i;
    inoFile.iModTime := i;
 
    putInoFile (dao, fd);
    putInoLink (dao, fd);
  END;  æwith fdå
æ*b*   printText ('end creatFile ');   *e*å
END;  æcreatFileå
 
 
æ$Eå
 
PROCEDURE InitUnixSys;
  VAR
    d, i : integer;
    res  : resultType;
    size : sizeType;
 
BEGIN
  printText (procId);
 
  sCheck ( schedRef.NewGate (OUT ioGate));
  FOR i := 1 TO  maxDisc DO
    sCheck ( schedRef.NewGate (OUT sbGateÆiÅ));
  sCheck ( ioGate.Lock );
 
  IN
    æ Declare sub-segment of unixData å
    sCheck ( ClearSize (size));
    sCheck ( AddSub (size));
    xCheck ( allocRef.NewObj (OUT pUnixDir ; IN size, OUT i));
    xCheck ( DeclSub (pUnixDir, VAR IN OUT pUnixData^^.dirBlock ));
 
    æ Initialize unixData å
    WITH pUnixData^^ DO
    BEGIN
      lastLocalId := 0;
      FOR i := 1 TO 3 DO
      BEGIN
        faoWork.iTabÆiÅ.curIb := 0;
        faoWork.iTabÆiÅ.update := false;
      END;
      FOR d := 1 TO maxDisc DO
        WITH  devTableÆdÅ DO
        BEGIN
          pathLength := 0;
          FOR i := 1 TO maxIdLength DO
            devNameÆiÅ := CHR(0);
        END;  æfor då
    END;  æwith pUnixDataå
 
    sCheck ( MakeReentrant (void) );
    res := makeRes (ok, ok, ok, ok);
  DO
    res := GetException;
 
æ#b#   printText ('----- end unixFs.InitUnixSys ----- ');   #e#å
END;  æInitUnixSyså
 
 
æ$Eå
 
æ***************************å
æ*****     INCLUDE     *****å
æ***************************å
 
ENTRY Include
  æ ; IN devName, IN rootName  Æ, readOnlyÅ å
  WITH RECORD
    t : ^^;
    discDriver : ioSysRefType;
  END;
 
  VAR
    roPtr : ^ integer;
    readOnly : integer;
    name, freeEntry : fullName;
    accessMode, daoIndex, devClass, i, localId, k, kind,
    minBufSize, pos, sz, used                            : integer;
    driverOpen : boolean;
    res : resultType;
 
BEGIN
æ#b#   printText ('----- unixFs.Include ----- ');   #e#å
 
  WITH ux = pUnixData^^ DO
  BEGIN
 
  IN
    xCheck ( ioGate.Lock );
  DO
    InitUnixSys;
 
  IN
    æ Take optional readOnly parameter å
    IF nextValArg (roPtr) THEN readOnly := roPtr^
                          ELSE readOnly := 0;
 
    æ Initialization å
    k := elements (devName);
    IF k > maxIdLength THEN k := maxIdLength;
    i := 1;
    REPEAT   æremove space/null from devNameå
      IF ORD(devNameÆiÅ) <= ORD(space) THEN k := i-1
                                       ELSE i := i+1;
    UNTIL i > k;
    FOR i := 1   TO k           DO nameÆiÅ := devNameÆiÅ;
    FOR i := k+1 TO maxIdLength DO nameÆiÅ := CHR(0);
    FOR i := 1   TO maxIdLength DO freeEntryÆiÅ := CHR(0);
 
    æ Search deviceName in device table å
æ#b#   printVar ('devName = ', devName);
       printVar ('rootName = ', rootName);   #e#å
    daoIndex := 0;
    FOR i := 1 TO maxDisc DO
    BEGIN
æ+b+   printVar ('devTable.devName = ', ux.devTableÆiÅ.devName);
       printVar ('i = ', i);                                      +e+å
      IF equal (ux.devTableÆiÅ.devName, name) THEN
         Exception ( makeRes (Reject * FileNameExists, IoFamily,
                              dNameArg, volAlreadyIncluded ));
 
      IF daoIndex = 0 THEN
        IF equal (ux.devTableÆiÅ.devName, freeEntry) THEN
        BEGIN
          daoIndex := i;
          ux.devTableÆiÅ.devName := name;
                          æ new entry ok, but rest of devTable must
                            still be searched for existing entry å
        END;  æfirst free entryå
    END;  æfor iå
æ#b#   printVar ('daoIndex= ', daoIndex);   #e#å
 
    IF daoIndex = 0 THEN
      Exception ( makeRes (Reject * NoResources, Universal,
                           entryA, devTabLimit) );
 
  DO
    BEGIN
      sCheck ( ioGate.Open );
      ObjReturn (GetException);
    END;
 
  æ Now an entry in devTable has been found, get driver ref and
    assign disc driver å
 
  IN
    res.main := ok;
    driverOpen := false;
    xCheck ( objDirRef.GetRef (OUT discDriver ;
                               IN devName, OUT used, OUT kind) );
 
    IF readOnly <> 0 THEN accessMode := ReadMode
                     ELSE accessMode := ReadWrite;
    xCheck ( discDriver.Assign (OUT daoÆdaoIndexÅ ;
                                IN devNameÆused+1..kÅ, IN accessMode) );
    driverOpen := true;
 
    IN     æ check device information å
      xCheck ( daoÆdaoIndexÅ.GetFileInf (OUT discDriver ;
                    OUT localId, OUT name, OUT devClass,
                    OUT i æbytesAllocatedå, OUT minBufSize,
                    OUT i æcylSIzeå ));
    DO     æ if getFileInf not implemented then assign ok values å
      BEGIN
        minBufSize := unixPageSize;
        devClass := Disc;
      END;
 
    IF devClass <> Disc THEN
      Exception ( makeRes (Reject * GiveUp, Universal,
                           dNameArg, deviceClass ));
 
    æ critical region for read "super-block" (sector 1)
                          and check UNIX format disc å
    xCheck ( sbGateÆdaoIndexÅ.Lock );
    IN
      WITH sb = ux.devTableÆdaoIndexÅ.super DO
      BEGIN
        xCheck ( daoÆdaoIndexÅ.ReadRandom (VAR IN OUT sb ;
                                           OUT sz, IN superAddr, OUT i ));
æ!b!   printVar ('super block = ', sb);     !e!å
 
        IF (sz           < unixPageSize) OR
           (sb.inodSize  > sb.volSize)   OR
           (sb.freeBlock.nFree > nicFree)      OR
           (sb.nIno      > nicInod)      OR
           (sb.tFreeBl   > sb.volSize)
        THEN
          Exception ( makeRes (Reject * VolumeFormatError, IoFamily,
                               dNameArg, notUnixDisc ));
      END;  æwith sbå
    DO
      res := GetException;
 
    æ leave critical region for super block å
    sCheck ( sbGateÆdaoIndexÅ.Open );
    IF res.main <> ok THEN Exception (res);
 
    æ Initialize devTable å
    WITH  ue = ux.devTableÆdaoIndexÅ DO
    BEGIN
      ue.devReadOnly := readOnly;
      k := elements (rootName);
      WHILE (rootNameÆkÅ = space) OR
            (rootNameÆkÅ = CHR(0)) DO k := k-1;
      ue.pathLength := k;
      FOR i := 1 TO k DO ue.pathNameÆiÅ := rootNameÆiÅ;
      FOR i := k+1 TO maxPathLength DO ue.pathNameÆiÅ := CHR(0);
    END;  æwith ueå
 
    res := makeRes (ok, ok, ok, ok);
  DO
    BEGIN
      æ Free dev table entry å
      ux.devTableÆdaoIndexÅ.devName := freeEntry;
      noCheck ( Dealloc (daoÆdaoIndexÅ, daoÆdaoIndexÅ ));
      res := GetException;
    END;  æerrorå
  END;  æwith uxå
 
  sCheck ( ioGate.Open );
æ#b#   printVar ('----- end unixFs.Include -----, res = ', res);   #e#å
  ObjReturn (res);
END;  æIncludeå
 
 
æ$Eå
 
æ**************************å
æ*****     ASSIGN     *****å
æ**************************å
 
ENTRY Assign
  æ OUT ownedFao ; IN pathName, IN ioMode,
                   optional IN createMode, volume å
  WITH RECORD
    t          : ^^;
    faoEnv     : pFaoLocals;
    tmpFaoMan  : ^^;
    nextFao    : pFaoLocals;
    saveFaoPtr : pFaoLocals;
  END;
 
  VAR
    res        : resultType;
    createMode : createType;
    cmPtr      : ^ createType;
    charNo, daoIndex, foundLength, freePos, i, k, locId,
    nameParts, readOnly                                  : integer;
    size, nullSize, voidSize : sizeType;
    curIno, parentIno   : ino;
    exist, found, last  : boolean;
    fName               : name14;
 
BEGIN
æ#b#   printText ('----- unixFs.Assign ----- ');   #e#å
 
  IN
    xCheck ( ioGate.Lock );
  DO
    InitUnixSys;
  sCheck ( ioGate.Open );
 
  æ Check ioMode parameter å
  IF (ioMode < NoRights) OR (ioMode > ReadWrite) THEN
    Exception (makeRes (Reject * DataValueIllegal, Universal,
                        ioModeArg, illIoMode ));
 
  æ Take optional Create parameter å
  IF NextValArg (cmPtr) THEN createMode := cmPtr^
                        ELSE createMode := NewOrOld;
  IF (createMode < NewOrOld) OR (createMode > NewFile) THEN
    Exception ( makeRes (Reject * DataValueIllegal, Universal,
                         createArg, illCreateMode ));
 
  æ Create fao and fao data å
  nullSize.user := 0;  nullSize.kernel := 0;
  voidSize.user := -1; voidSize.kernel := -1;
  sCheck ( ClearSize (size));
  sCheck ( AddGen (size, refs(faoLocals)));
  sCheck ( AddEmbSeg (size, bytes(faoData)));
  rCheck ( allocRef.NewObj (OUT ownedFao ; IN size, OUT i));
 
  rCheck ( DeclGen ( ownedFao, tmpFaoMan, faoEnv, refs(faoLocals), Close,
                     nullSize, voidSize, refs(unixFaoImplement),
                     bytes(unixFaoImplement), 0 ænoControlå,
                     addr(unixFaoImplement), nullSize, true));
 
  æ create fao data segment å
  rCheck ( NewSeg (faoEnv^^.pFaoData, bytes(faoData)));
  rCheck ( copy (egoEnv, faoEnv^^.unixEnv));
 
  æ create fao gate å
  rCheck ( schedRef.NewGate ( OUT faoEnv^^.faoGate ));
 
  æ Enter critical region before start search devTable å
  sCheck ( ioGate.Lock );
 
  IN
    WITH ux = pUnixData^^ DO
    BEGIN
      daoIndex := searchDevTab (ux, pathName, æoutå charNo);
      IF daoIndex = 0 THEN
        Exception (makeRes (Reject * FileNotFound, IoFamily,
                            fNameArg, volNotIncluded ));
      readOnly := ux.devTableÆdaoIndexÅ.devReadOnly;
    END;  æwith uxå
 
    æ Now the proper dao is found (daoIndex). Search rest of fileName
      through the directory hierarchy on this disc
    å
 
    nameParts := checkFileName (pathName, charNo);
    IF nameParts < 0 THEN
      Exception ( makeRes (Reject * DataValueIllegal, Universal,
                           fNameArg, nameFormat ));
 
    WITH fd = faoEnv^^.pFaoData^^ DO
    BEGIN
      æ Initialize new fd å
      fd.daoIndex := daoIndex;
      fd.devReadOnly := readOnly;
      FOR i := 1 TO 3 DO
      BEGIN
        FOR k := 0 TO 127 DO fd.iTabÆiÅ.tabÆkÅ := 0;
        fd.iTabÆiÅ.curIb := 0;
        fd.iTabÆiÅ.update := false;
      END;
 
      curIno := rootIno;  æi-number for root directory of discå
      IF readOnly = 1 THEN fd.rwMode := ReadMode
                      ELSE fd.rwMode := ReadWrite;
      getInoLink (daoÆdaoIndexÅ, fd, curIno);
      getInoFile (daoÆdaoIndexÅ, fd, curIno);
 
      IF nameParts = 0 THEN
        exist := true      æwanted file = root dirå
      ELSE
      BEGIN
        exist := false;
        REPEAT
          last := getNamePart (pathName, æoutå fName, æin outå charNo);
          nameParts := nameParts - 1;
          curIno := searchDirectory (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ,
                                     pUnixData, pUnixDir, fd,fName, freePos);
 
          IF curIno > 0 THEN
          BEGIN
            IF last THEN exist := true;
            getInoLink (daoÆdaoIndexÅ, fd, curIno);
            getInoFile (daoÆdaoIndexÅ, fd, curIno);
          END  æfoundå
          ELSE
 
          BEGIN    æfile not found: if last then create new i-node and fileå
            IF (createMode = OldFile) OR
               (ioMode = ReadMode)    THEN
              Exception ( makeRes (Reject * FileNotFound, IoFamily,
                                   createArg, charNo-2 ));
            IF NOT last THEN
              Exception ( makeRes (Reject * DataValueIllegal, Universal,
                                   fNameArg, dirUnknown ));
 
            parentIno := fd.iNumber;
            curIno := getFreeInum (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ,
                                   pUnixData, fd);
            IF curIno = 0 THEN
              Exception ( makeRes (Reject * NoVolumeSpace, IoFamily,
                                   fNameArg, noInode ));
 
            æ Initialize file and new i-node å
            fd.iNumber := curIno;
            creatFile (daoÆdaoIndexÅ, fd, dataMode);
 
            æ Insert new i-node in current directory å
            newDirEntry (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ, pUnixData, pUnixDir,
                         fd, parentIno, freePos, curIno, fName);
 
            æget iNode of new fileå
            getInoLink (daoÆdaoIndexÅ, fd, curIno);
            getInoFile (daoÆdaoIndexÅ, fd, curIno);
          END;  æfile not foundå
        UNTIL last;
      END;  ænameParts > 0å
 
      IF exist THEN
      BEGIN     æcheck create mode and file typeå
        IF createMode = NewFile Then
          Exception ( makeRes (Reject * FileNameExists, IoFamily,
                               createArg, 0 ));
        IF (fileType (fd.inoLink.iMode) = dirType) AND
           (ioMode > ReadMode) THEN
        ioMode := ioMode - WriteMode;
                      æ user not allowed to write in directory å
      END;  æexistå
    END;  æwith fdå
 
    æ Now the wanted file has been found (curIno). Check file reservation
      by searching all other faos connected to same disc å
æ#b#   printVar ('search other faos, own i-number = ', curIno);   #e#å
    found := false;
    res := FirstInSet (faoManÆdaoIndexÅ, nextFao);
    IF res.main = ok THEN
    BEGIN
      REPEAT
        WITH fd = nextFao^^.pFaoData^^ DO
        BEGIN
æ#b#   printVar ('fd.iNumber = ', fd.iNumber);   #e#å
          IF fd.iNumber = curIno THEN
          BEGIN     æsame fileå
            IF (ioMode > ReadMode) OR
               (fd.rwMode > ReadMode) THEN
                  Exception ( makeRes (Reject * RightsOccupied, IoFamily,
                                       ioModeArg, 0 ));
            IF NOT found THEN
            BEGIN    æfirst same, get localIdå
              found := true;
              locId := fd.localId;
            END;  æfirst foundå
          END;  æsame fileå
        END;  æwith fdå
        res := NextInSet (faoManÆdaoIndexÅ, nextFao);
æ#b#   printVar ('nextInSet, res = ', res);   #e#å
      UNTIL res.main <> ok;
    END;  æfaoMan set not emptyå
 
    æ Assign fd å
    WITH fd = faoEnv^^.pFaoData^^ DO
    BEGIN
      IF found THEN
        fd.localId := locId
      ELSE
      BEGIN    æfile not used before, create new localId and new file chainå
        WITH ux = pUnixData^^ DO
        BEGIN
          ux.lastLocalId := ux.lastLocalId + 1;
          fd.localId := ux.lastLocalId;
        END;  æwithå
      END;  ænot foundå
 
      fd.rwMode      := ioMode;
      fd.terminated  := false;
      fd.readMark    := false;
      fd.writeMark   := false;
 
æ#b#   printVar ('new fd = ', fd);   #e#å
    END;  æwith fdå
 
    æ Reservation ok, move new fao manager and insert pointers in fao å
    sCheck( MoveMan (tmpFaoMan, faoManÆdaoIndexÅ ));
    sCheck( Copy (code,             faoEnv^^.code ));
    sCheck( Copy (daoÆdaoIndexÅ,    faoEnv^^.daoRef ));
    sCheck( Copy (sbGateÆdaoIndexÅ, faoEnv^^.sbGateRef ));
 
    sCheck( MakeReentrant (faoEnv));
 
    res := makeRes (ok, ok, ok, ok);
  DO
    BEGIN
      æ Some error during Assign, undo everything å
      noCheck ( DelEnv (ownedFao, tmpFaoMan ));
      noCheck ( Dealloc (ownedFao, ownedFao ));
      res := GetException;
    END;  æerrorå
 
  æ Leave critical region å
  sCheck ( ioGate.Open );
æ#b#   printVar ('----- end unixFs.Assign -----, res = ', res);   #e#å
 
  ObjReturn (res);
END;  æAssignå
 
 
 
æ$Eå
 
æ*******************************å
æ*****     CREATE LINK     *****å
æ*******************************å
 
ENTRY CreateLink
  æ ; IN pathName, IN newPathName å
  WITH RECORD
    t : ^^;
  END;
 
  VAR
    res       : resultType;
    last      : boolean;
    charNo1, charNo2, daoIndex, freePos, ft, i, k, p1, p2 : integer;
    curIno, dirIno, foundIno : ino;
    saveInoLink : iLnk;
    fName     : name14;
 
BEGIN
æ#b#   printText ('----- unixFs.CreateLink ----- ');
       printVar ('pathName = ', pathName);
       printVar ('newPathName = ', newPathName);       #e#å
 
  IN
    xCheck ( ioGate.Lock );
  DO
    InitUnixSys;
 
  IN
    WITH ux = pUnixData^^ DO
    BEGIN
      daoIndex := searchDevTab (ux, pathName, æoutå charNo1);
      i        := searchDevTab (ux, newPathName, æoutå charNo2);
      IF DaoIndex <> i THEN
        Exception ( makeRes (Reject * DataValueIllegal, Universal,
                             newFileArg, notSameDevice ));
      IF daoIndex = 0 THEN
        Exception ( makeRes (Reject * FileNotFound, IoFamily,
                             fNameArg, volNotIncluded ));
      IF ux.devTableÆdaoIndexÅ.devReadOnly = 1 THEN
        Exception ( makeRes (Reject * EntryIllegal, Universal,
                             entryA, noWriteReservation ));
 
      p1 := checkFileName (pathName, charNo1);
      IF p1 < 0 THEN  æsyntax errorå
        Exception ( makeRes (Reject * DataValueIllegal, Universal,
                             fNameArg, nameFormat ));
 
      p2 := checkFileName (newPathName, charNo2);
      IF p2 <= 0 THEN  æ<0 means syntax error, =0 means root dirå
        Exception ( makeRes (Reject * DataValueIllegal, Universal,
                             newFileArg, nameFormat ));
 
      WITH fd = ux.faoWork DO
      BEGIN
        æ find file pointed to by 'pathName' å
        fd.daoIndex := daoIndex;
        curIno := rootIno;
        fd.rwmode := ReadWrite;
        fd.devReadOnly := 0;
        FOR i := 1 TO 3 DO
          WITH fd.iTabÆiÅ DO
          BEGIN
            FOR k := 0 TO 127 DO tabÆkÅ := 0;
            curIb := 0;
            update := false;
          END;
 
        getInoLink (daoÆdaoIndexÅ, fd, curIno);
        getInoFile (daoÆdaoIndexÅ, fd, curIno);
 
        IF p1 > 0 THEN
        REPEAT
          last := getNamePart (pathName, æoutå fName, æin outå charNo1);
          curIno := searchDirectory (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ,
                                     pUnixData, pUnixDir, fd, fName, i);
          IF curIno = 0 THEN
            Exception ( makeRes (Reject * FileNotFound, IoFamily,
                                 fNameArg, charNo1-2 ));
          getInoLink (daoÆdaoIndexÅ, fd, curIno);
          getInoFile (daoÆdaoIndexÅ, fd, curIno);
        UNTIL last;
        foundIno := curIno;
        saveInoLink := fd.inoLink;
 
        æ search lowest directory pointed to by 'newPathName' å
        curIno := rootIno;
        getInoLink (daoÆdaoIndexÅ, fd, curIno);
        getInoFile (daoÆdaoIndexÅ, fd, curIno);
 
        REPEAT
          last := getNamePart (newPathName, fName, charNo2);
          IF last THEN dirIno := curIno;
          curIno := searchDirectory (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ,
                                     pUnixData, pUnixDir, fd, fName, freePos);
          IF last THEN
          BEGIN
            IF curIno > 0 THEN
              Exception ( makeRes (Reject * FileNameExists, IoFamily,
                                   newFileArg, 0 ));
          END
          ELSE
          BEGIN
            IF curIno = 0 THEN
              Exception ( makeRes (Reject * DataValueIllegal, Universal,
                                   newFileArg, dirUnknown ));
            getInoLink (daoÆdaoIndexÅ, fd, curIno);
            getInoFile (daoÆdaoIndexÅ, fd, curIno);
          END;  ænot lastå
        UNTIL last;
 
        æ Now foundIno         = i-number of actual file
              fName            = name     -    -     -
              saveInoLink      = i-node   -    -     -
              dirIno           = i-number of directory in which file is to
                                 be inserted
              freePos          = position of first free entry in dirIno
              fd.inoLink/File  = i-node of directory file
        å
æ#b#   printVar ('foundIno   = ', foundIno);
       printVar ('fName      = ', fName);
       printVar ('saveInoLink= ', saveInoLink);
       printVar ('dirIno     = ', dirIno);
       printVar ('freePos    = ', freePos);
       printVar ('fd.InoLink = ', fd.InoLink);
       printVar ('fd.InoFile = ', fd.InoFile);   #e#å
 
        ft := fileType (fd.inoLink.iMode);
        IF ft <> dirType THEN
        BEGIN
          æ change fileType into directory type, but only if empty data fileå
          IF (ft <> dataType) OR
             (fd.eofPos <> 0) THEN
               Exception ( makeRes (Reject * DataValueIllegal, Universal,
                                    newFileArg, notDirFile ));
          ft := (fd.inoLink.iMode + p16) MOD p12 + dirType;
          IF dirIno = foundIno THEN
            saveInoLink.iMode := ft
          ELSE
          BEGIN
            fd.inoLink.iMode := ft;
            fd.iNumber := dirIno;
            putInoLink (daoÆdaoIndexÅ, fd);
          END;
        END;
 
        æ Increase link count in file's i-node å
        saveInoLink.iNLink := saveInoLink.iNLink + 1;
 
        æ Rewrite file's i-node å
        fd.iNumber := foundIno;
        fd.inoLink := saveInoLink;
        putInoLink (daoÆdaoIndexÅ, fd);
 
        newDirEntry (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ, pUnixData, pUnixDir,
                     fd, dirIno, freePos, foundIno, fName);
 
      END;  æwith fdå
    END;  æwith uxå
 
    res := makeRes (ok, ok, ok, ok);
  DO
    res := GetException;
 
  æ Leave critical region å
  sCheck ( ioGate.Open );
æ#b#   printVar ('----- end unixFs.CreateLink -----, res = ', res);   #e#å
  ObjReturn (res);
END;  æ CreateLink å
 
 
 
æ$Eå
 
æ*******************************å
æ*****     DELETE LINK     *****å
æ*******************************å
 
ENTRY DeleteLink
  æ ; IN pathName å
  WITH RECORD
    t : ^^;
    dummyGate : ^^;
  END;
 
  VAR
    res            : resultType;
    found, last    : boolean;
    curIno, dirIno : ino;
    charNo, daoIndex, dirPos, fileCount, p : integer;
    fName          : name14;
 
BEGIN
æ#b#   printText ('----- unixFs.DeleteLink ----- ');   #e#å
 
  IN
    xCheck( ioGate.Lock );
  DO
    InitUnixSys;
 
  IN
    WITH ux = pUnixData^^ DO
    BEGIN
      daoIndex := searchDevTab (ux, pathName, æoutå charno);
      IF daoIndex = 0 THEN
        Exception (makeRes (Reject * FileNotFound, IoFamily,
                            fNameArg, volNotIncluded ));
      IF ux.devTableÆdaoIndexÅ.devReadOnly = 1 THEN
        Exception (makeRes (Reject * EntryIllegal, Universal,
                            entryA, noWriteReservation ));
 
      p := checkFileName (pathName, charNo);
      IF p <= 0 THEN        æsyntax error or root directoryå
        Exception ( makeRes (Reject * DataValueIllegal, Universal,
                             fNameArg, nameFormat ));
 
      WITH fd = ux.faoWork DO
      BEGIN
        æ Search file through directory hierarchy å
        curIno := rootIno;
        fd.daoIndex := daoIndex;
        fd.rwMode := ReadWrite;
        fd.devReadOnly := 0;
        getInoLink (daoÆdaoIndexÅ, fd, curIno);
        getInoFile (daoÆdaoIndexÅ, fd, curIno);
        REPEAT
          last := getNamePart (pathName, æoutå fName, æin outå charNo);
          IF last THEN dirIno := curIno;
          curIno := searchDirectory (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ,
                                     pUnixData, pUnixDir, fd, fName, dirPos );
          IF curIno = 0 THEN
            Exception ( makeRes (Reject * FileNotFound, IoFamily,
                                 fNameArg, charNo-2 ));
          getInoLink (daoÆdaoIndexÅ, fd, curIno);
          getInoFile (daoÆdaoIndexÅ, fd, curIno);
        UNTIL last;
 
        æ Now curIno         = i-number of actual file
              fd.inoLink/File= i-node   -    -     -
              dirIno         = i-number of directory file from which file
                               is to be removed
              dirPos         = position of dir entry in directory file
        å
æ#b#   printVar ('curIno    = ', curIno);
       printVar ('fd.inoLink= ', fd.inoLink);
       printVar ('fd.inoFile= ', fd.inoFile);
       printVar ('dirIno    = ', dirIno);
       printVar ('dirPos    = ', dirPos);
       printVar ('no of links = ', fd.inoLink.iNLink);   #e#å
 
        IF fd.inoLink.iNLink > 1 THEN
        BEGIN    æ not last permanent link,
                   remove directory entry, and decrease link count å
          remDirEntry (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ, pUnixData,
                       pUnixDir, fd, dirIno, dirPos);
          fd.iNumber := curIno;
          fd.inoLink.iNLink := fd.inoLink.iNLink - 1;
          putInoLink (daoÆdaoIndexÅ, fd);
        END  æ >1 å
        ELSE
        IF fd.inoLink.iNLink = 1 THEN
        BEGIN    æ last permanent link. If file not used by any fao,
                   the file is deleted. å
          IF fileType (fd.inoLink.iMode) = dirType THEN
          BEGIN    æ a dir file is only removed if empty å
            IF countDirEntries (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ,
                                pUnixData, pUnixDir, fd) > 0
            THEN
              Exception ( makeRes (Reject * EntryIllegal, Universal,
                                   entryA, dirNotEmpty ));
          END;  ædirTypeå
 
          æ Remove directory entry å
          remDirEntry (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ, pUnixData,
                       pUnixDir, fd, dirIno, dirPos);
 
          æ Search fao list for number of open faos connected to this file å
          fileCount := countOpenFaos (daoIndex, curIno);
          IF fileCount = 0 THEN
          BEGIN
            getInoFile (daoÆdaoIndexÅ, fd, curIno);
            removeFile (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ,
                        dummyGate, pUnixData, fd);
          END;  æfileCount = 0å
                æotherwise deletion is postponed until last fao deallocatedå
        END;  æiNLink = 1å
      END;  æwith fdå
    END;  æwith uxå
    res := makeRes (ok, ok, ok, ok);
  DO
    res := GetException;
 
  æ Leave critical region å
  sCheck ( ioGate.Open );
 
æ#b#   printVar ('----- end unixFs.DeleteLink -----, res = ', res);   #e#å
  ObjReturn (res);
END;  æDeleteLinkå
 
 
 
æ$Eå
 
æ*********************************å
æ*****     TERMINATE FAO     *****å
æ*********************************å
 
PROCEDURE terminateFao (VAR faoEnv    : pFaoLocals;
                        VAR dummyGate : pGateType;
                        VAR fd        : faoData    );
  VAR
    daoIndex, date, i, ix : integer;
    res : resultType;
 
BEGIN
æ#b#   printVar ('-- terminate fao --, iNum = ', fd.iNumber);   #e#å
 
  sCheck ( faoEnv^^.faoGate.Lock );
 
  æ Prevent other users from accessing the fao during terminate å
  sCheck ( Abort (faoEnv));
 
  daoIndex := fd.daoIndex;
  getInoLink (daoÆdaoIndexÅ, fd, fd.iNumber);
 
  æ Check link-count in the file's i-node å
æ#b#   printVar ('link-count = ', fd.inoLink.iNLink);          #e#å
  IF fd.inoLink.iNLink = 0 THEN
  BEGIN
    æ temporary file;  delete if no other fao open to same file å
    i := countOpenFaos (daoIndex, fd.iNumber);
    IF i = 0 THEN
    BEGIN
      getInoFile (daoÆdaoIndexÅ, fd, fd.iNumber);
      removeFile (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ,
                  dummyGate, pUnixData, fd);
    END;
 
  END   ætemporaryå
  ELSE
  BEGIN
    æ >0, permanent file; rewrite any updated indirect blocks
      and the i-node å
    IF fd.devReadOnly = 0 THEN
    BEGIN
      getInoFile (daoÆdaoIndexÅ, fd, fd.iNumber);
      FOR ix := 1 TO 3 DO
      BEGIN
        IF fd.iTabÆixÅ.update THEN
        BEGIN
æ#b#   printVar ('indirect table index = ', ix);
       printVar ('indirect table put = ', fd.iTabÆixÅ);   #e#å
          i := putOneBlock (daoÆdaoIndexÅ, sbGateÆdaoIndexÅ,
                            pUnixData, fd.iTabÆixÅ.tab, fd,
                            1, unixPageSize, fd.iTabÆixÅ.curIb, 0);
        END;  æifå
      END;  æfor ixå
 
      æ Insert read/write date in i-node å
      sCheck ( clockRef.GetClock ( ; OUT date) );
      IF fd.readMark  THEN fd.inoFile.iAccTime := date;
      IF fd.writeMark THEN fd.inoFile.iModTime := date;
      putInoFile (daoÆdaoIndexÅ, fd);
    END;  ænot devReadOnlyå
æ#b#   printVar ('last fd = ', fd);   #e#å
  END;  æpermanent fileå
 
  æ put super block å
  IF fd.devReadOnly = 0 THEN
  BEGIN
    æ critical region for update super block å
    res.main := ok;
    xCheck ( sbGateÆdaoIndexÅ.Lock );
    IN
      WITH ux = pUnixData^^, sb = ux.devTableÆdaoIndexÅ.super DO
      BEGIN
        xCheck (daoÆdaoIndexÅ.WriteRandom (
                      VAR IN OUT pUnixData^^.devTableÆdaoIndexÅ.super ;
                      OUT i, IN superAddr, OUT i ));
æ!b!   printVar ('super block = ', sb);    !e!å
      END;  æwith sbå
    DO
      BEGIN
        res := GetException;
        IF res.main < 0 THEN res.main := -res.main;
      END;
 
    æ leave critical region å
    sCheck ( sbGateÆdaoIndexÅ.Open );
    IF res.main <> ok THEN Exception (res);
  END;  ænot devReadOnlyå
 
  æ remove faoGate å
  sCheck ( faoEnv^^.faoGate.Open );
 
æ#b#   printText ('-- end terminate fao -- ');   #e#å
END;  æterminateFaoå
 
æ$Eå
 
æ*************************å
æ*****     CLOSE     *****å
æ*************************å
 
PRIVATE Close
  æ fao, faoEnv å
  WITH RECORD
    t : ^^;
    dummyGate  : pGateType;
  END;
 
  VAR
    res      : resultType;
 
BEGIN
æ#b#   printText ('----- unixFs.Close ----- ');   #e#å
 
  sCheck ( ioGate.Lock );
 
  IN
    WITH fd = faoEnv^^.pFaoData^^ DO
    BEGIN
      IF fd.terminated THEN  ænothingå
      ELSE
      BEGIN
        terminateFao (faoEnv, dummyGate, fd);
        fd.terminated := true;
      END;
 
      sCheck ( DelEnv (fao, faoManÆfd.daoIndexÅ ));
      noCheck ( Dealloc (fao, fao ));
    END;  æwith fdå
 
    res := makeRes (ok, ok, ok, ok);
  DO
    res := GetException;
 
  sCheck ( ioGate.Open );
æ#b#   printText ('----- end unixFs.Close ----- ');   #e#å
  ObjReturn (res);
END;  æCloseå
 
æ$Eå
 
æ***************************å
æ*****     EXCLUDE     *****å
æ***************************å
 
ENTRY Exclude
  æ ; IN deviceName Æ, abortAllowedÅ å
  WITH RECORD
    t : ^^;
    dummyGate : pGateType;
    tmpDao : faoRefType;
    tmpMan : ^^;
    tmpEnv : ^^;
    faoEnv : pFaoLocals;
  END;
 
  VAR
    daoIndex, i, k : integer;
    aaPtr          : ^integer;
    abortAllowed   : integer;
    res   : resultType;
    name  : fullName;
 
BEGIN
æ#b#   printText ('----- unixFs.Exclude ----- ');   #e#å
 
  IN
    xCheck ( ioGate.Lock );
  DO
    InitUnixSys;
 
  IN
    æ Take optional abortAllowed parameter å
    IF NextValArg (aaPtr)
      THEN  abortAllowed := aaPtr^
      ELSE  abortAllowed := 0;
 
    k := elements (devName);
    IF k > maxIdLength THEN k := maxIdLength;
    i := 1;
    REPEAT   æremove space/null from devNameå
      IF ORD(devNameÆiÅ) <= ORD(space) THEN k := i-1
                                       ELSE i := i+1;
    UNTIL i > k;
    FOR i := 1   TO k           DO nameÆiÅ := devNameÆiÅ;
    FOR i := k+1 TO maxIdLength DO nameÆiÅ := CHR(0);
 
    æ Search devName in device table å
    WITH ux = pUnixData^^ DO
    BEGIN
      daoIndex := 0;
      FOR i := 1 TO maxDisc DO
      BEGIN
        IF equal (ux.devTableÆiÅ.devName, name) THEN
          daoIndex := i;
      END;  æforå
æ#b#   printVar ('daoIndex = ', daoIndex);   #e#å
    END;  æwith uxå
 
    IF daoIndex = 0 THEN
      Exception (makeRes (Reject * FileNotFound, IoFamily,
                          entryA, volNotIncluded ));
 
    æ Now the device name has been found in device table, check
      if any open fao å
    res := FirstInSet (faoManÆdaoIndexÅ, faoEnv);
    IF res.main = ok THEN
    BEGIN
      IF abortAllowed = 0 THEN
        Exception (makeRes (Reject * RightsOccupied, IoFamily,
                            entryA, filesOpen ));
      REPEAT
        WITH fd = faoEnv^^.pFaoData^^ DO
        BEGIN
          IF fd.terminated THEN  ænothingå
          ELSE
          BEGIN
            terminateFao (faoEnv, dummyGate, fd);
            fd.terminated := true;
          END;
        END;  æwith fdå
 
        sCheck ( Copy (faoEnv, tmpEnv));
        res := NextInSet (faoManÆdaoIndexÅ, faoEnv);
        sCheck ( MoveMan (tmpEnv, tmpMan));  æremove from manSetå
      UNTIL res.main <> ok;
    END;  æsome open faoå
 
    æ Move owner to check if dealloc disc driver is possible å
    sCheck ( MoveOwn (daoÆdaoIndexÅ, tmpDao ));
 
    noCheck( Dealloc (tmpDao, tmpDao ));
 
    WITH ux = pUnixData^^ DO
    BEGIN
      WITH ux.devTableÆdaoIndexÅ DO
      BEGIN
        FOR i := 1 TO maxIdLength DO
          devNameÆiÅ := CHR(0);
        pathLength := 0;
      END;  æwithå
    END;  æwith uxå
 
    res := makeRes (ok, ok, ok, ok);
  DO
    res := GetException;
 
  sCheck (ioGate.Open);
æ#b#   printVar ('----- end unixFs.Exclude -----, res = ', res);   #e#å
  ObjReturn (res);
END;  æExcludeå
 
æ$Eå
 
æ*****************************å
æ*****     OTHERWISE     *****å
æ*****************************å
 
OTHERWISE unixOther
  WITH RECORD  t : ^^;  END;
BEGIN
  Exception ( makeRes (Reject * EntryIllegal, Universal,
                       entryA, 0 ));
END;
 
 
END;  æunixFilImplementå
 
 
æ$Eå
 
INITIALIZE
  unixFilImplement 'unixfs' :
    allocRef 'allocate' ,
    schedRef 'scheduler' ,
    objDirRef 'objDir' ,
    clockRef 'clock' ,
    egoEnv  '**' ,
    pUnixData
 
 
END.
 
 
 
 
«eof»