public static BootSector read(BlockDevice device) throws IOException {
        final ByteBuffer bb = ByteBuffer.allocate(512);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        device.read(0, bb);
        
        if ((bb.get(510) & 0xff) != 0x55 ||
                (bb.get(511) & 0xff) != 0xaa) throw new IOException(
                "missing boot sector signature");
                
        final byte sectorsPerCluster = bb.get(SECTORS_PER_CLUSTER_OFFSET);

        if (sectorsPerCluster <= 0) throw new IOException(
                "suspicious sectors per cluster count " + sectorsPerCluster);
                
        final int rootDirEntries = bb.getShort(
                Fat16BootSector.ROOT_DIR_ENTRIES_OFFSET);
        final int rootDirSectors = ((rootDirEntries * 32) +
                (device.getSectorSize() - 1)) / device.getSectorSize();

        final int total16 =
                bb.getShort(TOTAL_SECTORS_16_OFFSET) & 0xffff;
        final long total32 =
                bb.getInt(TOTAL_SECTORS_32_OFFSET) & 0xffffffffl;
        
        final long totalSectors = total16 == 0 ? total32 : total16;
        
        final int fatSz16 =
                bb.getShort(Fat16BootSector.SECTORS_PER_FAT_OFFSET)  & 0xffff;
        final long fatSz32 =
                bb.getInt(Fat32BootSector.SECTORS_PER_FAT_OFFSET) & 0xffffffffl;
                
        final long fatSz = fatSz16 == 0 ? fatSz32 : fatSz16;
        final int reservedSectors = bb.getShort(RESERVED_SECTORS_OFFSET);
        final int fatCount = bb.get(FAT_COUNT_OFFSET);
        final long dataSectors = totalSectors - (reservedSectors +
                (fatCount * fatSz) + rootDirSectors);
                
        final long clusterCount = dataSectors / sectorsPerCluster;
        
        final BootSector result =
                (clusterCount > Fat16BootSector.MAX_FAT16_CLUSTERS) ?
            new Fat32BootSector(device) : new Fat16BootSector(device);
            
        result.read();
        return result;
    }