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

#include "os2types.h"

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

#include "fatpro.h"

#include "apipro.h"

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

ULONG FATReadSec( PFILE pFile, ULONG SecPos, PBYTE pBuf, ULONG Size )
{
  ULONG         rc;

  rc = FATExtentIO( pFile,
                    IOTYPE_EXTENT_READ,
                    &((PFATINFO)pFile->FSInfo)->FatCLMap,
                    SecPos << pFile->pVol->SecShift,
                    pBuf,
                    Size    );


  return( rc );
}

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

ULONG FATWriteSec( PFILE pFile, ULONG SecPos, PBYTE pBuf, ULONG Size )
{
  ULONG         rc;

  rc = FATExtentIO( pFile,
                    IOTYPE_EXTENT_WRITE,
                    &((PFATINFO)pFile->FSInfo)->FatCLMap,
                    SecPos << pFile->pVol->SecShift,
                    pBuf,
                    Size    );


  return( rc );
}

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

ULONG FATExtentIO( PFILE pFile, ULONG IOType, PFATCLMAP pCLMap, ULONG FilePos,
                   PBYTE pBuf, ULONG IOBytes )
{
  PVOL          pVol;

  ULONG         i;

  ULONG         IOPos;
  ULONG         CurSec;
  ULONG         ReadLSN;
  ULONG         ExtSecOffset;

  USHORT        IOCnt;
  USHORT        ReadCnt;
  ULONG         rc;
  PFATEXTENT    pExt = NULL;
  USHORT        NewMap;

  pVol = pFile->pVol;

  rc = 0;

  /*---------------------------------------------------*/
  /* Convert file offset and IO byte count to sectors  */
  /*---------------------------------------------------*/
  IOCnt = IOBytes >> pVol->SecShift;
  IOPos = FilePos >> pVol->SecShift;

  /*------------------------------------------------------*/
  /* Indicate we should locate the requested file offset  */
  /* within the cluster map                               */
  /*------------------------------------------------------*/
  NewMap = 1;

  while ( IOCnt && !rc )
  {
    /*-----------------------------------------------------*/
    /* If the current cluster map contains the file offset */
    /* we need then read the file using the current map    */
    /*-----------------------------------------------------*/
    if ( pCLMap->FileSecPos[0] <= IOPos &&
         pCLMap->FileSecPos[1] >  IOPos    )
    {
      /*------------------------------------------------------*/
      /* File offsets in the cluster map are cluster aligned. */
      /* If this is the first time we are processing this map */
      /* then we need find the position of the requested file */
      /* offset within the map                                */
      /*------------------------------------------------------*/
      ExtSecOffset = 0;
      if ( NewMap )
      {
        pExt   = pCLMap->FatExtent;
        CurSec = pCLMap->FileSecPos[0];

        /*--------------------------------------------------------*/
        /* Process until we find the cluster map entry containing */
        /* the requested file offset.                             */
        /*--------------------------------------------------------*/
        for ( i=0; i < pCLMap->ExtentCount; i++, pExt++ )
        {
          if ( IOPos < CurSec + pExt->ExtentSec )
          {
            break;
          }
          CurSec += pExt->ExtentSec;
        }
        NewMap = 0;

        /*----------------------------------------------------*/
        /* Locate the file data within the containing cluster */
        /* map entry                                          */
        /*----------------------------------------------------*/
        ExtSecOffset = IOPos - CurSec;
      }

      /*----------------------------------------------------*/
      /* Extent map contains logical sector numbers of      */
      /* file data.                                         */
      /*----------------------------------------------------*/
      ReadLSN = pExt->ExtentRBA + ExtSecOffset;
      ReadCnt = pExt->ExtentSec - ExtSecOffset;

      /*------------------------------------------------*/
      /* We may not need to read the entire file extent */
      /*------------------------------------------------*/
      if ( ReadCnt > IOCnt )
      {
        ReadCnt = IOCnt;
      }

      /*----------------------*/
      /* IO file data sectors */
      /*----------------------*/
      if ( IOType == IOTYPE_EXTENT_READ )
      {
        rc = MFSRead( pVol, ReadLSN, pBuf, ReadCnt << pVol->SecShift );
      }
      else if ( IOType == IOTYPE_EXTENT_WRITE )
      {
        rc = MFSWrite( pVol, ReadLSN, pBuf, ReadCnt << pVol->SecShift );
      }
      else
      {
        rc = ERR_INTERNAL_ERROR;
      }

      if ( rc )
      {
        break;
      }

      IOCnt  -= ReadCnt;
      IOPos  += ReadCnt;
      pExt++;
      pBuf   += ReadCnt << pVol->SecShift;
    }
    /*--------------------------------------------------*/
    /* If we are here the current cluster map did not   */
    /* contain any parts of the file we are interested  */
    /* in. Rebuild to cluster map so it contains the    */
    /* requested file offset.                           */
    /*--------------------------------------------------*/
    else
    {
      rc = FATBuildCLMap( pFile, pCLMap, IOPos );
      NewMap = 1;
    }
  }

  return( rc );
}

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

ULONG FATGetFSInfo( PVOL pVol, PBYTE pFileName, PMFSFILEINFO pFileInfo )
{
  ULONG         rc = 0;
  ULONG         i;
  PFILE         pFile = NULL;
  ULONG         TotalMapSec;
  LONG          s;
  USHORT        CurCLNum;
  USHORT        NextCLNum;
  PEXTENT       pExt;
  PFATVOLINFO   pFSVI;
  PFATINFO      pFSI;


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

  pFSVI = (PFATVOLINFO) pFile->pVol->pFSVolInfo;
  pFSI  = (PFATINFO)    pFile->FSInfo;

  /*-------------------------------------------------------*/
  /* Start building the cluster map by detecting chains of */
  /* contiguous clusters. We are done when we hit the last */
  /* cluster in the file or fill all the entries in the    */
  /* map                                                   */
  /*-------------------------------------------------------*/
  NextCLNum = CurCLNum = pFSI->FatCLMap.StartCluster;

  pExt = pFileInfo->Extents;

  TotalMapSec = 0;

  for ( i=0; i < pFileInfo->cMaxExtents && NextCLNum != pFSVI->FatEOFCLNum; i++  )
  {
    /*------------------------------*/
    /* Initialize cluster map entry */
    /*------------------------------*/
    pExt[i].ExtentRBA = FATClusterToLSN( pFile, NextCLNum ) + pVol->BootRecPos;
    pExt[i].ExtentSec = 0;

    /*--------------------------------------------*/
    /* Digest contiguous clusters for this entry  */
    /*--------------------------------------------*/
    do
    {
      pExt[i].ExtentSec += pFSVI->FatSecPerCL;
      TotalMapSec       += pFSVI->FatSecPerCL;
      NextCLNum = FATGetCLNum( pFile, (CurCLNum = NextCLNum) );
    }
    while ( NextCLNum == CurCLNum+1 );
  }

  pFileInfo->cExtents = i;
  pFileInfo->FileSize = pFile->FileEOFByte;
  pFileInfo->FSType   = FS_TYPE_FAT;
  pFileInfo->VolRBA   = pVol->BootRecPos;

  if (NextCLNum != pFSVI->FatEOFCLNum )
  {
    rc = ERR_TOO_MANY_EXTENTS;
    goto FatGetInfo_Exit;
  }

  s = TotalMapSec - (pFile->FileEOFSec >> pVol->SecShift);

  if ( s > 0 )
  {
    pFileInfo->Extents[i-1].ExtentSec -= s;
  }

FatGetInfo_Exit:  ;

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

  return( rc );

}

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

ULONG FATBuildCLMap( PFILE pFile, PFATCLMAP pCLMap, ULONG FileSecPos )
{
  USHORT        SkipCLCnt;
  USHORT        CurCLNum;
  USHORT        NextCLNum;
  ULONG         TotalMapSec;

  ULONG         rc = 0; // for now

  USHORT        i;

  PFATEXTENT    pExt;

  PFATVOLINFO   pFSVI;
  PFATINFO      pFSI;

  pFSVI = (PFATVOLINFO) pFile->pVol->pFSVolInfo;
  pFSI  = (PFATINFO)    pFile->FSInfo;

  /*---------------------------------------------------------*/
  /* Determine if there is any useable information in        */
  /* The current cluster map.                                */
  /*                                                         */
  /* If the map has a valid cluster number and begins before */
  /* the requested file offset, then  use the NextCluster    */
  /* field and FileSecPos[1] as our starting point for       */
  /* locating the requested file offset.                     */
  /* Otherwise we start from the begining of file.           */
  /*---------------------------------------------------------*/

  if ( !pCLMap->NextCluster || FileSecPos < pCLMap->FileSecPos[0] )
  {
    CurCLNum  = pCLMap->StartCluster;
    SkipCLCnt = FileSecPos / pFSVI->FatSecPerCL;
  }
  else
  {
    CurCLNum  = pCLMap->NextCluster;
    SkipCLCnt = (FileSecPos-pCLMap->FileSecPos[1]) / pFSVI->FatSecPerCL;
  }

  NextCLNum = CurCLNum;

  /*---------------------------------------------------*/
  /* Skip clusters prior the the requested file offset */
  /*---------------------------------------------------*/
  for ( i=0; i < SkipCLCnt && CurCLNum != pFSVI->FatEOFCLNum; i++ )
  {
    CurCLNum = FATGetCLNum( pFile, CurCLNum );
  }

  /*-------------------------------------------------------*/
  /* Start building the cluster map by detecting chains of */
  /* contiguous clusters. We are done when we hit the last */
  /* cluster in the file or fill all the entries in the    */
  /* map                                                   */
  /*-------------------------------------------------------*/
  NextCLNum = CurCLNum;

  pExt = pCLMap->FatExtent;

  TotalMapSec = 0;

  for ( i=0; i < NUM_FAT_EXTENTS && NextCLNum != pFSVI->FatEOFCLNum; i++  )
  {
    /*------------------------------*/
    /* Initialize cluster map entry */
    /*------------------------------*/
    pExt[i].ExtentRBA = FATClusterToLSN( pFile, NextCLNum );
    pExt[i].ExtentSec = 0;

    /*--------------------------------------------*/
    /* Digest contiguous clusters for this entry  */
    /*--------------------------------------------*/
    do
    {
      pExt[i].ExtentSec += pFSVI->FatSecPerCL;
      TotalMapSec       += pFSVI->FatSecPerCL;
      NextCLNum = FATGetCLNum( pFile, (CurCLNum = NextCLNum) );
    }
    while ( NextCLNum == CurCLNum+1 );
  }

  /*-----------------------------------------*/
  /* Return the number of entries in the map */
  /*-----------------------------------------*/
  pCLMap->ExtentCount = i;

  /*-------------------------------------------------*/
  /* Calculate the file offsets contained in the map */
  /*-------------------------------------------------*/
  pCLMap->FileSecPos[0] = (FileSecPos / pFSVI->FatSecPerCL) * pFSVI->FatSecPerCL;
  pCLMap->FileSecPos[1] = pCLMap->FileSecPos[0] + TotalMapSec;

  /*-------------------------------------------*/
  /* Record the next cluster number in the map */
  /*-------------------------------------------*/
  pCLMap->NextCluster = NextCLNum;

  /*-----------------------------------------------------*/
  /* Release FAT buffer. We really dont need it once the */
  /* cluster map is built.                               */
  /*-----------------------------------------------------*/
  if ( pFSI->pFatCLBuf )
  {
    free( pFSI->pFatCLBuf );
    pFSI->pFatCLBuf = 0;
  }

  return( rc );
}

/*------------------------------------------*/
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*------------------------------------------*/
ULONG FATGetCLNum( PFILE pFile, ULONG ReqCLNum )
{
  PFATINFO      pFSI;
  PFATVOLINFO   pFSVI;
  PVOL          pVol;
  USHORT        i;
  USHORT        j;
  USHORT        n;

  ULONG         rc;

  ULONG         CLIndex;
  ULONG         CLBitIndex;
  ULONG         CLFatSec;

  USHORT        CLNum = 0;
  USHORT        ShiftCnt;
  ULONG         BitsPerSec;

  pVol  = pFile->pVol;
  pFSI  = (PFATINFO)    pFile->FSInfo;
  pFSVI = (PFATVOLINFO) pVol->pFSVolInfo;

  /*-------------------------------------------------------*/
  /* Allocate a FAT buffer if not previously allocated and */
  /* indicate the buffer does not contain valid data.      */
  /*-------------------------------------------------------*/
  if ( !pFSI->pFatCLBuf )
  {
    if ( !(pFSI->pFatCLBuf = (PBYTE) malloc( pVol->SecSize * 2 )) )
    {
      rc = ERR_MALLOC_FAILED;
      goto GetCL_Exit;
    }
    pFSI->FatCLNum[0] = pFSI->FatCLNum[1] = 0;
  }

  /*--------------------------------------------------------*/
  /* Check if the requested cluster number is in our buffer */
  /*--------------------------------------------------------*/
  if ( !(pFSI->FatCLNum[0] <= ReqCLNum && pFSI->FatCLNum[1] > ReqCLNum) )
  {
    /*-------------------------------------*/
    /* Convert cluster number to bit index */
    /*-------------------------------------*/
    CLBitIndex = (ReqCLNum * pFSVI->FatBitsPerCL);

    /*------------------------------------------*/
    /* Calc sector offset from begining of FAT  */
    /*------------------------------------------*/
    BitsPerSec = pVol->SecSize << 3;
    CLFatSec   = CLBitIndex / BitsPerSec;

    /*-----------------------------------------*/
    /* Calc cluster number for this FAT sector */
    /*-----------------------------------------*/
    i = (CLFatSec * BitsPerSec) / pFSVI->FatBitsPerCL;
    j = (CLFatSec * BitsPerSec) % pFSVI->FatBitsPerCL;

    pFSI->FatCLNum[0]    = i;
    pFSI->FatCLBufAlign  = 0;

    /*--------------------------------------------------*/
    /* If this FAT sector starts with a partial cluster */
    /* then calc the bit alignment to the first cluster */
    /* entry and bump the starting cluster.             */
    /*--------------------------------------------------*/
    if ( j )
    {
      pFSI->FatCLBufAlign  = pFSVI->FatBitsPerCL - j;
      pFSI->FatCLNum[0]++;
    }

    /*---------------------------------------------------*/
    /* Calculate the number of clusters contained in the */
    /* next two FAT sectors.                             */
    /*---------------------------------------------------*/
    n = ((BitsPerSec * NUM_FAT_BUFSEC) - j) / pFSVI->FatBitsPerCL;

    pFSI->FatCLNum[1] = i + n;

    /*--------------*/
    /* Read sectors */
    /*--------------*/
    if ( rc = MFSRead( pVol,
                       CLFatSec + pFSVI->FatLSN,
                       pFSI->pFatCLBuf,
                       pVol->SecSize * NUM_FAT_BUFSEC ) )
    {
      goto GetCL_Exit;
    }
  }

  /*-----------------------------------------------------*/
  /* Calculate offset of requested cluster in FAT buffer */
  /*-----------------------------------------------------*/
  CLIndex    = (ReqCLNum - pFSI->FatCLNum[0]);

  /*------------------------------------------------*/
  /* Calculate bit index of requested cluster entry */
  /*------------------------------------------------*/
  CLBitIndex = (CLIndex * pFSVI->FatBitsPerCL) + pFSI->FatCLBufAlign;
  i          = CLBitIndex / 8;


  /*----------------------------------------------------------*/
  /* Get first byte of cluster entry. If it is not positioned */
  /* at bit 0 then shift it over                              */
  /*----------------------------------------------------------*/
  ShiftCnt  = CLBitIndex % 8;
  CLNum     = pFSI->pFatCLBuf[i] >> ShiftCnt;


  /*-------------------------------------------------------*/
  /* Get second byte of cluster entry. Position high order */
  /* cluster bits. Mask out extraneous bits from result.   */
  /*-------------------------------------------------------*/
  CLNum |= ((USHORT)pFSI->pFatCLBuf[i+1]) << (8 - ShiftCnt);
  CLNum &= pFSVI->FatCLMask;

GetCL_Exit: ;

  return( CLNum );
}

/*------------------------------------------*/
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*------------------------------------------*/
ULONG FATClusterToLSN( PFILE pFile, ULONG Cluster )
{
  PFATVOLINFO   pFSVI = (PFATVOLINFO) pFile->pVol->pFSVolInfo;

  /*-------------------------------------------------------*/
  /* Convert fat cluster number into logical sector number */
  /*-------------------------------------------------------*/

  return ( pFSVI->FatSecPerCL * (Cluster-2) + pFSVI->FatClusterLSN );
}

