/*static char *SCCSID = "@(#)chkboot.c	6.2 90/12/10";                                 */
/****************************************************************************/
/*                                                                          */
/*                       IBM/Microsoft Confidential                         */
/*                                                                          */
/*                 Copyright (c) IBM Corporation  1987, 1990                */
/*                 Copyright (c) Microsoft Corp.  1987, 1990                */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/
/************************* Start of Speicifications *************************/
/*                                                                          */
/* Source File Name: chkboot.c                                              */
/*                                                                          */
/* Subroutine Name: chkboot                                                 */
/*                                                                          */
/* Status: 1.2                                                              */
/*                                                                          */
/* Descriptive Name: Examine the boot record and first entry in FAT to see  */
/*                   that this is in fact a FAT disk.                       */
/*                                                                          */
/* Function: Read the boot strap record in sector 1 (+2 bytes),track 0 of   */
/*           the target drive.  Tries to determine if it's FAT.             */
/*                                                                          */
/* Entry Point: chkboot                                                     */
/*                                                                          */
/*         Linkage: fatflag = chkboot( drive )                              */
/*                                                                          */
/*         drive - Pass in the asciiz string for the drive name.            */
/*                                                                          */
/*                                                                          */
/* Exit Normal: fatflag = 1 (Looks like the disk is FAT.)                   */
/*                                                                          */
/* Exit Errors: fatflag = 2 (Can't tell if the disk is FAT, or              */
/*                           disk is definitely not FAT.                    */
/*                                                                          */
/* Effects:-Read sector 1, track 0 of the target drive.                     */
/*         -Resets the disk file pointer to beginning of the disk.          */
/*         -If there is no diskette in the drive, a hard-error-popup occurs */
/*          during the diskette open.                                       */
/*                                                                          */
/* Internal References: none                                                */
/*                                                                          */
/* External References: DosOpen                                             */
/*                      DosChgFilePtr                                       */
/*                      DosDevIOCtl                                         */
/*                      DosRead                                             */
/*                      DosClose                                            */
/*                                                                          */
/************************** End of Specifications ***************************/
/* #include <doscalls.h> */  /* csc */     /* DOS structs and functions */
#include "tmpdos16.h"        /* csc */
#include <string.h>                        /* For string compare        */



#define DRIVEOPEN                   0x80c2 /* Bits for DASD open.       */
#define EXISTS                      0x0001 /* Expect drive to exist.    */

#define NEWBOOT                         41 /*We know there could be a   */
                                           /* file system name in boot  */

/*This is the maximum number of sectors that the FAT FS can handle.     */
#define MAXFATSEC     (unsigned long) 0x008fffff /* 8,388,608 dec       */

#define FAT_ID_MIN    (unsigned short) 0xfff0  /* Minimal media descriptor value*/
#define FAT_ID_MAX    (unsigned short) 0xffff  /* Maximum media descriptor value*/

#define B_P_S         (unsigned short)    512   /* bytes per sector       */
#define FAT_ID_OFF    (unsigned short) B_P_S/2  /*Offset for 1st fat entry*/

#define SIGNATURE  (unsigned short) 0xAA55 /* Boot record signature     */
#define SIGNATURE_OFF                  255 /* offset of signature       */

/*The two possible fatflag values returned from chkboot() follow.       */
#define ERROR_UNSURE_FS (unsigned char) 2  /*Doesn't look like FAT FS.  */
#define IS_FAT          (unsigned char) 1  /*Think this is FAT!         */


#define CAT_DISK                     0x08  /* Logical disk ioctls.      */
#define GETDEVPARMS                  0x63  /* Get BPB.                  */
#define GET_MEDIA_BPB   (unsigned char) 1  /*Get BPB from the media.    */

#define BIG_FAT_THRESHOLD            4085  /*Max clusters for small fat */
#define SIZE_DIR_ENTRY                 32  /* bytes per dir entry       */

#define FS_FAT                       "FAT     "  /* OS/2 FAT fs type    */
#define FS_FAT12                     "FAT12   "  /* PC/DOS 4.0 FAT type */
#define FS_FAT16                     "FAT16   "  /* PC/DOS 4.0 FAT type */

#define FSLEN                                 8  /*length of System Ids */

/*----------------------------------------------------------------------*/
/* entry point for chkboot                                              */
/*----------------------------------------------------------------------*/

unsigned char chkboot( drive )                      /* entry point      */
  unsigned char * drive;                            /* Drive asciiz str */
{

/*----------------------------------------------------------------------*/
/*             data structures and variables                            */
/*----------------------------------------------------------------------*/
struct Boot_BPB {                    /* BPB without reserved field      */
  unsigned short bytes_per_sector;         /* sector size                   2 */
  unsigned char sectors_per_cluster; /* sectors per allocation unit   1 */
  unsigned short reserved_sectors;         /* number of reserved sectors    2 */
  unsigned char nbr_fats;            /* number of fats                1 */
  unsigned short root_entries;             /* number of directory entries   2 */
  unsigned short total_sectors;            /* number of sectors             2 */
  unsigned char media_type;          /* fatid byte                    1 */
  unsigned short sectors_per_fat;          /* sectors in a copy of the FAT  2 */
  unsigned short sectors_per_track;        /* number of sectors per track   2 */
  unsigned short number_of_heads;          /* number of heads               2 */
  unsigned long hidden_sectors;      /* number of hidden sectors      4 */
  unsigned long large_total_sectors; /* large total sectors           4 */
};                                   /*     total byte size = 25        */

union {                        /* union to access the boot record as an */

  unsigned short move_image[ (B_P_S/2)+1];  /* Boot Sector & FAT area.   */

  struct {                     /* external 512 byte boot strap record   */
    char jump_instr[3];        /* short jump instruction + NOP          */
    char version[8];           /* IBM  10.xx                            */
    struct Boot_BPB bios_parms;/* 25 byte BPB                           */
    unsigned char PHYDRV;      /*  /x00 = removable, /x80 = fixed       */
    unsigned char CURHD;       /*  not initialized                      */
    unsigned char boot_sig;    /* Init to NEWBOOT when created.         */
    unsigned long boot_ID;     /*  unique number set by DISKCOPY & SYS  */
    unsigned char boot_VolLabel[ 11 ]; /*@12 Label-set by FORMAT & LABEL*/
    unsigned char boot_SystemId[ 8 ];  /*@12 FS_NAME (file system type) */
  } boot_header;

} boot_area;

struct a_DeviceParameters   {       /*Device packet for Get Media BPB   */
    struct Boot_BPB DP_BPB;
    unsigned short  DP_NumberOfCylinders;
    unsigned char   DP_DeviceType;
    unsigned short  DP_DeviceAttributes;
} deviceParameters;

unsigned long action,NewPointer ;
unsigned short bytes_read,  handle, fatid;
short  rc = 0;

unsigned char command = GET_MEDIA_BPB;
unsigned char fatflag = ERROR_UNSURE_FS;  /*Default is uncertain FS type*/

register i;


/*----------------------------------------------------------------------*/
/*Open the drive to obtain a handle for reading in the boot sector.     */
/*----------------------------------------------------------------------*/

/* Open the Drive*/

rc = DOSOPEN( (char _far16 *) drive,          /* open entire drive         */
              (unsigned short _far16 *) &handle,    /* return handle             */
              (unsigned short _far16 *) &action,    /* return action taken       */
              0L,                          /* make same size            */
              0,                           /* file attribute            */
              EXISTS,                      /* open flag                 */
              (unsigned short)DRIVEOPEN,   /* open mode                 */
              0L);                         /* reserved, must be 0       */

if (rc)
    return( fatflag );

/*----------------------------------------------------------------------*/
/*Make sure R/W Pointer on disk points to beginning (at boot sector)    */
/*Read in the boot sector. Then re-set R/W pointer back to beginning.   */
/*----------------------------------------------------------------------*/

    fatid = boot_area.move_image[FAT_ID_OFF];

    if( (fatid >= FAT_ID_MIN) && (fatid <= FAT_ID_MAX) )  {

        /*Now compare the disk driver's fat_id with the FAT's */

        rc  = DOSDEVIOCTL( (char _far16 *)   & deviceParameters, /* Data area                      */
#ifdef IOCTLEN
                           (unsigned int)   sizeof(struct a_DeviceParameters), /*Data length    */
#endif  /*IOCTLEN*/
                           (char _far16 *)   & command,          /* Command-specific argument list */
#ifdef IOCTLEN
                           (unsigned int)   sizeof(command),  /*Length  of parameter packet     */
#endif  /*IOCTLEN*/
                           (unsigned short)   GETDEVPARMS,      /* Device-specific function code  */
                           (unsigned short)   CAT_DISK,         /* Device category, logical disk  */
                           (unsigned short)   handle  ) ;       /* Device handle returned by Open */


        if( !rc )   {   /*See if disk drivers media descriptor matches FATs*/

            fatid &= 0x00FF ;    /* Look at low byte of word value        */
                                 /* (works for 1.5 byte entries as well)  */

            if( deviceParameters.DP_BPB.media_type == (unsigned char) fatid  )
                fatflag = IS_FAT;

        }   /*End ioctl successful*/

    }   /*End fatid is within range*/

rc = DOSCHGFILEPTR( (unsigned short )             handle,
                    (long)           0L,   /* bytes to move.      */
                    (unsigned short)  0,   /* (0 = from beginning)*/
                    (unsigned long _far16 *) & NewPointer);

rc = DOSREAD( (unsigned short) handle,
              (char _far16 *)     boot_area.move_image,
              (unsigned short) B_P_S+2,
              (unsigned short _far16 * )&bytes_read );

rc = DOSCHGFILEPTR( (unsigned short )             handle,
                    (long)           0L,    /* bytes to move.      */
                    (unsigned short)  0,    /* (0 = from beginning)*/
                    (unsigned long _far16 *) & NewPointer);

i  = DOSCLOSE( (unsigned short) handle);

/*----------------------------------------------------------------------*/
/*If we can't read the boot record, we can't tell if it's FAT.          */
/*Return with fatflag FALSE.                                            */
/*----------------------------------------------------------------------*/
if( rc || !bytes_read )
    return( fatflag );

/*----------------------------------------------------------------------*/
/*Attempt to validate boot record.                                      */
/*First check the boot record signature to see it's not corrupted.      */
/*----------------------------------------------------------------------*/
if( boot_area.move_image[SIGNATURE_OFF] != SIGNATURE )
    return( fatflag );


/*----------------------------------------------------------------------*/
/*Next look in the boot record for a BPB, by seeing if the values in the*/
/*BPB are non-zero.                                                     */
/*Early version of PC/DOS had no BPB in the boot sector, so the BPB test*/
/*just lets us know if we think the BPB can be trusted.                 */
/*----------------------------------------------------------------------*/
if( boot_area.boot_header.bios_parms.bytes_per_sector          &&
    boot_area.boot_header.bios_parms.sectors_per_cluster       &&
    boot_area.boot_header.bios_parms.reserved_sectors          &&
    boot_area.boot_header.bios_parms.root_entries              &&
   (boot_area.boot_header.bios_parms.total_sectors ||
    boot_area.boot_header.bios_parms.large_total_sectors)      &&
    boot_area.boot_header.bios_parms.media_type                &&
    boot_area.boot_header.bios_parms.sectors_per_fat           &&
    boot_area.boot_header.bios_parms.sectors_per_track         &&
    boot_area.boot_header.bios_parms.number_of_heads   )  {   /*Have something for BPB*/


    /*----------------------------------------------------------------------*/
    /*Next look at the FAT specific info in the boot record info in the BPB.*/
    /*If any of this info is unexpected, this boot record may not be FAT!   */
    /*Also if LargeSectors is used, see if size exceeds FAT FS limit        */
    /*----------------------------------------------------------------------*/
    if( (boot_area.boot_header.bios_parms.nbr_fats         != 0)            &&
        (boot_area.boot_header.bios_parms.large_total_sectors < MAXFATSEC) ) {


        /*----------------------------------------------------------------------*/
        /*Then see if this boot record contains a File System Name by looking   */
        /*at the new info boot signature value.  If it is new, then look at the */
        /*File System name value.                                               */
        /*----------------------------------------------------------------------*/

        if( boot_area.boot_header.boot_sig == NEWBOOT ) {

        if( ( ! strnicmp( boot_area.boot_header.boot_SystemId, FS_FAT   ,FSLEN ) ) ||
            ( ! strnicmp( boot_area.boot_header.boot_SystemId, FS_FAT12 ,FSLEN ) ) ||
            ( ! strnicmp( boot_area.boot_header.boot_SystemId, FS_FAT16 ,FSLEN ) ) )
                fatflag = IS_FAT ;

        }   /*End new style boot sector*/
        else
            fatflag = IS_FAT; /*BPB looks FAT.*/


    }   /*End fields that are FAT related are valid, see if new boot record*/


}   /*End has a BPB, Try to tell by BPB values if FAT*/
else    {

/*----------------------------------------------------------------------*/
/*We have a boot sector without a BPB.  Look at the media descriptor in */
/*the FAT and see if it looks valid.                                    */
/* Validate the media descriptor in the FAT.                            */
/* See if the 1st entry in the FAT is within the valid media descriptor */
/* range, (f0 - ff).                                                    */
/*Issue a Get Media BPB to the disk device driver, which calculated     */
/*a media descriptor the the media based on its size.                   */
/*Compare the 2 media descriptors. If they match, the disk seems FAT.   */
/*----------------------------------------------------------------------*/

    fatid = boot_area.move_image[FAT_ID_OFF];

    if( (fatid >= FAT_ID_MIN) && (fatid <= FAT_ID_MAX) )  {

        /*Now compare the disk driver's fat_id with the FAT's */

        rc  = DOSDEVIOCTL( (char _far16 *)   & deviceParameters, /* Data area                      */
#ifdef IOCTLEN
                           (unsigned int)   sizeof(struct a_DeviceParameters), /*Data length    */
#endif  /*IOCTLEN*/
                           (char _far16 *)   & command,          /* Command-specific argument list */
#ifdef IOCTLEN
                           (unsigned int)   sizeof(command),  /*Length  of parameter packet     */
#endif  /*IOCTLEN*/
                           (unsigned short)   GETDEVPARMS,      /* Device-specific function code  */
                           (unsigned short)   CAT_DISK,         /* Device category, logical disk  */
                           (unsigned short)   handle  ) ;       /* Device handle returned by Open */


        if( !rc )   {   /*See if disk drivers media descriptor matches FATs*/

            fatid &= 0x00FF ;    /* Look at low byte of word value        */
                                 /* (works for 1.5 byte entries as well)  */

            if( deviceParameters.DP_BPB.media_type == (unsigned char) fatid  )
                fatflag = IS_FAT;

        }   /*End ioctl successful*/

    }   /*End fatid is within range*/

}   /*End a BPB-less Boot Sector*/


return( fatflag );


}   /*End chkboot*/
