/*--------------------------------------------------------------------*/
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*--------------------------------------------------------------------*/

#include "os2types.h"

#include "cons.h"
#include "types.h"
#include "mfspro.h"

#ifdef _PPC
#pragma Push_align_members(1)
#endif

#include "const.h"
#include "misc.h"
#include "superb.h"
#include "storage.h"
#include "fnode.h"
#include "buf.h"
#include "chain.h"
#include "dir.h"

#ifdef _PPC
#pragma Pop_align_members()
#endif

#include "hpfspro.h"

#include "apipro.h"

/*------------------------------------------*/
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*------------------------------------------*/

ULONG HPFSGetFSInfo( PVOL pVol, PBYTE pFileName, PMFSFILEINFO pFileInfo )
{
  ULONG         rc;
  ULONG         i;
  ULONG         n;
  ULONG         k;
  PALBLK        pALB;
  PALLEAF       pALF;
  ULONG         IOPos;
  ULONG         IOLeft;
  PFILE         pFile = NULL;

  if ( rc = MFSOpen( pFileName, (PHFILE) &pFile ) )
  {
    rc = ERR_FILE_NOT_FOUND;
    goto HPFSGetInfo_Exit;
  }

  if ( !pFileInfo->cMaxExtents )
  {
    rc = ERR_TOO_MANY_EXTENTS;
    goto HPFSGetInfo_Exit;
  }

  IOLeft  = pFile->FileEOFSec >> pVol->SecShift;
  IOPos   = 0;
  rc      = 0;
  n       = 0;


  while( !rc && IOLeft )
  {
    /*---------------------------------------------------------*/
    /* Start with the embedded ABLK structure in the fnode     */
    /* this will either point directly to the file data blocks */
    /* or to a sector containing another ABLK.                 */
    /*---------------------------------------------------------*/
    pALB = &((PFNODE)((PHPFSINFO)pFile->FSInfo)->pFNode)->fst.alb;

    /*----------------------------------------------*/
    /* Locate the ALBLK leaf node that contains the */
    /* request file offset.                         */
    /*----------------------------------------------*/
    if ( !(rc = HPFSFindLeafNode( pFile, &pALB, IOPos )) )
    {
      pALF = (PALLEAF)(pALB+1);


      for ( i = 0;
            (i < pALB->cUsed) && (n < pFileInfo->cMaxExtents) && IOLeft;
            i++, n++ )
      {

        k = pALF[i].lsnLog - IOPos;

        if ( k )
        {
          k = pALF[i].lsnLog - IOPos;

          pFileInfo->Extents[n].ExtentRBA = pVol->BootRecPos;
          pFileInfo->Extents[n].ExtentSec = k;

          IOPos  += k;
          IOLeft -= k;
          n++;
        }

        pFileInfo->Extents[n].ExtentRBA = pALF[i].lsnPhys + pVol->BootRecPos;
        pFileInfo->Extents[n].ExtentSec = pALF[i].csecRun;

        IOPos  += pALF[i].csecRun;
        IOLeft -= pALF[i].csecRun;
      }
    }
    if ( n >= pFileInfo->cMaxExtents )
    {
      break;
    }
  }

  pFileInfo->cExtents = n;
  pFileInfo->FileSize = pFile->FileEOFByte;
  pFileInfo->FSType   = FS_TYPE_HPFS;
  pFileInfo->VolRBA   = pVol->BootRecPos;

  if ( IOLeft )
  {
    rc = ERR_TOO_MANY_EXTENTS;
  }

HPFSGetInfo_Exit: ;

  if ( pFile )
  {
    MFSClose( (HFILE) pFile );
  }

  return( rc );

}

/*------------------------------------------*/
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*------------------------------------------*/

ULONG HPFSReadSec( PFILE pFile, ULONG SecPos, PBYTE pBuf, ULONG Size )
{
  return( HPFSDoFileIO( pFile,
                        IOTYPE_EXTENT_READ,
                        SecPos,
                        pBuf,
                        Size    ) );
}

/*------------------------------------------*/
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*------------------------------------------*/

ULONG HPFSWriteSec( PFILE pFile, ULONG SecPos, PBYTE pBuf, ULONG Size )
{
  return( HPFSDoFileIO( pFile,
                        IOTYPE_EXTENT_WRITE,
                        SecPos,
                        pBuf,
                        Size    ) );
}


/*------------------------------------------*/
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*------------------------------------------*/

ULONG HPFSDoFileIO( PFILE pFile, ULONG IOType, ULONG SecPos, PBYTE pBuf,
                    ULONG Size )
{
  ULONG         rc;
  PALBLK        pALB;
  ULONG         IOBytes;
  ULONG         IOPos;

  IOBytes = Size;
  IOPos   = SecPos;
  rc      = 0;

  while( !rc && IOBytes )
  {
    /*---------------------------------------------------------*/
    /* Start with the embedded ABLK structure in the fnode     */
    /* this will either point directly to the file data blocks */
    /* or to a sector containing another ABLK.                 */
    /*---------------------------------------------------------*/
    pALB = &((PFNODE)((PHPFSINFO)pFile->FSInfo)->pFNode)->fst.alb;

    /*----------------------------------------------*/
    /* Locate the ALBLK leaf node that contains the */
    /* request file offset.                         */
    /*----------------------------------------------*/
    if ( !(rc = HPFSFindLeafNode( pFile, &pALB, IOPos )) )
    {
      /*------------------------------------------------------*/
      /* Read the file data pointed to by the ABLK leaf node. */
      /*                                                      */
      /* Since the requested I/O operation span more than one */
      /* leaf node, HPFSReadExtent returns the current file   */
      /* offset and bytes remaining to the caller.            */
      /*------------------------------------------------------*/

      rc = HPFSExtentIO( pFile, IOType, pALB, &IOPos, &pBuf, &IOBytes );
    }
  }

  return( rc );
}

/*------------------------------------------*/
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*------------------------------------------*/

ULONG HPFSFindLeafNode( PFILE pFile, PALBLK *ppALB, ULONG SecPos )
{
  PHPFSINFO     pHFI;
  ULONG         i;
  ULONG         rc = 0;
  PALNODE       pALN;
  PALBLK        pALB;


  /*-----------------------------------------*/
  /* Point to HPFS specific file information */
  /*-----------------------------------------*/
  pHFI = (PHPFSINFO) pFile->FSInfo;

  /*----------------------------------------------*/
  /* Traverse indirect ABLKs until we find a      */
  /* leaf node ABLK containing the requested file */
  /* offset.                                      */
  /*----------------------------------------------*/

  pALB = *ppALB;

  while ( pALB && (pALB->bFlag & ABF_NODE) )
  {
    /*-------------------------------------*/
    /* We are processing an indirect ABLK. */
    /*-------------------------------------*/
    pALN = (PALNODE)(pALB+1);

    for (i=0; i < pALB->cUsed; i++ )
    {
      /*----------------------------------------------------*/
      /* Search its entries until we find one containing    */
      /* the requested file offset.                         */
      /*----------------------------------------------------*/
      if ( pALN[i].lsnLog > SecPos )
      {
        pALB = NULL;

        /*----------------------------------------------*/
        /* Read the node containing the next level ABLK */
        /*----------------------------------------------*/
        if ( !pHFI->pNodeBuf )
        {
          if ( pHFI->pNodeBuf = (PBYTE) malloc(pFile->pVol->pDCB->SecSize) )
          {
            return( ERR_MALLOC_FAILED );
          }
        }

        if ( !(rc = MFSRead( pFile->pVol,
                             pALN[i].lsnPhys,
                             pHFI->pNodeBuf,
                             pFile->pVol->pDCB->SecSize   )) )
        {
          /*--------------------------------------------*/
          /* This may seem redundant, however, the ABLK */
          /* is at a different offset in the 1st level  */
          /* fnode.                                     */
          /*--------------------------------------------*/
          pALB = &((PALSEC)pHFI->pNodeBuf)->alb;
        }

        break;
      }
    }
  }

  if ( !(*ppALB = pALB) )
  {
    return( ERR_EXTENT_NOT_FOUND );
  }

  return( rc );
}



/*------------------------------------------*/
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*------------------------------------------*/


ULONG HPFSExtentIO( PFILE pFile, ULONG IOType,  PALBLK pALB, PULONG pSecPos,
                    PBYTE *ppBuf, PULONG pIOBytes )
{
  PALLEAF       pALF;

  ULONG         ExtPos[2];
  ULONG         IOPos;
  ULONG         IOLeft;
  PBYTE         pBuf;
  ULONG         i;
  ULONG         n;
  ULONG         j;
  ULONG         rc = 0;
  ULONG         SecShift;

  pALF   = (PALLEAF)(pALB+1);

  SecShift = pFile->pVol->SecShift;

  IOPos  = *pSecPos;
  IOLeft = *pIOBytes;
  pBuf   = *ppBuf;


  /*-----------------------------------------------*/
  /* Process extents until the required byte count */
  /* is exhausted or there are no extents left     */
  /* in the current leaf node                      */
  /*-----------------------------------------------*/
  for ( i=0; (i < pALB->cUsed) && IOLeft; i++ )
  {
    /*--------------------------------------------------------*/
    /* Calc start and end file sector offsets for this extent */
    /*--------------------------------------------------------*/
    ExtPos[0] = pALF[i].lsnLog;
    ExtPos[1] = ExtPos[0] + pALF[i].csecRun;

    /*-------------------------------------------------*/
    /* If we are processing an extent past the current */
    /* file offset, zero fill to catch-up              */
    /*-------------------------------------------------*/
    if ( ExtPos[0] > IOPos )
    {
      n = (ExtPos[0] - IOPos) << SecShift;
      n = (n > IOLeft) ? IOLeft : n;

      memset( pBuf, 0, n );

      IOPos  += (n >> SecShift);
      pBuf   += n;
      IOLeft -= n;
    }

    /*----------------------------------------------------*/
    /* Check if requested offset is within current extent */
    /*----------------------------------------------------*/
    if ( IOLeft && ExtPos[0] <= IOPos && ExtPos[1] >= IOPos )
    {
      /*---------------------------------------*/
      /* Convert to logical volume coordinates */
      /*---------------------------------------*/
      j = pALF[i].lsnPhys   + (IOPos - ExtPos[0]);
      n = (pALF[i].csecRun  - (IOPos - ExtPos[0])) << SecShift;
      n = (n > IOLeft) ? IOLeft : n;

      /*--------------*/
      /* IO file data */
      /*--------------*/
      if ( IOType == IOTYPE_EXTENT_READ )
      {
        rc = MFSRead( pFile->pVol, j, pBuf, n );
      }
      else if ( IOType == IOTYPE_EXTENT_WRITE )
      {
        rc = MFSWrite( pFile->pVol, j, pBuf, n );
      }
      else
      {
        rc = ERR_INTERNAL_ERROR;
      }

      if ( rc )
      {
        break;
      }

      IOPos  += (n >> SecShift);
      pBuf   += n;
      IOLeft -= n;
    }
  }

ReadExt_Exit: ;

  *pSecPos =  IOPos;
  *pIOBytes = IOLeft;
  *ppBuf    = pBuf;

  return( rc );

}

