Search notes:

Partition tables and file systems

Structures

BlockDevice.h

#include <stdio.h>    // FILE
#include <sys/stat.h>

typedef struct /* _BlockDevice*/ {

       //
       // Modelled after libparted, PedDevice (include/parted/device.h)
       //


//        PedDevice*      next;
//
//        char*           model;          /**< \brief description of hardware
//                                             (manufacturer, model) */
          char*           path;           /**< device /dev entry */
//
//        PedDeviceType   type;           /**< SCSI, IDE, etc. \sa PedDeviceType */
//        long long       sector_size;            /**< logical sector size */
//        long long       phys_sector_size;       /**< physical sector size */
//        PedSector       length;                 /**< device length (LBA) */
//
//        int             open_count; /**< the number of times this device has
//                                         been opened with ped_device_open(). */
//        int             read_only;
//        int             external_mode;
//        int             dirty;
//        int             boot_dirty;
//
//        PedCHSGeometry  hw_geom;
//        PedCHSGeometry  bios_geom;
//        short           host, did;
//
//
//     ------------------------- 
//        void*           arch_specific;
//
//     Following is modelled after LinuxSpecific (libparted/arch/linux.h)

//	int	fd;
    FILE     *f;
  	int	      major;
  	int	      minor;
//	char*    	dmtype;         /**< device map target type */
#if USE_BLKID
              blkid_probe probe;
              blkid_topology topology;
#endif

    // TQ84...

    struct    stat stat_;

} BlockDevice;

MSDosPartition.h

#include <stdint.h>  // uint8_t etc.

/* note: lots of bit-bashing here, thus, you shouldn't look inside it.
 * Use chs_to_sector() and sector_to_chs() instead.
 */
typedef struct {
	uint8_t		head;
	uint8_t		sector;
	uint8_t		cylinder;
} __attribute__((packed)) RawCHS;

/* ripped from Linux source */
typedef struct /* parted names it: _DosRawPartition */ {
 //
 // The boot indicator is either 0x80 (active) or 0x00 (inactive): 
 //
 // Only ONE partition table entry should be active. And it should be
 // in the the master boot record, not in the extended boot record.
 //
    uint8_t    boot_indicator;

 // ----------------------------------------------
 //
 //    https://thestarman.pcministry.com/asm/mbr/PartTables.htm says:
 //        Starting Sector in CHS values. These values pinpoint the location of
 //        a partition's first sector, if  it's within the first 1024 cylinders
 //        of a hard disk.
 //        When a sector is beyond that point, the CHS tuples are normally set
 //        to their maximum allowed values of 1023, 254, 63; which stand for
 //        the 1024th cylinder, 255th head and 63rd sector, due to the fact,
 //        cylinder and head counts begin at zero. These values appear on the
 //        disk as the three bytes: FE FF FF (in that order).

    RawCHS     partition_start_chs;
 // ----------------------------------------------
 //
 //   Partition type (https://www.win.tue.nl/~aeb/partitions/partition_types-1.html, https://thestarman.pcministry.com/asm/mbr/PartTypes.htm)
 //       0x00:  empty
 //       0x01:  FAT12
 //       0x04:  FAT16 SM (?)
 //       0x05:  Dos Ext
 //       0x06:  FAT16
 //       0x07:  NTFS or HPFS
 //       0x0b:  FAT32
 //       0x0c:  FAT32 LBA
 //       0x0e:  FAT16 LBA
 //       0x0f:  Ext LBA
 //    The FAT* and NTFS types can be combined with the 0x10 flag which
 //    means: hidden.
 //
 //    The partition type (of extended partitions?) allows to determine if
 //    CHS or LBA addressing is to be applied.
 //       0x05: CHS Addressing
 //       0x0F: LBA Addressing
 //
 //
    uint8_t    partition_type;
//  ----------------------------------------------
    RawCHS     partition_end_chs;
    uint32_t   partition_start_lba;
    uint32_t   nof_sectors;
}__attribute__((packed)) MSDosPartitionTableEntry;

MSDosMasterBootRecord.h

#include "MSDosPartition.h"

#define NOF_MSDOS_PRIMARY_PARTITIONS 4

typedef unsigned char MSDosBootRecordSignature[2];

typedef struct /* parted names it: _DosRawTable */  {
//
//   The MBR (Master Boot Record)
//
//   Originally ripped from Linux source.
//
    union {
     //
     // The first two bytes of the boot code apparently also indicate
     // the version of the MBR as follows (https://www.bibase.com/mbr.htm):
     //    fa 33: Dos 3.3 through Windows 95 a
     //    33 c0: Windows 95B, 98, 98SE, ME, 2000, XP, Vista 
     //    fa eb: LILO
     //    eb 3c: Windows Floppy Disk boot record 
     //
     //  https://github.com/joyent/syslinux/commit/d0f275981c9289dc4b8df64e72cd9902bf85aebe says: 
     //     Apparently some BIOSes (including some Acer Travelmate machines)
     //     require an MBR to start with 0x33; apparently Micro$oft MBRs start
     //     with 33 C0, an alternate coding of the "xorw %ax,%ax" instruction.  As
     //     such, follow suit to work on these braindead BIOSes.
        unsigned char          version   [  2];
     // --------------------------------------------------------------
        char                   boot_code [440];
    };
 // ------------------------------------------------------------------
 // mbr_signature apparently is a unique ID.
 // Beginning with Windows 2K, the value of mbr_signature is stored
 // in the registry under
 //     HKLM\System\MountedDevices
 //
    uint32_t                  mbr_signature;
 // ------------------------------------------------------------------
    uint16_t                  unknown;
    MSDosPartitionTableEntry  partitions [NOF_MSDOS_PRIMARY_PARTITIONS];
 // ------------------------------------------------------------------
 // The msdos_boot_record_signature contains 0xaa55 in little endian format.
 // Thus msdos_boot_record_signature[0] is the low byte and should equal to 0x55 while
 // msdos_boot_record_signature[1] is the high byte and shoul equal to 0xaa.
 //
    MSDosBootRecordSignature  msdos_boot_record_signature;
 // ------------------------------------------------------------------
}  __attribute__((packed)) MSDosMasterBootRecord;

MSDosExtendedBootRecord.h

typedef struct {

 //
 // https://en.wikipedia.org/wiki/Extended_boot_record
 //

 // The first 446 bytes somewhat corresponds to boot_code in MBR (MSDosMBR) (but PROBABLY (to be verified) does not have boot code.
 // But boot code in the MBR consists of 440 bytes, though.
 //
    char unused[446];

 // --------------------------------------------------------------------------------------
 //
 // The first entry of an EBR points to the logical partition of that EBR.
 //
 // The second entry of an EBR either
 //   - points to the next EBR in the chain, or
 //   - is filled with zeroes
 //
    MSDosPartitionTableEntry   thisLogicalPartition;
    MSDosPartitionTableEntry   nextExtendedBootRecord;
    

    unsigned char unused_third_entry [16]; // should be filled with zeroes
    unsigned char unused_fourth_entry[16]; // should be filled with zeroes

 //
 // Analogous to msdos_boot_record_signature in MSDosMasterBootRecord.
 //
    MSDosBootRecordSignature  msdos_boot_record_signature;

} MSDosExtendedBootRecord;

main.c

// #include <stdio.h>
// #include <fcntl.h>
#include <stdlib.h>
// #include <stdint.h>
#include <sys/sysmacros.h>

typedef unsigned long long sector;
const sector sector_size = 512;

#include "structs/BlockDevice.h"
#include "structs/MSDosMasterBootRecord.h"
#include "structs/MSDosExtendedBootRecord.h"

#include <stdio.h>
#include <inttypes.h> // PRIu32 etc.

#define SCSI_DISK0_MAJOR          8
#define SCSI_CDROM_MAJOR         11
#define SCSI_DISK1_MAJOR         65
#define SCSI_DISK7_MAJOR         71
#define SCSI_DISK8_MAJOR        128
#define SCSI_DISK15_MAJOR       135

//
// TODO: Sector size is assumed to be 512. Is this assumption a given?
//
#define ASSUMED_SECTOR_SIZE     512

// libparted/arch/linux.c: 
#define SCSI_BLK_MAJOR(M) (                                             \
                (M) == SCSI_DISK0_MAJOR                                 \
                || (M) == SCSI_CDROM_MAJOR                              \
                || ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) \
                || ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))

void openBlockDevice(BlockDevice* b) {
 //
 // Modelled after libparted: linux_open() - libparted/arch/linux.c
 //

    b->f    = fopen(b->path, "r");

    if (!b->f) exit(1);
}

void stat_(BlockDevice* b) {

    if (stat(b->path, &b->stat_)) {
      printf("Could not stat %s\n", b->path);
      exit(-1);
    }

    if (! S_ISBLK(b->stat_.st_mode)) {
      printf("Not a block device\n");
      exit(-1);
    }

 //
 // major() and minor() are defined in <sys/sysmacros.h>
 // 
    b->major = major(b->stat_.st_rdev);
    b->minor = minor(b->stat_.st_rdev);
}

int isSCSI(BlockDevice* b) {
 //
 // See libparted/arch/linux.c - _device_probe_type
 //
    return SCSI_BLK_MAJOR (b->major) && (b->minor % 0x10 == 0);
}
int isIDE(BlockDevice* b) {
 //
 // See libparted/arch/linux.c - _device_probe_type
 //

    if (b->major ==  3 ||
        b->major == 22 ||
        b->major == 33 ||
        b->major == 34 ||
        b->major == 56 ||
        b->major == 57) {
 
       if (b->minor & 0x40) {
         return 1;
       }
       else {
         printf("almost IDE");
       }
    }
    return 0;
}

int checkMSDosBootRecordSignature(MSDosBootRecordSignature *signature) {

    return (*signature)[0] == 0x55 && 
           (*signature)[1] == 0xaa;

}

void readSector(sector nr, BlockDevice *b, void *dest) {

    if (fseek(b->f, nr * ASSUMED_SECTOR_SIZE, SEEK_SET)) {
        perror("fseek");
    }
    if (fread(dest, ASSUMED_SECTOR_SIZE, 1, b->f) != 1) {
        perror("fread");
    }
}

int isMSDosMasterBootRecord(BlockDevice *b, MSDosMasterBootRecord *mbr) {

 //
 // Read first sector
 //
    readSector(0, b, mbr);
    return checkMSDosBootRecordSignature(&mbr->msdos_boot_record_signature);
}

void disassemble_msdos_boot_code(MSDosMasterBootRecord *t) {

  FILE *f = fopen("msdos_boot_code.bin", "w");
  if (!f) exit(2);

  fwrite(t->boot_code, sizeof(t->boot_code), 1, f);
  fclose(f);

//system("objdump -D -b binary -m i386  -Mintel,x86-64 -z msdos_boot_code.bin");
//system("objdump -D -b binary -m i386  -Mintel           msdos_boot_code.bin");
  system("objdump -D -b binary -m i8086 -Mintel           msdos_boot_code.bin");

}

char const* partitionTypeToString(MSDosPartitionTableEntry *entry) {

//
// Wikipedia: Other extended partition types which may hold EBRs include the
//            deliberately hidden types 0x15, 0x1F, 0x91 and 0x9B, the access-restricted
//            types 0x5E and 0x5F, and the secured types 0xCF and 0xD5. 

   if (entry->partition_type == 0x00) return "Empty";
   if (entry->partition_type == 0x01) return "FAT12 ";
   if (entry->partition_type == 0x02) return "Xenix";
   if (entry->partition_type == 0x03) return "Xenix";
   if (entry->partition_type == 0x04) return "FAT16 (max. 32 MB)";
   if (entry->partition_type == 0x05) return "Extended DOS-Partition" ;  // Max. 2 GB
   if (entry->partition_type == 0x06) return "FAT16"                  ;  // Max. 2 GB
   if (entry->partition_type == 0x07) return "HPFS/NTFS";
   if (entry->partition_type == 0x08) return "AIX ";
   if (entry->partition_type == 0x09) return "AIX bootable";
   if (entry->partition_type == 0x0A) return "OS/2 Bootmanager";
   if (entry->partition_type == 0x0B) return "FAT32 (CHS)";
   if (entry->partition_type == 0x0C) return "FAT32 (LBA)";
   if (entry->partition_type == 0x0E) return "FAT16 (LBA)";
   if (entry->partition_type == 0x0F) return "Extended partition (LBA)";  // More than 1024 cylinders
   if (entry->partition_type == 0x10) return "OPUS"                    ;  // Or »hidden« ?
   if (entry->partition_type == 0x11) return "Hidden FAT12";
   if (entry->partition_type == 0x12) return "Compaq diagnost";
   if (entry->partition_type == 0x14) return "Hidden FAT16 bis 32MB";
   if (entry->partition_type == 0x16) return "Hidden FAT16";
   if (entry->partition_type == 0x17) return "Hidden HPFS / NTFS";
   if (entry->partition_type == 0x18) return "AST Windows swap";
   if (entry->partition_type == 0x1B) return "Hidden WIN95 FAT32";
   if (entry->partition_type == 0x1C) return "Hidden WIN95 FAT32 (LBA)";
   if (entry->partition_type == 0x1E) return "Hidden WIN95 FAT16 (LBA)";
   if (entry->partition_type == 0x24) return "NEC DOS";
   if (entry->partition_type == 0x27) return "MSFT Recovery";
   if (entry->partition_type == 0x39) return "Plan 9";
   if (entry->partition_type == 0x3C) return "Partition Magic";
   if (entry->partition_type == 0x40) return "Venix 80286";
   if (entry->partition_type == 0x41) return "PPC PReP boot";
   if (entry->partition_type == 0x42) return "LDM"                            ;// or LDM ?
   if (entry->partition_type == 0x4D) return "QNX4.x";
   if (entry->partition_type == 0x4E) return "QNX4.x 2nd partition";
   if (entry->partition_type == 0x4F) return "QNX4.x 3rd partition";
   if (entry->partition_type == 0x50) return "OnTrack DM";
   if (entry->partition_type == 0x51) return "OnTrack DM6 Aux";
   if (entry->partition_type == 0x52) return "CP/M";
   if (entry->partition_type == 0x53) return "OnTrack DM6 Aux";
   if (entry->partition_type == 0x54) return "OnTrack DM6";
   if (entry->partition_type == 0x55) return "EZ-Drive";
   if (entry->partition_type == 0x56) return "Golden Bow";
   if (entry->partition_type == 0x5c) return "Priam Edisk";
   if (entry->partition_type == 0x61) return "Speed Stor";
   if (entry->partition_type == 0x63) return "GNU HURD or SYS";
   if (entry->partition_type == 0x64) return "Novell NetWare";
   if (entry->partition_type == 0x65) return "Novell";
   if (entry->partition_type == 0x70) return "Disk Secure Mult";
   if (entry->partition_type == 0x75) return "UNIX PC/IX";
   if (entry->partition_type == 0x80) return "aktiv (old Minix)";
   if (entry->partition_type == 0x81) return "Booten von Laufwerk D:";
   if (entry->partition_type == 0x82) return "Linux Swap"            ;
   if (entry->partition_type == 0x83) return "Linux native"          ;
   if (entry->partition_type == 0x84) return "OS/2 hidden C:"        ;
   if (entry->partition_type == 0x85) return "LINUX extended"        ;  // Wikipedia:  Linux supports the concept of a second extended partition chain with type 0x85 — this type is hidden (unknown) for other operating systems supporting only one chain.
   if (entry->partition_type == 0x86) return "NTFS volume set"       ;
   if (entry->partition_type == 0x87) return "NTFS volume set"       ;
   if (entry->partition_type == 0x8e) return "LINUX LVM";
   if (entry->partition_type == 0x93) return "Amoebla";
   if (entry->partition_type == 0x94) return "Amoebla BBT";
   if (entry->partition_type == 0x9F) return "BSD/OS";
   if (entry->partition_type == 0xA0) return "IBM Thinkpad hidden";
   if (entry->partition_type == 0xA5) return "BSD/386";
   if (entry->partition_type == 0xA6) return "Open BSD";
   if (entry->partition_type == 0xA7) return "NeXT STEP";
   if (entry->partition_type == 0xAF) return "HFS";
   if (entry->partition_type == 0xB7) return "BSDI fs";
   if (entry->partition_type == 0xB8) return "BSDI swap";
   if (entry->partition_type == 0xBF) return "SUN UFS";
   if (entry->partition_type == 0xC1) return "DRDOS/sec (FAT32)";
   if (entry->partition_type == 0xC4) return "DRDOS/sec (FAT32(LBA))";
// if (entry->partition_type == 0xC5) return "DRDOS/sec ?";                        // Wikipedia: DR DOS 6.0 and higher support secured extended partitions using 0xC5, which are invisible to other operating systems.
   if (entry->partition_type == 0xC6) return "DRDOS/sec (FAT16(LBA))";
   if (entry->partition_type == 0xC7) return "Syrinx";
   if (entry->partition_type == 0xDA) return "Non-Fs data";
   if (entry->partition_type == 0xDB) return "Concurrent DOS, CP/M, CTOS";
   if (entry->partition_type == 0xDE) return "Dell Utility";                       // Or »Dell Diagnostics«?
   if (entry->partition_type == 0xE1) return "DOS access";
   if (entry->partition_type == 0xE3) return "DOS R/o";
   if (entry->partition_type == 0xE4) return "Speed Stor";
   if (entry->partition_type == 0xEB) return "BeOS fs";
   if (entry->partition_type == 0xEE) return "EFI GPT";
   if (entry->partition_type == 0xEF) return "EFI (FAT12/16/32)";
   if (entry->partition_type == 0xF0) return "Palo";
   if (entry->partition_type == 0xF1) return "Speed Stor";
   if (entry->partition_type == 0xF2) return "DOS secondary";
   if (entry->partition_type == 0xF4) return "Speed Stor";
   if (entry->partition_type == 0xFD) return "LINUX raid auto";
   if (entry->partition_type == 0xFE) return "LVM old";                            // Or LANstep ?
   if (entry->partition_type == 0xFF) return "BBT";
   return "?";

}

int isActivePartition(MSDosPartitionTableEntry* entry) {

  if (entry->boot_indicator == 0x80) return 1;
  if (entry->boot_indicator == 0x00) return 0;

  printf("Unexpected boot_indicator %x\n", entry->boot_indicator);
  return 0;

}

int isLBAPartition(MSDosPartitionTableEntry* entry) {

  if (entry-> partition_type == 0x0C) return 1; // FAT32 LBA
  if (entry-> partition_type == 0x0E) return 1; // FAT16 LBA
  if (entry-> partition_type == 0x0F) return 1; // Extended LBA
  if (entry-> partition_type == 0x1C) return 1; // FAT32 LBA Hidden
  if (entry-> partition_type == 0x1E) return 1; // FAT16 LBA Hidden

  return 0;

}


sector CHS_to_LBA(RawCHS* chs) {

  int c = chs->cylinder + ((chs->sector >> 6) << 8);
  if (c > 1021) return 0;

	int h = chs->head;
  int s =(chs->sector & 0x3f) - 1;

  if (s<0) return 0;

 // TODO return (c * bios_geom->heads + h) * bios_geom->sectors + s;
  
}

int isExtendedPartitiontype(MSDosPartitionTableEntry* entry) {
  if (entry->partition_type == 0x05  /* Extended DOS Partition */ ||
      entry->partition_type == 0x0F  /* Extended LBA           */ ||
      entry->partition_type == 0x85  /* LINUX extended         */) {
    return 1;
  }
  return 0;
}

void printPartitionLine(MSDosPartitionTableEntry *entry, int partNo, sector sectorStartPrimaryOrExtended, sector sectorStartLogical) {

      sector start       = entry -> partition_start_lba;
      sector sector_1st  = start + sectorStartPrimaryOrExtended + sectorStartLogical;
      sector nof_sectors = entry -> nof_sectors ;
      sector sector_last = sector_1st + nof_sectors - 1;

      char const* type = partitionTypeToString(entry);

      printf("    %2d:  0x%02x %-25s  %-3s  %10llu + %10llu = %10llu  %10llu    %10llu  %-3s  %-3s | %4d %4d %4d  %4d %4d %4d\n", partNo, entry->partition_type, type,
          isLBAPartition(entry) ? "LBA" : "CHS", 
          sectorStartPrimaryOrExtended, start, sector_1st, nof_sectors, sector_last,
          isActivePartition(entry) ? "Act" : "",
          isExtendedPartitiontype(entry) ? "Ext": "",
          entry->partition_start_chs.cylinder, entry->partition_start_chs.head, entry->partition_start_chs.sector,
          entry->partition_end_chs  .cylinder, entry->partition_end_chs  .head  , entry->partition_end_chs.sector);

}

void showMSDosExtendedBootRecord(BlockDevice *dev, MSDosExtendedBootRecord *ebr, sector sectorStartExtended, sector sectorStartLogical) {

    int partNo=5;

    if (!checkMSDosBootRecordSignature(&ebr->msdos_boot_record_signature)) {
      printf("Unexpected boot record signature: %x %x\n", ebr->msdos_boot_record_signature[0], ebr->msdos_boot_record_signature[1]);
    }

    for (int i = 0; i<16; i ++) { if (ebr->unused_third_entry [i]) { printf("That was not expected!\n"); }}
    for (int i = 0; i<16; i ++) { if (ebr->unused_fourth_entry[i]) { printf("That was not expected!\n"); }}

    printPartitionLine(&ebr->thisLogicalPartition, partNo, sectorStartExtended, sectorStartLogical);
//  disassemble_msdos_boot_code(&ebr);

    if (isExtendedPartitiontype(&ebr->nextExtendedBootRecord)) {

         printPartitionLine(&ebr->nextExtendedBootRecord, partNo, sectorStartExtended, sectorStartLogical);
         MSDosExtendedBootRecord ebrNext;
 
         sector sector_1st = ebr->nextExtendedBootRecord.partition_start_lba + sectorStartExtended;
         readSector(sector_1st, dev, &ebrNext);
 
//       for (int i = 0; i<16; i ++) { if (ebrNext.unused_third_entry [i]) { printf("That was not expected!\n"); }}
//       for (int i = 0; i<16; i ++) { if (ebrNext.unused_fourth_entry[i]) { printf("That was not expected!\n"); }}
 
         showMSDosExtendedBootRecord(dev, &ebrNext, sectorStartExtended, ebr->nextExtendedBootRecord.partition_start_lba);
    }
}

void showMSDosMasterBootRecord(BlockDevice* dev, MSDosMasterBootRecord* mbr, int nofRecords, sector sectorStartPrimaryOrExtended/*, sector start__*/) {

  //  an extended partition looks like a whole disk with its own partition
  //  table and contains at most two partitions: a logical partition, and
  //  another extended partition nested inside it, which in turn may have
  //  another extended partition nested inside it, etc

    if      (mbr->version[0] == 0xfa && mbr->version[1] == 0x33) printf("  MBR version = Dos 3.3 through Windows 95a\n");
    else if (mbr->version[0] == 0x33 && mbr->version[1] == 0xc0) printf("  MBR version = Windows 95B, 98, 98SE, ME, 2K, XP or Vista\n");
    else if (mbr->version[0] == 0xfa && mbr->version[1] == 0xeb) printf("  MBR version = LILO\n");
    else if (mbr->version[0] == 0xeb && mbr->version[1] == 0x3c) printf("  MBR version = Windows floopy disk\n");
    else                                                         printf("  Unrecognized MBR version %02X%02X.\n", mbr->version[0], mbr->version[1]);

//  disassemble_msdos_boot_code(&mbr);

    int partNo = 1;

    for (MSDosPartitionTableEntry *part = &mbr->partitions[0]; part< &mbr->partitions[nofRecords]; part ++, partNo++) {

      printPartitionLine(part, partNo, 0, 0);

      if (isExtendedPartitiontype(part)) {

        MSDosExtendedBootRecord ebr;

        sector sector_1st = part->partition_start_lba;

        printf("going to read sector %llu\n", sector_1st);
        readSector(sector_1st, dev, &ebr);
//      if (fseek(dev->f, sector_1st * sector_size, SEEK_SET)) {
//         perror("fseek");
//      }
//      if (fread(&ebr, sizeof(ebr), 1, dev->f) != 1) {
//         perror("fread");
//      }

        if (! checkMSDosBootRecordSignature(&ebr.msdos_boot_record_signature)) {
          printf("Unexpected MSDos boot record signature: %x %x\n", ebr.msdos_boot_record_signature[0], ebr.msdos_boot_record_signature[1]);
        }

        showMSDosExtendedBootRecord(dev, &ebr, sector_1st, 0);

      }
    }
}

void checkSizes() {
  
  if (sizeof(MSDosMasterBootRecord) != 512) {
    printf("sizeof(MSDosMasterBootRecord) = %ld\n", sizeof(MSDosMasterBootRecord));
  }

  if (sizeof(MSDosExtendedBootRecord) != 512) {
    printf("sizeof(MSDosExtendedBootRecord) = %ld\n", sizeof(MSDosExtendedBootRecord));
  }

}

int main() {

  BlockDevice blockDevice;
  blockDevice.path = "/dev/sda";

  checkSizes();

  openBlockDevice    (&blockDevice);
  stat_              (&blockDevice);
  
  if (isSCSI(&blockDevice)) {
     printf("SCSI\n");
  }
  else if (isIDE(&blockDevice)) {
     printf("IDE\n");
  }

  MSDosMasterBootRecord mbr;
  if (isMSDosMasterBootRecord(&blockDevice, &mbr)) {
    printf("Device has a MSDos partition table.\n");

    printf("     #   Type                            C/L      st. Ext+      start = 1st sector      length   last sector  Act  Ext |    c    h    s     c    h    s\n");
    showMSDosMasterBootRecord(&blockDevice, &mbr, NOF_MSDOS_PRIMARY_PARTITIONS, 0/*, 0*/);
  }

}
Github repository Partition-tables-and-file-systems, path: /main.c

See also

File system, partition
NTFS

Index