/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "src/dev/dasd/os2dasd/dmbpb.c, dsdm, r205apar 93/06/02";*/
#define SCCSID  "src/dev/dasd/os2dasd/dmbpb.c, dsdm, r205apar 93/06/02"

/**************************************************************************
 *
 * SOURCE FILE NAME = DMBPB.C
 *
 * DESCRIPTIVE NAME = OS2DASD.DMD - OS/2 DASD Device Manager
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : Partition/BPB Management Routines
 *
 *
 *
*/
#include "dmh.h"

/*--------------------------------------------------------------------------
;
;** Process_Partition - Process the partition table of the fixed disk
;
;   Process_Partition processes the partition table obtained from the
;   fixed disk and determines where the DOS boot sector is found on the
;   disk.
;
;   USHORT Process_Partition (NPVOLCB pVolCB, PULONG VolBootRBA,
;                                             PULONG NumSectors)
;
;   ENTRY:    pVolCB           - input pointer to VolCB
;             VolBootRBA       - returned RBA of Volume Boot Sector
;             NumSectors       - returned number of sectors in partition
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if valid partition)
;
;   EFFECTS:
;
;   NOTES:    Global variable ScratchBuffer contains boot sector
;             on input.
;
;----------------------------------------------------------------------------*/

USHORT Process_Partition (pVolCB, VolBootRBA, NumSectors)

NPVOLCB pVolCB;
PULONG  VolBootRBA;
PULONG  NumSectors;
{
   BOOL   found;
   USHORT i;
   MBR    *pMBR = (MBR *) ScratchBuffer;
   ULONG  temp;
   fBigFat = 0;

   pVolCB->Flags &= ~vf_NoDOSPartition;         /* Partition ok so far */

   if (pMBR->Signature != 0xAA55)               /* Check for signature word */
      pVolCB->Flags |= vf_NoDOSPartition;       /* Partition invalid */
   else
   {
      found = FALSE;
      for (i = 0; i < 4 && found == FALSE ; i++)
      {
         found = TRUE;
         switch (pMBR->PartitionTable[i].SysIndicator)
         {
            case PARTITION_16M:         /* Partition up to 16M */
                   break;
            case PARTITION_16Mto32M:    /* Partition > 16M and <= 32 M */
                   break;
            case PARTITION_32M:         /* Partition > 32M  */
                   break;
            case PARTITION_IFS:         /* IFS Partition */
                   break;
            case PARTITION_FTACTIVE:    /* Active Fault Tolerant partition */
                   pVolCB->Flags |= vf_FTPartition;
                   break;
            case PARTITION_FTINACTIVE:  /* Inactive Fault Tolerant partition */
                   pVolCB->Flags |= vf_FTPartition;
                   break;
            default:
               found = FALSE;
         }
      }

      /* If invalid partition type or valid found and < 32K in size */
      /* then indicate not a valid partition.                       */

      i--;
      if (!found || pMBR->PartitionTable[i].NumSectors < 64)
         pVolCB->Flags |= vf_NoDOSPartition;    /* Partition invalid */
      else
      {
         if ((DDFlags & DDF_INIT_TIME) &&                            /*@V64818*/
                                 (pVolCB->Flags & vf_FTPartition))   /*@V64818*/
            NumFTPartitions ++;

         pVolCB->PartitionType = pMBR->PartitionTable[i].SysIndicator;

         /* Make sure end of partition within 4G sectors of start of disk */
         if (f_add32(pMBR->PartitionTable[i].RelativeSectors,
             pMBR->PartitionTable[i].NumSectors) == 0)
                 fBigFat |= vf_TooBig;

         pVolCB->MediaBPB.HiddenSectors=pMBR->PartitionTable[i].RelativeSectors;

         if (pMBR->PartitionTable[i].NumSectors <= (ULONG) 0xffff)
            pVolCB->MediaBPB.TotalSectors=(USHORT)pMBR->PartitionTable[i].NumSectors;
         else
         {
            pVolCB->MediaBPB.TotalSectors = 0;
            pVolCB->MediaBPB.BigTotalSectors=pMBR->PartitionTable[i].NumSectors;
         }

         /* Return RBA of Volume Boot Sector and NumSectors in Partition */

         *VolBootRBA = pVolCB->MediaBPB.HiddenSectors;

         *NumSectors = pMBR->PartitionTable[i].NumSectors;
      }
   }
   if (pVolCB->Flags & vf_NoDOSPartition)
      return(ERROR);
   else
      return(NO_ERROR);
}



/*--------------------------------------------------------------------------
;
;** Process_Boot - Process the boot sector of the fixed disk
;
;   Process_Boot examines the boot sector read off the fixed disk
;   and builds a BPB for it in the BDS for the drive. If the boot
;   sector is not valid, it assumes a default BPB from a table.
;   If this is a > 32M partition and the boot sector does not have
;   a valid BPB, 'C' is returned. The caller should then set up a
;   "minimum" BPB and set the fTooBig bit.
;
;   USHORT Process_Boot (NPVOLCB pVolCB, ULONG SectorsInPartition)
;
;   ENTRY:    pVolCB             - input pointer to VolCB
;             SectorsInPartition - Number of sectors in partition
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if valid partition)
;
;   EFFECTS:
;
;   NOTES:    Global variable ScratchBuffer contains boot sector
;             on input.
;
;----------------------------------------------------------------------------*/

USHORT Process_Boot(pVolCB, SectorsInPartition)

NPVOLCB  pVolCB;
ULONG    SectorsInPartition;

{
   USHORT rc, i;
   ULONG  TotalSectors, temp;
   USHORT Unknown = YES;
   PDOSBOOTREC pBootRec = (PDOSBOOTREC) ScratchBuffer;

   /*--------------------------------------------------------*/
   /* Check for a valid boot sector and use the BPB in it to */
   /* build the MediaBPB in the VolCB.  It is assumed that   */
   /* ONLY SectorsPerCluster, NumFatSectors, MaxDirEntries   */
   /* and MediaType need to be set.                          */
   /*--------------------------------------------------------*/

   if (Is_BPB_Boot(pVolCB,pBootRec) == NO_ERROR)                     /*@V64818*/
   {
      pVolCB->MediaBPB.MediaType = pBootRec->bpb.MediaType;
      if (pBootRec->bpb.TotalSectors != 0)
          TotalSectors = pBootRec->bpb.TotalSectors;
      else
          TotalSectors = pBootRec->bpb.BigTotalSectors;

      /* Make sure there are enough sectors for the boot sector, */
      /* plus the FAT sectors plus the directory sectors.        */

      if (TotalSectors > (1 + (pBootRec->bpb.NumFATSectors * 2) +
                         (pBootRec->bpb.MaxDirEntries / 16)))
      {
        pVolCB->MediaBPB.NumFATSectors = pBootRec->bpb.NumFATSectors;
        pVolCB->MediaBPB.MaxDirEntries = pBootRec->bpb.MaxDirEntries;
        pVolCB->MediaBPB.SectorsPerCluster = pBootRec->bpb.SectorsPerCluster;

        /* Calculate sectors left for data sectors */

        TotalSectors = TotalSectors - (1 + (pBootRec->bpb.NumFATSectors * 2) +
                                      (pBootRec->bpb.MaxDirEntries / 16));

        if ( (pVolCB->PartitionType != PARTITION_IFS) &&             /*@V64818*/
             !(pVolCB->Flags & vf_FTPartition)        &&             /*@V64818*/
             (pVolCB->MediaBPB.SectorsPerCluster)     &&             /*@V64818*/
             (TotalSectors / pVolCB->MediaBPB.SectorsPerCluster) > 4096-10 )
           fBigFat |= vf_Big;           /* Set FBIG if 16 bit FAT */

        Unknown = NO;
      }
   }

   if (Unknown == YES)
   {
      /* If IFS, zero out FAT related fields */

      if (pVolCB->PartitionType == PARTITION_IFS)
      {
         pVolCB->MediaBPB.SectorsPerCluster = 0;
         pVolCB->MediaBPB.ReservedSectors = 0;
         pVolCB->MediaBPB.NumFATs = 0;
         pVolCB->MediaBPB.MaxDirEntries = 512;
         if (pVolCB->MediaBPB.TotalSectors != 0)
            pVolCB->MediaBPB.BigTotalSectors = pVolCB->MediaBPB.TotalSectors;
         pVolCB->MediaBPB.TotalSectors = 0;
         pVolCB->MediaBPB.MediaType = MEDIA_FIXED_DISK;              /*@V64818*/
         pVolCB->MediaBPB.NumFATSectors = 0;
      }
      else
      {

         /* Find appropriate DiskTable entry based on SectorsInPartition */

         for (i = 0; i < DISKTABLECOUNT; i++)
           if (SectorsInPartition <= DiskTable[i].NumSectors)
              break;

         fBigFat = DiskTable[i].Flags;
         pVolCB->MediaBPB.MaxDirEntries = DiskTable[i].MaxDirEntries;
         pVolCB->MediaBPB.SectorsPerCluster= DiskTable[i].SectorsPerCluster;
         pVolCB->MediaBPB.MediaType = MEDIA_FIXED_DISK;

         /* Calculate number of FAT table sectors */

         if (fBigFat & vf_Big)
         {
            temp = (pVolCB->MediaBPB.SectorsPerCluster * 256) + 2;
            pVolCB->MediaBPB.NumFATSectors = (SectorsInPartition -
               ((pVolCB->MediaBPB.MaxDirEntries / 16) + 1) + /* Dir + Reserved*/
               temp - 1 +
               (pVolCB->MediaBPB.SectorsPerCluster * 2)) / temp;
         }
         else
         {
            TotalSectors = SectorsInPartition +
                           pVolCB->MediaBPB.SectorsPerCluster - 1;

            TotalSectors >>= DiskTable[i].Log2SectorsPerCluster;

            TotalSectors = ((TotalSectors + 1) & (0xFFFFFFFE)) + 2;

            temp = TotalSectors;

            TotalSectors >>= 1;

            pVolCB->MediaBPB.NumFATSectors = (TotalSectors + temp + 511) / 512;
         }
      }
   }

   pVolCB->Flags |= vf_BigFat;

   pVolCB->RecBPB = pVolCB->MediaBPB;   /* Copy media BPB to recommended BPB */
}


/*--------------------------------------------------------------------------
;
;** BPBFromBoot - Get BPB from the boot sector passed in
;
;   BPBFromBoot builds a BPB for the disk/media type from the boot
;   sector passed to it.
;
;   USHORT BPBFromBoot (NPVOLCB pVolCB, PDOSBOOTREC pBootSector)
;
;   ENTRY:    pVolCB           - VolCB for the drive
;             pBootSector      - DOS Boot Sector
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if valid boot sector)
;
;   EFFECTS:
;
;   NOTES:  This code assumes that the media you are attempting to
;           build on is
;               a)  a DOS disk
;               b)  if it is a >2.x disk, the boot sector
;                   lies at sector 1 on track 0 (head 0).  The first
;                   3 bytes are a JMP, the next 8 bytes are a media
;                   ID ASCII string, and the 11 bytes following that
;                   are the BPB for that disk.
;               c)  if the above conditions (3 byte jump, 8 ascii ID
;                   string) are not met, then we assume the media is
;                   a DOS 1.x disk.  On a 1.x disk sector 2 on
;                   track 0 (head 0) contains the first FAT sector.
;                   The first byte of the first FAT sector contains
;                   the "FAT ID byte" which is the media ID byte for
;                   this disk.
;
;----------------------------------------------------------------------------*/
USHORT FAR f_BPBFromBoot (pVolCB, pBootSector)

NPVOLCB     pVolCB;
PDOSBOOTREC pBootSector;
{
   return(BPBFromBoot (pVolCB, pBootSector));
}

USHORT BPBFromBoot (pVolCB, pBootSector)

NPVOLCB pVolCB;
PDOSBOOTREC pBootSector;
{
   USHORT rc = NO_ERROR;

   if (!(pVolCB->Flags & vf_ReturnFakeBPB))    /* Do nothing if this is set */
   {
      if ( (rc = Is_BPB_Boot(pVolCB,pBootSector)) == NO_ERROR)       /*@V64818*/
      {
         BootBPB_To_MediaBPB (pVolCB, pBootSector);

         /*------------------------------------------------------------------
         ; In pre-DOS 3.20 versions of Format, there was a     that caused
         ; the sectors per cluster field in the BPB in the boot sector to
         ; be set to 2 even when the medium was single-sided. We "fix" this
         ; by setting the field to 1 on the diskettes that may have been
         ; affected. This will ensure that whatever has been recorded on
         ; those media will remain intact.  Also, we fix the     in the
         ; EZ-FORMAT utility which incorrectly set the hidden sectors field
         ; to 1. We'll reset it back to 0 so those diskettes can be read.
         ------------------------------------------------------------------*/

         if (pVolCB->pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE)
         {

           if ( ((pVolCB->MediaBPB.MediaType == MEDIA_160KB) ||
                 (pVolCB->MediaBPB.MediaType == MEDIA_180KB)) &&
                 (GetBootVersion(pBootSector) < 32) )

                pVolCB->MediaBPB.SectorsPerCluster = 1;


           if ( (pVolCB->MediaBPB.HiddenSectors != 0) &&
                ( *((PBYTE)pBootSector + 0xA1) == 'F') &&
                ( *((PBYTE)pBootSector + 0xA2) == 'a') &&
                ( *((PBYTE)pBootSector + 0xA3) == 'l') &&
                ( *((PBYTE)pBootSector + 0xA4) == 'k') )

                pVolCB->MediaBPB.HiddenSectors = 0;
         }
      }
   }

   return(rc);
}

/*--------------------------------------------------------------------------
;
;** BPBFromScratch - Build a BPB for the disk
;
;   BPBFromScratch builds a BPB for the drive where the boot sector
;   contains an invalid BPB in it.
;
;   For fixed disks, the partition table is read and from it the
;   location of the boot sector is obtained. The boot sector is then
;   read and the BPB built from there. An error is returned if the
;   partition table is invalid or is too small (< 32K bytes).
;
;   USHORT BPBFromScratch (NPVOLCB pVolCB, PRP pRP)
;
;   ENTRY:    pVolCB           - VolCB for the drive
;             pRP              - Request Packet
;
;   RETURN:   USHORT           - Packet Status
;
;   EFFECTS:
;
;----------------------------------------------------------------------------*/
USHORT FAR f_BPBFromScratch (pVolCB)

NPVOLCB  pVolCB;

{
   return(BPBFromScratch(pVolCB));
}


USHORT BPBFromScratch (pVolCB)

NPVOLCB  pVolCB;

{
   UCHAR   MediaType;
   USHORT  rc;
   ULONG   VolBootRBA, NumSectors;

   /* Wait for the ScratchBuffer to be free before doing a read */

   f_SWait (&ScratchBufSem);

   if (pVolCB->pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE)      /* If floppy drive */
   {
      /* Read in the first FAT sector */

      if ((rc = ReadSecInScratch_RBA (pVolCB, 1L, 0)) == NO_ERROR)
      {
         MediaType = ScratchBuffer[0];
         if (pVolCB->MediaBPB.MediaType != MediaType)
         {
            switch(MediaType)
            {
               case MEDIA_144MB:                         /* 1.44MB & 2.88MB */
                     if (pVolCB->RecBPB.SectorsPerTrack == 18)
                        pVolCB->MediaBPB = BPB_144MB;    /* 1.44 MB */
                     else
                        pVolCB->MediaBPB = BPB_288MB;    /* 2.88 MB */
                     break;

               case MEDIA_12MB:                          /* 1.2MB & 720 KB */
                     if (pVolCB->RecBPB.SectorsPerTrack == 15)
                        pVolCB->MediaBPB = BPB_12MB;     /* 1.2 MB */
                     else
                        pVolCB->MediaBPB = BPB_720KB;    /* 720 KB */
                     break;

               case MEDIA_360KB:
                        pVolCB->MediaBPB = BPB_360KB;
                        break;

               case MEDIA_320KB:
                        pVolCB->MediaBPB = BPB_320KB;
                        break;

               case MEDIA_180KB:
                        pVolCB->MediaBPB = BPB_180KB;
                        break;

               case MEDIA_160KB:
                        pVolCB->MediaBPB = BPB_160KB;
                        break;

               default:
                        rc = STDON + STERR + ERROR_I24_NOT_DOS_DISK;
            }
         }
      }
   }
   else  /* Fixed disk */
   {
      /* Read RBA 0 - Master Boot Record  */

      if ((rc = ReadSecInScratch_RBA (pVolCB, 0, 1)) == NO_ERROR)
      {
         if ((rc = Process_Partition (pVolCB, &VolBootRBA, &NumSectors))
                                                            == NO_ERROR)
         {
            if ((rc = ReadSecInScratch_RBA (pVolCB, VolBootRBA, 1))
                                                            == NO_ERROR)
               Process_Boot (pVolCB, NumSectors);
         }
         else
         {
            pVolCB->MediaBPB = BPB_Minimum;      /* Setup minimum BPB */
            pVolCB->RecBPB = pVolCB->MediaBPB;   /* Copy MinBPB to recommended*/
            rc = NO_ERROR;
         }
      }
   }

   f_SSig (&ScratchBufSem);     /* Release scratch buffer */

   return (rc);
}

/*--------------------------------------------------------------------------
;
;**  BootBPB_To_MediaBPB
;
;   Copies the BPB from the passed boot sector to the media BPB in the
;   Volume Control Block. If the boot sector is older than DOS 3.2,
;   the 12 byte extended BPB is not copied because it may contain garbage.
;
;   VOID BootBPB_to_MediaBPB (NPVOLCB pVolCB, PDOSBOOTREC pBootSector
;                              USHORT Type)
;
;   ENTRY:    pVolCB           - input pointer to VolCB
;             pBootSector      - Boot Sector
;             Type             - 0 = disk, 1 = diskette
;
;   RETURN:   VOID
;
;   EFFECTS:
;
;   NOTES:
;
--------------------------------------------------------------------------*/

VOID BootBPB_To_MediaBPB (pVolCB, pBootSector)

NPVOLCB    pVolCB;
PDOSBOOTREC pBootSector;
{
   USHORT  i;
   BOOL    done = FALSE;
   USHORT  version = 0;

   version = GetBootVersion(pBootSector);


/* There's a     in DR DOS which incorrectly adds the partition offset */
/* to the hidden sectors field in the BPB.  We recognize this case     */
/* by checking for version 3.3 and subtracting out the partition offset.*/

   if ( (version == 33) &&
        ( !(pVolCB->pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE) ) &&
        (pVolCB->PartitionOffset > 0) &&
        (pBootSector->bpb.HiddenSectors ==
              (pVolCB->MediaBPB.HiddenSectors + pVolCB->PartitionOffset) ) )

           pBootSector->bpb.HiddenSectors = pVolCB->MediaBPB.HiddenSectors;


   /* Boot sector < DOS 4.0 (and known version!) */

   if ((version > 0) && (version < 40))                             /*@V108555*/
      if (version != 33)     //P62137
          pBootSector->bpb.HiddenSectors &= 0xffffff7f;

   /* Copy over BPB */

   pVolCB->MediaBPB = pBootSector->bpb;


   /* If version < DOS 3.3 (and known!) or its a diskette and using */
   /* TotalSectors then zero out the extended portion of the BPB    */

   if ( ((version > 0) && (version < 33)) ||                        /*@V108555*/
      ((pVolCB->pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE) &&
       (pBootSector->bpb.TotalSectors != 0))  )
   {
      pVolCB->MediaBPB.HiddenSectors &= 0x0000FFFF;
      pVolCB->MediaBPB.BigTotalSectors = 0;
      for (i = 0; i < sizeof(pVolCB->MediaBPB.Reserved_1); i++)
         pVolCB->MediaBPB.Reserved_1[i] = 0;
   }
}

/*--------------------------------------------------------------------------
;
;** f_BPBfromGeom - Initialize BPB using Geometry info
;
;   Initialize the BPB for a VolCB from the Geometry info returned by
;   the adapter driver.
;
;   VOID InitBPBfromGeom (NPVOLCB pVolCB, NPBPB pBPB, PGEOMETRY pGeometry)
;
;   ENTRY:    pVolCB           - input pointer to VolCB
;             pBPB             - pointer to BPB to fill in
;             pGeometry        - pointer to Geometry info
;
;   RETURN:   VOID
;
;   EFFECTS:
;
;----------------------------------------------------------------------------*/

/* Moved from DMINIT */                                              /*@V63867*/
                                                                     /*@V63867*/
VOID FAR f_BPBfromGeom (pVolCB, pBPB, pGeometry)                     /*@V63867*/

NPVOLCB   pVolCB;
NPBPB     pBPB;
PGEOMETRY pGeometry;

{
   ULONG  TotalCylinders, TotalSectors, TotalDataSectors, temp;
   ULONG  SectorsPerCyl;                                             /*@V63867*/
   USHORT i;
   NPUNITCB pUnitCB;

   pUnitCB = pVolCB->pUnitCB;
   TotalSectors = pGeometry->TotalSectors;

   /* If removable SCSI device which doesnt return geometry data when */
   /* no media is in the drive, then just put a default size in.      */

   if (TotalSectors == 0)                                            /*@V46569*/
      TotalSectors = 5760;                                           /*@V46569*/

   pUnitCB->LastRBA = TotalSectors;

   /* If the unit is removable and it's not bigger than a 2.88M drive  */
   /* then use one of the canned BPBs we have for the specified drive. */

   if ((pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE) && (TotalSectors <= 5760))
   {
      pVolCB->NumPhysCylinders = pGeometry->TotalCylinders;
      pVolCB->NumLogCylinders  = pGeometry->TotalCylinders;
      pVolCB->BootRecCyl       = 0;                                  /*@V89787*/

      switch (TotalSectors)
      {
          case 720:         /* 5.25 inch - 360 KB Drive */
            *pBPB = BPB_360KB;
            break;

          case 1440:        /* 3.5 inch - 720 KB Drive */
            *pBPB = BPB_720KB;
            break;

          case 2400:        /* 5.25 inch - 1.2M Drive */
            *pBPB = BPB_12MB;
            break;

          case 2880:        /* 3.5 inch - 1.44M Drive */
            *pBPB = BPB_144MB;
            break;

          case 5760:        /* 3.5 inch - 2.88M Drive */
            *pBPB = BPB_288MB;
            break;

          default:
            *pBPB = BPB_144MB;
            break;
      }
   }

   /* If it's a fixed disk, or a removable drive we dont have a canned */
   /* BPB for, then calculate the rest of the BPB.                     */

   else
   {
      /* If the drive doesnt return any Geometry information other *//*@V63867*/
      /* than TotalSectors, then create a virtual geometry for     *//*@V63867*/
      /* the drive, else copy the Geometry data into the BPB.      *//*@V63867*/
                                                                     /*@V63867*/
      if (pGeometry->NumHeads != 0                                   /*@V63867*/
           && pGeometry->BytesPerSector != 0                         /*@V63867*/
               && pGeometry->SectorsPerTrack != 0)                   /*@V63867*/
      {                                                              /*@V63867*/
         pBPB->BytesPerSector  = pGeometry->BytesPerSector;          /*@V63867*/
         pBPB->NumHeads        = pGeometry->NumHeads;                /*@V63867*/
         pBPB->SectorsPerTrack = pGeometry->SectorsPerTrack;         /*@V63867*/
      }                                                              /*@V63867*/
      else                                                           /*@V63867*/
      {                                                              /*@V63867*/
         pBPB->BytesPerSector  = 512;                                /*@V63867*/
         pBPB->NumHeads        = 64;                                 /*@V63867*/
         pBPB->SectorsPerTrack = 32;                                 /*@V63867*/
      }                                                              /*@V63867*/
                                                                     /*@V63867*/
      /* Make TotalSectors consistent with CHS geometry     */       /*@V63867*/
      /*                                                    */       /*@V63867*/
      /* This prevents later problems during FORMAT if the  */       /*@V63867*/
      /* last partial cylinder is accessed.                 */       /*@V63867*/
                                                                     /*@V63867*/
      TotalSectors  = pGeometry->TotalSectors;                       /*@V63867*/
                                                                     /*@V63867*/
      SectorsPerCyl = (ULONG) pBPB->SectorsPerTrack *                /*@V63867*/
                      (ULONG) pBPB->NumHeads;                        /*@V63867*/
                                                                     /*@V63867*/
      TotalCylinders = TotalSectors / SectorsPerCyl;                 /*@V63867*/
                                                                     /*@V63867*/
      TotalSectors = TotalCylinders * SectorsPerCyl;                 /*@V63867*/
                                                                     /*@V63867*/
      pVolCB->NumLogCylinders  = TotalCylinders;                     /*@V63867*/
      pVolCB->NumPhysCylinders = TotalCylinders;                     /*@V63867*/
      pVolCB->BootRecCyl       = 0;                                  /*@V89787*/
                                                                     /*@V63867*/
      if (TotalSectors > 0xffff)                                     /*@V63867*/
      {                                                              /*@V63867*/
         pBPB->BigTotalSectors = TotalSectors;                       /*@V63867*/
         pBPB->TotalSectors = 0;                                     /*@V63867*/
      }                                                              /*@V63867*/
      else                                                           /*@V63867*/
      {                                                              /*@V63867*/
         pBPB->BigTotalSectors = 0;                                  /*@V63867*/
         pBPB->TotalSectors = TotalSectors;                          /*@V63867*/
      }                                                              /*@V63867*/
                                                                     /*@V63867*/

      /* If it's a removable drive, then calculate the file system fields */
      /* i.e. NumFATSectors, etc. in the BPB.                             */

      if (pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE)
      {
         /* Find appropriate DiskTable entry based on TotalSectors */

         for (i = 0; i < DISKTABLECOUNT; i++)
           if (TotalSectors <= DiskTable[i].NumSectors)
              break;

         fBigFat = DiskTable[i].Flags;
         pBPB->ReservedSectors = 1;
         pBPB->MaxDirEntries = DiskTable[i].MaxDirEntries;
         pBPB->SectorsPerCluster = DiskTable[i].SectorsPerCluster;
         pBPB->NumFATs = 2;
         pBPB->MediaType = 0xF0;

         /* Calculate number of FAT table sectors */

         if (fBigFat & vf_Big)
         {
            temp = (pBPB->SectorsPerCluster * 256) + 2;
            pBPB->NumFATSectors = (TotalSectors -
               ((pBPB->MaxDirEntries / 16) + 1) +        /* Dir + Reserved*/
               temp - 1 + (pBPB->SectorsPerCluster * 2)) / temp;
         }
         else
         {
            TotalDataSectors = TotalSectors + pBPB->SectorsPerCluster - 1;

            TotalDataSectors >>= DiskTable[i].Log2SectorsPerCluster;

            TotalDataSectors = ((TotalDataSectors + 1) & (0xFFFFFFFE)) + 2;

            temp = TotalDataSectors;

            TotalDataSectors >>= 1;

            pBPB->NumFATSectors = (TotalDataSectors + temp + 511) / 512;
         }
      }
   }
}

/*--------------------------------------------------------------------------
;
;** Is_BPB_Boot - Is this a valid boot sector ?
;
;   ScratchBuffer points to the boot sector.  In theory, the BPB is
;   correct.  We can, therefore, get all the relevant information
;   from the media (those that we cannot get from the partition
;   table) if we can recognize it.
;
;   USHORT Is_BPB_Boot (PDOSBOOTREC pBootSector)
;
;   ENTRY:    pBootSector      - DOS Boot Sector
;
;   RETURN:   USHORT           - Result Code (NO_ERROR if valid boot sector)
;
;   EFFECTS:
;
;   NOTES:
;
--------------------------------------------------------------------------*/
USHORT Is_BPB_Boot (pVolCB, pBootSector)                             /*@V64818*/
                                                                     /*@V64818*/
PDOSBOOTREC pBootSector;                                             /*@V64818*/
NPVOLCB     pVolCB;                                                  /*@V64818*/
{                                                                    /*@V64818*/
   USHORT rc = NO_ERROR;                                             /*@V64818*/
   UCHAR  MediaType;                                                 /*@V64818*/
   UCHAR  PartType;                                                  /*@V64818*/
                                                                     /*@V64818*/
   /* 1. Make sure short or near jmp is at start of boot sector */   /*@V64818*/
   /* 2.     and high nibble of MediaType in BPB is 0xF         */   /*@V64818*/
   /* 3.         and SectorsPerCluster in BPB is a power of 2   */   /*@V64818*/
                                                                     /*@V64818*/
   MediaType = pBootSector->bpb.MediaType & 0xF0;                    /*@V64818*/
   PartType  = pVolCB->PartitionType;                                /*@V64818*/
                                                                     /*@V64818*/
   if (!((pBootSector->JmpCode == 0xE9) ||                           /*@V64818*/
         (pBootSector->JmpCode == 0xEB && pBootSector->nop == 0x90)))/*@V64818*/
   {                                                                 /*@V64818*/
      rc = ERROR;                                                    /*@V64818*/
   }                                                                 /*@V64818*/
   else if ( !pBootSector->bpb.MaxDirEntries )                       /*@V64818*/
   {                                                                 /*@V64818*/
      rc = ERROR;                                                    /*@V64818*/
   }                                                                 /*@V64818*/
   /* Checks are relaxed for IFS partitions or clones */             /*@V64818*/
   /* of IFS partitions                               */             /*@V64818*/
                                                                     /*@V64818*/
   else if ( PartType == PARTITION_IFS       ||                      /*@V64818*/
             PartType == PARTITION_FTACTIVE  ||                      /*@V64818*/
             PartType == PARTITION_FTINACTIVE   )                    /*@V64818*/
   {                                                                 /*@V64818*/
     if ( MediaType && (MediaType != 0xF0) )                         /*@V64818*/
     {                                                               /*@V64818*/
        rc = ERROR;                                                  /*@V64818*/
     }                                                               /*@V64818*/
   }                                                                 /*@V64818*/
   else                                                              /*@V64818*/
   {                                                                 /*@V64818*/
     if ( MediaType != 0xF0 )                                        /*@V64818*/
     {                                                               /*@V64818*/
        rc = ERROR;                                                  /*@V64818*/
     }                                                               /*@V64818*/
     else if ( !PowerOF2(pBootSector->bpb.SectorsPerCluster) )       /*@V64818*/
     {                                                               /*@V64818*/
        rc = ERROR;                                                  /*@V64818*/
     }                                                               /*@V64818*/
   }                                                                 /*@V64818*/
                                                                     /*@V64818*/
   return(rc);                                                       /*@V64818*/
}                                                                    /*@V64818*/

/*--------------------------------------------------------------------------
;
;** GetBootVersion - Get boot version from boot record
;
;   Get the boot version of the DOS boot sector which appears as a
;   set of characters in the form 'XX.XX' after the 'DOS' or 'IBM'
;   characters.
;
;   USHORT GetBootVersion (PDOSBOOTREC pBootSector)
;
;   ENTRY:    pBootSector      - DOS Boot Sector
;
;   RETURN:   USHORT           - Boot Version
;
;   EFFECTS:
;
;   NOTES:
;
--------------------------------------------------------------------------*/
USHORT GetBootVersion (pBootSector)

PDOSBOOTREC pBootSector;
{
   USHORT i;
   USHORT version = 0;

   for (i = 0; i < sizeof(pBootSector->Release); i++)
   {
      if (pBootSector->Release[i] >= '0' && pBootSector->Release[i] <= '9')
      {
         version = version * 10;
         version = version + (pBootSector->Release[i] - '0');
         if (pBootSector->Release[i+1] == '.')
         {
            version = version * 10;
            version = version + (pBootSector->Release[i+2] - '0');
            break;
         }
      }
   }
   return(version);
}




