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

#include "os2types.h"

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

PDCB            pDCBAnchor;

/*------------------------------------------*/
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*                                          */
/*------------------------------------------*/
ULONG MFSInit()
{
  ULONG         i;
  ULONG         j;
  ULONG         rc = 0;

  PDCB          pDCB;
  PDCB          *ppDCBNext;

  /*----------------------------------------------------*/
  /* Call low-level driver to get supported drive count */
  /*----------------------------------------------------*/

  j = DD_GetDriveCount();                /* get the # of partitionable disks */

  /*----------------------------------------------------------*/
  /* For each device build a DCB and pass it to the low-level */
  /* driver to provide information about the device.          */
  /*----------------------------------------------------------*/

  ppDCBNext = &pDCBAnchor;

  for ( i=0; i < j; i++ )                     /* for each partitionable disk */
  {

    if ( !(pDCB = (PDCB) malloc(sizeof(DCB))) )   /* allocate a DCB per disk */
    {
      rc = ERR_MALLOC_FAILED;
      break;
    }

    memset( pDCB, 0, sizeof(DCB) );

    if ( rc = DD_GetDriveInfo( i, pDCB ) )
    {
      free( pDCB );
      continue;
    }

    *ppDCBNext = pDCB;                                     /* chain the DCBs */
     ppDCBNext = &pDCB->pDCBNext;
  }


  /*---------------------------------------------------------*/
  /* If the device is partitionable, then scan the partition */
  /* records and build logical volumes. Otherwise, build a   */
  /* single volume for the device.                           */
  /*---------------------------------------------------------*/

  pDCB = pDCBAnchor;

  while ( pDCB )
  {
    if ( pDCB->Flags & DCBF_PARTITIONED )
    {
      MFSPartDevice( pDCB );
    }
    else
    {
      MFSVolDevice( pDCB );
    }

    pDCB = pDCB->pDCBNext;
  }

  /*--------------------------------------------------------*/
  /* Assign drive letters to the volumes created previously */
  /*--------------------------------------------------------*/

  MFSAssignVolLetters();

  /*--------------------------------------------------*/
  /* Assign filesystem the volumes created previously */
  /*--------------------------------------------------*/

  MFSAssignFS();

  return( rc );
}


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

ULONG MFSPartDevice( PDCB pDCB )
{
  ULONG         rc = 0;
  ULONG         i;

  PMBRENTRY     pMBR;
  ULONG         MBRPos;
  ULONG         NewMBR;

  PVOL          pVol;
  PVOL          *ppVolNext;

  PBYTE         pSecBuf;

  if ( !(pSecBuf = (PBYTE) malloc(pDCB->SecSize)) )
  {
    rc = ERR_MALLOC_FAILED;
    goto PartDev_Exit;
  }

  ppVolNext = &pDCB->pVolChain;

  NewMBR = 1;                                    /* more boot record to read */
  MBRPos = 0;                         /* sector # of the current boot record */

  while ( !rc && NewMBR )
  {                                     /* read the boot record into pSecBuf */
    if ( rc = pDCB->pDriveRead( pDCB, MBRPos, pSecBuf, pDCB->SecSize ) )
    {
      break;
    }

    pMBR = (PMBRENTRY) &pSecBuf[MBR_OFFSET]; /* start of the partition table */

    NewMBR = 0;                        /* assume no extended partition found */

    /*--------------------------------------------------*/
    /* Find a valid partition table entry and build the */
    /* initial values in the volume control block       */
    /*--------------------------------------------------*/
    for ( i = 0; i < MAX_MBR_ENTRY; i++, pMBR++ )
    {
      if ( pMBR->PartType == PARTTYPE_OS2IFS ||     /* valid partition type? */
           pMBR->PartType == PARTTYPE_FAT12  ||
           pMBR->PartType == PARTTYPE_FAT16       )
      {
        if ( !(pVol = (PVOL) malloc(sizeof(VOL))) )   /* alloc control block */
        {
          rc = ERR_MALLOC_FAILED;
          goto PartDev_Exit;
        }

        memset( pVol, 0, sizeof(VOL) );    /* clear the volume control block */

        *ppVolNext = pVol;           /* setup the volume control block chain */
         ppVolNext = &pVol->pVolNext;


        pVol->pDCB       = pDCB;                            /* chain the DCB */
        pVol->MBRPos     = MBRPos;                   /* start of boot record */
        pVol->BootRecPos = MBRPos + NONALIGNED(pMBR,RelOffset);
        pVol->PartType   = pMBR->PartType;        /* save the partition type */
        pVol->SecSize    = pDCB->SecSize;         /*          sector size    */
        pVol->SecShift   = pDCB->SecShift;

        if ( !MBRPos )                                /* master boot record? */
        {
          pVol->Flags = VOLF_PRIMARY;            /* primary partition volume */
        }
      }
    }

    pMBR = (PMBRENTRY) &pSecBuf[MBR_OFFSET];     /* start of partition table */

    /*--------------------------------------------------*/
    /* Look for an extended partition (logical drive)   */
    /* and use the RelOffset to get to the beginning of */
    /* the extended partition boot record.              */
    /*--------------------------------------------------*/
    for ( i = 0; i < MAX_MBR_ENTRY; i++, pMBR++ )
    {
      if ( pMBR->PartType == PARTTYPE_EXTENDED )      /* extented partition? */
      {
        MBRPos += NONALIGNED(pMBR,RelOffset);        /* # of sectors to skip */
        NewMBR  = 1;                                /* read next boot record */
      }
    }
  }


PartDev_Exit:  ;
  if ( pSecBuf )
  {
    free( pSecBuf );
  }

  return( rc );
}

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

ULONG MFSVolDevice( PDCB pDCB )
{
  ULONG         rc = 0;
  PVOL          pVol;

  if ( !(pVol = (PVOL) malloc(sizeof(VOL))) )
  {
    rc = ERR_MALLOC_FAILED;
    goto VolDev_Exit;
  }

  memset( pVol, 0, sizeof(VOL) );

  pDCB->pVolChain = pVol;

  pVol->pDCB     = pDCB;
  pVol->SecSize  = pDCB->SecSize;
  pVol->SecShift = pDCB->SecShift;

VolDev_Exit: ;

  return( rc );
}


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


ULONG MFSAssignVolLetters()
{
  ULONG         DriveLetter;
  ULONG         DsktLetter;
  ULONG         FirstVol;

  PDCB          pDCB;
  PVOL          pVol;


  DriveLetter = 'C';
  DsktLetter  = 'A';


  /*--------------------------------------------*/
  /* Assign drive letters to primary partitions */
  /* starting with C:                           */
  /*--------------------------------------------*/

  FirstVol    = 1;
  pDCB        = pDCBAnchor;                            /* disk chain anchor */

  while( pDCB )                                   /* walk down the DCB chain */
  {
    if ( pDCB->Flags & DCBF_PARTITIONED )             /* partitionable disk? */
    {
      if ( pVol = pDCB->pVolChain )       /* volume control block allocated? */
      {
        if ( pVol->Flags & VOLF_PRIMARY || FirstVol )  /* primary partition? */
        {
          pVol->DriveLetter = DriveLetter++;          /* assign drive letter */
        }
      }
    }
    FirstVol = 0;

    pDCB = pDCB->pDCBNext;                         /* next disk in the chain */
  }

  /*------------------------------------------------*/
  /* Assign drive letters to non-primary partitons  */
  /* starting with next available drive letter      */
  /*------------------------------------------------*/

  pDCB = pDCBAnchor;                                    /* disk chain anchor */

  while( pDCB )                                   /* walk down the DCB chain */
  {
    if ( pDCB->Flags & DCBF_PARTITIONED )             /* partitionable disk? */
    {
      if ( pVol = pDCB->pVolChain )       /* volume control block allocated? */
      {
        while ( pVol )              /* walk down each partition on this disk */
        {
          if ( !pVol->DriveLetter )              /* not a primary partition? */
          {
            pVol->DriveLetter = DriveLetter++;   /* assign it a drive letter */
          }

          pVol = pVol->pVolNext;                           /* next partition */
        }
      }
    }
    pDCB = pDCB->pDCBNext;                         /* next disk in the chain */
  }

  /*---------------------------------------------*/
  /* Assign removable drive letters              */
  /*---------------------------------------------*/

  pDCB = pDCBAnchor;                                    /* disk chain anchor */

  while( pDCB )                                  /* walk down the disk chain */
  {
    if ( !(pDCB->Flags & DCBF_PARTITIONED) )             /* removable media? */
    {
      if ( pVol = pDCB->pVolChain )       /* volume control block allocated? */
      {
        while ( pVol )            /* walk down each partition on this device */
        {
          if ( (pDCB->DevType == DCB_DEVTYPE_DSKT) && DsktLetter <= 'B' )
          {
            pVol->DriveLetter = DsktLetter++;              /* diskette drive */
          }
          else
          {
            pVol->DriveLetter = DriveLetter++;   /* assign it a drive letter */
          }

          pVol = pVol->pVolNext;                           /* next partition */
        }
      }
    }
    pDCB = pDCB->pDCBNext;                         /* next disk in the chain */
  }

  return( 0 );
}

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


ULONG MFSAssignFS()
{
  PDCB          pDCB;
  PVOL          pVol;


  pDCB  = pDCBAnchor;                                   /* disk chain anchor */

  while( pDCB )                                  /* walk down the disk chain */
  {
    pVol = pDCB->pVolChain;

    while( pVol )                          /* volume control block allocated */
    {
      pVol->Flags |= VOLF_MOUNT_REQ;             /* volume requires mounting */

      if ( pDCB->Flags & DCBF_REMOVABLE )                /* removable media? */
      {
        if ( pDCB->DevType == DCB_DEVTYPE_CDROM )           /* CDROM device? */
        {
          pVol->pFSMount  = NULL; /* CDFSMount */       /* no support for it */
        }
        else
        {
          pVol->pFSMount  = (PMFSFN) FATMount;          /* FAT mount routine */
        }
      }
      else                                                 /* fix disk media */
      {
        switch ( pVol->PartType )
        {
          case PARTTYPE_OS2IFS:                                     /* HPFS? */
            pVol->pFSMount  = (PMFSFN) HPFSMount;      /* HPFS mount routine */
            break;

          default:                                            /* must be FAT */
            pVol->pFSMount  = (PMFSFN) FATMount;        /* FAT mount routine */
        }

      }

      pVol = pVol->pVolNext;                     /* next volume on this disk */
    }
    pDCB = pDCB->pDCBNext;                         /* next disk in the chain */
  }

  return( 0 );
}

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

ULONG MFSMapDrive (PCHAR pDrive)
{
  register PDCB pDCB;
  register PVOL pVol;
  ULONG HPFSPartCnt = 0;
  ULONG FATPartCnt = 0;
  INT driveLetter;

  /*
  **--------------------------------------------
  ** Search the volume chain counting the number
  ** of HPFS partitions until we reach the target
  ** drive (pDrive).  Skip removeable media. We
  ** can't assume a drive ordering.
  **--------------------------------------------
  */

  pDCB  = pDCBAnchor;

  driveLetter = toupper (*pDrive);

  while (pDCB) {
    
    pVol = pDCB->pVolChain;
    
    while (pVol) {
      
      if (pDCB->Flags & DCBF_REMOVABLE)

	continue;

      else
	
        switch (pVol->PartType) {

	case PARTTYPE_OS2IFS:

	  /*
	  **--------------------------------------------
	  ** Make sure the target drive is not an HPFS
	  ** partition. Only add up those HPFS partitions
	  ** which are before the target drive letter.
	  **--------------------------------------------
	  */

	  if (pVol->DriveLetter < driveLetter)
	    HPFSPartCnt++;
	  else if (pVol->DriveLetter == driveLetter)
	    goto OnError;

	  break;
	  
	default:

	  FATPartCnt++;

        } /* end switch */
	
      pVol = pVol->pVolNext;

    } /* end while */

    pDCB = pDCB->pDCBNext;

  } /* end while */

  /*
  **-------------------------------------------
  ** Adjust the target drive to reflect a file
  ** system with HPFS drives ignored.
  **--------------------------------------------
  */
  
  *pDrive -= HPFSPartCnt;

  return 0;

 OnError:

  return ERR_FILE_NOT_FOUND;

}
