WIP checkin adding lots more SHA256 support
authorSteve McIntyre <steve@einval.com>
Wed, 6 Nov 2019 01:53:00 +0000 (01:53 +0000)
committerSteve McIntyre <steve@einval.com>
Wed, 6 Nov 2019 01:53:00 +0000 (01:53 +0000)
ChangeLog
Makefile
README
jigdo.h
jigit-mkimage.1
libjte/doc/NOTES
mkimage.c

index a88d372..c1790bf 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+Version 1.22 (2019-11-05)
+
+ * Add support for SHA256 checksums
+
 Version 1.21 (2019-02-04)
 
  * parallel-sums:
index a1db6ce..f9cee2c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ CKSUM_OBJS = libjte/libjte_libjte_la-checksum.o libjte/libjte_libjte_la-md5.o li
 
 all: $(BINS)
 
-jigit-mkimage: mkimage.o endian.o md5.o uncompress.o jig-base64.o
+jigit-mkimage: mkimage.o endian.o md5.o libjte/libjte_libjte_la-sha256.o uncompress.o jig-base64.o
        $(CC) $(LDFLAGS) -o $@ $+ -lz -lbz2
 
 extract-data: extract-data.o endian.o uncompress.o
diff --git a/README b/README
index 8e67f59..ac6ccdb 100644 (file)
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
-README for JTE 1.21
+README for JTE 1.22
 
 Steve McIntyre <steve@einval.com>
-04 February 2019
+05 November 2019
 
 License - GPL v2+. See the file COPYING for more details.
 
diff --git a/jigdo.h b/jigdo.h
index ff33a20..cb2b23e 100644 (file)
--- a/jigdo.h
+++ b/jigdo.h
@@ -3,7 +3,7 @@
  *
  * Common prototypes/macros/definitions
  *
- * Copyright (c) 2007 Steve McIntyre <steve@einval.com>
+ * Copyright (c) 2007-2019 Steve McIntyre <steve@einval.com>
  *
  * GPL v2 - see COPYING
  */
index 12da377..05c30ce 100644 (file)
@@ -1,4 +1,4 @@
-.TH JIGIT-MKIMAGE 1 "February 2019" "Jigit jigdo tools"
+.TH JIGIT-MKIMAGE 1 "November 2019" "Jigit jigdo tools"
 .SH NAME
 jigit-mkimage \- Create an ISO image from jigdo files
 .SH SYNOPSIS
@@ -13,6 +13,10 @@ jigit-mkimage \- Create an ISO image from jigdo files
 .B jigit-mkimage
 \fI\-f md5\-list\fR \fI\-t template\fR \fI\-M missing\-list\fR
 [\fIoptions\fR]... 
+.PP
+.B jigit-mkimage
+\fI\-F sha256\-list\fR \fI\-t template\fR \fI\-M missing\-list\fR
+[\fIoptions\fR]... 
 .SH DESCRIPTION
 .PP
 jigit-mkimage knows how to parse a jigdo template file, commonly used
@@ -53,8 +57,11 @@ must list the absolute path to the file. For example:
 00083436a3899a09633fc1026ef1e66e         22762  /mirror/debian/file3
 .fi
 .PP
+A SHA256 file uses a similar format, but needs more space for the
+bigger checksum output (64 characters rather than 12).
+.PP
 To rebuild an image you must specify \fBat least\fR one of the
-MD5 file and a jigdo file (\fB\-j\fR).
+MD5/SHA256 file and a jigdo file (\fB\-j\fR).
 .TP
 \fB\-m item=path\fR
 Used in conjunction with a jigdo file; specify where jigit-mkimage should
@@ -63,7 +70,7 @@ image. (e.g. "Debian=/mirror/debian").
 .TP
 \fB\-M missing file\fR
 If you're not sure if you have all the files needs to create an image,
-specify \fBboth\fR the jigdo file and an MD5 file along with the
+specify \fBboth\fR the jigdo file and an MD5/SHA256 file along with the
 template file and \-M <file>. jigit-mkimage will check to see that all the
 files are available \fBinstead of\fR building the image. Any files
 that are missing will be listed in the file specified here. See jigit
@@ -91,22 +98,23 @@ into cdrecord or to iso-image.pl. Specify an output filename if you
 want it written to disk instead.
 .TP
 \fB\-q\fR
-jigit-mkimage will normally check the MD5 sum of every file it reads and
-writes, and will fail if it finds any mismatches. Specify \fB-q\fR to
-tell it not to. This will normally run more quickly, but can leave you
-with a broken image so is \fBPOTENTIALLY DANGEROUS\fR. Use with care!
+jigit-mkimage will normally check the MD5/SHA256 sum of every file it
+reads and writes, and will fail if it finds any mismatches. Specify
+\fB-q\fR to tell it not to. This will normally run more quickly, but
+can leave you with a broken image so is \fBPOTENTIALLY
+DANGEROUS\fR. Use with care!
 .TP
 \fB\-s start offset\fR
 Rather than start at the beginning of the image, skip to the specified
 offset and start output there. Probably only useful in iso-image.pl
 when resuming a download. Specifying a non-zero offset also implies
-\fB-q\fR, as it's difficult to check MD5 sums when you're not checking
-the whole image.
+\fB-q\fR, as it's difficult to check MD5/SHA256 sums when you're not
+checking the whole image.
 \fB\-e end offset\fR
 Rather than end at the end of the image, stop at the specified
 offset. Probably only useful in iso-image.pl when resuming a
 download. Specifying an end offset also implies \fB-q\fR, as it's
-difficult to check MD5 sums when you're not checking the whole image.
+difficult to check MD5/SHA256 sums when you're not checking the whole image.
 .TP
 \fB\-z\fR
 Simply parse the template file and print the size of the image that
index 95944c6..35874af 100644 (file)
@@ -10,9 +10,11 @@ Implemented are :
 -jigdo-template FILE        Produce a jigdo .template file as well as the .iso
 -jigdo-min-file-size SIZE   Minimum size for a file to be listed in the jigdo file
 -jigdo-force-md5 PATTERN    Pattern(s) where files MUST match an externally-supplied MD5sum
+-jigdo-force-sha256 PATTERN Pattern(s) where files MUST match an externally-supplied SHA256sum
 -jigdo-exclude PATTERN      Pattern(s) to exclude from the jigdo file
 -jigdo-map PATTERN1=PATTERN2 Pattern(s) to map paths (e.g. Debian=/mirror/debian)
 -md5-list FILE              File containing MD5 sums of the files that should be checked
+-sha256-list FILE           File containing SHA256 sums of the files that should be checked
 -jigdo-template-compress ALGORITHM Choose to use gzip or bzip2 compression for template data; default is gzip
 
 Not yet implemented:
@@ -40,6 +42,9 @@ Global variables from application POV (declated in jte.h)
 - extern char *jmd5_list;
   XORRISO: -md5-list filepath
 
+- extern char *jsha256_list;
+  XORRISO: -sha256-list filepath
+
 - extern FILE *jtjigdo;
   XORRISO: handle initialized with -jigdo-jigdo filepath
 
@@ -94,6 +99,19 @@ user, i.e. path to .jigdo and .template were given via options):
               mirror_name - as returned previously by list_file_in_jigdo()
   LIBISOFS:   see below in list_file_in_jigdo() section
 
+- void write_jt_match_record_sha256(char *filename, char *mirror_name, int sector_size, off_t size, unsigned char sha256[32]);
+  DESCRIPTION: to be called after list_file_in_jigdo() call:
+               int include_in_jigdo = list_file_in_jigdo()
+               if (include_in_jigdo)
+                   write_jt_match_record();
+               ...
+               call_to_write_data_to_image();
+               if (!include_in_jigdo)
+                   jtwrite();
+  PARAMETERS: filename - disk file path
+              mirror_name - as returned previously by list_file_in_jigdo()
+  LIBISOFS:   see below in list_file_in_jigdo() section
+
 - int  list_file_in_jigdo(char *filename, off_t size, char **realname, unsigned char md5[16]);
   DESCRIPTION: decides whether a file should be placed in .jigdo
   PARAMETERS: filename - disk file path
@@ -105,6 +123,17 @@ user, i.e. path to .jigdo and .template were given via options):
   LIBISOFS: libisofs/filesrc.c:filesrc_writer_write_data() has a loop which iterates over the 
             array of IsoFileSrc of all data files and writes their data content to the output stream.
 
+- int  list_file_in_jigdo_sha256(char *filename, off_t size, char **realname, unsigned char sha256[32]);
+  DESCRIPTION: decides whether a file should be placed in .jigdo
+  PARAMETERS: filename - disk file path
+              mirror_name - to be returned and given to write_jt_match_record()
+             sha256[32] - We have the SHA256. But ok. If it works that way then we should not 
+             change it without need. Eventually write a new function without calling 
+             calculate_sha256sum(), but supply the sha256sum instead as we already have it on that
+              libisofs spot.
+  LIBISOFS: libisofs/filesrc.c:filesrc_writer_write_data() has a loop which iterates over the 
+            array of IsoFileSrc of all data files and writes their data content to the output stream.
+
 - int  jte_add_exclude(char *pattern);
   DESCRIPTION: files matching patterns to be included in .jigdo
   PARAMETERS: pattern string
@@ -114,6 +143,7 @@ user, i.e. path to .jigdo and .template were given via options):
   DESCTIPRITON: files matching patterns to be included in .jigdo
   PARAMETERS: pattern string
   XORRISO: -jigdo-force-md5 "/pool/"
+  XORRISO: -jigdo-force-sha256 "/pool/"
 
 - int  jte_add_mapping(char *arg);
   DESCRIPTION: provides String:/a/b/c mappings for [Parts] section .jigdo file
@@ -123,12 +153,12 @@ user, i.e. path to .jigdo and .template were given via options):
 
 >>> Where are the argument parsers of algorithm setters ?
 
-- libjte: MD5, size, and basename of the path have to match MD5, size,
-  and basename of the data file.
+- libjte: MD5/SHA256, size, and basename of the path have to match
+  MD5/SHA256, size, and basename of the data file.
 
 - libjte: The directory part of path is just a literal string that shall be
   checked for the address mapping
-     -jigdo-map Debian=literal_md5_path_start
+     -jigdo-map Debian=literal_checksum_path_start
 
 -jigit-mkimage: The directory part of the path is of importance when producing
   the payload image from template file.
@@ -138,8 +168,9 @@ user, i.e. path to .jigdo and .template were given via options):
       -m Debian=/really/usable/path/for/fopen
     and the parts of the path, that was not consumed by the genisoimage
     mapping.
-  - If the payload image gets generated by option -f md5_file, then the
-    paths in the md5_file have to be usable for fopen().
+  - If the payload image gets generated by option -f md5_file or -F
+    sha256_file, then the paths in the checksum file have to be usable
+    for fopen().
 
 If we follow the mainstream and ignore the case of jigit-mkimage -f md5_file,
 then we can reduce the constraint for paths in .md5 to the advise to use
index dfd3d40..87909d6 100644 (file)
--- a/mkimage.c
+++ b/mkimage.c
@@ -3,7 +3,7 @@
  *
  * Tool to create an ISO image from jigdo files
  *
- * Copyright (c) 2004 Steve McIntyre <steve@einval.com>
+ * Copyright (c) 2004-2019 Steve McIntyre <steve@einval.com>
  *
  * GPL v2 - see COPYING
  */
 #include <fcntl.h>
 #include <unistd.h>
 #include <sys/mman.h>
+#include <stdint.h>
 #include "endian.h"
 #include "jig-base64.h"
 #include "md5.h"
+#include "sha256.h"
 #include "jigdo.h"
 
 #define MISSING -1
@@ -36,10 +38,28 @@ static long long start_offset = 0;
 static long long end_offset = 0;
 static int quick = 0;
 static int verbose = 0;
+static int image_md5_valid = 0;
+static int image_sha256_valid = 0;
 static int check_jigdo_header = 1;
 static UINT64 out_size = 0;
 static char *missing_filename = NULL;
 
+#define ROUND_UP(N, S)      ((((N) + (S) - 1) / (S)) * (S))
+
+#define MD5_BITS            128
+#define MD5_BYTES           (MD5_BITS / 8)
+#define HEX_MD5_BYTES       (MD5_BITS / 4)
+#define BASE64_MD5_BYTES    ((ROUND_UP (MD5_BITS, 6)) / 6)
+
+#define SHA256_BITS         256
+#define SHA256_BYTES        (SHA256_BITS / 8)
+#define HEX_SHA256_BYTES    (SHA256_BITS / 4)
+#define BASE64_SHA256_BYTES ((ROUND_UP (SHA256_BITS, 6)) / 6)
+
+/* number of chars used to print a file size in our input checksum
+ * file */
+#define SIZE_BYTES          12
+
 typedef struct match_list_
 {
     struct match_list_ *next;
@@ -61,6 +81,16 @@ typedef struct md5_list_
 static md5_list_t *md5_list_head = NULL;
 static md5_list_t *md5_list_tail = NULL;
 
+typedef struct sha256_list_
+{
+    struct sha256_list_ *next;
+    INT64 file_size;
+    char *sha256;
+    char *full_path;
+} sha256_list_t;
+
+static sha256_list_t *sha256_list_head = NULL;
+static sha256_list_t *sha256_list_tail = NULL;
 static zip_state_t zip_state;
 
 /* Grab the file component from a full path */
@@ -188,7 +218,7 @@ static md5_list_t *find_file_in_md5_list(unsigned char *base64_md5, int need_siz
             fprintf(logfile, "find_file_in_md5_list: looking for %s, looking at %s (%s)\n", 
                     base64_md5, md5_list_entry->md5, md5_list_entry->full_path);
         
-        if (!memcmp(md5_list_entry->md5, base64_md5, 22)) {
+        if (!memcmp(md5_list_entry->md5, base64_md5, BASE64_MD5_BYTES)) {
             if (need_size &&
                 md5_list_entry->file_size == UNKNOWN)
                 md5_list_entry->file_size =
@@ -202,6 +232,30 @@ static md5_list_t *find_file_in_md5_list(unsigned char *base64_md5, int need_siz
     return NULL; /* Not found */
 }
 
+static sha256_list_t *find_file_in_sha256_list(unsigned char *base64_sha256, int need_size)
+{
+    sha256_list_t *sha256_list_entry = sha256_list_head;
+
+    while (sha256_list_entry)
+    {
+        if (verbose > 2)
+            fprintf(logfile, "find_file_in_sha256_list: looking for %s, looking at %s (%s)\n", 
+                    base64_sha256, sha256_list_entry->sha256, sha256_list_entry->full_path);
+
+        if (!memcmp(sha256_list_entry->sha256, base64_sha256, BASE64_SHA256_BYTES)) {
+            if (need_size &&
+                sha256_list_entry->file_size == UNKNOWN)
+                sha256_list_entry->file_size =
+                    get_file_size(sha256_list_entry->full_path);
+
+            return sha256_list_entry;
+        }
+        /* else */
+        sha256_list_entry = sha256_list_entry->next;
+    }
+    return NULL; /* Not found */
+}
+
 static int find_file_in_mirror(char *jigdo_match, char *jigdo_name,
                                char *match, INT64 *file_size, char **mirror_path)
 {
@@ -241,6 +295,16 @@ static int find_file_in_mirror(char *jigdo_match, char *jigdo_name,
     return ENOENT;
 }
 
+static int hex_to_nibble(char hex)
+{
+    if (hex >= '0' && hex <= '9')
+        return hex - '0';
+    else if (hex >= 'A' && hex <= 'F')
+        return 10 + hex - 'A';
+    else if (hex >= 'a' && hex <= 'f')
+        return 10 + hex - 'a';
+    return 0;
+}
 
 static int add_md5_entry(INT64 size, char *md5, char *path)
 {
@@ -267,17 +331,6 @@ static int add_md5_entry(INT64 size, char *md5, char *path)
     return 0;
 }
 
-static int hex_to_nibble(char hex)
-{
-    if (hex >= '0' && hex <= '9')
-        return hex - '0';
-    else if (hex >= 'A' && hex <= 'F')
-        return 10 + hex - 'A';
-    else if (hex >= 'a' && hex <= 'f')
-        return 10 + hex - 'a';
-    return 0;
-}
-
 /* Parse an incoming MD5 file entry, working in place in the
  * (strduped) buffer we've been passed */
 static int parse_md5_entry(char *md5_entry)
@@ -285,22 +338,22 @@ static int parse_md5_entry(char *md5_entry)
     int error = 0;
     char *file_name = NULL;
     char *md5 = NULL;
-    unsigned char bin_md5[16];
+    unsigned char bin_md5[MD5_BYTES];
     int i;
 
-    md5_entry[32] = 0;
-    md5_entry[33] = 0;
+    md5_entry[HEX_MD5_BYTES] = 0;
+    md5_entry[HEX_MD5_BYTES + 1] = 0;
 
     /* Re-encode hex as base64 and overwrite in place; safe, as the
      * md5 will be shorter than the hex. */
-    for (i = 0; i < 16; i++)
+    for (i = 0; i < MD5_BYTES; i++)
         bin_md5[i] = (hex_to_nibble(md5_entry[2 * i]) << 4) |
                       hex_to_nibble(md5_entry[2 * i + 1]);
-    strncpy(md5_entry, base64_dump(bin_md5, 16), 22);
+    strncpy(md5_entry, base64_dump(bin_md5, MD5_BYTES), BASE64_MD5_BYTES);
 
-    md5_entry[22] = 0;
+    md5_entry[BASE64_MD5_BYTES] = 0;
     md5 = md5_entry;
-    file_name = &md5_entry[48];
+    file_name = &md5_entry[HEX_MD5_BYTES + 2 + SIZE_BYTES + 2];
 
     if ('\n' == file_name[strlen(file_name) -1])
         file_name[strlen(file_name) - 1] = 0;
@@ -335,6 +388,88 @@ static int parse_md5_file(char *filename)
     return 0;
 }
 
+static int add_sha256_entry(INT64 size, char *sha256, char *path)
+{
+    sha256_list_t *new = NULL;
+    new = calloc(1, sizeof(*new));
+    if (!new)
+        return ENOMEM;
+
+    new->sha256 = sha256;
+    new->full_path = path;
+    new->file_size = size;
+
+    if (!sha256_list_head)
+    {
+        sha256_list_head = new;
+        sha256_list_tail = new;
+    }
+    else
+    {
+        sha256_list_tail->next = new;
+        sha256_list_tail = new;
+    }
+
+    return 0;
+}
+
+/* Parse an incoming SHA256 file entry, working in place in the
+ * (strduped) buffer we've been passed */
+static int parse_sha256_entry(char *sha256_entry)
+{
+    int error = 0;
+    char *file_name = NULL;
+    char *sha256 = NULL;
+    unsigned char bin_sha256[SHA256_BYTES];
+    int i;
+
+    sha256_entry[HEX_SHA256_BYTES] = 0;
+    sha256_entry[HEX_SHA256_BYTES + 1] = 0;
+
+    /* Re-encode hex as base64 and overwrite in place; safe, as the
+     * sha256 will be shorter than the hex. */
+    for (i = 0; i < SHA256_BYTES; i++)
+        bin_sha256[i] = (hex_to_nibble(sha256_entry[2 * i]) << 4) |
+                      hex_to_nibble(sha256_entry[2 * i + 1]);
+    strncpy(sha256_entry, base64_dump(bin_sha256, SHA256_BYTES), BASE64_SHA256_BYTES);
+
+    sha256_entry[BASE64_SHA256_BYTES] = 0;
+    sha256 = sha256_entry;
+    file_name = &sha256_entry[HEX_SHA256_BYTES + 2 + SIZE_BYTES + 2];
+
+    if ('\n' == file_name[strlen(file_name) -1])
+        file_name[strlen(file_name) - 1] = 0;
+
+    error = add_sha256_entry(UNKNOWN, sha256, file_name);
+    return error;
+}
+
+static int parse_sha256_file(char *filename)
+{
+    char buf[2048];
+    FILE *file = NULL;
+    char *ret = NULL;
+    int error = 0;
+
+    file = fopen(filename, "rb");
+    if (!file)
+    {
+        fprintf(logfile, "Failed to open SHA256 file %s, error %d!\n", filename, errno);
+        return errno;
+    }
+
+    while(1)
+    {
+        ret = fgets(buf, sizeof(buf), file);
+        if (NULL == ret)
+            break;
+        error = parse_sha256_entry(strdup(buf));
+        if (error)
+            return error;
+    }
+    return 0;
+}
+
 /* DELIBERATELY do not sort these, or do anything clever with
    insertion. The entries in the jigdo file should be in the same
    order as the ones we'll want from the template. Simply add to the
@@ -345,12 +480,13 @@ static int add_file_entry(char *jigdo_entry)
     char *file_name = NULL;
     INT64 file_size = 0;
     char *ptr = jigdo_entry;
-    char *base64_md5 = NULL;
+    char *base64_checksum = NULL;
+    int csum_length;
     char *match = NULL;
     char *jigdo_name = NULL;
     
     /* Grab out the component strings from the entry in the jigdo file */
-    base64_md5 = jigdo_entry;
+    base64_checksum = jigdo_entry;
     while (0 != *ptr)
     {
         if ('=' == *ptr)
@@ -371,15 +507,32 @@ static int add_file_entry(char *jigdo_entry)
             ptr++;
     }
 
-    if (find_file_in_md5_list((unsigned char *)base64_md5, 0))
+    csum_length = strlen(base64_checksum);
+    if (csum_length == BASE64_SHA256_BYTES)
     {
-        free(jigdo_entry);
-        return 0; /* We already have an entry for this file; don't
-                   * waste any more time on it */
+        if (find_file_in_sha256_list((unsigned char *)base64_checksum, 0))
+        {
+            free(jigdo_entry);
+            return 0; /* We already have an entry for this file; don't
+                       * waste any more time on it */
+       }
+    }
+    else if (csum_length == BASE64_MD5_BYTES)
+    {
+        if (find_file_in_md5_list((unsigned char *)base64_checksum, 0))
+        {
+            free(jigdo_entry);
+            return 0; /* We already have an entry for this file; don't
+                       * waste any more time on it */
+       }
+    }
+    else
+    {
+        csum_length = -1; /* flag error */
     }
 
     /* else look for the file in the filesystem */
-    if (NULL == match || NULL == jigdo_name)
+    if (-1 == csum_length || NULL == match || NULL == jigdo_name)
     {
         fprintf(logfile, "Could not parse malformed jigdo entry \"%s\"\n", jigdo_entry);
         free(jigdo_entry);
@@ -390,10 +543,13 @@ static int add_file_entry(char *jigdo_entry)
     switch (error)
     {
         case 0:
-            base64_md5 = strdup(jigdo_entry);
-            if (base64_md5)
+            base64_checksum = strdup(jigdo_entry);
+            if (base64_checksum)
             {
-                add_md5_entry(file_size, base64_md5, file_name);
+                if (csum_length == BASE64_MD5_BYTES)
+                    add_md5_entry(file_size, base64_checksum, file_name);
+                else
+                    add_sha256_entry(file_size, base64_checksum, file_name);
                 free(jigdo_entry);
                 break;
             }
@@ -407,7 +563,12 @@ static int add_file_entry(char *jigdo_entry)
 
         default: /* ENOENT */
             if (missing_filename)
-                add_md5_entry(MISSING, base64_md5, jigdo_name);
+            {
+                if (csum_length == BASE64_MD5_BYTES)
+                    add_md5_entry(MISSING, base64_checksum, jigdo_name);
+                else
+                    add_sha256_entry(MISSING, base64_checksum, jigdo_name);
+            }
             else
             {
                 fprintf(logfile, "Unable to find a file to match %s\n", jigdo_name);
@@ -518,8 +679,21 @@ static int skip_data_block(INT64 data_size, FILE *template_file)
     return error;
 }
 
+/* Trivial helper - update all valid checksums */
+static void update_checksum_context(struct mk_MD5Context *md5_context,
+                                   struct sha256_ctx *sha256_context,
+                                   const void *buffer,
+                                   size_t len)
+{
+    if (md5_context && image_md5_valid)
+        mk_MD5Update(md5_context, buffer, len);
+    if (sha256_context && image_sha256_valid)
+        sha256_process_bytes(buffer, len, sha256_context);
+}
+
 static int parse_data_block(INT64 data_size, FILE *template_file,
-                            struct mk_MD5Context *context)
+                            struct mk_MD5Context *md5_context,
+                           struct sha256_ctx *sha256_context)
 {
     int error = 0;
     INT64 remaining = data_size;
@@ -550,9 +724,9 @@ static int parse_data_block(INT64 data_size, FILE *template_file,
             display_progress(outfile, "template data");
 
         if (!quick)
-            mk_MD5Update(context,
-                         (unsigned char *)&zip_state.data_buf[zip_state.offset_in_curr_buf],
-                         size);
+            update_checksum_context(md5_context, sha256_context,
+                                   (unsigned char *)&zip_state.data_buf[zip_state.offset_in_curr_buf],
+                                   size);
         zip_state.offset_in_curr_buf += size;
         remaining -= size;
         
@@ -567,17 +741,17 @@ static int parse_data_block(INT64 data_size, FILE *template_file,
     return error;
 }
 
-static int parse_file_block(INT64 offset, INT64 data_size, INT64 file_size, 
-                            unsigned char *md5, struct mk_MD5Context *image_context,
-                            char *missing)
+static int parse_file_block_md5(INT64 offset, INT64 data_size, INT64 file_size, 
+                               unsigned char *md5, struct mk_MD5Context *md5_context,
+                               struct sha256_ctx *sha256_context, char *missing)
 {
-    char *base64_md5 = base64_dump(md5, 16);
+    char *base64_md5 = base64_dump(md5, MD5_BYTES);
     FILE *input_file = NULL;
     char buf[BUF_SIZE];
     INT64 remaining = data_size;
     int num_read = 0;
     struct mk_MD5Context file_context;
-    unsigned char file_md5[16];
+    unsigned char file_md5[MD5_BYTES];
     int out_size = 0;
     md5_list_t *md5_list_entry = NULL;
     
@@ -620,7 +794,7 @@ static int parse_file_block(INT64 offset, INT64 data_size, INT64 file_size,
             }
             if (!quick)
             {
-                mk_MD5Update(image_context, (unsigned char *)buf, size);
+                update_checksum_context(md5_context, sha256_context, (unsigned char *)buf, size);
                 mk_MD5Update(&file_context, (unsigned char *)buf, size);
             }
             
@@ -645,7 +819,7 @@ static int parse_file_block(INT64 offset, INT64 data_size, INT64 file_size,
         {
             mk_MD5Final(file_md5, &file_context);
         
-            if (memcmp(file_md5, md5, 16))
+            if (memcmp(file_md5, md5, MD5_BYTES))
             {
                 fprintf(logfile, "MD5 MISMATCH for file %s\n", md5_list_entry->full_path);
                 fprintf(logfile, "    template looking for %s\n", md5);
@@ -657,7 +831,7 @@ static int parse_file_block(INT64 offset, INT64 data_size, INT64 file_size,
     }
     if ( missing &&
          (MISSING == md5_list_entry->file_size) &&
-         (!memcmp(md5_list_entry->md5, base64_md5, 16) ) )
+         (!memcmp(md5_list_entry->md5, base64_md5, MD5_BYTES) ) )
     {
         write_missing_entry(missing, md5_list_entry->full_path);
         return 0;
@@ -665,10 +839,10 @@ static int parse_file_block(INT64 offset, INT64 data_size, INT64 file_size,
     /* else */
     if (verbose)
     {
-        char hex_md5[33];
+        char hex_md5[HEX_MD5_BYTES + 1];
         int i;
 
-        for (i = 0; i < 16; i++)
+        for (i = 0; i < MD5_BYTES; i++)
             sprintf(hex_md5 + 2 * i, "%2.2x", (unsigned int) md5[i]);
 
         fprintf(logfile, "Unable to find a file for block with md5 %s (%s)\n", hex_md5, base64_md5);
@@ -676,20 +850,137 @@ static int parse_file_block(INT64 offset, INT64 data_size, INT64 file_size,
     return ENOENT;
 }
 
+static int parse_file_block_sha256(INT64 offset, INT64 data_size, INT64 file_size,
+                                  unsigned char *sha256, struct mk_MD5Context *md5_context,
+                                  struct sha256_ctx *sha256_context, char *missing)
+{
+    char *base64_sha256 = base64_dump(sha256, SHA256_BYTES);
+    FILE *input_file = NULL;
+    char buf[BUF_SIZE];
+    INT64 remaining = data_size;
+    int num_read = 0;
+    struct sha256_ctx file_context;
+    unsigned char file_sha256[SHA256_BYTES];
+    int out_size = 0;
+    sha256_list_t *sha256_list_entry = NULL;
+
+    if (!quick)
+        sha256_init_ctx(&file_context);
+
+    sha256_list_entry = find_file_in_sha256_list((unsigned char *)base64_sha256, 1);
+    if (sha256_list_entry && file_size == sha256_list_entry->file_size)
+    {
+        if (verbose > 1)
+            fprintf(logfile, "Reading %s\n", sha256_list_entry->full_path);
+
+        input_file = fopen(sha256_list_entry->full_path, "rb");
+        if (!input_file)
+        {
+            fprintf(logfile, "Unable to open mirror file %s, error %d\n",
+                    sha256_list_entry->full_path, errno);
+            return errno;
+        }
+
+        if (missing)
+        {
+            fclose(input_file);
+            return 0;
+        }
+
+        fseek(input_file, offset, SEEK_SET);
+        while (remaining)
+        {
+            int size = MIN(BUF_SIZE, remaining);
+            memset(buf, 0, BUF_SIZE);
+
+            num_read = fread(buf, size, 1, input_file);
+            if (!num_read)
+            {
+                fprintf(logfile, "Unable to read from mirror file %s, error %d (offset %ld, length %d)\n",
+                        sha256_list_entry->full_path, errno, ftell(input_file), size);
+                fclose(input_file);
+                return errno;
+            }
+            if (!quick)
+            {
+                update_checksum_context(md5_context, sha256_context, (unsigned char *)buf, size);
+                sha256_process_bytes((unsigned char *)buf, size, &file_context);
+            }
+
+            out_size = fwrite(buf, size, 1, outfile);
+            if (!out_size)
+            {
+                fprintf(logfile, "parse_file_block: fwrite %d failed with error %d; aborting\n", size, ferror(outfile));
+                return ferror(outfile);
+            }
+
+            if (verbose)
+                display_progress(outfile, file_base_name(sha256_list_entry->full_path));
+
+            remaining -= size;
+        }
+        if (verbose > 1)
+            fprintf(logfile, "parse_file_block: wrote %lld bytes of data from %s\n",
+                    file_size, sha256_list_entry->full_path);
+        fclose(input_file);
+
+        if (!quick)
+        {
+            sha256_finish_ctx(&file_context, file_sha256);
+
+            if (memcmp(file_sha256, sha256, SHA256_BYTES))
+            {
+                fprintf(logfile, "SHA256 MISMATCH for file %s\n", sha256_list_entry->full_path);
+                fprintf(logfile, "    template looking for %s\n", sha256);
+                fprintf(logfile, "    file in mirror is    %s\n", file_sha256);
+                return EINVAL;
+            }
+        }
+        return 0;
+    }
+    if ( missing &&
+         (MISSING == sha256_list_entry->file_size) &&
+         (!memcmp(sha256_list_entry->sha256, base64_sha256, SHA256_BYTES) ) )
+    {
+        write_missing_entry(missing, sha256_list_entry->full_path);
+        return 0;
+    }
+    /* else */
+    if (verbose)
+    {
+        char hex_sha256[HEX_SHA256_BYTES + 1];
+        int i;
+
+        for (i = 0; i < SHA256_BYTES; i++)
+            sprintf(hex_sha256 + 2 * i, "%2.2x", (unsigned int) sha256[i]);
+
+        fprintf(logfile, "Unable to find a file for block with sha256 %s (%s)\n", hex_sha256, base64_sha256);
+    }
+    return ENOENT;
+}
+
 static int parse_template_file(char *filename, int sizeonly, char *missing, char *output_name)
 {
     INT64 template_offset = 0;
-    INT64 bytes = 0;
     char *buf = NULL;
     FILE *file = NULL;
     INT64 file_size = 0;
     INT64 desc_start = 0;
     INT64 written_length = 0;
     INT64 output_offset = 0;
+    INT64 desc_size = 0;
+    INT64 image_size = 0;
+    size_t bytes_read = 0;
+    size_t total_read = 0;
     int i = 0;
     int error = 0;
-    struct mk_MD5Context template_context;
-    unsigned char image_md5sum[16];
+    char *bufptr;
+    struct mk_MD5Context template_md5_context;
+    struct sha256_ctx template_sha256_context;
+    unsigned char image_md5sum[MD5_BYTES];
+    unsigned char image_sha256sum[SHA256_BYTES];
+    unsigned char image_md5sum_from_tmpl[MD5_BYTES];
+    unsigned char image_sha256sum_from_tmpl[SHA256_BYTES];
 
     zip_state.total_offset = 0;
     
@@ -712,70 +1003,147 @@ static int parse_template_file(char *filename, int sizeonly, char *missing, char
     file_size = get_file_size(filename);
     fseek(file, file_size - 6, SEEK_SET);
     fread(buf, 6, 1, file);
-    desc_start = file_size - read_le48((unsigned char *)buf);
+    desc_size = read_le48((unsigned char *)buf);
+    desc_start = file_size - desc_size;
+
+    /* Load the DESC block in from the template file in and find the
+     * final descriptor that describes the image
+     * itself. Unfortunately, only way to do this is by scanning
+     * through the whole set of descriptors in the template. */
+    fseek(file, desc_start, SEEK_SET);
+    buf = realloc(buf, desc_size);
+    if (!buf)
+    {
+        fprintf(logfile, "Failed to malloc %lld bytes. Abort!\n", desc_size);
+        fclose(file);
+        return ENOMEM;
+    }
+    while (total_read < desc_size)
+    {
+        bytes_read = fread(buf, 1, desc_size, file);
+        if (ferror(file))
+        {
+            fprintf(logfile, "Failed to read to the end of the template file, error %d\n", ferror(file));
+            fclose(file);
+            free(buf);
+            return EIO;
+        }
+        total_read += bytes_read;
+    }
 
-    /* Now seek back to the beginning image desc block to grab the MD5
-       and image length */
-    fseek(file, file_size - 33, SEEK_SET);
-    fread(buf, BUF_SIZE, 1, file);
-    if (buf[0] != 5) /* image data */
+    /* Now start parsing the DESC block */
+    bufptr = buf;
+    if (strncmp(bufptr, "DESC", 4))
     {
-        fprintf(logfile, "Failed to find image desc in the template file\n");
+        fprintf(logfile, "Failed to find desc start in the template file\n");
         fclose(file);
+        free(buf);
         return EINVAL;
     }
+    bufptr += 4;
 
-    if (sizeonly)
+    if ((file_size - desc_start) != read_le48((unsigned char *)bufptr))
     {
+        fprintf(logfile, "Inconsistent desc length in the template file!\n");
+        fprintf(logfile, "Final chunk says %lld, first chunk says %lld\n",
+                file_size - desc_start, read_le48((unsigned char *)bufptr));
         fclose(file);
-        printf("%lld\n", read_le48((unsigned char *)&buf[1]));
-        return 0;
+        free(buf);
+        return EINVAL;
     }
+    bufptr += 6;
 
-    if (verbose)
+    while (bufptr < (buf + desc_size - 6))
     {
-        fprintf(logfile, "Image MD5 should be    ");
-        for (i = 0; i < 16; i++)
-            fprintf(logfile, "%2.2x", (unsigned char)buf[i+7]);
-        fprintf(logfile, "\n");
-        fprintf(logfile, "Image size should be   %lld bytes\n", read_le48((unsigned char *)&buf[1]));
+        switch (bufptr[0]) {
+            case BLOCK_DATA:
+                bufptr += 7;
+                break;
+            case BLOCK_MATCH_MD5:
+                bufptr += 31;
+                break;
+            case BLOCK_MATCH_SHA256:
+                bufptr += 47;
+                break;
+            case BLOCK_IMAGE_MD5:
+                image_size = read_le48((unsigned char *)&bufptr[1]);
+                memcpy(image_md5sum_from_tmpl, (unsigned char*)&bufptr[7], MD5_BYTES);
+               image_md5_valid = 1;
+                bufptr += 27;
+                break;
+            case BLOCK_IMAGE_SHA256:
+                image_size = read_le48((unsigned char *)&bufptr[1]);
+                memcpy(image_sha256sum_from_tmpl, (unsigned char*)&bufptr[7], SHA256_BYTES);
+               image_sha256_valid = 1;
+                bufptr += 43;
+                break;
+            default:
+                fprintf(logfile, "Unknown block type %d!\n", buf[0]);
+                fclose(file);
+                free(buf);
+                return EINVAL;
+        }
     }
 
-    out_size = read_le48((unsigned char *)&buf[1]);
-    
-    /* Now seek back to the start of the desc block */
-    fseek(file, desc_start, SEEK_SET);
-    fread(buf, 10, 1, file);
-    if (strncmp(buf, "DESC", 4))
+    if (!image_md5_valid && !image_sha256_valid)
     {
-        fprintf(logfile, "Failed to find desc start in the template file\n");
+        fprintf(logfile, "Failed to find a valid image information block in the template file\n");
         fclose(file);
+       free(buf);
         return EINVAL;
     }
-    if ((file_size - desc_start) != read_le48((unsigned char *)&buf[4]))
+
+    if (sizeonly)
     {
-        fprintf(logfile, "Inconsistent desc length in the template file!\n");
-        fprintf(logfile, "Final chunk says %lld, first chunk says %lld\n",
-                file_size - desc_start, read_le48((unsigned char *)&buf[4]));
         fclose(file);
-        return EINVAL;
+       free(buf);
+        printf("%lld\n", image_size);
+        return 0;
+    }
+
+    if (verbose)
+    {
+        if (image_md5_valid)
+        {
+            fprintf(logfile, "Image MD5 should be    ");
+            for (i = 0; i < MD5_BYTES; i++)
+                fprintf(logfile, "%2.2x", image_md5sum_from_tmpl[i]);
+            fprintf(logfile, "\n");
+        }
+        if (image_sha256_valid)
+        {
+            fprintf(logfile, "Image SHA256 should be    ");
+            for (i = 0; i < SHA256_BYTES; i++)
+                fprintf(logfile, "%2.2x", image_sha256sum_from_tmpl[i]);
+            fprintf(logfile, "\n");
+        }
+        fprintf(logfile, "Image size should be   %lld bytes\n", image_size);
     }
 
     if (!quick)
-        mk_MD5Init(&template_context);
-    template_offset = desc_start + 10;
+    {
+        if (image_md5_valid)
+            mk_MD5Init(&template_md5_context);
+        if (image_sha256_valid)
+            sha256_init_ctx(&template_sha256_context);
+    }
 
     if (verbose)
         fprintf(logfile, "Creating ISO image %s\n", output_name);
 
-    /* Main loop - walk through the template file and expand each entry we find */
+    template_offset = 10;
+
+    /* Main loop - back to the start of the DESC block and now walk
+     * through and expand each entry we find */
     while (1)
     {
         INT64 extent_size;
         INT64 skip = 0;
         INT64 read_length = 0;
 
-        if (template_offset >= (file_size - 33))
+        bufptr = &buf[template_offset];
+
+        if (template_offset >= (desc_size - 6))
         {
             if (verbose > 1)
                 fprintf(logfile, "Reached end of template file\n");
@@ -787,28 +1155,12 @@ static int parse_template_file(char *filename, int sizeonly, char *missing, char
             fprintf(logfile, "Reached end of range requested\n");            
             break;
         }
-        
-        fseek(file, template_offset, SEEK_SET);
-        bytes = fread(buf, (MIN (BUF_SIZE, file_size - template_offset)), 1, file);
-        if (1 != bytes)
-        {
-            fprintf(logfile, "Failed to read template file!\n");
-            fclose(file);
-            return EINVAL;
-        }
-        
-        extent_size = read_le48((unsigned char *)&buf[1]);
+
+        extent_size = read_le48((unsigned char *)&bufptr[1]);
         read_length = extent_size;
-        
-        if (start_offset > output_offset)
-            skip = start_offset - output_offset;
-        if ((output_offset + extent_size) > end_offset)
-            read_length -= (output_offset + extent_size - end_offset - 1);
-        read_length -= skip;
-        
-        switch (buf[0])
+
+        switch (bufptr[0])
         {
-            
             case BLOCK_DATA: /* unmatched data */
                 template_offset += 7;
                 if (missing)
@@ -823,7 +1175,7 @@ static int parse_template_file(char *filename, int sizeonly, char *missing, char
                         fclose(file);
                         return error;
                     }
-                    error = parse_data_block(read_length, file, &template_context);
+                    error = parse_data_block(read_length, file, &template_md5_context, &template_sha256_context);
                     if (error)
                     {
                         fprintf(logfile, "Unable to read data block, error %d\n", error);
@@ -835,11 +1187,27 @@ static int parse_template_file(char *filename, int sizeonly, char *missing, char
                 else
                     error = skip_data_block(extent_size, file);
                 break;
-            case BLOCK_MATCH:
+            case BLOCK_MATCH_MD5:
+                template_offset += 31;
+                if ((output_offset + extent_size) >= start_offset)
+                {
+                    error = parse_file_block_md5(skip, read_length, extent_size, (unsigned char *)&bufptr[15],
+                                                &template_md5_context, &template_sha256_context, missing);
+                    if (error)
+                    {
+                        fprintf(logfile, "Unable to read file block, error %d\n", error);
+                        fclose(file);
+                        return error;
+                    }
+                    written_length += read_length;
+                }
+                break;
+            case BLOCK_MATCH_SHA256:
                 template_offset += 31;
                 if ((output_offset + extent_size) >= start_offset)
                 {
-                    error = parse_file_block(skip, read_length, extent_size, (unsigned char *)&buf[15], &template_context, missing);
+                    error = parse_file_block_sha256(skip, read_length, extent_size, (unsigned char *)&bufptr[15],
+                                                   &template_md5_context, &template_sha256_context, missing);
                     if (error)
                     {
                         fprintf(logfile, "Unable to read file block, error %d\n", error);
@@ -866,11 +1234,46 @@ static int parse_template_file(char *filename, int sizeonly, char *missing, char
         fprintf(logfile, "\n");
         if (!quick)
         {
-            mk_MD5Final (image_md5sum, &template_context);
-            fprintf(logfile, "Output image MD5 is    ");
-            for (i = 0; i < 16; i++)
-                fprintf(logfile, "%2.2x", image_md5sum[i]);
-            fprintf(logfile, "\n");
+            if (image_md5_valid)
+            {
+                mk_MD5Final (image_md5sum, &template_md5_context);
+               fprintf(logfile, "Output image MD5 is    ");
+               for (i = 0; i < MD5_BYTES; i++)
+                    fprintf(logfile, "%2.2x", image_md5sum[i]);
+               fprintf(logfile, "\n");
+               if (0 == memcmp(image_md5sum, image_md5sum_from_tmpl, MD5_BYTES))
+               {
+                    fprintf(logfile, "OK: MD5 checksums match, image is good!\n");
+                    fprintf(logfile, "WARNING: MD5 is not considered a secure hash!\n");
+                    fprintf(logfile, "WARNING: It is recommended to verify your image in other ways too!\n");
+               }
+               else
+               {
+                    fprintf(logfile, "CHECKSUMS DO NOT MATCH - PROBLEM DETECTED\n");
+                   fclose(file);
+                   free(buf);
+                   return EIO;
+               }
+           }
+            if (image_sha256_valid)
+            {
+                sha256_finish_ctx(&template_sha256_context, image_sha256sum);
+               fprintf(logfile, "Output image SHA256 is    ");
+               for (i = 0; i < SHA256_BYTES; i++)
+                    fprintf(logfile, "%2.2x", image_sha256sum[i]);
+               fprintf(logfile, "\n");
+               if (0 == memcmp(image_sha256sum, image_sha256sum_from_tmpl, SHA256_BYTES))
+               {
+                    fprintf(logfile, "OK: SHA256 checksums match, image is good!\n");
+               }
+               else
+               {
+                    fprintf(logfile, "CHECKSUMS DO NOT MATCH - PROBLEM DETECTED\n");
+                   fclose(file);
+                   free(buf);
+                   return EIO;
+               }
+           }
         }
         fprintf(logfile, "Output image length is %lld bytes\n", written_length);
     }
@@ -884,6 +1287,8 @@ static void usage(char *progname)
     printf(" Options:\n");
     printf(" -f <MD5 name>       Specify an input MD5 file. MD5s must be in jigdo's\n");
     printf("                     pseudo-base64 format\n");
+    printf(" -F <SHA256 name>    Specify an input SHA256 file. SHA256s must be in jigdo's\n");
+    printf("                     pseudo-base64 format\n");
     printf(" -j <jigdo name>     Specify the input jigdo file\n");
     printf(" -t <template name>  Specify the input template file\n");
     printf(" -m <item=path>      Map <item> to <path> to find the files in the mirror\n");
@@ -910,6 +1315,7 @@ int main(int argc, char **argv)
     char *template_filename = NULL;
     char *jigdo_filename = NULL;
     char *md5_filename = NULL;
+    char *sha256_filename = NULL;
     char *output_name = NULL;
     int c = -1;
     int error = 0;
@@ -922,7 +1328,7 @@ int main(int argc, char **argv)
 
     while(1)
     {
-        c = getopt(argc, argv, ":ql:o:j:t:f:m:M:h?s:e:zvO");
+        c = getopt(argc, argv, ":ql:o:j:t:f:F:m:M:h?s:e:zvO");
         if (-1 == c)
             break;
         
@@ -979,6 +1385,15 @@ int main(int argc, char **argv)
                 /* else */
                 md5_filename = optarg;
                 break;                
+            case 'F':
+                if (sha256_filename)
+                {
+                    fprintf(logfile, "Can only specify one SHA256 file!\n");
+                    return EINVAL;
+                }
+                /* else */
+                sha256_filename = optarg;
+                break;
             case 'm':
                 error = add_match_entry(strdup(optarg));
                 if (error)
@@ -1023,9 +1438,10 @@ int main(int argc, char **argv)
 
     if ((NULL == jigdo_filename) &&
         (NULL == md5_filename) && 
+       (NULL == sha256_filename) &&
         !sizeonly)
     {
-        fprintf(logfile, "No jigdo file or MD5 file specified!\n");
+        fprintf(logfile, "No jigdo file or MD5/SHA256 file specified!\n");
         usage(argv[0]);
         return EINVAL;
     }
@@ -1048,6 +1464,17 @@ int main(int argc, char **argv)
         }
     }
 
+    if (sha256_filename)
+    {
+        /* Build up a list of the files we've been fed */
+        error = parse_sha256_file(sha256_filename);
+        if (error)
+        {
+            fprintf(logfile, "Unable to parse the SHA256 file %s, error %d\n", sha256_filename, error);
+            return error;
+        }
+    }
+
     if (jigdo_filename)
     {
         /* Build up a list of file mappings */