/*****************************************************************************/
/* File name: Server.java                                                    */
/* Purpose: to provide NFS service                                           */
/*                                                                           */
/* Last modified: 15.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*                                                                           */
/* This program is distributed in the hope that it will be useful, but       */
/* WITHOUT ANY WARRANTY; without even the implied warranty of                */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General */
/* Public License for more details.                                          */
/*                                                                           */
/* This software is distributed under the terms                              */
/* of the GNU General Public License as published                            */
/* by the Free Software Foundation; either version 2, or (at your option)    */
/* any later version.                                                        */
/* See file COPYING for your rights (GNU GPL)                                */
/*****************************************************************************/

package nfs;


import java.net.*;
import java.io.*;
import io.*;
import rpc.*;
import mount.*;


public
class

Server
        extends rpc.RegistrationServer
                                        implements Runnable
{

  private final static String RCSID =
"$Header: /d/NFS/nfs/RCS/Server.java,v 1.29 1999/08/15 23:39:05 vklimov Exp $";

  protected Thread mountServerThread = null;
  protected mount.Registry registry = null;
  public mount.Registry exportedFileSystemsRegistry = null;
  protected Thread currentThread = null;


  public

  Server( java.lang.String propertiesFilePath,
          java.lang.String exportsFilePath     )

                                                throws  SocketException
/*****************************************************************************/
/* Function name: Server                                                     */
/* File name: Server.java                                                    */
/* Purpose: constructor for the Server class                                 */
/* Throws: SocketException                                                   */
/* Parameters: propertiesFilePath                                            */
/*             exportsFilePath                                               */
/* Returns:                                                                  */
/* Last modified: 15.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      super( (int) Constants.NFS_PORT,
             propertiesFilePath         );

      FileReader exportsFileReader = null;
      Exporter exporter = null;

                                                           /* Server */
/*---------------------------------------------------------------------------*/

      try
        {
         exportsFileReader = new FileReader( exportsFilePath );
        }
      catch( FileNotFoundException e )
        {
          return;                                           /* return ! */
        }
    /*end of try-catch*/

      exportedFileSystemsRegistry = new
        mount.Registry( false,
                        cacheDir );                         /* do not save */

      exporter = new Exporter( exportsFileReader,
                               exportedFileSystemsRegistry,
                               cacheDir                      );
      exporter.run();

    }
/*end of Server*/


  public
  void

  sendNFSErrorReply( 
    ParserResponderReturnArguments
      parserResponderReturnArguments,
    long nfsErrorNumber               )
/*****************************************************************************/
/* Function name: sendNFSErrorReply                                          */
/* File name: Server.java                                                    */
/* Purpose: to send an error message to NFS client                           */
/* Parameters: parserResponderReturnArguments                                */
/*             nfsErrorNumber                                                */
/* Returns:                                                                  */
/* Last modified: 30.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      byte[] ibuf = null;
      InetAddress ipAddressWhereToSend = null;
      int portToSendTo = 0;
      DatagramPacket udpPacket = null;
      Message rpcMessage = null;
                                                  /* sendNFSErrorReply */
/*---------------------------------------------------------------------------*/

      rpcMessage =
        createRPCMessageAndPutSuccessPrefix(
          Constants.
            NFSPROC_ERR_UDP_PACKET_DATA_LENGTH,
          parserResponderReturnArguments       );

      ibuf = rpcMessage.getData();

      rpcMessage.
        putUnsignedInteger( nfsErrorNumber );

      ipAddressWhereToSend =
        parserResponderReturnArguments.
          inRPCMessage.getInetAddress();
      portToSendTo =
        parserResponderReturnArguments.
          inRPCMessage.getPort();

      udpPacket = new
        DatagramPacket( ibuf, ibuf.length,
                        ipAddressWhereToSend,
                        portToSendTo          );

      try
        {
          udpSocket.send( udpPacket,
                          parserResponderReturnArguments.transactionId,
                          "sendNFSErrorReply"                           );
        }
      catch( IOException e )
        {
          System.out.println(
            "sendNFSErrorReply: " +
            "IOException in sending reply\n" +
            e.getMessage()                              );
        }
    /*end of try-catch*/

    }
/*end of sendNFSErrorReply*/


  public
  void

  putDirEntriesIntoRPCMessage(
    long maxNumberOfBytesToSendBack,
    long nStartingEntry,
    mount.Fhandle[] entryFhandles,
    Message rpcMessage               )
/*****************************************************************************/
/* Function name: putDirEntriesIntoRPCMessage                                */
/* File name: Server.java                                                    */
/* Purpose: to put directory entries into the message                        */
/* Parameters: maxNumberOfBytesToSendBack                                    */
/*             nStartingEntry                                                */
/*             entryFhandles                                                 */
/*             rpcMessage                                                    */
/* Returns:                                                                  */
/* Last modified: 03.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      int nBytesNeededToPutThisStringIntoRPCMessage;
      int currentEntryLength;
      long i;
      long fileId;
      String name;
      boolean continueToPutEntries = true;
                                              /* putDirEntriesIntoRPCMessage */
/*---------------------------------------------------------------------------*/

      for (i = nStartingEntry;
            ( continueToPutEntries &&
              (i < entryFhandles.length)); i++)
        {
          nBytesNeededToPutThisStringIntoRPCMessage =
            rpcMessage.
              lengthXDRString(
                entryFhandles[ (int) i ].getName() );
          nBytesNeededToPutThisStringIntoRPCMessage +=
            ( 3 *                                      /* TRUE, fileId and */
            rpc.Constants.XDR_UNSIGNED_INT_LENGTH );   /* cookie           */
          currentEntryLength =
            nBytesNeededToPutThisStringIntoRPCMessage;
          nBytesNeededToPutThisStringIntoRPCMessage +=
            ( 2 *                                      /* possible end      */
            rpc.Constants.XDR_UNSIGNED_INT_LENGTH );   /* of the entry list */
                                                       /* and eof flag      */

          if (nBytesNeededToPutThisStringIntoRPCMessage >
              maxNumberOfBytesToSendBack                  )
            continueToPutEntries = false;
          else
            {
              fileId = entryFhandles[ (int) i ].getFileId();
              name = entryFhandles[ (int) i ].getName();

              rpcMessage.
                putUnsignedInteger( xdr.Constants.TRUE );
              rpcMessage.
                putUnsignedInteger( fileId );
              rpcMessage.
                putString( name,
                           Constants.MAXNAMLEN );
              rpcMessage.
                putUnsignedInteger( i ); /* cookie */
              maxNumberOfBytesToSendBack -=
                currentEntryLength;
            }
        /*end of if on maxNumberOfBytesToSendBack*/
        }
    /*end of for-loop*/

      rpcMessage.                               /* end of entries list */
        putUnsignedInteger(
          xdr.Constants.FALSE );

      if (continueToPutEntries)                        /* eof */
        rpcMessage.
          putUnsignedInteger(
            xdr.Constants.TRUE );
      else
        rpcMessage.
          putUnsignedInteger(
            xdr.Constants.FALSE );
    /*end of if*/

    }
/*end of putDirEntriesIntoRPCMessage*/


  public
  boolean

  validateFileHandleAndSendErrorMessagesIfNeeded(
    mount.Fhandle fileHandle,
    ParserResponderReturnArguments
      parserResponderReturnArguments             )
/*****************************************************************************/
/* Function name: validateFileHandleAndSendErrorMessagesIfNeeded             */
/* File name: Server.java                                                    */
/* Purpose: to validate file handle, send an error message if needed         */
/* Parameters: fileHandle                                                    */
/*             parserResponderReturnArguments                                */
/* Returns: true - if the file handle is OK, false - otherwise               */
/* Last modified: 08.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      boolean filableInstanceSet;

                         /* validateFileHandleAndSendErrorMessagesIfNeeded */
/*---------------------------------------------------------------------------*/

      if (fileHandle == null)
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_STALE           );
          return false;                                       /* return ! */
        }
      else
        {
        }
    /*end of if*/

      filableInstanceSet =
        fileHandle.setFilableInstance();

      if ( ! filableInstanceSet )
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_NXIO           );
          return false;                                       /* return ! */
        }
      else
        {

           FileSystemExpert.setFileHandle( fileHandle );

        }
    /*end of if*/

      if ( ! fileHandle.exists() )
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_NOENT           );
          return false;                                       /* return ! */
        }
      else
        ;
    /*end of if*/

      if ( ! fileHandle.canRead() )
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_ACCES          );
          return false;                                       /* return ! */
        }
      else
        ;
    /*end of if*/

      return true;

    }
/*end of validateFileHandleAndSendErrorMessagesIfNeeded*/


  public
  boolean

  validateDirectoryFileHandleAndSendErrorMessagesIfNeeded(
    mount.Fhandle directoryFhandle,
    ParserResponderReturnArguments
      parserResponderReturnArguments                        )
/*****************************************************************************/
/* Function name: validateDirectoryFileHandleAndSendErrorMessagesIfNeeded    */
/* File name: Server.java                                                    */
/* Purpose: to validate directory file handle                                */
/*          send an error message if needed                                  */
/* Parameters: directoryFhandle                                              */
/*             parserResponderReturnArguments                                */
/* Returns: true - if the directory file handle is OK, false - otherwise     */
/* Last modified: 25.06.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      boolean generallyValid;

               /* validateDirectoryFileHandleAndSendErrorMessagesIfNeeded */
/*---------------------------------------------------------------------------*/

      generallyValid =
        validateFileHandleAndSendErrorMessagesIfNeeded(
          directoryFhandle,
          parserResponderReturnArguments                );

      if ( ! generallyValid )
        return false;                                       /* return ! */
      else
        ;
    /*end of if*/

      if ( ! directoryFhandle.isDirectory() )
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_NOTDIR         );
          return false;                                       /* return ! */
        }
      else
        ;
    /*end of if*/

      return true;

    }
/*end of validateDirectoryFileHandleAndSendErrorMessagesIfNeeded*/


  public
  Message

  getDirEntriesCreateAndFillUpMessage(
    mount.Fhandle directoryFhandle,
    long cookie,
    long maxNumberOfBytesToSendBack,
    ParserResponderReturnArguments
      parserResponderReturnArguments )
/*****************************************************************************/
/* Function name: getDirEntriesCreateAndFillUpMessage                        */
/* File name: Server.java                                                    */
/* Purpose: to get directory entries and to fill up the message              */
/* Parameters: directoryFhandle                                              */
/*             cookie                                                        */
/*             maxNumberOfBytesToSendBack                                    */
/*             parserResponderReturnArguments                                */
/* Returns: rpcMessage or null in case of an error                           */
/* Last modified: 03.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      Fhandle[] entryFhandles = null;
      long dirFileId;
      int dirEntriesIbufLength;
      boolean startDirectoryEntriesFromTheBeginning;
      boolean directoryFhandleIsValidForDirOperations;
      Message rpcMessage = null;

                                  /* getDirEntriesCreateAndFillUpMessage */
/*---------------------------------------------------------------------------*/

      dirFileId =
        directoryFhandle.
          getFileId();

      directoryFhandle = registry.get( dirFileId );

      directoryFhandleIsValidForDirOperations =
        validateDirectoryFileHandleAndSendErrorMessagesIfNeeded(
          directoryFhandle,
          parserResponderReturnArguments                         );

      if ( ! directoryFhandleIsValidForDirOperations )
        return null;                                   /* return ! */
      else
        ;
    /*end of if*/

      if (maxNumberOfBytesToSendBack <=
           (Constants.NFSPROC_READDIR_MIN_N_UINTS_DATA_LEN *
            rpc.Constants.XDR_UNSIGNED_INT_LENGTH)
         )
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_FBIG             );
          return null;                                       /* return ! */
        }
      else
        ;
    /*end of if*/

      startDirectoryEntriesFromTheBeginning =
        Cookie.signifiesDirectoryStart(
          cookie                         );

      if (startDirectoryEntriesFromTheBeginning)
        directoryFhandle.refreshList();
      else
        ;
    /*end of if*/

      entryFhandles =
        directoryFhandle.list( registry );

      dirEntriesIbufLength =
        Constants.
          NFSPROC_READDIR_REPLY_PREFIX_LENGTH +
        (int) maxNumberOfBytesToSendBack +
        rpc.Constants.XDR_UNSIGNED_INT_LENGTH;

      rpcMessage =
        createRPCMessageAndPutSuccessPrefix(
          dirEntriesIbufLength,
          parserResponderReturnArguments       );

      rpcMessage.
        putUnsignedInteger( Constants.NFS_OK );

      putDirEntriesIntoRPCMessage(
        maxNumberOfBytesToSendBack,
        cookie,
        entryFhandles,
        rpcMessage                 );

      return rpcMessage;

    }
/*end of getDirEntriesCreateAndFillUpMessage*/


  public
  long

  fillUpFileAttributes(
    mount.Fhandle fileHandle,
    FAttributes fileAttributes )
/*****************************************************************************/
/* Function name: fillUpFileAttributes                                       */
/* File name: Server.java                                                    */
/* Purpose: to set file attributes                                           */
/* Parameters: fileHandle                                                    */
/*             fileAttributes                                                */
/* Returns: NFS_OK or other code to send to the client                       */
/* Last modified: 25.06.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      int mode = 0;
                                            /* fillUpFileAttributes */
/*---------------------------------------------------------------------------*/

      if ( ! fileHandle.exists() ) return Constants.NFSERR_NOENT;  /* ! */

      if ( fileHandle.isFile() )
        {
          fileAttributes.type = Constants.NFREG;
          mode |= Constants.NFS_MODE_MASK_REG;
        }
      else
        {
          if ( fileHandle.isDirectory() )
            {
              fileAttributes.type = Constants.NFDIR;
              mode |= Constants.NFS_MODE_MASK_DIR;
            }
          else
            {
              fileAttributes.type = Constants.NFNON;
            }
        /*end of if on dir*/
        }
    /*end of if on reg file*/

      if ( fileHandle.canRead() )
        mode |= Constants.NFS_MODE_MASK_ROT;
      else
        ;
    /*end of if*/
      
      if ( fileHandle.canWrite() )
        mode |= Constants.NFS_MODE_MASK_WOT;
      else
        ;
    /*end of if*/

      fileAttributes.mode = (long) mode;

      fileAttributes.size = fileHandle.length();
      fileAttributes.fileid = fileHandle.getFileId();

      fileAttributes.nlink = Constants.ATTRIBUTE_VALUE_NOT_SET;
      fileAttributes.uid = Constants.ATTRIBUTE_VALUE_NOT_SET;
      fileAttributes.gid = Constants.ATTRIBUTE_VALUE_NOT_SET;
      fileAttributes.blocksize = Constants.ATTRIBUTE_VALUE_NOT_SET;
      fileAttributes.rdev = Constants.ATTRIBUTE_VALUE_NOT_SET;
      fileAttributes.blocks = Constants.ATTRIBUTE_VALUE_NOT_SET;
      fileAttributes.fsid = Constants.ATTRIBUTE_VALUE_NOT_SET;
      fileAttributes.atime = new
        Timeval( Constants.ATTRIBUTE_VALUE_NOT_SET,
                 Constants.ATTRIBUTE_VALUE_NOT_SET );
      fileAttributes.mtime = new
        Timeval( Constants.ATTRIBUTE_VALUE_NOT_SET,
                 Constants.ATTRIBUTE_VALUE_NOT_SET );
      fileAttributes.ctime = new
        Timeval( Constants.ATTRIBUTE_VALUE_NOT_SET,
                 Constants.ATTRIBUTE_VALUE_NOT_SET );

      return Constants.NFS_OK;

    }
/*end of fillUpFileAttributes*/


  public
  Message

  readFileCreateAndFillUpMessage(
    ReadArguments readArgs,
    ParserResponderReturnArguments
      parserResponderReturnArguments )
/*****************************************************************************/
/* Function name: readFileCreateAndFillUpMessage                             */
/* File name: Server.java                                                    */
/* Purpose: to read file, create a RPC message and to fill it up             */
/* Parameters: readArgs                                                      */
/*             parserResponderReturnArguments                                */
/* Returns: rpcMessage or null in case of an error                           */
/* Last modified: 06.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      long nActualBytesSkipped;
      int actualNumberOfBytesRead;
      long fileId;
      long nfsErrorCode;
      FAttributes fileAttributes = null;
      boolean fileHandleIsValid;
      io.Readable inputStream;
      Message rpcMessage = null;
      Fhandle fileHandle = null;
      byte[] tempReadBuffer;

                                           /* readFileCreateAndFillUpMessage */
/*---------------------------------------------------------------------------*/

      fileId =
        readArgs.fileHandle.getFileId();

      fileHandle = registry.get( fileId );

      if ( (logTransactions) &&
           (log != null)
         )
        {
          log.write( 
            "NFSPROC_READ " +
            "transaction ID=" +
            parserResponderReturnArguments.
              transactionId +
            " readArgs.fileId=" + fileId +
            " offset=" + readArgs.offset +
            " count=" + readArgs.count +
            ( (fileHandle==null) ?
              ("\nfileId in the registry NOT found") :
              ("\nhave found the fileId in the registry")
            ) );

        }
      else
        ;
    /*end of if*/

      fileHandleIsValid =
        validateFileHandleAndSendErrorMessagesIfNeeded(
          fileHandle,
          parserResponderReturnArguments               );

      if ( ! fileHandleIsValid )
        return null;                                   /* return ! */
      else
        ;
    /*end of if*/

      inputStream =
        fileHandle.openReadable();

      if (inputStream == null)                            /* not found */
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_IO             );
          return null;                                       /* return ! */
        }
      else /* ready to read */
        ;
    /*end of if on inputStream*/

      try
        {
          nActualBytesSkipped =
            inputStream.skip( readArgs.offset );

          if (nActualBytesSkipped !=
              readArgs.offset        )
            throw new IOException();
          else
            ;
        /*end of if*/

          tempReadBuffer = new byte[ (int) readArgs.count ];

          actualNumberOfBytesRead =
            inputStream.read( tempReadBuffer,
                              0, /* put from the beginning */
                              (int) readArgs.count            );

          if (actualNumberOfBytesRead < 0)
            {
              fileHandle.closeReadable();
              sendNFSErrorReply(
                parserResponderReturnArguments,
                Constants.NFSERR_IO             );
              return null;                                    /* return ! */
            }
          else
            ;
        /*end of if*/
              

        }
      catch( IOException e )
        {
          fileHandle.closeReadable();
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_IO             );
          return null;                                       /* return ! */
        }
      /*end of try-catch*/


      fileHandle.closeReadable();

      rpcMessage =
        createRPCMessageAndPutSuccessPrefix(
          nfs.Constants.
            NFSPROC_FILE_READ_REPLY_PREFIX_LENGTH +
          actualNumberOfBytesRead +
          rpc.Constants.XDR_UNSIGNED_INT_LENGTH,
          parserResponderReturnArguments             );

      fileAttributes = new FAttributes();

      nfsErrorCode =
        fillUpFileAttributes( fileHandle,
                              fileAttributes );
      rpcMessage.
        putUnsignedInteger( nfsErrorCode );

      if ( nfsErrorCode == Constants.NFS_OK )
        {
          fileAttributes.
            putItselfIntoRPCMessage(
              rpcMessage             );
          rpcMessage.
             putUnsignedInteger( (long) actualNumberOfBytesRead );

          rpcMessage.
            putByteArray( actualNumberOfBytesRead,
                          tempReadBuffer           );
        }
      else
        ;
    /*end of if*/

      fileHandle.closeReadable();

      return rpcMessage;

    }
/*end of readFileCreateAndFillUpMessage*/


  public
  Message

  lookUpCreateAndFillUpMessage(
    DirectoryOperationArguments
      dirOpArgs,
    ParserResponderReturnArguments
      parserResponderReturnArguments )
/*****************************************************************************/
/* Function name: lookUpCreateAndFillUpMessage                               */
/* File name: Server.java                                                    */
/* Purpose: to look up file name, create a RPC message and to fill it up     */
/* Parameters: dirOpArgs                                                     */
/*             parserResponderReturnArguments                                */
/* Returns: rpcMessage or null in case of an error                           */
/* Last modified: 08.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      int i;
      long directoryFileId;
      long nfsErrorCode;
      FAttributes fileAttributes = null;
      boolean filableInstanceSet;
      boolean directoryFhandleIsValidForDirOperations;
      Message rpcMessage = null;
      DirectoryOperationResults dirOpRes = null;
      Fhandle directoryFileHandle = null;
      Fhandle fileHandle = null;
      Fhandle[] entryFhandles = null;
      String fileName = null;
      boolean continueToLoop = true;

                                           /* lookUpCreateAndFillUpMessage */
/*---------------------------------------------------------------------------*/

      fileName = util.String.
        removeTrailingPathSeparator(
          dirOpArgs.fileName         );

      if ( (logTransactions) &&
           (log != null)
         )
        {
          log.write( 
            "NFSPROC_LOOKUP " +
            "transaction ID=" +
            parserResponderReturnArguments.
              transactionId +
            " dirOpArgs.directoryFHandle.fileId=" +
            dirOpArgs.directoryFHandle.getFileId() +
            " fileName=" + fileName                  );
        }
      else
        ;
    /*end of if*/

      directoryFileId =
        dirOpArgs.directoryFHandle.getFileId();

      directoryFileHandle = registry.get( directoryFileId );

      directoryFhandleIsValidForDirOperations =
        validateDirectoryFileHandleAndSendErrorMessagesIfNeeded(
          directoryFileHandle,
          parserResponderReturnArguments                         );

      if ( ! directoryFhandleIsValidForDirOperations )
        {
          return null;                                   /* return ! */
        }
      else
        ;
    /*end of if*/

      directoryFileHandle.refreshList();
      entryFhandles =
        directoryFileHandle.list( registry );

      i=0;
      while( (i < entryFhandles.length ) &&
             continueToLoop
           )
        {
          if ( fileName.equals(
                 entryFhandles[i].getName() )
             )
            continueToLoop = false;
          else
            i++;
        /*end of if*/
        }
    /*end of while-loop on entryFhandles*/

      if ( continueToLoop )                            /* not found */
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_NOENT             );
          return null;                                       /* return ! */
        }
      else /* found */
        ;
    /*end of if*/

      fileHandle =
        entryFhandles[i];

      filableInstanceSet =
        fileHandle.setFilableInstance();

      if ( ! filableInstanceSet )
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_NXIO           );
          return null;                                       /* return ! */
        }
      else
        {

           FileSystemExpert.setFileHandle( fileHandle );

        }
    /*end of if*/

      fileAttributes = new FAttributes();

      nfsErrorCode =
        fillUpFileAttributes( fileHandle,
                              fileAttributes );
      
      rpcMessage =
        createRPCMessageAndPutSuccessPrefix(
          nfs.Constants.NFSPROC_LOOKUP_REPLY_LENGTH,
          parserResponderReturnArguments                     );

      rpcMessage.
        putUnsignedInteger( nfsErrorCode );

      if ( nfsErrorCode == Constants.NFS_OK )
        {
          dirOpRes = new DirectoryOperationResults();
          dirOpRes.fileHandle = fileHandle;
          dirOpRes.fileAttributes = fileAttributes;
          dirOpRes.
            putItselfIntoRPCMessage(
              rpcMessage             );
        }
      else
        ;
    /*end of if*/

      return rpcMessage;

    }
/*end of lookUpCreateAndFillUpMessage*/


  public
  Message

  getAttributesCreateAndFillUpMessage(
    mount.Fhandle fileHandle,
    ParserResponderReturnArguments
      parserResponderReturnArguments )
/*****************************************************************************/
/* Function name: getAttributesCreateAndFillUpMessage                        */
/* File name: Server.java                                                    */
/* Purpose: to get file attributes, create a RPC message and to fill it up   */
/* Parameters: fileHandle                                                    */
/*             parserResponderReturnArguments                                */
/* Returns: rpcMessage or null in case of an error                           */
/* Last modified: 08.08.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      long fileId;
      long nfsErrorCode;
      FAttributes fileAttributes = null;
      boolean filableInstanceSet;
      Message rpcMessage = null;

                                  /* getAttributesCreateAndFillUpMessage */
/*---------------------------------------------------------------------------*/

      fileId =
        fileHandle.getFileId();

      fileHandle = registry.get( fileId );

      if (fileHandle == null)
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_STALE           );
          return null;                                       /* return ! */
        }
      else
        {
        }
    /*end of if*/

      filableInstanceSet =
        fileHandle.setFilableInstance();

      if ( ! filableInstanceSet )
        {
          sendNFSErrorReply(
            parserResponderReturnArguments,
            Constants.NFSERR_NOENT         );
          return null;                                       /* return ! */
        }
      else
        {
           FileSystemExpert.setFileHandle( fileHandle );
        }
    /*end of if*/

      fileAttributes = new FAttributes();

      nfsErrorCode =
        fillUpFileAttributes( fileHandle,
                              fileAttributes );

      rpcMessage =
        createRPCMessageAndPutSuccessPrefix(
          nfs.Constants.NFSPROC_GETATTR_REPLY_LENGTH,
          parserResponderReturnArguments                     );

      rpcMessage.
        putUnsignedInteger( nfsErrorCode );

      if ( nfsErrorCode == Constants.NFS_OK )
        fileAttributes.
          putItselfIntoRPCMessage(
            rpcMessage             );
      else
        ;
    /*end of if*/

      return rpcMessage;

    }
/*end of getAttributesCreateAndFillUpMessage*/


  public
  void

  readDirAndSendReply(
    ParserResponderReturnArguments
      parserResponderReturnArguments )
/*****************************************************************************/
/* Function name: readDirAndSendReply                                        */
/* File name: Server.java                                                    */
/* Purpose: to read a directory and to send the resulting info to the client */
/* Parameters: parserResponderReturnArguments                                */
/* Returns:                                                                  */
/* Last modified: 30.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      long maxNumberOfBytesToSendBack = 0;
      long cookie = 0;
      byte[] ibuf = null;
      InetAddress ipAddressWhereToSend = null;
      int portToSendTo = 0;
      DatagramPacket udpPacket = null;
      Message rpcMessage = null;
      Message incomingRPCMessage = null;
      mount.Fhandle directoryFhandle = null;
                                                   /* readDirAndSendReply */
/*---------------------------------------------------------------------------*/

      incomingRPCMessage = new
        Message(
          parserResponderReturnArguments.
            byteArrayContainingParametersToRemoteProcedure );

      directoryFhandle = new mount.Fhandle();
      directoryFhandle.
        getItselfFromRPCMessage(
          incomingRPCMessage     );

      cookie =
        incomingRPCMessage.
          getUnsignedInteger();

      maxNumberOfBytesToSendBack =
        incomingRPCMessage.
          getUnsignedInteger();
                                                  /* got all incoming data */
      if ( (logTransactions) &&
           (log != null)
         )
        {
          log.write( 
            "NFSPROC_READDIR " +
            "transaction ID=" +
            parserResponderReturnArguments.
              transactionId +
            " directoryFhandle=" +
            directoryFhandle +
            " cookie=" + cookie +
            " maxNumberOfBytesToSendBack=" +
            maxNumberOfBytesToSendBack       );
        }
      else
        ;
    /*end of if*/

      rpcMessage =
        getDirEntriesCreateAndFillUpMessage(
          directoryFhandle,
          cookie,
          maxNumberOfBytesToSendBack,
          parserResponderReturnArguments );

      if ( rpcMessage == null )
        return;                                           /* return ! */
      else
        ;
    /*end of if on null*/

      ibuf = rpcMessage.getData();

      ipAddressWhereToSend =
        parserResponderReturnArguments.
          inRPCMessage.getInetAddress();
      portToSendTo =
        parserResponderReturnArguments.
          inRPCMessage.getPort();

      udpPacket = new
        DatagramPacket( ibuf, ibuf.length,
                        ipAddressWhereToSend,
                        portToSendTo          );
      try
        {
          udpSocket.send( udpPacket,
                          parserResponderReturnArguments.transactionId,
                          "readDirAndSendReply"                         );
        }
      catch( IOException e )
        {
          System.out.println(
            "readDirAndSendReply: " +
            "IOException in sending reply\n" +
            e.getMessage()                      );
        }
    /*end of try-catch*/

    }
/*end of readDirAndSendReply*/


  public
  void

  readFileSendReply(
    ParserResponderReturnArguments
      parserResponderReturnArguments )
/*****************************************************************************/
/* Function name: readFileSendReply                                          */
/* File name: Server.java                                                    */
/* Purpose: to read file and to send the resulting info to the client        */
/* Parameters: parserResponderReturnArguments                                */
/* Returns:                                                                  */
/* Last modified: 30.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      byte[] ibuf = null;
      InetAddress ipAddressWhereToSend = null;
      int portToSendTo = 0;
      DatagramPacket udpPacket = null;
      Message rpcMessage = null;
      Message incomingRPCMessage = null;
      ReadArguments readArgs = null;
      mount.Fhandle fileHandle = null;
                                                   /* readFileSendReply */
/*---------------------------------------------------------------------------*/

      incomingRPCMessage = new
        Message(
          parserResponderReturnArguments.
            byteArrayContainingParametersToRemoteProcedure );

      readArgs = new ReadArguments();
      readArgs.
        getItselfFromRPCMessage(
          incomingRPCMessage     );
                                                  /* got all incoming data */
      rpcMessage =
        readFileCreateAndFillUpMessage(
          readArgs,
          parserResponderReturnArguments     );

      if ( rpcMessage == null )
        return;                                           /* return ! */
      else
        ;
    /*end of if on null*/

      ibuf = rpcMessage.getData();

      ipAddressWhereToSend =
        parserResponderReturnArguments.
          inRPCMessage.getInetAddress();
      portToSendTo =
        parserResponderReturnArguments.
          inRPCMessage.getPort();

      udpPacket = new
        DatagramPacket( ibuf, ibuf.length,
                        ipAddressWhereToSend,
                        portToSendTo          );

      try
        {
          udpSocket.send( udpPacket,
                          parserResponderReturnArguments.transactionId,
                          "readFileSendReply"                           );
        }
      catch( IOException e )
        {
          System.out.println(
            "readFileSendReply: " +
            "IOException in sending reply\n" +
            e.getMessage()                      );
        }
    /*end of try-catch*/

    }
/*end of readFileSendReply*/


  public
  void

  readLookUpSendReply(
    ParserResponderReturnArguments
      parserResponderReturnArguments )
/*****************************************************************************/
/* Function name: readLookUpSendReply                                        */
/* File name: Server.java                                                    */
/* Purpose: to look up a file in a directory and to send the resulting info  */
/*          to the client                                                    */
/* Parameters: parserResponderReturnArguments                                */
/* Returns:                                                                  */
/* Last modified: 30.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      byte[] ibuf = null;
      InetAddress ipAddressWhereToSend = null;
      int portToSendTo = 0;
      DatagramPacket udpPacket = null;
      Message rpcMessage = null;
      Message incomingRPCMessage = null;
      DirectoryOperationArguments dirOpArgs = null;
      mount.Fhandle fileHandle = null;
                                                   /* readLookUpSendReply */
/*---------------------------------------------------------------------------*/

      incomingRPCMessage = new
        Message(
          parserResponderReturnArguments.
            byteArrayContainingParametersToRemoteProcedure );

      dirOpArgs = new DirectoryOperationArguments();
      dirOpArgs.
        getItselfFromRPCMessage(
          incomingRPCMessage     );
                                                  /* got all incoming data */
      rpcMessage =
        lookUpCreateAndFillUpMessage(
          dirOpArgs,
          parserResponderReturnArguments     );

      if ( rpcMessage == null )
        return;                                           /* return ! */
      else
        ;
    /*end of if on null*/

      ibuf = rpcMessage.getData();

      ipAddressWhereToSend =
        parserResponderReturnArguments.
          inRPCMessage.getInetAddress();
      portToSendTo =
        parserResponderReturnArguments.
          inRPCMessage.getPort();

      udpPacket = new
        DatagramPacket( ibuf, ibuf.length,
                        ipAddressWhereToSend,
                        portToSendTo          );

      try
        {
          udpSocket.send( udpPacket,
                          parserResponderReturnArguments.transactionId,
                          "readLookUpSendReply"                         );
        }
      catch( IOException e )
        {
          System.out.println(
            "readLookUpSendReply: " +
            "IOException in sending reply\n" +
            e.getMessage()                      );
        }
    /*end of try-catch*/

    }
/*end of readLookUpSendReply*/


  public
  void

  readGetAttrSendReply(
    ParserResponderReturnArguments
      parserResponderReturnArguments )
/*****************************************************************************/
/* Function name: readGetAttrSendReply                                       */
/* File name: Server.java                                                    */
/* Purpose: to get file attributes and to send the resulting info            */
/*          to the client                                                    */
/* Parameters: parserResponderReturnArguments                                */
/* Returns:                                                                  */
/* Last modified: 30.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      byte[] ibuf = null;
      InetAddress ipAddressWhereToSend = null;
      int portToSendTo = 0;
      DatagramPacket udpPacket = null;
      Message rpcMessage = null;
      Message incomingRPCMessage = null;
      mount.Fhandle fileHandle = null;
                                                   /* readGetAttrSendReply */
/*---------------------------------------------------------------------------*/

      incomingRPCMessage = new
        Message(
          parserResponderReturnArguments.
            byteArrayContainingParametersToRemoteProcedure );

      fileHandle = new mount.Fhandle();
      fileHandle.
        getItselfFromRPCMessage(
          incomingRPCMessage     );
                                                  /* got all incoming data */
      if ( (logTransactions) &&
           (log != null)
         )
        {
          log.write( 
            "NFSPROC_GETATTR " +
            "transaction ID=" +
            parserResponderReturnArguments.
              transactionId +
            " fileHandle.fileId=" +
            fileHandle.getFileId()           );
        }
      else
        ;
    /*end of if*/

      rpcMessage =
        getAttributesCreateAndFillUpMessage(
          fileHandle,
          parserResponderReturnArguments     );

      if ( rpcMessage == null )
        return;                                           /* return ! */
      else
        ;
    /*end of if on null*/

      ibuf = rpcMessage.getData();

      ipAddressWhereToSend =
        parserResponderReturnArguments.
          inRPCMessage.getInetAddress();
      portToSendTo =
        parserResponderReturnArguments.
          inRPCMessage.getPort();

      udpPacket = new
        DatagramPacket( ibuf, ibuf.length,
                        ipAddressWhereToSend,
                        portToSendTo          );

      try
        {
          udpSocket.send( udpPacket,
                          parserResponderReturnArguments.transactionId,
                          "readGetAttrSendReply"                        );
        }
      catch( IOException e )
        {
          System.out.println(
            "readGetAttrSendReply: " +
            "IOException in sending reply\n" +
            e.getMessage()                      );
        }
    /*end of try-catch*/

    }
/*end of readGetAttrSendReply*/


  public
  void

  listenAndProcessRequests()
/*****************************************************************************/
/* Function name: listenAndProcessRequests                                   */
/* File name: Server.java                                                    */
/* Purpose: to listen to incoming requests and process them                  */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 25.06.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      Long remoteProcedureNumberLong = null;
      boolean nowMayProcessTheParametersToRemoteProcedure;
      boolean thereWasAGeneralError = false;
      ParserResponderReturnArguments parserResponderReturnArguments;
      DatagramPacket udpPacket = null;
      byte[] byteBuffer = null;
      boolean continueLoopReceivePackets = true;

                                             /* listenAndProcessRequests */
/*---------------------------------------------------------------------------*/

      while (continueLoopReceivePackets)
        {
          byteBuffer = new byte[ pm.Constants.UDP_PACKET_LENGTH ];

          udpPacket = new
            DatagramPacket( byteBuffer,
                            byteBuffer.length );

          try
            {
              udpSocket.receive( udpPacket );
            }
          catch( IOException e )
            {
              System.out.println(
                "udpSocket.receive: " +
                e.getMessage()          );
            }
        /*end of try-catch*/

          parserResponderReturnArguments = new
            ParserResponderReturnArguments();

          nowMayProcessTheParametersToRemoteProcedure =
            processUDPPacket(
              udpPacket,
              parserResponderReturnArguments      );

          thereWasAGeneralError =
            processGeneralErrors(
              parserResponderReturnArguments,
              Constants.NFS_PROGRAM,
              Constants.NFS_VERSION_LOW,
              Constants.NFS_VERSION_HIGH      );

          if (thereWasAGeneralError)
            continue;                                  /* continue ! */
          else
            ;
        /*end of if*/

          if ( nowMayProcessTheParametersToRemoteProcedure )
            {                                          /* OK, do procedure, */
                                                       /* process arguments */

              switch ( (int) parserResponderReturnArguments.
                               remoteProcedureNumber         )
                {
                case (int) Constants.NFSPROC_NULL:
                  sendProcedureNullRPCReplyMessage(
                    parserResponderReturnArguments );
                  break;
                case (int) Constants.NFSPROC_GETATTR:
                  readGetAttrSendReply(
                    parserResponderReturnArguments );
                  break;
                case (int) Constants.NFSPROC_SETATTR:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_SETATTR"                      );
                  break;
                case (int) Constants.NFSPROC_ROOT:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_ROOT"                         );
                  break;
                case (int) Constants.NFSPROC_LOOKUP:
                  readLookUpSendReply(
                    parserResponderReturnArguments );
                  break;
                case (int) Constants.NFSPROC_READLINK:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_READLINK"                     );
                  break;
                case (int) Constants.NFSPROC_READ:
                  readFileSendReply(
                    parserResponderReturnArguments         );
                  break;
                case (int) Constants.NFSPROC_WRITECACHE:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_WRITECACHE"                   );
                  break;
                case (int) Constants.NFSPROC_WRITE:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_WRITE"                        );
                  break;
                case (int) Constants.NFSPROC_CREATE:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_CREATE"                       );
                  break;
                case (int) Constants.NFSPROC_REMOVE:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_REMOVE"                       );
                  break;
                case (int) Constants.NFSPROC_RENAME:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_RENAME"                       );
                  break;
                case (int) Constants.NFSPROC_LINK:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_LINK"                         );
                  break;
                case (int) Constants.NFSPROC_SYMLINK:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_SYMLINK"                       );
                  break;
                case (int) Constants.NFSPROC_MKDIR:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_MKDIR"                        );
                  break;
                case (int) Constants.NFSPROC_RMDIR:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_RMDIR"                       );
                  break;
                case (int) Constants.NFSPROC_READDIR:
                  readDirAndSendReply(
                    parserResponderReturnArguments );
                  break;
                case (int) Constants.NFSPROC_STATFS:
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    "NFSPROC_STATFS"                       );
                  break;

                default:
                  remoteProcedureNumberLong = new Long(
                    parserResponderReturnArguments.
                      remoteProcedureNumber            );
                  sendProcedureUnavailableRPCReplyMessage(
                    parserResponderReturnArguments,
                    remoteProcedureNumberLong.toString()  );
                  break;
                }
            /*end of switch*/

            }
          else /* there was an error */
            ;
        /*end of if on nowMayProcessTheParametersToRemoteProcedure*/

        }
    /*end of while-loop*/
    }
/*end of listenAndProcessRequests*/


  public
  void

  run()
/*****************************************************************************/
/* Function name: run                                                        */
/* File name: Server.java                                                    */
/* Purpose: to perform actual NFS service                                    */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 11.06.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      boolean registeredOK = false;
      boolean unregisteredOK = false;
                                                                 /* run */
/*---------------------------------------------------------------------------*/

      currentThread = Thread.currentThread();
      try
        {
          currentThread.
            setName( Constants.NFS_SERVER_THREAD_NAME_STRING );
        }
      catch( SecurityException e )
        {
        }
    /*end of try-catch*/

      if (registerWithPortMapper)
        {
          sendRegistrationRPCMessageToPortMapper(
            Constants.INITIAL_TRANSACTION_ID,
            Constants.NFS_PROGRAM,
            Constants.NFS_VERSION,
            pm.Constants.IPPROTO_UDP,
            Constants.NFS_PORT                   );

          registeredOK =
            receiveRegistrationConfirmationRPCMessageFromPortMapper();

        }
      else
        ;
    /*end of if on registerWithPortMapper*/

      if ( ( ! registerWithPortMapper ) ||
           registeredOK                    )
        {
          listenAndProcessRequests();
        }
      else
        ;
    /*end of if on registerWithPortMapper*/

      if (registeredOK)
        {
          sendUnregisterRPCMessageToPortMapper(
            Constants.INITIAL_TRANSACTION_ID,
            Constants.NFS_PROGRAM,
            Constants.NFS_VERSION,
            pm.Constants.IPPROTO_UDP,
            Constants.NFS_PORT                 );

          unregisteredOK =
            receiveUnregisterConfirmationRPCMessageFromPortMapper();

          System.out.println( "\n\nrun: unregisteredOK=" + unregisteredOK );

        }
      else
        ;
    /*end of if on registeredOK*/

      udpSocket.close();

  }
/*end of run*/


  public
  static
  void

  main( String[] args )
/*****************************************************************************/
/* Function name: main                                                       */
/* File name: Server.java                                                    */
/* Purpose: main method for the Server class                                 */
/* Parameters: agrument strings array                                        */
/* Returns:                                                                  */
/* Last modified: 23.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      pm.Mapper portmapper = null;
      mount.Mount mountServer = null;
      Server nfsServer = null;
                                                          /* main */
/*---------------------------------------------------------------------------*/

      try
        {
          nfsServer = new Server( Constants.PROPERTIES_FILE_PATH,
                                  Constants.EXPORTS_FILE_PATH     );
        }
      catch( SocketException e )
        {
          System.out.println(
            "can\'t create io.UdpSocket: " +
            e.getMessage()                      );
          System.exit( 1 );
        }
    /*end of try-catch*/

      try
        {
          mountServer = new
            mount.Mount( Constants.PROPERTIES_FILE_PATH,
                         nfsServer.exportedFileSystemsRegistry );
        }
      catch( SocketException e )
        {
          System.out.println(
            "can\'t create " +
            "io.UdpSocket for the mount server: " +
            e.getMessage()                      );
          System.exit( 1 );
        }
    /*end of try-catch*/


      if (args.length > 0)
        {
          if (args[0].equals( "noreg" ))
            {
              nfsServer.registerWithPortMapper = false;
              mountServer.registerWithPortMapper = false;
            }
          else
            {
              if (args[0].equals( "pmthread" ))
                {
                  try
                    {
                      portmapper = new
                        pm.Mapper( Constants.PROPERTIES_FILE_PATH );
                    }
                  catch( SocketException e )
                    {
                      System.out.println(
                        "can\'t create " +
                        "io.UdpSocket for portmapper: " +
                        e.getMessage()                      );
                      System.exit( 1 );
                    }
                /*end of try-catch*/

                  nfsServer.portmapperThread = new
                    Thread( portmapper,
                            Constants.
                              PORTMAPPER_THREAD_NAME_STRING );

                  nfsServer.portmapperThread.start();

                }
              else
                ;
            /*end of if on pmthread*/
            }
        /*end of if on noreg*/
        }
      else
        ;
    /*end of if on args.length*/

      nfsServer.mountServerThread = new
        Thread( mountServer,
                nfs.Constants.
                  MOUNT_SERVER_THREAD_NAME_STRING );

      nfsServer.mountServerThread.start();

      nfsServer.registry = mountServer.getRegistry();

      nfsServer.run();
      System.exit( 0 );

    }
/*end of main*/


} /* end of Server class */

/*****************************************************************************/
/* End of file: Server.java                                                  */
/*****************************************************************************/
