/*static char *SCCSID = "@(#)chkboot.c	6.1 90/11/16";*/
/*static char *SCCSID = "@(#)chkboot.c	6.3 90/01/24";*/

/****************************************************************************/
/*	 IBM/Microsoft Confidential					    */
/*									    */
/*	 Copyright (c) IBM Corporation 1987, 1989			    */
/*	 Copyright (c) Microsoft Corporation 1987, 1989			    */
/*									    */
/*	 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>			   /* DOS structs and functions */
#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 bytes_per_sector;	     /* sector size		      2 */
  unsigned char sectors_per_cluster; /* sectors per allocation unit   1 */
  unsigned reserved_sectors;	     /* number of reserved sectors    2 */
  unsigned char nbr_fats;	     /* number of fats		      1 */
  unsigned root_entries;	     /* number of directory entries   2 */
  unsigned total_sectors;	     /* number of sectors	      2 */
  unsigned char media_type;	     /* fatid byte		      1 */
  unsigned sectors_per_fat;	     /* sectors in a copy of the FAT  2 */
  unsigned sectors_per_track;	     /* number of sectors per track   2 */
  unsigned 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 far *) drive,	   /* open entire drive		*/
	      (unsigned far *) &handle,	   /* return handle		*/
	      (unsigned far *) &action,	   /* return action taken	*/
	      0L,			   /* make same size		*/
	      0,			   /* file attribute		*/
	      EXISTS,			   /* open flag			*/
	      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.	*/
/*----------------------------------------------------------------------*/
rc = DOSCHGFILEPTR( (unsigned )		    handle,
		    (unsigned long)	    0L,	 /* bytes to move.	*/
		    (unsigned)		    0,	 /* (0 = from beginning)*/
		    (unsigned long far *) & NewPointer);

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

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

i  = DOSCLOSE( (unsigned) 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 far *)	  & deviceParameters, /* Data area			*/
#ifdef IOCTLEN
			   (unsigned int)   sizeof(struct a_DeviceParameters), /*Data length	*/
#endif	/*IOCTLEN*/
			   (char far *)	  & command,	      /* Command-specific argument list */
#ifdef IOCTLEN
			   (unsigned int)   sizeof(command),  /*Length	of parameter packet	*/
#endif	/*IOCTLEN*/
			   (unsigned int)   GETDEVPARMS,      /* Device-specific function code	*/
			   (unsigned int)   CAT_DISK,	      /* Device category, logical disk	*/
			   (unsigned int)   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*/
