Beef up the sanity checking of the boot sector. For really better checking
authorhpa <hpa>
Sun, 19 Dec 2004 00:25:14 +0000 (00:25 +0000)
committerhpa <hpa>
Sun, 19 Dec 2004 00:25:14 +0000 (00:25 +0000)
we should be checking the FAT for the media signature, too.

dos/Makefile
dos/argv.c
dos/syslinux.c
mtools/syslinux.c
syslinux.h
syslxmod.c
unix/syslinux.c
win32/syslinux.c

index 5b58af0..c423ded 100644 (file)
@@ -7,6 +7,7 @@ CFLAGS   = -W -Wall -ffreestanding -msoft-float $(OPTFLAGS) $(INCLUDES)
 LDFLAGS         = -T com16.ld
 AR       = ar
 RANLIB   = ranlib
+LIBGCC  := $(shell $(CC) --print-libgcc)
 
 SRCS     = syslinux.c \
           ../syslxmod.c ../bootsect_bin.c ../ldlinux_bin.c \
@@ -33,7 +34,7 @@ spotless: clean
 installer: syslinux.com
 
 syslinux.elf: $(OBJS) libcom.a
-       $(LD) $(LDFLAGS) -o $@ $^
+       $(LD) $(LDFLAGS) -o $@ $^ $(LIBGCC)
 
 libcom.a: $(LIBOBJS)
        -rm -f $@
index 5535331..4c83589 100644 (file)
@@ -73,13 +73,13 @@ int __parse_argv(char ***argv, const char *str)
     if ( ! *p )
       break;
   }
-  q--;                         /* Point to final null */
 
   /* Now create argv */
   arg = ALIGN_UP(q,char *);
   *argv = arg;
   *arg++ = mem;                        /* argv[0] */
 
+  q--;                         /* Point q to final null */
   for ( r = mem ; r < q ; r++ ) {
     if ( *r == '\0' ) {
       *arg++ = r+1;
index cc611c4..3c58edd 100644 (file)
@@ -37,7 +37,7 @@ uint16_t dos_version;
 
 void __attribute__((noreturn)) usage(void)
 {
-  puts("Usage: syslinux [-sf] drive:\n");
+  puts("Usage: syslinux [-sf] drive: [bootsecfile]\n");
   exit(1);
 }
 
@@ -250,7 +250,7 @@ int main(int argc, char *argv[])
   libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */
   int32_t ldlinux_cluster;
   int nsectors;
-  char *device = NULL;
+  const char *device = NULL, *bootsecfile = NULL;
   const char *errmsg;
   int i;
 
@@ -279,8 +279,11 @@ int main(int argc, char *argv[])
        opt++;
       }
     } else {
-      if ( device )
+      if ( bootsecfile )
        usage();
+      else if ( device )
+       bootsecfile = *argp;
+      else
       device = *argp;
     }
   }
@@ -304,7 +307,7 @@ int main(int argc, char *argv[])
   /*
    * Check to see that what we got was indeed an MS-DOS boot sector/superblock
    */
-  if(!syslinux_check_bootsect(sectbuf,&errmsg)) {
+  if( (errmsg = syslinux_check_bootsect(sectbuf)) ) {
     unlock_device(0);
     puts(errmsg);
     putchar('\n');
@@ -314,7 +317,7 @@ int main(int argc, char *argv[])
   ldlinux_name[0] = dev_fd | 0x40;
 
   set_attributes(ldlinux_name, 0);
-  fd = creat(ldlinux_name, 0x27); /* ARCHIVE SYSTEM HIDDEN READONLY */
+  fd = creat(ldlinux_name, 0x07); /* SYSTEM HIDDEN READONLY */
   write_file(fd, syslinux_ldlinux, syslinux_ldlinux_len);
   close(fd);
 
@@ -359,10 +362,15 @@ int main(int argc, char *argv[])
   syslinux_make_bootsect(sectbuf);
 
   /* Write new boot sector */
-  write_device(dev_fd, sectbuf, 1, 0);
-
-  /* Release device lock */
-  unlock_device(0);
+  if ( bootsecfile ) {
+    unlock_device(0);
+    fd = creat(bootsecfile, 0x20); /* ARCHIVE */
+    write_file(fd, sectbuf, 512);
+    close(fd);
+  } else {
+    write_device(dev_fd, sectbuf, 1, 0);
+    unlock_device(0);
+  }
 
   /* Done! */
 
index 004bca3..20111c6 100644 (file)
@@ -193,7 +193,7 @@ int main(int argc, char *argv[])
   /*
    * Check to see that what we got was indeed an MS-DOS boot sector/superblock
    */
-  if( !syslinux_check_bootsect(sectbuf,&errmsg) ) {
+  if( (errmsg = syslinux_check_bootsect(sectbuf)) ) {
     fprintf(stderr, "%s: %s\n", program, errmsg);
     exit(1);
   }
index dd555a9..ccfb0f0 100644 (file)
@@ -31,7 +31,7 @@ void syslinux_make_stupid(void);
 void syslinux_make_bootsect(void *);
 
 /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */
-int syslinux_check_bootsect(const void *bs, const char **errmsg);
+const char *syslinux_check_bootsect(const void *bs);
 
 /* This patches the boot sector and ldlinux.sys based on a sector map */
 int syslinux_patch(const uint32_t *sectors, int nsectors);
index 9a1087e..8599625 100644 (file)
 #include <stdio.h>
 #include <inttypes.h>
 #include <string.h>
+#include <stddef.h>
 
 #include "syslinux.h"
 
 #define LDLINUX_MAGIC  0x3eb202fe
 
 enum bs_offsets {
-  bsJump          = 0x00,
-  bsOemName       = 0x03,
-  bsBytesPerSec   = 0x0b,
-  bsSecPerClust   = 0x0d,
-  bsResSectors    = 0x0e,
-  bsFATs          = 0x10,
-  bsRootDirEnts   = 0x11,
-  bsSectors       = 0x13,
-  bsMedia         = 0x15,
-  bsFATsecs       = 0x16,
-  bsSecPerTrack   = 0x18,
-  bsHeads         = 0x1a,
-  bsHiddenSecs    = 0x1c,
-  bsHugeSectors   = 0x20,
-  bsDriveNumber   = 0x24,
-  bsReserved1     = 0x25,
-  bsBootSignature = 0x26,
-  bsVolumeID      = 0x27,
-  bsVolumeLabel   = 0x2b,
-  bsFileSysType   = 0x36,
-  bsCode          = 0x3e,
+  bsJump            = 0x00,
+  bsOemName         = 0x03,
+  bsBytesPerSec     = 0x0b,
+  bsSecPerClust     = 0x0d,
+  bsResSectors      = 0x0e,
+  bsFATs            = 0x10,
+  bsRootDirEnts     = 0x11,
+  bsSectors         = 0x13,
+  bsMedia           = 0x15,
+  bsFATsecs         = 0x16,
+  bsSecPerTrack     = 0x18,
+  bsHeads           = 0x1a,
+  bsHiddenSecs      = 0x1c,
+  bsHugeSectors     = 0x20,
+
+  /* FAT12/16 only */
+  bs16DriveNumber   = 0x24,
+  bs16Reserved1     = 0x25,
+  bs16BootSignature = 0x26,
+  bs16VolumeID      = 0x27,
+  bs16VolumeLabel   = 0x2b,
+  bs16FileSysType   = 0x36,
+  bs16Code          = 0x3e,
+
+  /* FAT32 only */
+  bs32FATSz32       = 36,
+  bs32ExtFlags      = 40,
+  bs32FSVer         = 42,
+  bs32RootClus      = 44,
+  bs32FSInfo        = 48,
+  bs32BkBootSec     = 50,
+  bs32Reserved      = 52,
+  bs32DriveNumber   = 64,
+  bs32Reserved1     = 65,
+  bs32BootSignature = 66,
+  bs32VolumeID      = 67,
+  bs32VolumeLabel   = 71,
+  bs32FileSysType   = 82,
+  bs32Code          = 90,
+  
   bsSignature     = 0x1fe
 };
 
 #define bsHead      bsJump
 #define bsHeadLen   (bsOemName-bsHead)
-#define bsCodeLen   (bsSignature-bsCode)
+#define bsCode     bs32Code    /* The common safe choice */
+#define bsCodeLen   (bsSignature-bs32Code)
 
 /*
  * Access functions for littleendian numbers, possibly misaligned.
  */
+static inline uint8_t get_8(const unsigned char *p)
+{
+  return *(const uint8_t *)p;
+}
+
 static inline uint16_t get_16(const unsigned char *p)
 {
 #if defined(__i386__) || defined(__x86_64__)
   /* Littleendian and unaligned-capable */
-  return *(uint16_t *)p;
+  return *(const uint16_t *)p;
 #else
   return (uint16_t)p[0] + ((uint16_t)p[1] << 8);
 #endif
@@ -71,7 +97,7 @@ static inline uint32_t get_32(const unsigned char *p)
 {
 #if defined(__i386__) || defined(__x86_64__)
   /* Littleendian and unaligned-capable */
-  return *(uint32_t *)p;
+  return *(const uint32_t *)p;
 #else
   return (uint32_t)p[0] + ((uint32_t)p[1] << 8) +
     ((uint32_t)p[2] << 16) + ((uint32_t)p[3] << 24);
@@ -119,65 +145,87 @@ void syslinux_make_bootsect(void *bs)
 
 /*
  * Check to see that what we got was indeed an MS-DOS boot sector/superblock;
- * Return 0 if bad and 1 if OK.
+ * Return NULL if OK and otherwise an error message;
  */
-int syslinux_check_bootsect(const void *bs, const char **errmsg)
+const char *syslinux_check_bootsect(const void *bs)
 {
   int veryold;
-  unsigned int sectors, clusters;
+  int sectorsize;
+  long long sectors, fatsectors, dsectors;
+  long long clusters;
+  int rootdirents, clustersize;
   const unsigned char *sectbuf = bs;
 
-  *errmsg = 0;
-
-  /*** FIX: Handle FAT32 ***/
+  veryold = 0;
 
-  if ( sectbuf[bsBootSignature] == 0x29 ) {
-    /* It's DOS, and it has all the new nice fields */
-
-    veryold = 0;
-
-    sectors = get_16(sectbuf+bsSectors);
-    sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors);
-    clusters = sectors / sectbuf[bsSecPerClust];
-
-    if ( !memcmp(sectbuf+bsFileSysType, "FAT12   ", 8) ) {
-      if ( clusters > 4086 ) {
-       *errmsg = "FAT12 but claims more than 4086 clusters";
-       return 0;
-      }
-    } else if ( !memcmp(sectbuf+bsFileSysType, "FAT16   ", 8) ) {
-      if ( clusters <= 4086 ) {
-       *errmsg = "FAT16 but claims less than 4086 clusters";
-       return 0;
+  /* Must be 0xF0 or 0xF8..0xFF */
+  if ( get_8(sectbuf+bsMedia) != 0xF0 &&
+       get_8(sectbuf+bsMedia) < 0xF8 )
+    goto invalid;
+  
+  sectorsize = get_16(sectbuf+bsBytesPerSec);
+  if ( sectorsize == 512 )
+    ; /* ok */
+  else if ( sectorsize == 1024 || sectorsize == 2048 || sectorsize == 4096 )
+    return "only 512-byte sectors are supported";
+  else
+    goto invalid;
+
+  clustersize = get_8(sectbuf+bsSecPerClust);
+  if ( clustersize == 0 || (clustersize & (clustersize-1)) )
+    goto invalid;              /* Must be nonzero and a power of 2 */
+
+  sectors = get_16(sectbuf+bsSectors);
+  sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors);
+
+  dsectors = sectors - get_16(sectbuf+bsResSectors);
+
+  fatsectors = get_16(sectbuf+bsFATsecs);
+  fatsectors = fatsectors ? fatsectors : get_32(sectbuf+bs32FATSz32);
+  fatsectors *= get_8(sectbuf+bsFATs);
+  dsectors -= fatsectors;
+
+  rootdirents = get_16(sectbuf+bsRootDirEnts);
+  dsectors -= (rootdirents+sectorsize/32-1)/sectorsize;
+
+  if ( dsectors < 0 || fatsectors == 0 )
+    goto invalid;
+
+  clusters = dsectors/clustersize;
+
+  if ( clusters < 0xFFF5 ) {
+    /* FAT12 or FAT16 */
+
+    if ( !get_16(sectbuf+bsFATsecs) )
+      goto invalid;
+
+    if ( get_8(sectbuf+bs16BootSignature) == 0x29 ) {
+      if ( !memcmp(sectbuf+bs16FileSysType, "FAT12   ", 8) ) {
+       if ( clusters >= 4085 )
+         return "more than 4084 clusters but claims FAT12";
+      } else if ( !memcmp(sectbuf+bs16FileSysType, "FAT16   ", 8) ) {
+       if ( clusters < 4085 )
+         return "less than 4084 clusters but claims FAT16";
+      } else if ( memcmp(sectbuf+bs16FileSysType, "FAT     ", 8) ) {
+       static char fserr[] = "filesystem type \"????????\" not supported";
+       memcpy(fserr+17, sectbuf+bs16FileSysType, 8);
+       return fserr;
       }
-    } else if ( !memcmp(sectbuf+bsFileSysType, "FAT     ", 8) ) {
-      /* OS/2 sets up the filesystem as just `FAT'. */
-    } else {
-      static char fserr[] = "filesystem type \"????????\" not supported";
-      memcpy(fserr+17, sectbuf+bsFileSysType, 8);
-      *errmsg = fserr;
-      return 0;
     }
+  } else if ( clusters < 0x0FFFFFF5 ) {
+    /* FAT32 */
+    /* Moving the FileSysType and BootSignature was a lovely stroke of M$ idiocy */
+    if ( get_8(sectbuf+bs32BootSignature) != 0x29 ||
+        !memcmp(sectbuf+bs32FileSysType, "FAT32   ", 8) )
+      goto invalid;
   } else {
-    veryold = 1;
-
-    if ( sectbuf[bsSecPerClust] & (sectbuf[bsSecPerClust] - 1) ||
-        sectbuf[bsSecPerClust] == 0 ) {
-      *errmsg = "this doesn't look like a FAT filesystem";
-      return 0;
-    }
-
-    sectors = get_16(sectbuf+bsSectors);
-    sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors);
-    clusters = sectors / sectbuf[bsSecPerClust];
-  }
-
-  if ( get_16(sectbuf+bsBytesPerSec) != 512 ) {
-    *errmsg = "sector sizes other than 512 not supported";
-    return 0;
+    goto invalid;
   }
+  
+  return NULL;
 
-  return 1;
+ invalid:
+  return "this doesn't look like a valid FAT filesystem";
 }
 
 /*
@@ -231,6 +279,6 @@ int syslinux_patch(const uint32_t *sectors, int nsectors)
 
   set_32(patcharea+4, csum);
 
-  return 0;
+    return 0;
 }
 
index 362fcc3..60d114f 100644 (file)
@@ -240,7 +240,7 @@ int main(int argc, char *argv[])
   /*
    * Check to see that what we got was indeed an MS-DOS boot sector/superblock
    */
-  if(!syslinux_check_bootsect(sectbuf,&errmsg)) {
+  if( (errmsg = syslinux_check_bootsect(sectbuf)) ) {
     fprintf(stderr, "%s: %s\n", device, errmsg);
     exit(1);
   }
index 2f614b4..758b73f 100644 (file)
@@ -170,7 +170,7 @@ int main(int argc, char *argv[])
   }
   
   /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */
-  if(!syslinux_check_bootsect(sectbuf,&errmsg)) {
+  if( (errmsg = syslinux_check_bootsect(sectbuf)) ) {
     fprintf(stderr, "%s\n", errmsg);
     exit(1);
   }