WIP sha256 support
authorSteve McIntyre <steve@einval.com>
Thu, 31 Oct 2019 19:04:19 +0000 (19:04 +0000)
committerSteve McIntyre <steve@einval.com>
Thu, 31 Oct 2019 19:04:19 +0000 (19:04 +0000)
Compiles cleanly at least, but needs some plumbing yet

23 files changed:
changelog
src/Makefile.in
src/cachefile.cc
src/cachefile.hh
src/jigdo-file-cmd.cc
src/jigdo-file-cmd.hh
src/jigdo-file.cc
src/mkimage.cc
src/mkimage.hh
src/mktemplate.cc
src/mktemplate.hh
src/scan.cc
src/scan.hh
src/torture.cc
src/util/glibc-sha256.cc [new file with mode: 0644]
src/util/glibc-sha256.hh [new file with mode: 0644]
src/util/md5sum.cc
src/util/md5sum.hh
src/util/sha256sum-test.cc [new file with mode: 0644]
src/util/sha256sum.cc [new file with mode: 0644]
src/util/sha256sum.fh [new file with mode: 0644]
src/util/sha256sum.hh [new file with mode: 0644]
src/util/sha256sum.ih [new file with mode: 0644]

index fbef979..5c1dda2 100644 (file)
--- a/changelog
+++ b/changelog
@@ -1,6 +1,13 @@
 Changelog for Jigsaw Download                               -*- Text -*-
 ------------------------------------------------------------------------
 
+jigdo 0.8.0 -- Steve McIntyre, XXXXX
+
+  - Roll up lots of older fixes from Debian packaging
+  - Fixed lots of warnings thrown by newer compilers
+  - Extensive changes throughout to add support for using SHA256 as
+    well as / instead of MD5 checksums
+
 jigdo 0.7.3 -- Richard Atterer, 19 May 2006
 
   - A maintenance release with some bug fixes
index b8ee5a8..63d151d 100644 (file)
@@ -53,7 +53,7 @@ test-programs =       job/jigdo-io-test@exe@ \
                net/proxyguess-test@exe@ \
                util/autonullptr-test@exe@ util/rsyncsum-test@exe@ \
                util/gunzip-test@exe@ util/log-test@exe@ \
-               util/md5sum-test@exe@ util/mimestream-test@exe@ \
+               util/md5sum-test@exe@ util/sha256sum-test@exe@ util/mimestream-test@exe@ \
                util/string-utf-test@exe@
 # net/uri-test@exe@ needs curl
 
@@ -67,8 +67,8 @@ objects-jigdo =       compat.o glibcurl/glibcurl.o gtk/gtk-makeimage.o \
                job/makeimagedl.o job/single-url.o \
                job/url-mapping.o net/download.o net/uri.o net/proxyguess.o \
                util/bstream.o util/configfile.o util/glibc-getopt.o \
-               util/glibc-getopt1.o util/glibc-md5.o util/gunzip.o \
-               util/log.o util/md5sum.o util/progress.o util/string-utf.o \
+               util/glibc-getopt1.o util/glibc-md5.o util/glibc-sha256.o util/gunzip.o \
+               util/log.o util/md5sum.o util/sha256sum.o util/progress.o util/string-utf.o \
                $(windows-res) \
                util/debug.o # this must come last!
 #^ net/glibwww-callbacks.o net/glibwww-init.o
@@ -76,16 +76,18 @@ objects-jigdo-file = cachefile.o compat.o jigdo-file-cmd.o jigdo-file.o \
                jigdoconfig.o mkimage.o mkjigdo.o mktemplate.o \
                partialmatch.o recursedir.o scan.o util/bstream.o \
                util/configfile.o util/glibc-getopt.o util/glibc-getopt1.o \
-               util/glibc-md5.o util/log.o util/md5sum.o util/rsyncsum.o \
+               util/glibc-md5.o util/glibc-sha256.o util/log.o util/md5sum.o \
+               util/sha256sum.o util/rsyncsum.o \
                util/string.o zstream.o zstream-bz.o zstream-gz.o \
                util/debug.o # this must come last!
 objects-torture = cachefile.o compat.o jigdoconfig.o mkimage.o mkjigdo.o \
                mktemplate.o partialmatch.o recursedir.o scan.o torture.o \
-               util/bstream.o util/configfile.o util/glibc-md5.o \
-               util/log.o util/md5sum.o util/rsyncsum.o util/string.o \
+               util/bstream.o util/configfile.o util/glibc-md5.o util/glibc-sha256.o \
+               util/log.o util/md5sum.o util/sha256sum.o util/rsyncsum.o util/string.o \
                zstream.o zstream-bz.o zstream-gz.o \
                util/debug.o # this must come last!
-objects-random = util/glibc-md5.o util/log.o util/md5sum.o util/random.o \
+objects-random = util/glibc-md5.o util/glibc-sha256.o util/log.o util/md5sum.o \
+               util/sha256sum.o util/random.o \
                util/string.o \
                util/debug.o # this must come last!
 #______________________________
index 0bddcce..6f5a488 100644 (file)
@@ -7,7 +7,7 @@
   it under the terms of the GNU General Public License, version 2. See
   the file COPYING for details.
 
-  Cache with MD5 sums of file contents - used by JigdoCache in scan.hh
+  Cache with checksums of file contents - used by JigdoCache in scan.hh
 
 */
 
@@ -40,7 +40,7 @@ CacheFile::CacheFile(const char* dbName) {
   db->set_cachesize(db, 0, 4*1024*1024, 1);
 
   // Use a btree, create database file if not yet present
-  e = compat_dbOpen(db, dbName, "jigdo filecache v0", DB_BTREE, DB_CREATE,
+  e = compat_dbOpen(db, dbName, "jigdo filecache v1", DB_BTREE, DB_CREATE,
                     0666);
   if (e != 0) {
     // Re-close, in case it is necessary
@@ -50,7 +50,7 @@ CacheFile::CacheFile(const char* dbName) {
     /* If the DB file is old or corrupted, just regenerate it from
        scratch, otherwise throw error. */
     debug("Cache file corrupt, recreating it");
-    if (compat_dbOpen(db, dbName, "jigdo filecache v0", DB_BTREE,
+    if (compat_dbOpen(db, dbName, "jigdo filecache v1", DB_BTREE,
                       DB_CREATE | DB_TRUNCATE, 0666) != 0)
       throw DbError(e);
   }
index ada80c4..62943cc 100644 (file)
@@ -11,7 +11,7 @@
 
 /** @file
 
-  Cache with MD5 sums of file contents - used by JigdoCache in scan.hh
+  Cache with checksums of file contents - used by JigdoCache in scan.hh
 
   The created libdb3 database contains one table with a mapping from
   filenames (without trailing zero byte) to a binary structure. The
   This is not handled by CacheFile; it is passed as an opaque string of
   bytes to scan.hh classes:<pre>
    4   blockLength (of rsync sum)
-   4   md5BlockLength
-   4   blocks (number of valid md5 blocks in this entry)
+   4   csumBlockLength
+   4   blocks (number of valid checksum blocks in this entry)
    8   rsyncSum of file start (only valid if blocks > 0)
   16   fileMD5Sum (only valid if
-                   blocks == (fileSize+md5BlockLength-1)/md5BlockLength )
+                   blocks == (fileSize+csumBlockLength-1)/csumBlockLength )
+  32   fileSHA256Sum (only valid if
+                      blocks == (fileSize+csumBlockLength-1)/csumBlockLength )
   followed by n entries:
-  16   md5sum of block of size md5BlockLength</pre>
+  16   md5sum of block of size csumBlockLength</pre>
+  32   sha256sum of block of size csumBlockLength</pre>
 
   Why is mtime and size not part of the key? Because we only want to
   store one entry per file, not an additional entry whenever the file
@@ -68,7 +71,7 @@ struct DbError : public Error {
 };
 //______________________________________________________________________
 
-/** Cache with MD5 sums of file contents */
+/** Cache with checksums of file contents */
 class CacheFile {
 public:
   /** Create new database or open existing database */
index 8cdaec7..dad4055 100644 (file)
@@ -241,7 +241,7 @@ int JigdoFileCmd::makeTemplate() {
   //____________________
 
   JigdoCache cache(cacheFile, optCacheExpiry, readAmount, *optReporter);
-  cache.setParams(blockLength, md5BlockLength);
+  cache.setParams(blockLength, csumBlockLength);
   cache.setCheckFiles(optCheckFiles);
   if (addLabels(cache)) return 3;
   while (true) {
@@ -289,7 +289,7 @@ int JigdoFileCmd::makeImage() {
 
   if (imageFile != "-" && willOutputTo(imageFile, optForce) > 0) return 3;
   JigdoCache cache(cacheFile, optCacheExpiry, readAmount, *optReporter);
-  cache.setParams(blockLength, md5BlockLength);
+  cache.setParams(blockLength, csumBlockLength);
   while (true) {
     try { cache.readFilenames(fileNames); } // Recurse through directories
     catch (RecurseError e) { optReporter->error(e.message); continue; }
@@ -358,7 +358,7 @@ int JigdoFileCmd::listTemplate() {
 }
 //______________________________________________________________________
 
-int JigdoFileCmd::verifyImage() {
+int JigdoFileCmd::verifyImageMD5() {
   if (imageFile.empty() || templFile.empty()) {
     cerr << subst(_(
       "%1 verify: Not both --image and --template specified.\n"
@@ -415,6 +415,63 @@ int JigdoFileCmd::verifyImage() {
 }
 //______________________________________________________________________
 
+int JigdoFileCmd::verifyImageSHA256() {
+  if (imageFile.empty() || templFile.empty()) {
+    cerr << subst(_(
+      "%1 verify: Not both --image and --template specified.\n"
+      "(Attempt to deduce missing names failed.)\n"), binaryName);
+    exit_tryHelp();
+  }
+
+  bistream* image;
+  auto_ptr<bistream> imageDel(openForInput(image, imageFile));
+
+  JigdoDescVec contents;
+  JigdoDesc::ImageInfoSHA256* info;
+  try {
+    bistream* templ;
+    auto_ptr<bistream> templDel(openForInput(templ, templFile));
+
+    if (JigdoDesc::isTemplate(*templ) == false)
+      optReporter->info(
+          _("Warning: This does not seem to be a template file"));
+
+    JigdoDesc::seekFromEnd(*templ);
+    *templ >> contents;
+    if (!*templ) {
+      string err = subst(_("%1 verify: %2"), binaryName, strerror(errno));
+      optReporter->error(err);
+      return 3;
+    }
+    info = dynamic_cast<JigdoDesc::ImageInfoSHA256*>(contents.back());
+    if (info == 0) {
+      string err = subst(_("%1 verify: Invalid template data - "
+                           "corrupted file?"), binaryName);
+      optReporter->error(err);
+      return 3;
+    }
+  } catch (JigdoDescError e) {
+    string err = subst(_("%1: %2"), binaryName, e.message);
+    optReporter->error(err);
+    return 3;
+  }
+
+  SHA256Sum md; // SHA256Sum of image
+  md.updateFromStream(*image, info->size(), readAmount, *optReporter);
+  md.finish();
+  if (*image) {
+    image->get();
+    if (image->eof() && md == info->sha256()) {
+      optReporter->info(_("OK: Checksums match, image is good!"));
+      return 0;
+    }
+  }
+  optReporter->error(_(
+      "ERROR: Checksums do not match, image might be corrupted!"));
+  return 2;
+}
+//______________________________________________________________________
+
 /* Look up a query (e.g. "MyServer:foo/path/bar") in the JigdoConfig
    mapping. Returns true if something was found, and prints out all
    resulting URIs. */
@@ -562,7 +619,7 @@ int JigdoFileCmd::scanFiles() {
   }
 
   JigdoCache cache(cacheFile, optCacheExpiry, readAmount, *optReporter);
-  cache.setParams(blockLength, md5BlockLength);
+  cache.setParams(blockLength, csumBlockLength);
   if (addLabels(cache)) return 3;
   while (true) {
     try { cache.readFilenames(fileNames); } // Recurse through directories
@@ -575,7 +632,7 @@ int JigdoFileCmd::scanFiles() {
     while (ci != ce) { ci->getMD5Sum(&cache); ++ci; }
   } else {
     // Only cause first md5 block to be read; not scanning the whole file
-    while (ci != ce) { ci->getSums(&cache, 0); ++ci; }
+    while (ci != ce) { ci->getMD5Sums(&cache, 0); ++ci; }
   }
   return 0;
   // Cache data is written out when the JigdoCache is destroyed
@@ -589,7 +646,7 @@ int JigdoFileCmd::scanFiles() {
    actually very similar to scanFiles() above. */
 int JigdoFileCmd::md5sumFiles() {
   JigdoCache cache(cacheFile, optCacheExpiry, readAmount, *optReporter);
-  cache.setParams(blockLength, md5BlockLength);
+  cache.setParams(blockLength, csumBlockLength);
   cache.setCheckFiles(optCheckFiles);
   while (true) {
     try { cache.readFilenames(fileNames); } // Recurse through directories
@@ -618,3 +675,41 @@ int JigdoFileCmd::md5sumFiles() {
   return 0;
   // Cache data is written out when the JigdoCache is destroyed
 }
+//______________________________________________________________________
+
+/* Print SHA256 checksums of arguments like sha256sum(1), but using our
+   Base64-like encoding for the checksum, not hexadecimal like
+   sha256sum(1). Additionally, try to make use of the cache, and only
+   print out the part of any filename following any "//". This is
+   actually very similar to scanFiles() above. */
+int JigdoFileCmd::sha256sumFiles() {
+  JigdoCache cache(cacheFile, optCacheExpiry, readAmount, *optReporter);
+  cache.setParams(blockLength, csumBlockLength);
+  cache.setCheckFiles(optCheckFiles);
+  while (true) {
+    try { cache.readFilenames(fileNames); } // Recurse through directories
+    catch (RecurseError e) { optReporter->error(e.message); continue; }
+    break;
+  }
+
+  if (JigdoFileCmd::optHex) Base64String::hex = true;
+
+  JigdoCache::iterator ci = cache.begin(), ce = cache.end();
+  while (ci != ce) {
+    Base64String m;
+    // Causes whole file to be read
+    const SHA256Sum* md = ci->getSHA256Sum(&cache);
+    if (md != 0) {
+      m.write(md->digest(), 32).flush();
+      string& s(m.result());
+      s += "  ";
+      if (ci->getPath() == "/") s += '/';
+      s += ci->leafName();
+      // Output checksum line
+      optReporter->coutInfo(s);
+    }
+    ++ci;
+  }
+  return 0;
+  // Cache data is written out when the JigdoCache is destroyed
+}
index 8914c49..3af9ece 100644 (file)
@@ -25,6 +25,7 @@
 #include <jigdoconfig.hh>
 #include <scan.hh>
 #include <md5sum.hh>
+#include <sha256sum.hh>
 #include <mkimage.hh>
 #include <mktemplate.hh>
 //______________________________________________________________________
@@ -35,6 +36,7 @@ struct AnyReporter : public MkTemplate::ProgressReporter,
                      public JigdoCache::ProgressReporter,
                      public JigdoDesc::ProgressReporter,
                      public MD5Sum::ProgressReporter,
+                     public SHA256Sum::ProgressReporter,
                      public JigdoConfig::ProgressReporter {
   virtual void error(const string& message) {
     MD5Sum::ProgressReporter::error(message);
@@ -56,7 +58,7 @@ class JigdoFileCmd {
   enum Command {
     MAKE_TEMPLATE, MAKE_IMAGE,
     PRINT_MISSING, PRINT_MISSING_ALL,
-    SCAN, VERIFY, LIST_TEMPLATE, MD5SUM
+    SCAN, VERIFY, LIST_TEMPLATE, MD5SUM, SHA256SUM
   };
   //________________________________________
 
@@ -79,19 +81,19 @@ class JigdoFileCmd {
   static vector<string> optLabels; // Strings of the form "Label=/some/path"
   static vector<string> optUris;   // "Label=http://some.server/"
   static size_t blockLength; // of rsync algorithm, is also minimum file size
-  static size_t md5BlockLength;
+  static size_t csumBlockLength;
   static size_t readAmount;
   static int optZipQuality;
   static bool optBzip2;
   static bool optForce; // true => Silently delete existent output
-  static bool optMkImageCheck; // true => check MD5sums
+  static bool optMkImageCheck; // true => check checksums
   static bool optCheckFiles; // true => check if files exist
   static bool optScanWholeFile; // false => read only first block
   // true => skip smaller matches if a larger match could be possible
   static bool optGreedyMatching;
   static bool optAddImage; // true => Add [Image] section to output .jigdo
   static bool optAddServers; // true => Add [Servers] to output .jigdo
-  static bool optHex; // true => Use hex not base64 output for md5/ls cmds
+  static bool optHex; // true => Use hex not base64 output for checksum/ls cmds
   static string optDebug; // list of debug msg to turn on, or all/help
   // Reporter is defined in config.h and is the base of all other *Reporter's
   static AnyReporter* optReporter;
@@ -113,9 +115,11 @@ class JigdoFileCmd {
   static int makeImage();
   static int printMissing(Command command = PRINT_MISSING);
   static int scanFiles();
-  static int verifyImage();
+  static int verifyImageMD5();
+  static int verifyImageSHA256();
   static int listTemplate();
   static int md5sumFiles();
+  static int sha256sumFiles();
   //@}
 
   /** @name
index 3f84a1e..1453ddd 100644 (file)
@@ -49,7 +49,7 @@ size_t JigdoFileCmd::optCacheExpiry = 60*60*24*30; // default: 30 days
 vector<string> JigdoFileCmd::optLabels;
 vector<string> JigdoFileCmd::optUris;
 size_t JigdoFileCmd::blockLength    =   1*1024U;
-size_t JigdoFileCmd::md5BlockLength = 128*1024U - 55;
+size_t JigdoFileCmd::csumBlockLength = 128*1024U - 55;
 size_t JigdoFileCmd::readAmount     = 128*1024U;
 int JigdoFileCmd::optZipQuality = Z_BEST_COMPRESSION;
 bool JigdoFileCmd::optBzip2 = false;
@@ -137,7 +137,7 @@ public:
     m += _("scanning image");
     print(m, false);
   }
-  virtual void readingMD5(uint64 offInStream, uint64 size) {
+  virtual void readingChecksum(uint64 offInStream, uint64 size) {
     if (!printProgress) return;
     string m;
     append(m, 100 * offInStream / size, 3); // 3
@@ -282,7 +282,7 @@ MyQuietProgressReporter reporterQuiet;
 //______________________________________________________________________
 
 inline void printUsage(bool detailed, size_t blockLength,
-                       size_t md5BlockLength, size_t readAmount) {
+                       size_t csumBlockLength, size_t readAmount) {
   if (detailed) {
     cout << subst(_(
     "\n"
@@ -308,7 +308,8 @@ inline void printUsage(bool detailed, size_t blockLength,
     "  scan sc          Update cache with information about supplied files\n");
   cout << _(
     "  verify ver       Check whether image matches checksum from template\n"
-    "  md5sum md5       Print MD5 checksums similar to md5sum(1)\n");
+    "  md5sum md5       Print MD5 checksums similar to md5sum(1)\n"
+    "  sha256sum sha256 Print SHA256 checksums similar to sha256sum(1)\n");
   if (detailed) {
     cout << _(
     "  list-template ls Print low-level listing of contents of template\n"
@@ -361,17 +362,19 @@ inline void printUsage(bool detailed, size_t blockLength,
     "  --min-length=BYTES [default %1]\n"
     "                   [make-template] Minimum length of files to search\n"
     "                   for in image data\n"
-    "  --md5-block-size=BYTES [default %2]\n"
+    "  --checksum-block-size=BYTES [default %2]\n"
     "                   Uninteresting internal parameter -\n"
-    "                   jigdo-file enforces: min-length < md5-block-size\n"
+    "                   jigdo-file enforces: min-length < checksum-block-size\n"
+    "  --md5-block-size=BYTES\n"
+    "                   Alias for --checksum-block-size, deprecated.\n"
     "  --readbuffer=BYTES [default %3k]\n"
     "                   Amount of data to read at a time\n"
     "  --check-files [default]\n"
-    "                   [make-template,md5sum] Check if files exist and\n"
+    "                   [make-template,md5sum,sha256sum] Check if files exist and\n"
     "                   get or verify checksums, date and size\n"
     "                   [make-image] Verify checksum of files written to\n"
     "                   image\n"
-    "  --no-check-files [make-template,md5sum] when used with --cache,\n"
+    "  --no-check-files [make-template,md5sum,sha256sum] when used with --cache,\n"
     "                   [make-image] Do not verify checksums of files\n"
     "  --scan-whole-file [scan] Scan whole file instead of only first block\n"
     "  --no-scan-whole-file [scan] Scan only first block [default]\n"
@@ -394,13 +397,13 @@ inline void printUsage(bool detailed, size_t blockLength,
     "  --no-debug       No debugging info [default]\n"
     "  --match-exec=CMD [make-template] Execute command when files match\n"
     "                   CMD is passed to a shell, with environment set up:\n"
-    "                   LABEL, LABELPATH, MATCHPATH, LEAF, MD5SUM, FILE\n"
+    "                   LABEL, LABELPATH, MATCHPATH, LEAF, MD5SUM, SHA256SUM, FILE\n"
     "                   e.g. 'mkdir -p \"${LABEL:-.}/$MATCHPATH\" && ln -f \"$FILE\" \"${LABEL:-.}/$MATCHPATH$LEAF\"'\n"
     "  --no-hex [default]\n"
-    "  --hex            [md5sum, list-template] Output checksums in\n"
+    "  --hex            [md5sum,sha256sum,list-template] Output checksums in\n"
     "                   hexadecimal, not Base64\n"
     "  --gzip           [default] Use gzip compression, not --bzip2\n"),
-    blockLength, md5BlockLength, readAmount / 1024) << endl;
+    blockLength, csumBlockLength, readAmount / 1024) << endl;
   }
   return;
 }
@@ -490,7 +493,7 @@ void outOfMemory() {
 
 enum {
   LONGOPT_BUFSIZE = 0x100, LONGOPT_NOFORCE, LONGOPT_MINSIZE,
-  LONGOPT_MD5SIZE, LONGOPT_MKIMAGECHECK, LONGOPT_NOMKIMAGECHECK,
+  LONGOPT_CHECKSUMSIZE, LONGOPT_MKIMAGECHECK, LONGOPT_NOMKIMAGECHECK,
   LONGOPT_LABEL, LONGOPT_URI, LONGOPT_ADDSERVERS, LONGOPT_NOADDSERVERS,
   LONGOPT_ADDIMAGE, LONGOPT_NOADDIMAGE, LONGOPT_NOCACHE, LONGOPT_CACHEEXPIRY,
   LONGOPT_MERGE, LONGOPT_HEX, LONGOPT_NOHEX, LONGOPT_DEBUG, LONGOPT_NODEBUG,
@@ -525,7 +528,8 @@ JigdoFileCmd::Command JigdoFileCmd::cmdOptions(int argc, char* argv[]) {
       { "jigdo",              required_argument, 0, 'j' },
       { "label",              required_argument, 0, LONGOPT_LABEL },
       { "match-exec",         required_argument, 0, LONGOPT_MATCHEXEC },
-      { "md5-block-size",     required_argument, 0, LONGOPT_MD5SIZE },
+      { "md5-block-size",     required_argument, 0, LONGOPT_CHECKSUMSIZE },
+      { "checksum-block-size",required_argument, 0, LONGOPT_CHECKSUMSIZE },
       { "merge",              required_argument, 0, LONGOPT_MERGE },
       { "min-length",         required_argument, 0, LONGOPT_MINSIZE },
       { "no-cache",           no_argument,       0, LONGOPT_NOCACHE },
@@ -571,7 +575,7 @@ JigdoFileCmd::Command JigdoFileCmd::cmdOptions(int argc, char* argv[]) {
     case 'f': optForce = true; break;
     case LONGOPT_NOFORCE: optForce = false; break;
     case LONGOPT_MINSIZE:    blockLength = scanMemSize(optarg); break;
-    case LONGOPT_MD5SIZE: md5BlockLength = scanMemSize(optarg); break;
+    case LONGOPT_CHECKSUMSIZE: csumBlockLength = scanMemSize(optarg); break;
     case LONGOPT_BUFSIZE:     readAmount = scanMemSize(optarg); break;
     case 'r':
       if (strcmp(optarg, "default") == 0) {
@@ -623,7 +627,7 @@ JigdoFileCmd::Command JigdoFileCmd::cmdOptions(int argc, char* argv[]) {
   if (optHelp != '\0' || optVersion) {
     if (optVersion) cout << "jigdo-file version " JIGDO_VERSION << endl;
     if (optHelp != '\0') printUsage(optHelp == 'H', blockLength,
-                                    md5BlockLength, readAmount);
+                                    csumBlockLength, readAmount);
     throw Cleanup(0);
   }
 
@@ -634,14 +638,14 @@ JigdoFileCmd::Command JigdoFileCmd::cmdOptions(int argc, char* argv[]) {
 # endif
   //______________________________
 
-  // Silently correct invalid blockLength/md5BlockLength args
+  // Silently correct invalid blockLength/csumBlockLength args
   if (blockLength < MINIMUM_BLOCKLENGTH) blockLength = MINIMUM_BLOCKLENGTH;
-  if (blockLength >= md5BlockLength) md5BlockLength = blockLength + 1;
-  // Round to next k*64+55 for efficient MD5 calculation
-  md5BlockLength = ((md5BlockLength + 63 - 55) & ~63U) + 55;
+  if (blockLength >= csumBlockLength) csumBlockLength = blockLength + 1;
+  // Round to next k*64+55 for efficient checksum calculation
+  csumBlockLength = ((csumBlockLength + 63 - 55) & ~63U) + 55;
 
   Paranoid(blockLength >= MINIMUM_BLOCKLENGTH
-           && blockLength < md5BlockLength);
+           && blockLength < csumBlockLength);
   //______________________________
 
   // Complain if name of command isn't there
@@ -671,7 +675,9 @@ JigdoFileCmd::Command JigdoFileCmd::cmdOptions(int argc, char* argv[]) {
       { (char *)"list-template",     LIST_TEMPLATE },
       { (char *)"ls",                LIST_TEMPLATE },
       { (char *)"md5sum",            MD5SUM },
-      { (char *)"md5",               MD5SUM }
+      { (char *)"md5",               MD5SUM },
+      { (char *)"sha256sum",         SHA256SUM },
+      { (char *)"sha256",            SHA256SUM }
     };
 
     const CodesEntry *c = codes;
@@ -769,12 +775,16 @@ int main(int argc, char* argv[]) {
     case JigdoFileCmd::SCAN:
       returnValue = JigdoFileCmd::scanFiles();    break;
     case JigdoFileCmd::VERIFY:
-      returnValue = JigdoFileCmd::verifyImage();  break;
+      returnValue = JigdoFileCmd::verifyImageMD5();  break;
+      // FIXME! Needs update to use SHA256
     case JigdoFileCmd::LIST_TEMPLATE:
       returnValue = JigdoFileCmd::listTemplate(); break;
     case JigdoFileCmd::MD5SUM:
       JigdoFileCmd::optCheckFiles = true; // Quick fix, possibly not 100% correct
       returnValue = JigdoFileCmd::md5sumFiles();  break;
+    case JigdoFileCmd::SHA256SUM:
+      JigdoFileCmd::optCheckFiles = true; // Quick fix, possibly not 100% correct
+      returnValue = JigdoFileCmd::sha256sumFiles();  break;
     }
   }
   catch (bad_alloc &) { outOfMemory(); }
index 0987f7e..13b2a2e 100755 (executable)
@@ -130,6 +130,7 @@ bistream& JigdoDescVec::get(bistream& file) {
   uint64 off = 0; // Offset in image
   uint64 read = 0; // Nr of bytes read
   MD5 entryMd5;
+  SHA256 entrySha256;
   uint64 entryLen;
   RsyncSum64 rsum;
   size_t blockLength;
@@ -150,6 +151,18 @@ bistream& JigdoDescVec::get(bistream& file) {
       read += 1 + 6 + entryMd5.serialSizeOf() + 4;
       break;
 
+    case JigdoDesc::IMAGE_INFO_SHA256:
+      unserialize6(entryLen, f);
+      unserialize(entrySha256, f);
+      unserialize4(blockLength, f);
+      if (!file) break;
+      debug("JigdoDesc::read: ImageInfo %1 %2",
+            entryLen, entrySha256.toString());
+      desc.reset(new JigdoDesc::ImageInfoSHA256(entryLen, entrySha256, blockLength));
+      push_back(desc.release());
+      read += 1 + 6 + entrySha256.serialSizeOf() + 4;
+      break;
+
     case JigdoDesc::UNMATCHED_DATA:
       unserialize6(entryLen, f);
       if (!file) break;
@@ -178,6 +191,24 @@ bistream& JigdoDescVec::get(bistream& file) {
       off += entryLen;
       break;
 
+    case JigdoDesc::MATCHED_FILE_SHA256:
+    case JigdoDesc::WRITTEN_FILE_SHA256:
+      unserialize6(entryLen, f);
+      unserialize(rsum, f);
+      unserialize(entrySha256, f);
+      if (!file) break;
+      debug("JigdoDesc::read: %1 %2File %3 %4",
+            off, (type == JigdoDesc::MATCHED_FILE_SHA256 ? "Matched" : "Written"),
+            entryLen, entrySha256.toString());
+      if (type == JigdoDesc::MATCHED_FILE_SHA256)
+        desc.reset(new JigdoDesc::MatchedFileSHA256(off, entryLen, rsum,entrySha256));
+      else
+        desc.reset(new JigdoDesc::WrittenFileSHA256(off, entryLen, rsum,entrySha256));
+      push_back(desc.release());
+      read += 1 + 6 + rsum.serialSizeOf() + entrySha256.serialSizeOf();
+      off += entryLen;
+      break;
+
       // Template entry types that were obsoleted with version 0.6.3:
 
     case JigdoDesc::OBSOLETE_IMAGE_INFO:
@@ -236,7 +267,7 @@ bistream& JigdoDescVec::get(bistream& file) {
 }
 //______________________________________________________________________
 
-bostream& JigdoDescVec::put(bostream& file, MD5Sum* md) const {
+bostream& JigdoDescVec::put(bostream& file, MD5Sum* md, SHA256Sum* sd) const {
   // Pass 1: Accumulate sizes of entries, calculate descLen
   // 4 for DESC, 6 each for length of part at start & end
   uint64 descLen = 4 + 6*2; // Length of DESC part
@@ -255,12 +286,17 @@ bostream& JigdoDescVec::put(bostream& file, MD5Sum* md) const {
   p = serialize4(0x43534544, buf); // "DESC" in little-endian order
   p = serialize6(descLen, p);
   writeBytes(file, buf, 4 + 6);
-  if (md != 0) md->update(buf, 4 + 6);
+  if (md != 0)
+         md->update(buf, 4 + 6);
+  if (sd != 0)
+         sd->update(buf, 4 + 6);
   for (const_iterator i = begin(), e = end(); i != e; ++i) {
     JigdoDesc::ImageInfo* info;
     JigdoDesc::UnmatchedData* unm;
     JigdoDesc::MatchedFile* matched;
     JigdoDesc::WrittenFile* written;
+    JigdoDesc::MatchedFileSHA256* matchedsha;
+    JigdoDesc::WrittenFileSHA256* writtensha;
     /* NB we must first try to cast to WrittenFile, then to
        MatchedFile, because WrittenFile derives from MatchedFile. */
     if ((info = dynamic_cast<JigdoDesc::ImageInfo*>(*i)) != 0)
@@ -271,40 +307,92 @@ bostream& JigdoDescVec::put(bostream& file, MD5Sum* md) const {
       p = written->serialize(buf);
     else if ((matched = dynamic_cast<JigdoDesc::MatchedFile*>(*i)) != 0)
       p = matched->serialize(buf);
+    else if ((writtensha = dynamic_cast<JigdoDesc::WrittenFileSHA256*>(*i)) != 0)
+      p = writtensha->serialize(buf);
+    else if ((matchedsha = dynamic_cast<JigdoDesc::MatchedFileSHA256*>(*i)) != 0)
+      p = matchedsha->serialize(buf);
     else { Assert(false); continue; }
     writeBytes(file, buf, p - buf);
-    if (md != 0) md->update(buf, p - buf);
+    if (md != 0)
+      md->update(buf, p - buf);
+    if (sd != 0)
+      sd->update(buf, p - buf);
   }
   p = serialize6(descLen, buf);
   writeBytes(file, buf, 6);
-  if (md != 0) md->update(buf, 6);
+  if (md != 0)
+    md->update(buf, 6);
+  if (sd != 0)
+    sd->update(buf, p - buf);
   if (DEBUG) { Assert(buf[bufLen - 1] == 0xa5); }
-  return file;
+    return file;
 }
 //______________________________________________________________________
 
 namespace {
   const int J_SIZE_WIDTH = 12;
+  const int J_CSUM_WIDTH = 46;
+  const int J_RSYNC_WIDTH = 12;
 }
 
 ostream& JigdoDesc::ImageInfo::put(ostream& s) const {
-  s << "image-info  " << setw(J_SIZE_WIDTH) << size() << "              "
-    << md5() << ' ' << blockLength() << '\n';
+  s << "image-info-md5    "
+    << setw(J_SIZE_WIDTH) << size()
+    << " " << setw(J_SIZE_WIDTH) << blockLength()
+    << " " << setw(J_CSUM_WIDTH) << md5()
+    << "\n";
+  return s;
+}
+ostream& JigdoDesc::ImageInfoSHA256::put(ostream& s) const {
+  s << "image-info-sha256 "
+    << setw(J_SIZE_WIDTH) << size()
+    << " " << setw(J_SIZE_WIDTH) << blockLength()
+    << " " << setw(J_CSUM_WIDTH) << sha256()
+    << "\n";
   return s;
 }
 ostream& JigdoDesc::UnmatchedData::put(ostream& s) const {
-  s << "in-template " << setw(J_SIZE_WIDTH) << offset() << ' '
-    << setw(J_SIZE_WIDTH) << size() << '\n';
+  s << "in-template       "
+    << setw(J_SIZE_WIDTH) << offset()
+    << " " << setw(J_SIZE_WIDTH) << size()
+    << "\n";
   return s;
 }
 ostream& JigdoDesc::MatchedFile::put(ostream& s) const {
-  s << "need-file   " << setw(J_SIZE_WIDTH) << offset() << ' '
-    << setw(J_SIZE_WIDTH) << size() << ' ' << md5() << ' ' << rsync() << '\n';
+  s << "need-file-md5     "
+    << setw(J_SIZE_WIDTH) << offset()
+    << " " << setw(J_SIZE_WIDTH) << size()
+    << " " << setw(J_CSUM_WIDTH) << md5()
+    << " " << setw(J_RSYNC_WIDTH) << rsync()
+    << "\n";
+  return s;
+}
+ostream& JigdoDesc::MatchedFileSHA256::put(ostream& s) const {
+  s << "need-file-sha256  "
+    << setw(J_SIZE_WIDTH) << offset()
+    << " " << setw(J_SIZE_WIDTH) << size()
+    << " " << setw(J_CSUM_WIDTH) << sha256()
+    << " " << setw(J_RSYNC_WIDTH) << rsync()
+    << "\n";
   return s;
 }
+
 ostream& JigdoDesc::WrittenFile::put(ostream& s) const {
-  s << "have-file   " << setw(J_SIZE_WIDTH) << offset() << ' '
-    << setw(J_SIZE_WIDTH) << size() << ' ' << md5() << ' ' << rsync() << '\n';
+  s << "have-file        "
+    << setw(J_SIZE_WIDTH) << offset()
+    << " " << setw(J_SIZE_WIDTH) << size()
+    << " " << setw(J_CSUM_WIDTH) << md5()
+    << " " << setw(J_RSYNC_WIDTH) << rsync()
+    << "\n";
+  return s;
+}
+ostream& JigdoDesc::WrittenFileSHA256::put(ostream& s) const {
+  s << "have-file-sha256 "
+    << setw(J_SIZE_WIDTH) << offset()
+    << " " << setw(J_SIZE_WIDTH) << size()
+    << " " << setw(J_CSUM_WIDTH) << sha256()
+    << " " << setw(J_RSYNC_WIDTH) << rsync()
+    << "\n";
   return s;
 }
 
@@ -342,8 +430,8 @@ namespace {
   /* Read up to file.size() of bytes from file, write it to image
      stream. Check MD5/rsync sum if requested. Take care not to write
      more than specified amount to image, even if file is longer. */
-  int fileToImage(bostream* img, FilePart& file,
-      const JigdoDesc::MatchedFile& matched, bool checkMD5, size_t rsyncLen,
+  int fileToImageMD5(bostream* img, FilePart& file,
+      const JigdoDesc::MatchedFile& matched, bool checkChecksum, size_t rsyncLen,
       ProgressReporter& reporter, byte* buf, size_t readAmount, uint64& off,
       uint64& nextReport, const uint64 totalBytes) {
     uint64 toWrite = file.size();
@@ -357,7 +445,7 @@ namespace {
 
     // Read from file, write to image
     // First couple of k: Calculate RsyncSum rs and MD5Sum md
-    if (checkMD5 && rsyncLen > 0) {
+    if (checkChecksum && rsyncLen > 0) {
       while (*img && f && !f.eof() && toWrite > 0) {
         size_t n = (toWrite < readAmount ? toWrite : readAmount);
         readBytes(f, buf, n);
@@ -384,7 +472,7 @@ namespace {
       writeBytes(*img, buf, n);
       reportBytesWritten(n, off, nextReport, totalBytes, reporter);
       toWrite -= n;
-      if (checkMD5) md.update(buf, n);
+      if (checkChecksum) md.update(buf, n);
     }
 
     if (toWrite > 0 && (!f || f.eof())) {
@@ -400,7 +488,7 @@ namespace {
         reportBytesWritten(n, off, nextReport, totalBytes, reporter);
         toWrite -= n;
       }
-    } else if (checkMD5
+    } else if (checkChecksum
                && (md.finish() != matched.md5()
                    || (rsyncLen > 0 && rs != matched.rsync()))) {
       err = subst(_("Error: `%1' does not match checksum in template data"),
@@ -416,6 +504,83 @@ namespace {
   }
   //______________________________
 
+  /* Read up to file.size() of bytes from file, write it to image
+     stream. Check MD5/rsync sum if requested. Take care not to write
+     more than specified amount to image, even if file is longer. */
+  int fileToImageSHA256(bostream* img, FilePart& file,
+      const JigdoDesc::MatchedFileSHA256& matched, bool checkChecksum, size_t rsyncLen,
+      ProgressReporter& reporter, byte* buf, size_t readAmount, uint64& off,
+      uint64& nextReport, const uint64 totalBytes) {
+    uint64 toWrite = file.size();
+    SHA256Sum md;
+    RsyncSum64 rs;
+    size_t rl = 0; // Length covered by rs so far
+    string fileName(file.getPath());
+    fileName += file.leafName();
+    bifstream f(fileName.c_str(), ios::binary);
+    string err; // !err.empty() => error occurred
+
+    // Read from file, write to image
+    // First couple of k: Calculate RsyncSum rs and MD5Sum md
+    if (checkChecksum && rsyncLen > 0) {
+      while (*img && f && !f.eof() && toWrite > 0) {
+        size_t n = (toWrite < readAmount ? toWrite : readAmount);
+        readBytes(f, buf, n);
+        n = f.gcount();
+        writeBytes(*img, buf, n);
+        reportBytesWritten(n, off, nextReport, totalBytes, reporter);
+        toWrite -= n;
+        md.update(buf, n);
+        // Update RsyncSum
+        Paranoid(rl < rsyncLen);
+        size_t rsyncToAdd = rsyncLen - rl;
+        if (rsyncToAdd > n) rsyncToAdd = n;
+        rs.addBack(buf, rsyncToAdd);
+        rl += rsyncToAdd;
+        Paranoid(rl <= rsyncLen);
+        if (rl >= rsyncLen) break;
+      }
+    }
+    // Rest of file: Only calculate MD5Sum md
+    while (*img && f && !f.eof() && toWrite > 0) {
+      size_t n = (toWrite < readAmount ? toWrite : readAmount);
+      readBytes(f, buf, n);
+      n = f.gcount();
+      writeBytes(*img, buf, n);
+      reportBytesWritten(n, off, nextReport, totalBytes, reporter);
+      toWrite -= n;
+      if (checkChecksum) md.update(buf, n);
+    }
+
+    if (toWrite > 0 && (!f || f.eof())) {
+      const char* errDetail = "";
+      if (errno != 0) errDetail = strerror(errno);
+      else if (f.eof()) errDetail = _("file is too short");
+      err = subst(_("Error reading from `%1' (%2)"), fileName, errDetail);
+      // Even if there was an error - always try to write right amount
+      memClear(buf, readAmount);
+      while (*img && toWrite > 0) {
+        size_t n = (toWrite < readAmount ? toWrite : readAmount);
+        writeBytes(*img, buf, n);
+        reportBytesWritten(n, off, nextReport, totalBytes, reporter);
+        toWrite -= n;
+      }
+    } else if (checkChecksum
+               && (md.finish() != matched.sha256()
+                   || (rsyncLen > 0 && rs != matched.rsync()))) {
+      err = subst(_("Error: `%1' does not match checksum in template data"),
+                  fileName);
+    }
+
+    if (err.empty()) return 0; // Success
+    reporter.error(err);
+    if (toWrite == 0)
+      return 2; // "May have to fix something before you can continue"
+    else
+      return 3; // Yaargh, disaster! Please delete the .tmp file for me
+  }
+  //______________________________
+
   /* Write all bytes of the image data, i.e. both UnmatchedData and
      MatchedFiles. If any UnmatchedFiles are present in 'files', write
      zeroes instead of the file content and also append a DESC section
@@ -434,7 +599,7 @@ namespace {
      cout, caller should rename file to remove .tmp extension. */
   inline int writeAll(const Task& task, JigdoDescVec& files,
       queue<FilePart*>& toCopy, bistream* templ, const size_t readAmount,
-      bostream* img, const char* name, bool checkMD5,
+      bostream* img, const char* name, bool checkChecksum,
       ProgressReporter& reporter, JigdoCache* cache,
       const uint64 totalBytes) {
 
@@ -471,6 +636,7 @@ namespace {
            nice, but using virtual methods looks even worse.] */
         switch ((*i)->type()) {
           case JigdoDesc::IMAGE_INFO:
+          case JigdoDesc::IMAGE_INFO_SHA256:
             break;
           case JigdoDesc::UNMATCHED_DATA: {
             // Copy data from Zibstream to image.
@@ -517,7 +683,7 @@ namespace {
             } else {
               /* Copy data from file to image, taking care not to
                  write beyond toWrite. */
-              int status = fileToImage(img, *mfile, *self, checkMD5,
+              int status = fileToImageMD5(img, *mfile, *self, checkChecksum,
                   imageInfo.blockLength(), reporter, buf, readAmount, off,
                   nextReport, totalBytes);
               toCopy.pop();
@@ -538,7 +704,54 @@ namespace {
             }
             break;
           }
+          case JigdoDesc::MATCHED_FILE_SHA256: {
+            /* If file present in cache, copy its data to image, if
+               not, copy zeroes. if check==true, verify SHA256 sum match.
+               If successful, turn MatchedFileSHA256 into WrittenFileSHA256. */
+            JigdoDesc::MatchedFileSHA256* self =
+                dynamic_cast<JigdoDesc::MatchedFileSHA256*>(*i);
+            uint64 toWrite = self->size();
+            FilePart* mfile = 0;
+            if (!toCopy.empty()) mfile = toCopy.front();
+            debug("mkimage writeAll(): FilePart@%1, %2 of matched file `%3',"
+                  " toCopy size %4", mfile, toWrite,
+                  (mfile != 0 ? mfile->leafName() : ""), toCopy.size());
+            if (mfile == 0 || self->sha256() != *(mfile->getSHA256Sum(cache))) {
+              // Write right amount of zeroes
+              memClear(buf, readAmount);
+              while (*img && toWrite > 0) {
+                size_t n = (toWrite < readAmount ? toWrite : readAmount);
+                writeBytes(*img, buf, n);
+                reportBytesWritten(n, off, nextReport, totalBytes, reporter);
+                toWrite -= n;
+              }
+              if (result == 0) result = 1; // Soft failure
+            } else {
+              /* Copy data from file to image, taking care not to
+                 write beyond toWrite. */
+              int status = fileToImageSHA256(img, *mfile, *self, checkChecksum,
+                  imageInfo.blockLength(), reporter, buf, readAmount, off,
+                  nextReport, totalBytes);
+              toCopy.pop();
+              if (result < status) result = status;
+              if (status == 0) { // Mark file as written to image
+                *i = new JigdoDesc::WrittenFileSHA256(self->offset(), self->size(),
+                                                     self->rsync(), self->sha256());
+                delete self;
+              } else if (*img && (status > 2 || task == SINGLE_PASS)) {
+                // If !*img, exit after error msg below
+                /* If status <= 2 and task == {CREATE_TMP,MERGE_TMP},
+                   we can continue; there has been an error copying
+                   this individual file, but the right *amount* of
+                   data has been written to the .tmp output file, and
+                   the user may retry the failed one later. */
+                return result;
+              }
+            }
+            break;
+          }
           case JigdoDesc::WRITTEN_FILE:
+          case JigdoDesc::WRITTEN_FILE_SHA256:
           // These are never present in memory, cannot occur:
           case JigdoDesc::OBSOLETE_IMAGE_INFO:
           case JigdoDesc::OBSOLETE_MATCHED_FILE:
@@ -587,7 +800,7 @@ namespace {
      returned, caller should rename file to remove .tmp extension. */
   inline int writeMerge(JigdoDescVec& files, queue<FilePart*>& toCopy,
       const int missing, const size_t readAmount, bfstream* img,
-      const string& imageTmpFile, bool checkMD5, ProgressReporter& reporter,
+      const string& imageTmpFile, bool checkChecksum, ProgressReporter& reporter,
       JigdoCache* cache, const uint64 totalBytes) {
     vector<byte> bufVec(readAmount);
     byte* buf = &bufVec[0];
@@ -621,7 +834,7 @@ namespace {
         result = 2;
         break;
       }
-      int status = fileToImage(img, *mfile, *self, checkMD5,
+      int status = fileToImageMD5(img, *mfile, *self, checkChecksum,
           imageInfo.blockLength(), reporter, buf, readAmount, bytesWritten,
           nextReport, totalBytes);
       toCopy.pop();
index 3341477..205915c 100644 (file)
@@ -26,6 +26,7 @@
 #include <bstream.hh>
 #include <debug.hh>
 #include <md5sum.hh>
+#include <sha256sum.hh>
 #include <scan.hh>
 #include <serialize.hh>
 //______________________________________________________________________
@@ -43,9 +44,16 @@ class JigdoDesc {
 public:
   /** Types of entries in a description section */
   enum Type {
-    IMAGE_INFO = 5, UNMATCHED_DATA = 2, MATCHED_FILE = 6, WRITTEN_FILE = 7,
-    OBSOLETE_IMAGE_INFO = 1, OBSOLETE_MATCHED_FILE = 3,
-    OBSOLETE_WRITTEN_FILE = 4
+    OBSOLETE_IMAGE_INFO = 1,
+    UNMATCHED_DATA = 2,
+    OBSOLETE_MATCHED_FILE = 3,
+    OBSOLETE_WRITTEN_FILE = 4,
+    IMAGE_INFO = 5,
+    MATCHED_FILE = 6,
+    WRITTEN_FILE = 7,
+    IMAGE_INFO_SHA256 = 8,
+    MATCHED_FILE_SHA256 = 9,
+    WRITTEN_FILE_SHA256 = 10
   };
   class ProgressReporter;
   //____________________
@@ -87,9 +95,12 @@ public:
     const string& templFile, bistream* templ, ProgressReporter& reporter);
 
   class ImageInfo;
+  class ImageInfoSHA256;
   class UnmatchedData;
   class MatchedFile;
+  class MatchedFileSHA256;
   class WrittenFile;
+  class WrittenFileSHA256;
 
 private:
   static ProgressReporter noReport;
@@ -122,6 +133,30 @@ private:
 };
 //________________________________________
 
+/** Information about the image file */
+class JigdoDesc::ImageInfoSHA256 : public JigdoDesc {
+public:
+  inline ImageInfoSHA256(uint64 s, const SHA256& m, size_t b);
+  inline ImageInfoSHA256(uint64 s, const SHA256Sum& m, size_t b);
+  inline bool operator==(const JigdoDesc& x) const;
+  Type type() const { return IMAGE_INFO_SHA256; }
+  uint64 size() const { return sizeVal; }
+  const SHA256& sha256() const { return sha256Val; }
+  size_t blockLength() const { return blockLengthVal; }
+  // Default dtor, operator==
+  virtual ostream& put(ostream& s) const;
+
+  template<class Iterator>
+  inline Iterator serialize(Iterator i) const;
+  inline size_t serialSizeOf() const;
+
+private:
+  uint64 sizeVal;
+  SHA256 sha256Val;
+  size_t blockLengthVal;
+};
+//________________________________________
+
 /** Info about data that was not matched by any input file, i.e.
     that is included in the template data verbatim */
 class JigdoDesc::UnmatchedData : public JigdoDesc {
@@ -192,6 +227,53 @@ public:
 };
 //______________________________________________________________________
 
+/** Info about data that *was* matched by an input file */
+class JigdoDesc::MatchedFileSHA256 : public JigdoDesc {
+public:
+  inline MatchedFileSHA256(uint64 o, uint64 s, const RsyncSum64& r, const SHA256& m);
+  inline MatchedFileSHA256(uint64 o, uint64 s, const RsyncSum64& r,
+                     const SHA256Sum& m);
+  inline bool operator==(const JigdoDesc& x) const;
+  Type type() const { return MATCHED_FILE_SHA256; }
+  uint64 offset() const { return offsetVal; }
+  uint64 size() const { return sizeVal; }
+  const SHA256& sha256() const { return sha256Val; }
+  const RsyncSum64& rsync() const { return rsyncVal; }
+  // Default dtor, operator==
+  virtual ostream& put(ostream& s) const;
+
+  template<class Iterator>
+  inline Iterator serialize(Iterator i) const;
+  inline size_t serialSizeOf() const;
+
+private:
+  uint64 offsetVal; // Offset in image
+  uint64 sizeVal;
+  RsyncSum64 rsyncVal;
+  SHA256 sha256Val;
+};
+//________________________________________
+
+/** Like MatchedFileSHA256 - used only in .tmp files to express that the
+    file data was successfully written to the image. NB: Because this
+    derives from MatchedFileSHA256 and because of the implementation of
+    JigdoDesc::operator==, MatchedFileSHA256's and WrittenFileSHA256's will
+    compare equal if their data fields are identical. */
+class JigdoDesc::WrittenFileSHA256 : public MatchedFileSHA256 {
+public:
+  WrittenFileSHA256(uint64 o, uint64 s, const RsyncSum64& r, const SHA256& m)
+    : MatchedFileSHA256(o, s, r, m) { }
+  // Implicit cast to allow MatchedFileSHA256 and WrittenFileSHA256 to compare equal
+  inline bool operator==(const JigdoDesc& x) const;
+  Type type() const { return WRITTEN_FILE_SHA256; }
+  virtual ostream& put(ostream& s) const;
+
+  template<class Iterator>
+  inline Iterator serialize(Iterator i) const;
+  inline size_t serialSizeOf() const;
+};
+//______________________________________________________________________
+
 /** Class allowing JigdoDesc to convey information back to the caller.
     The default versions of the methods do nothing at all (except for
     error(), which prints the error to cerr) - you need to supply an
@@ -236,7 +318,7 @@ public:
       not be two contiguous Unmatched regions - this is not checked.
       Similarly, the length of the ImageInfo part must match the
       accumulated lengths of the other parts. */
-  bostream& put(bostream& file, MD5Sum* md = 0) const;
+  bostream& put(bostream& file, MD5Sum* md = 0, SHA256Sum* sd = 0) const;
 
   /** List contents of a JigdoDescVec to a stream in human-readable format. */
   void list(ostream& s) throw();
@@ -253,13 +335,23 @@ JigdoDesc::ImageInfo::ImageInfo(uint64 s, const MD5& m, size_t b)
   : sizeVal(s), md5Val(m), blockLengthVal(b) { }
 JigdoDesc::ImageInfo::ImageInfo(uint64 s, const MD5Sum& m, size_t b)
   : sizeVal(s), md5Val(m), blockLengthVal(b) { }
+JigdoDesc::ImageInfoSHA256::ImageInfoSHA256(uint64 s, const SHA256& m, size_t b)
+  : sizeVal(s), sha256Val(m), blockLengthVal(b) { }
+JigdoDesc::ImageInfoSHA256::ImageInfoSHA256(uint64 s, const SHA256Sum& m, size_t b)
+  : sizeVal(s), sha256Val(m), blockLengthVal(b) { }
 
 JigdoDesc::MatchedFile::MatchedFile(uint64 o, uint64 s, const RsyncSum64& r,
                                     const MD5& m)
   : offsetVal(o), sizeVal(s), rsyncVal(r), md5Val(m) { }
+JigdoDesc::MatchedFileSHA256::MatchedFileSHA256(uint64 o, uint64 s, const RsyncSum64& r,
+                                    const SHA256& m)
+  : offsetVal(o), sizeVal(s), rsyncVal(r), sha256Val(m) { }
 JigdoDesc::MatchedFile::MatchedFile(uint64 o, uint64 s, const RsyncSum64& r,
                                     const MD5Sum& m)
   : offsetVal(o), sizeVal(s), rsyncVal(r), md5Val(m) { }
+JigdoDesc::MatchedFileSHA256::MatchedFileSHA256(uint64 o, uint64 s, const RsyncSum64& r,
+                                    const SHA256Sum& m)
+  : offsetVal(o), sizeVal(s), rsyncVal(r), sha256Val(m) { }
 
 //________________________________________
 
@@ -269,6 +361,12 @@ bool JigdoDesc::ImageInfo::operator==(const JigdoDesc& x) const {
   else return size() == i->size() && md5() == i->md5();
 }
 
+bool JigdoDesc::ImageInfoSHA256::operator==(const JigdoDesc& x) const {
+  const ImageInfoSHA256* i = dynamic_cast<const ImageInfoSHA256*>(&x);
+  if (i == 0) return false;
+  else return size() == i->size() && sha256() == i->sha256();
+}
+
 bool JigdoDesc::UnmatchedData::operator==(const JigdoDesc& x) const {
   const UnmatchedData* u = dynamic_cast<const UnmatchedData*>(&x);
   if (u == 0) return false;
@@ -282,6 +380,13 @@ bool JigdoDesc::MatchedFile::operator==(const JigdoDesc& x) const {
               && md5() == m->md5();
 }
 
+bool JigdoDesc::MatchedFileSHA256::operator==(const JigdoDesc& x) const {
+  const MatchedFileSHA256* m = dynamic_cast<const MatchedFileSHA256*>(&x);
+  if (m == 0) return false;
+  else return offset() == m->offset() && size() == m->size()
+              && sha256() == m->sha256();
+}
+
 bool JigdoDesc::WrittenFile::operator==(const JigdoDesc& x) const {
   // NB MatchedFile and WrittenFile considered equal!
   const MatchedFile* m = dynamic_cast<const MatchedFile*>(&x);
@@ -289,6 +394,14 @@ bool JigdoDesc::WrittenFile::operator==(const JigdoDesc& x) const {
   else return offset() == m->offset() && size() == m->size()
               && md5() == m->md5();
 }
+
+bool JigdoDesc::WrittenFileSHA256::operator==(const JigdoDesc& x) const {
+  // NB MatchedFileSHA256 and WrittenFileSHA256 considered equal!
+  const MatchedFileSHA256* m = dynamic_cast<const MatchedFileSHA256*>(&x);
+  if (m == 0) return false;
+  else return offset() == m->offset() && size() == m->size()
+              && sha256() == m->sha256();
+}
 //________________________________________
 
 inline bistream& operator>>(bistream& s, JigdoDescVec& v) {
@@ -314,6 +427,16 @@ Iterator JigdoDesc::ImageInfo::serialize(Iterator i) const {
 }
 size_t JigdoDesc::ImageInfo::serialSizeOf() const { return 1 + 6 + 16 + 4; }
 
+template<class Iterator>
+Iterator JigdoDesc::ImageInfoSHA256::serialize(Iterator i) const {
+  i = serialize1(IMAGE_INFO, i);
+  i = serialize6(size(), i);
+  i = ::serialize(sha256(), i);
+  i = serialize4(blockLength(), i);
+  return i;
+}
+size_t JigdoDesc::ImageInfoSHA256::serialSizeOf() const { return 1 + 6 + 32 + 4; }
+
 template<class Iterator>
 Iterator JigdoDesc::UnmatchedData::serialize(Iterator i) const {
   i = serialize1(UNMATCHED_DATA, i);
@@ -332,6 +455,16 @@ Iterator JigdoDesc::MatchedFile::serialize(Iterator i) const {
 }
 size_t JigdoDesc::MatchedFile::serialSizeOf() const { return 1 + 6 + 8 + 16;}
 
+template<class Iterator>
+Iterator JigdoDesc::MatchedFileSHA256::serialize(Iterator i) const {
+  i = serialize1(MATCHED_FILE, i);
+  i = serialize6(size(), i);
+  i = ::serialize(rsync(), i);
+  i = ::serialize(sha256(), i);
+  return i;
+}
+size_t JigdoDesc::MatchedFileSHA256::serialSizeOf() const { return 1 + 6 + 8 + 32;}
+
 template<class Iterator>
 Iterator JigdoDesc::WrittenFile::serialize(Iterator i) const {
   i = serialize1(WRITTEN_FILE, i);
@@ -342,4 +475,15 @@ Iterator JigdoDesc::WrittenFile::serialize(Iterator i) const {
 }
 size_t JigdoDesc::WrittenFile::serialSizeOf() const { return 1 + 6 + 8 + 16;}
 
+template<class Iterator>
+Iterator JigdoDesc::WrittenFileSHA256::serialize(Iterator i) const {
+  i = serialize1(WRITTEN_FILE_SHA256, i);
+  i = serialize6(size(), i);
+  i = ::serialize(rsync(), i);
+  i = ::serialize(sha256(), i);
+  return i;
+}
+size_t JigdoDesc::WrittenFileSHA256::serialSizeOf() const { return 1 + 6 + 8 + 32;}
+
+
 #endif
index 830596c..7123b43 100644 (file)
@@ -172,6 +172,11 @@ public:
     files.reserve((files.size() + 16) % 16);
     files.push_back(new JigdoDesc::ImageInfo(len, md5, blockLength));
   }
+  // Alternative: insert in DESC section: information about whole image
+  inline void imageInfoSha256(uint64 len, const SHA256Sum& sha256, size_t blockLength) {
+    files.reserve((files.size() + 32) % 32);
+    files.push_back(new JigdoDesc::ImageInfoSHA256(len, sha256, blockLength));
+  }
   // Insert in DESC section: info about some data that was not matched
   inline void unmatchedData(uint64 len) {
     JigdoDesc::UnmatchedData* u;
@@ -193,6 +198,13 @@ public:
     files.push_back(new JigdoDesc::MatchedFile(offset, len, r, md5));
     offset += len;
   }
+  // Alternative: Insert in DESC section: information about a file that matched
+  inline void matchedFileSHA256(uint64 len, const RsyncSum64& r,
+                          const SHA256Sum& sha256) {
+    files.reserve((files.size() + 32) % 32);
+    files.push_back(new JigdoDesc::MatchedFileSHA256(offset, len, r, sha256));
+    offset += len;
+  }
   inline bostream& put(bostream& s, MD5Sum* md) {
     files.put(s, md);
     return s;
@@ -208,10 +220,10 @@ private:
    them, anyway. */
 
 inline bool MkTemplate::scanFiles(size_t blockLength, uint32 blockMask,
-                                  size_t md5BlockLength) {
+                                  size_t csumBlockLength) {
   bool result = SUCCESS;
 
-  cache->setParams(blockLength, md5BlockLength);
+  cache->setParams(blockLength, csumBlockLength);
   FileVec::iterator hashPos;
 
   for (JigdoCache::iterator file = cache->begin();
@@ -227,7 +239,7 @@ inline bool MkTemplate::scanFiles(size_t blockLength, uint32 blockMask,
 //________________________________________
 
 void MkTemplate::checkRsyncSumMatch2(const size_t blockLen,
-    const size_t back, const size_t md5BlockLength, uint64& nextEvent,
+    const size_t back, const size_t csumBlockLength, uint64& nextEvent,
     FilePart* file) {
 
   /* Don't schedule match if its startOff (== off - blockLen) is impossible.
@@ -265,8 +277,8 @@ void MkTemplate::checkRsyncSumMatch2(const size_t blockLen,
   /* Rolling rsum matched - schedule an MD5Sum match. NB: In extreme cases,
      nextEvent may be equal to off */
   x->setStartOffset(off - blockLen);
-  size_t eventLen = (file->size() < md5BlockLength ?
-                     file->size() : md5BlockLength);
+  size_t eventLen = (file->size() < csumBlockLength ?
+                     file->size() : csumBlockLength);
   x->setNextEvent(matches, x->startOffset() + eventLen);
   debug(" %1: Head of %2 match at offset %3, my next event %4",
         off, file->leafName(), x->startOffset(), x->nextEvent());
@@ -280,7 +292,7 @@ void MkTemplate::checkRsyncSumMatch2(const size_t blockLen,
    appropriate entry in "matches". */
 void MkTemplate::checkRsyncSumMatch(const RsyncSum64& sum,
     const uint32& bitMask, const size_t blockLen, const size_t back,
-    const size_t md5BlockLength, uint64& nextEvent) {
+    const size_t csumBlockLength, uint64& nextEvent) {
 
   typedef const vector<FilePart*> FVec;
   FVec& hashEntry = block[sum.getHi() & bitMask];
@@ -292,7 +304,7 @@ void MkTemplate::checkRsyncSumMatch(const RsyncSum64& sum,
     const RsyncSum64* fileSum = file->getRsyncSum(cache);
     if (fileSum != 0 && *fileSum == sum)
       // Insert new partial file match in "matches" queue
-      checkRsyncSumMatch2(blockLen, back, md5BlockLength, nextEvent, file);
+      checkRsyncSumMatch2(blockLen, back, csumBlockLength, nextEvent, file);
     ++i;
   } while (i != e);
   return;
@@ -446,7 +458,7 @@ bool MkTemplate::matchExecCommands(PartialMatch* x) {
    buf[(data+stillBuffered-1)%bufferLength] */
 bool MkTemplate::checkMD5Match(byte* const buf,
     const size_t bufferLength, const size_t data,
-    const size_t md5BlockLength, uint64& nextEvent,
+    const size_t csumBlockLength, uint64& nextEvent,
     const size_t stillBuffered, Desc& desc) {
   PartialMatch* x = matches->front();
   Paranoid(x != 0 && matches->nextEvent() == off);
@@ -465,7 +477,7 @@ bool MkTemplate::checkMD5Match(byte* const buf,
   md.finishForReuse();
   //____________________
 
-  const MD5* xfileSum = x->file()->getSums(cache, x->blockNumber());
+  const MD5* xfileSum = x->file()->getMD5Sums(cache, x->blockNumber());
   if (debug)
     debug("checkMD5Match?: image %1, file %2 block #%3 %4",
           md.toString(), x->file()->leafName(), x->blockNumber(),
@@ -484,7 +496,7 @@ bool MkTemplate::checkMD5Match(byte* const buf,
     // Still some more to go - update x and its position in queue
     x->setBlockOffset(data);
     x->setBlockNumber(x->blockNumber() + 1);
-    x->setNextEvent(matches, min(x->nextEvent() + md5BlockLength,
+    x->setNextEvent(matches, min(x->nextEvent() + csumBlockLength,
                                  x->startOffset() + x->file()->size()));
     nextEvent = min(nextEvent, x->nextEvent());
     debug("checkMD5Match: match and more to go, next at off %1",
@@ -608,7 +620,7 @@ bool MkTemplate::unmatchedAtEnd(byte* const buf,
 void MkTemplate::scanImage_mainLoop_fastForward(uint64 nextEvent,
     RsyncSum64* rsum, byte* buf, size_t* data, size_t* n, size_t* rsumBack,
     size_t bufferLength, size_t blockLength, uint32 blockMask,
-    size_t md5BlockLength) {
+    size_t csumBlockLength) {
 
 # if 0
   // Simple version
@@ -622,7 +634,7 @@ void MkTemplate::scanImage_mainLoop_fastForward(uint64 nextEvent,
     *rsumBack = modAdd(*rsumBack, 1, bufferLength);
     if (((off - blockLength) & sectorMask) == 0) {
       checkRsyncSumMatch(*rsum, blockMask, blockLength, *rsumBack,
-                         md5BlockLength, nextEvent);
+                         csumBlockLength, nextEvent);
       sectorMask = sectorLength - 1;
       Paranoid(matches->empty()
                || matches->front()->startOffset() >= unmatchedStart);
@@ -690,7 +702,7 @@ void MkTemplate::scanImage_mainLoop_fastForward(uint64 nextEvent,
     if (off == nextAlignedOff) {
       Paranoid(((off - blockLength) & sectorMask) == 0);
       checkRsyncSumMatch(*rsum, blockMask, blockLength, *rsumBack,
-                         md5BlockLength, nextEvent);
+                         csumBlockLength, nextEvent);
       Paranoid(matches->empty()
                || matches->front()->startOffset() >= unmatchedStart);
       sectorMask = sectorLength - 1;
@@ -706,22 +718,22 @@ void MkTemplate::scanImage_mainLoop_fastForward(uint64 nextEvent,
    Treat buf as a circular buffer. Read new data into at most half the
    buffer. Calculate a rolling checksum covering blockLength bytes. When it
    matches an entry in block, start calculating MD5Sums of blocks of length
-   md5BlockLength.
+   csumBlockLength.
 
    Since both image and templ can be non-seekable, we run into a problem in
    the following case: After the initial RsyncSum match, a few of the
-   md5BlockLength-sized chunks of one input file were matched, but not all,
+   csumBlockLength-sized chunks of one input file were matched, but not all,
    so in the end, there is no match. Consequently, we would now need to
    re-read that part of the image and pump it through zlib to templ - but we
    can't if the image is stdin! Solution: Since we know that the MD5Sum of a
    block matched part of an input file, we can re-read from there. */
 inline bool MkTemplate::scanImage(byte* buf, size_t bufferLength,
-    size_t blockLength, uint32 blockMask, size_t md5BlockLength,
+    size_t blockLength, uint32 blockMask, size_t csumBlockLength,
     MD5Sum& templMd5Sum) {
   bool result = SUCCESS;
 
   /* Cause input files to be analysed */
-  if (scanFiles(blockLength, blockMask, md5BlockLength))
+  if (scanFiles(blockLength, blockMask, csumBlockLength))
     result = FAILURE;
 
   /* Initialise rolling sums with blockSize bytes 0x7f, and do the same with
@@ -853,7 +865,7 @@ inline bool MkTemplate::scanImage(byte* buf, size_t bufferLength,
             } else {
               dataOld = data - dataOld; off += dataOld; n -= dataOld;
               checkRsyncSumMatch(rsum, blockMask, blockLength, rsumBack,
-                                 md5BlockLength, nextEvent);
+                                 csumBlockLength, nextEvent);
             }
             if (matches->full()) break;
           }
@@ -871,7 +883,7 @@ inline bool MkTemplate::scanImage(byte* buf, size_t bufferLength,
             /* Look for matches of rsum. If found, insert appropriate
                entry in matches list and maybe modify nextEvent. */
             checkRsyncSumMatch(rsum, blockMask, blockLength, rsumBack,
-                               md5BlockLength, nextEvent);
+                               csumBlockLength, nextEvent);
 
             /* We mustn't by accident schedule an event for a part of
                the image that has already been flushed out of the
@@ -883,7 +895,7 @@ inline bool MkTemplate::scanImage(byte* buf, size_t bufferLength,
           // Innermost loop - MATCHES IS FULL
           scanImage_mainLoop_fastForward(nextEvent, &rsum, buf, &data, &n,
               &rsumBack, bufferLength, blockLength, blockMask,
-              md5BlockLength);
+              csumBlockLength);
         } // endif (matches->full())
         if (matches->empty())
           debug(" %1: Event, matches empty", off);
@@ -898,7 +910,7 @@ inline bool MkTemplate::scanImage(byte* buf, size_t bufferLength,
         while (!matches->empty() && matches->nextEvent() == off) {
           size_t stillBuffered = bufferLength - n;
           if (stillBuffered > off) stillBuffered = off;
-          if (checkMD5Match(buf, bufferLength, data, md5BlockLength,
+          if (checkMD5Match(buf, bufferLength, data, csumBlockLength,
                             nextEvent, stillBuffered, desc))
             return FAILURE; // no recovery possible, exit immediately
         }
@@ -971,7 +983,7 @@ bool MkTemplate::run(const string& imageLeafName,
      be able to read readAmount bytes into one buffer half in one go;
      must be able to start calculating an MD5Sum at a position that is
      blockLength bytes back in input; must be able to write out at
-     least previous md5BlockLength bytes in case there is no match. */
+     least previous csumBlockLength bytes in case there is no match. */
   size_t bufferLength = 2 *
     (max_MD5Len_blockLen > readAmount ? max_MD5Len_blockLen : readAmount);
   // Avoid reading less bytes than readAmount at any time
@@ -985,7 +997,7 @@ bool MkTemplate::run(const string& imageLeafName,
     debug("Nr of files: %1 (%2 bits)", fileCount, blockBits);
     debug("Total bytes: %1", fileSizeTotal);
     debug("blockLength: %1", cache->getBlockLen());
-    debug("md5BlockLen: %1", cache->getMD5BlockLen());
+    debug("csumBlockLen: %1", cache->getMD5BlockLen());
     debug("bufLen (kB): %1", bufferLength/1024);
     debug("zipQual:     %1", zipQual);
   }
index 4a4559f..c24f60b 100644 (file)
@@ -137,19 +137,19 @@ private:
   void finalizeJigdo(const string& imageLeafName,
     const string& templLeafName, const MD5Sum& templMd5Sum);
   INLINE bool scanFiles(size_t blockLength, uint32 blockMask,
-    size_t md5BlockLength);
+    size_t csumBlockLength);
   INLINE bool scanImage(byte* buf, size_t bufferLength, size_t blockLength,
-    uint32 blockMask, size_t md5BlockLength, MD5Sum&);
+    uint32 blockMask, size_t csumBlockLength, MD5Sum&);
   static INLINE void insertInTodo(PartialMatchQueue& matches,
     PartialMatch* x);
   void checkRsyncSumMatch2(const size_t blockLen, const size_t back,
-    const size_t md5BlockLength, uint64& nextEvent, FilePart* file);
+    const size_t csumBlockLength, uint64& nextEvent, FilePart* file);
   INLINE void checkRsyncSumMatch(const RsyncSum64& sum,
     const uint32& bitMask, const size_t blockLen, const size_t back,
-    const size_t md5BlockLength, uint64& nextEvent);
+    const size_t csumBlockLength, uint64& nextEvent);
   INLINE bool checkMD5Match(byte* const buf,
     const size_t bufferLength, const size_t data,
-    const size_t md5BlockLength, uint64& nextEvent,
+    const size_t csumBlockLength, uint64& nextEvent,
     const size_t stillBuffered, Desc& desc);
   INLINE bool checkMD5Match_mismatch(const size_t stillBuffered,
     PartialMatch* x, Desc& desc);
@@ -159,7 +159,7 @@ private:
   INLINE void scanImage_mainLoop_fastForward(uint64 nextEvent,
     RsyncSum64* rsum, byte* buf, size_t* data, size_t* n, size_t* rsumBack,
     size_t bufferLength, size_t blockLength, uint32 blockMask,
-    size_t md5BlockLength);
+    size_t csumBlockLength);
   INLINE bool matchExecCommands(PartialMatch* x);
 
   inline void debugRangeInfo(uint64 start, uint64 end, const char* msg,
index 4fed654..44cd070 100644 (file)
@@ -48,18 +48,25 @@ struct stat JigdoCache::fileInfo;
 //______________________________________________________________________
 
 #if HAVE_LIBDB
+
+// How much checksum data do we have per checksum entry?
+#define CSUM_SIZE (16 + 32) // md5 size + sha256 size
+
 /* Interpret a string of bytes (out of the file cache) like this:
 
    4   blockLength (of rsync sum)
-   4   md5BlockLength
+   4   csumBlockLength
    4   blocks (number of valid md5 blocks in this entry), curr. always >0
    8   rsyncSum of file start (only valid if blocks > 0)
   16   fileMD5Sum (only valid if
-                   blocks == (fileSize+md5BlockLength-1)/md5BlockLength )
+                   blocks == (fileSize+csumBlockLength-1)/csumBlockLength )
+  32   fileSHA256Sum (only valid if
+                      blocks == (fileSize+csumBlockLength-1)/csumBlockLength )
   followed by n entries:
-  16   md5sum of block of size md5BlockLength
+    16   md5sum of block of size csumBlockLength
+    32   sha256sum of block of size csumBlockLength
 
-  If stored md5BlockLength doesn't match supplied length, do nothing.
+  If stored csumBlockLength doesn't match supplied length, do nothing.
   Otherwise, restore *this from cached data and return cached
   blockLength (0 if not cached). The caller needs to make sure the
   blockLength matches.
@@ -69,45 +76,55 @@ struct stat JigdoCache::fileInfo;
   iter is missing. It only creates a cache entry. */
 
 size_t FilePart::unserializeCacheEntry(const byte* data, size_t dataSize,
-                                       size_t md5BlockLength){
-  Assert(dataSize > PART_MD5SUM);
+                                       size_t csumBlockLength){
+  Assert(dataSize > PART_MD5SUMS);
 
   // The resize() must have been made by the caller
-  Paranoid(sums.size() == (size() + md5BlockLength - 1) / md5BlockLength);
+  Paranoid(MD5sums.size() == (size() + csumBlockLength - 1) / csumBlockLength);
+  Paranoid(SHA256sums.size() == (size() + csumBlockLength - 1) / csumBlockLength);
 
   size_t cachedBlockLength;
   data = unserialize4(cachedBlockLength, data);
-  size_t cachedMd5BlockLength;
-  data = unserialize4(cachedMd5BlockLength, data);
-  if (cachedMd5BlockLength != md5BlockLength) return 0;
+  size_t cachedCsumBlockLength;
+  data = unserialize4(cachedCsumBlockLength, data);
+  if (cachedCsumBlockLength != csumBlockLength) return 0;
 
   size_t blocks;
   data = unserialize4(blocks, data);
   // Ignore strange-looking entries
-  if (blocks * serialSizeOf(md5Sum) != dataSize - PART_MD5SUM
-      || blocks == 0) {
-    if (blocks == 0) debug("ERR #blocks == 0");
-    else debug("ERR wrong entry size (%1 vs %2)",
-               blocks * 16, dataSize - PART_MD5SUM);
+  if (blocks == 0) {
+    debug("ERR #blocks == 0");
+    return 0;
+  }
+  if (dataSize - PART_MD5SUMS != (blocks * CSUM_SIZE)) {
+    debug("ERR wrong entry size (%1 vs %2)",
+         blocks * CSUM_SIZE, dataSize - PART_MD5SUMS);
     return 0;
   }
   Paranoid(serialSizeOf(rsyncSum) == 8);
   data = unserialize(rsyncSum, data);
   Paranoid(serialSizeOf(md5Sum) == 16);
+  Paranoid(serialSizeOf(sha256Sum) == 32);
   // All blocks of file present?
-  if (blocks == sums.size()) {
+  if (blocks == MD5sums.size() + SHA256sums.size()) {
     setFlag(MD_VALID);
     data = unserialize(md5Sum, data);
+    data = unserialize(sha256Sum, data);
   } else {
     clearFlag(MD_VALID);
-    data += 16;
+    data += CSUM_SIZE;
   }
-  // Read md5 sums of individual chunks of file
-  vector<MD5>::iterator sum = sums.begin();
+  // Read md5sums of individual chunks of file
+  vector<MD5>::iterator sum = MD5sums.begin();
   for (size_t i = blocks; i > 0; --i) {
     data = unserialize(*sum, data);
     ++sum;
   }
+  vector<SHA256>::iterator sum2 = SHA256sums.begin();
+  for (size_t i = blocks; i > 0; --i) {
+    data = unserialize(*sum2, data);
+    ++sum2;
+  }
 
   return cachedBlockLength;
 }
@@ -119,34 +136,42 @@ size_t FilePart::unserializeCacheEntry(const byte* data, size_t dataSize,
 struct FilePart::SerializeCacheEntry {
   SerializeCacheEntry(const FilePart& f, JigdoCache* c, size_t blockLen,
                       size_t md5Len)
-    : file(f), cache(c), blockLength(blockLen), md5BlockLength(md5Len) { }
+    : file(f), cache(c), blockLength(blockLen), csumBlockLength(md5Len) { }
   const FilePart& file;
   JigdoCache* cache;
   size_t blockLength;
-  size_t md5BlockLength;
+  size_t csumBlockLength;
 
   size_t serialSizeOf() {
-    return PART_MD5SUM + (file.mdValid() ? file.sums.size() * 16 : 16);
+    return PART_MD5SUMS + (file.mdValid() ? file.MD5sums.size() * CSUM_SIZE : CSUM_SIZE);
   }
 
   void operator()(byte* data) {
     Paranoid(file.getFlag(TO_BE_WRITTEN));
     // If empty(), shouldn't have been marked TO_BE_WRITTEN:
-    Assert(!file.sums.empty());
+    Assert(!file.MD5sums.empty());
+    Assert(!file.SHA256sums.empty());
 
     data = serialize4(blockLength, data);
-    data = serialize4(md5BlockLength, data);
+    data = serialize4(csumBlockLength, data);
     // Nr of valid blocks - either 1 or all
-    size_t blocks = (file.mdValid() ? file.sums.size() : 1);
+    size_t blocks = (file.mdValid() ? file.MD5sums.size() : 1);
     data = serialize4(blocks, data);
     data = serialize(file.rsyncSum, data);
     data = serialize(file.md5Sum, data);
-    // Write md5 sums of individual chunks of file
-    vector<MD5>::const_iterator sum = file.sums.begin();
+    data = serialize(file.sha256Sum, data);
+    // Write md5sums of individual chunks of file
+    vector<MD5>::const_iterator sum = file.MD5sums.begin();
     for (size_t i = blocks; i > 0; --i) {
       data = serialize(*sum, data);
       ++sum;
     }
+    // Write md5sums of individual chunks of file
+    vector<SHA256>::const_iterator sum2 = file.SHA256sums.begin();
+    for (size_t i = blocks; i > 0; --i) {
+      data = serialize(*sum2, data);
+      ++sum2;
+    }
   }
 };
 #endif
@@ -155,7 +180,7 @@ struct FilePart::SerializeCacheEntry {
 #if HAVE_LIBDB
 JigdoCache::JigdoCache(const string& cacheFileName, size_t expiryInSeconds,
                        size_t bufLen, ProgressReporter& pr)
-  : blockLength(0), md5BlockLength(0), checkFiles(true), files(), nrOfFiles(0),
+  : blockLength(0), csumBlockLength(0), checkFiles(true), files(), nrOfFiles(0),
     locationPaths(), readAmount(bufLen), buffer(), reporter(pr),
     cacheExpiry(expiryInSeconds) {
   cacheFile = 0;
@@ -170,7 +195,7 @@ JigdoCache::JigdoCache(const string& cacheFileName, size_t expiryInSeconds,
 #else
 JigdoCache::JigdoCache(const string&, size_t, size_t bufLen,
                        ProgressReporter& pr)
-  : blockLength(0), md5BlockLength(0), files(), nrOfFiles(0),
+  : blockLength(0), csumBlockLength(0), files(), nrOfFiles(0),
     locationPaths(), readAmount(bufLen), buffer(), reporter(pr) { }
 #endif
 //______________________________________________________________________
@@ -184,7 +209,7 @@ JigdoCache::~JigdoCache() {
       if (i->deleted() || !i->getFlag(FilePart::TO_BE_WRITTEN)) continue;
       debug("Writing %1", i->leafName());
       FilePart::SerializeCacheEntry serializer(*i, this, blockLength,
-                                               md5BlockLength);
+                                               csumBlockLength);
       try {
         cacheFile->insert(serializer, serializer.serialSizeOf(),
                           i->leafName(), i->mtime(), i->size());
@@ -215,18 +240,25 @@ JigdoCache::~JigdoCache() {
 }
 //______________________________________________________________________
 
-/* Either reads data for the first MD5 block and creates sums[0] and
-   rsyncSum, or reads whole file and creates all sums[] entries and
-   rsyncSum and the whole file's MD5 sum. */
-const MD5* FilePart::getSumsRead(JigdoCache* c, size_t blockNr) {
+/* Either:
+   
+   1. read data for the first block and create rsyncSum, MD5sums[0]
+      SHA256sums[0], or;
+
+   2. read the whole file and create rsyncSum, plus both checksums for
+      all the blocks and both checksums for the whole file.
+*/
+
+bool FilePart::getChecksumsRead(JigdoCache* c, size_t blockNr) {
   // Should do this check before calling:
-  Paranoid((blockNr == 0 && sums.empty()) || !mdValid());
+  Paranoid((blockNr == 0 && MD5sums.empty() && SHA256sums.empty()) || !mdValid());
 
   // Do not forget to setParams() before calling this!
-  Assert(c->md5BlockLength != 0);
+  Assert(c->csumBlockLength != 0);
   const size_t thisBlockLength = c->blockLength;
 
-  sums.resize((size() + c->md5BlockLength - 1) / c->md5BlockLength);
+  MD5sums.resize((size() + c->csumBlockLength - 1) / c->csumBlockLength);
+  SHA256sums.resize((size() + c->csumBlockLength - 1) / c->csumBlockLength);
   //____________________
 
 # if HAVE_LIBDB
@@ -236,30 +268,31 @@ const MD5* FilePart::getSumsRead(JigdoCache* c, size_t blockNr) {
     const byte* data;
     size_t dataSize;
     try {
-      /* Unserialize will do nothing if md5BlockLength differs. If
-         md5BlockLength matches, but returned blockLength doesn't, we
+      /* Unserialize will do nothing if csumBlockLength differs. If
+         csumBlockLength matches, but returned blockLength doesn't, we
          need to re-read the first block. */
       if (c->cacheFile->find(data, dataSize, leafName(), size(), mtime())
           .ok()) {
         debug("%1 found, want block#%2", leafName(), blockNr);
         size_t cachedBlockLength = unserializeCacheEntry(data, dataSize,
-                                                         c->md5BlockLength);
+                                                         c->csumBlockLength);
         // Was all necessary data in cache? Yes => return it now.
         if (cachedBlockLength == thisBlockLength
             && (blockNr == 0 || mdValid())) {
           debug("%1 loaded, blockLen (%2) matched, %3/%4 in cache",
-                leafName(), thisBlockLength, (mdValid() ? sums.size() : 1),
-                sums.size());
-          return &sums[blockNr];
+                leafName(), thisBlockLength, (mdValid() ? MD5sums.size() : 1),
+                MD5sums.size());
+          return true;
         }
         /* blockLengths didn't match and/or the cache only contained
-           the first md5 sum while we asked for a later one. It's as
-           if we never queried the cache, except for the case when we
-           need to re-read the first block because the blockLength
-           changed, but *all* blocks' md5sums were in the cache. */
+           the checksum for the first block while we asked for a later
+           one. It's as if we never queried the cache, except for the
+           case when we need to re-read the first block because the
+           blockLength changed, but *all* blocks' checksums were in the
+           cache. */
         debug("%1 loaded, NO match (blockLen %2 vs %3), %4/%5 in cache",
               leafName(), cachedBlockLength, thisBlockLength,
-              (mdValid() ? sums.size() : 1), sums.size());
+              (mdValid() ? MD5sums.size() : 1), MD5sums.size());
       }
     } catch (DbError e) {
       string err = subst(_("Error accessing cache: %1"), e.message);
@@ -298,26 +331,29 @@ const MD5* FilePart::getSumsRead(JigdoCache* c, size_t blockNr) {
   setFlag(TO_BE_WRITTEN);
 
   // Allocate or resize buffer, or do nothing if already right size
-  c->buffer.resize(c->readAmount > c->md5BlockLength ?
-                   c->readAmount : c->md5BlockLength);
+  c->buffer.resize(c->readAmount > c->csumBlockLength ?
+                   c->readAmount : c->csumBlockLength);
   //______________________________
 
-  // Read data and create sums
+  // Read data and create checksums
 
   uint64 off = 0; // File offset of first byte in buf
   // Nr of bytes before we are to reset() md
-  size_t mdLeft = c->md5BlockLength;
+  size_t mdLeft = c->csumBlockLength;
   /* Call reporter once off reaches this value - only report something
-     if scanning >1 md5 block */
+     if scanning >1 checksum block */
   uint64 nextReport = mdLeft;
   MD5Sum md;
   md5Sum.reset();
-  vector<MD5>::iterator sum = sums.begin();
+  vector<MD5>::iterator sum = MD5sums.begin();
+  SHA256Sum sd;
+  sha256Sum.reset();
+  vector<SHA256>::iterator sum2 = SHA256sums.begin();
   //____________________
 
-  // Calculate RsyncSum of head of file and MD5Sums for all blocks
+  // Calculate RsyncSum of head of file and MD5 and SHA256 for all blocks
 
-  Assert(thisBlockLength <= c->md5BlockLength);
+  Assert(thisBlockLength <= c->csumBlockLength);
   byte* buf = &c->buffer[0];
   byte* bufpos = buf;
   byte* bufend = buf + (c->readAmount > thisBlockLength ?
@@ -329,49 +365,63 @@ const MD5* FilePart::getSumsRead(JigdoCache* c, size_t blockNr) {
     debug("Read %1", nn);
   }
   size_t n = bufpos - buf;
+
   // Create RsyncSum of 1st bytes of file, or leave at 0 if file too small
   rsyncSum.reset();
-  if (n >= thisBlockLength) rsyncSum.addBack(buf, thisBlockLength);
+  if (n >= thisBlockLength)
+    rsyncSum.addBack(buf, thisBlockLength);
   //__________
 
   while (true) { // Will break out if error or whole file read
 
     // n is number of valid bytes in buf[]
     off += n;
-    if (off > size()) break; // Argh - file size changed
+    if (off > size())
+      break; // Argh - file size changed
 
     if (off >= nextReport) {
       c->reporter.scanningFile(this, off);
       nextReport += REPORT_INTERVAL;
     }
 
-    // Create MD5 for chunks of size md5BlockLength
+    // Create checksums for chunks of size csumBlockLength
     if (n < mdLeft) {
       md.update(buf, n);
+      sd.update(buf, n);
       mdLeft -= n;
     } else {
       md.update(buf, mdLeft);
+      sd.update(buf, mdLeft);
       byte* cur = buf + mdLeft;
       size_t nn = n - mdLeft;
       do {
         md.finishForReuse();
+        sd.finishForReuse();
         debug("%1: mdLeft (0), switching to next md at off %2, left %3, "
-              "writing sum#%4: %5", name, off - n + cur - buf, nn,
-              sum - sums.begin(), md.toString());
-        Paranoid(sum != sums.end());
+              "writing sum#%4: %5/%6", name, off - n + cur - buf, nn,
+              sum - MD5sums.begin(), md.toString(), sd.toString());
+        Paranoid(sum != MD5sums.end());
         *sum = md;
+        *sum2 = sd;
         ++sum;
-        size_t m = (nn < c->md5BlockLength ? nn : c->md5BlockLength);
+       ++sum2;
+        size_t m = (nn < c->csumBlockLength ? nn : c->csumBlockLength);
         md.reset().update(cur, m);
-        cur += m; nn -= m;
-        mdLeft = c->md5BlockLength - m;
+        sd.reset().update(cur, m);
+        cur += m;
+       nn -= m;
+        mdLeft = c->csumBlockLength - m;
       } while (nn > 0);
     }
 
     md5Sum.update(buf, n); // Create MD5 for the whole file
+    sha256Sum.update(buf, n); // Create MD5 for the whole file
+
+    if (blockNr == 0 && sum != MD5sums.begin())
+      break; // Only wanted 1st block
 
-    if (blockNr == 0 && sum != sums.begin()) break; // Only wanted 1st block
-    if (!input) break; // End of file or error
+    if (!input)
+      break; // End of file or error
 
     // Read more data
     readBytes(input, buf, c->readAmount);
@@ -380,28 +430,32 @@ const MD5* FilePart::getSumsRead(JigdoCache* c, size_t blockNr) {
 
   } // Endwhile (true), will break out if error or whole file read
 
-  Paranoid(sum != sums.end() // >=1 trailing bytes
-           || mdLeft == c->md5BlockLength); // 0 trailing bytes
+  Paranoid(sum != MD5sums.end() // >=1 trailing bytes
+           || mdLeft == c->csumBlockLength); // 0 trailing bytes
   if (off == size() && input.eof()) {
     // Whole file was read
     c->reporter.scanningFile(this, size()); // 100% scanned
-    if (mdLeft < c->md5BlockLength) {
+    if (mdLeft < c->csumBlockLength) {
       (*sum) = md.finish(); // Digest of trailing bytes
-      debug("%1: writing trailing sum#%2: %3",
-            name, sum - sums.begin(), md.toString());
+      (*sum2) = sd.finish(); // Digest of trailing bytes
+      debug("%1: writing trailing sum#%2: %3/%4",
+            name, sum - MD5sums.begin(), md.toString(), sd.toString());
     }
     md5Sum.finish(); // Digest of whole file
+    sha256Sum.finish(); // Digest of whole file
     setFlag(MD_VALID);
-    return &sums[blockNr];
-  } else if (blockNr == 0 && sum != sums.begin()) {
+    return true;
+  } else if (blockNr == 0 && sum != MD5sums.begin()) {
     // Only first md5 block of file was read
     debug("%1: file header read, sum#0 written", name);
 #   if DEBUG
     md5Sum.finish(); // else failed assert in FilePart::SerializeCacheEntry
+    sha256Sum.finish();
 #   else
     md5Sum.abort(); // Saves the memory until whole file is read
+    sha256Sum.abort(); // Saves the memory until whole file is read
 #   endif
-    return &sums[0];
+    return true;
   }
   //____________________
 
@@ -415,30 +469,38 @@ const MD5* FilePart::getSumsRead(JigdoCache* c, size_t blockNr) {
 //______________________________________________________________________
 
 const MD5Sum* FilePart::getMD5SumRead(JigdoCache* c) {
-  if (getSumsRead(c,
-                  (fileSize + c->md5BlockLength - 1) / c->md5BlockLength - 1)
-      == 0) return 0;
+  if (!getChecksumsRead(c, (fileSize + c->csumBlockLength - 1) / c->csumBlockLength - 1))
+      return 0;
   Paranoid(mdValid());
   return &md5Sum;
 }
 //______________________________________________________________________
 
-void JigdoCache::setParams(size_t blockLen, size_t md5BlockLen) {
-  if (blockLen == blockLength && md5BlockLen == md5BlockLength) return;
+const SHA256Sum* FilePart::getSHA256SumRead(JigdoCache* c) {
+  if (!getChecksumsRead(c, (fileSize + c->csumBlockLength - 1) / c->csumBlockLength - 1))
+      return 0;
+  Paranoid(mdValid());
+  return &sha256Sum;
+}
+//______________________________________________________________________
+
+void JigdoCache::setParams(size_t blockLen, size_t csumBlockLen) {
+  if (blockLen == blockLength && csumBlockLen == csumBlockLength) return;
 
   blockLength = blockLen;
-  md5BlockLength = md5BlockLen;
-  Assert(blockLength <= md5BlockLength);
+  csumBlockLength = csumBlockLen;
+  Assert(blockLength <= csumBlockLength);
   for (list<FilePart>::iterator file = files.begin(), end = files.end();
        file != end; ++file) {
-    file->sums.resize(0);
+    file->MD5sums.resize(0);
+    file->SHA256sums.resize(0);
   }
 }
 //______________________________________________________________________
 
 void JigdoCache::addFile(const string& name) {
   // Do not forget to setParams() before calling this!
-  Assert(md5BlockLength != 0);
+  Assert(csumBlockLength != 0);
   // Assumes nonempty filenames
   Paranoid(name.length() > 0);
 
index b5aa21f..b41d1ec 100644 (file)
@@ -27,6 +27,7 @@
 #include <cachefile.hh>
 #include <debug.hh>
 #include <md5sum.hh>
+#include <sha256sum.hh>
 #include <recursedir.fh>
 #include <rsyncsum.hh>
 #include <scan.fh>
@@ -64,9 +65,9 @@ typedef set<LocationPath> LocationPathSet;
     consists of. The object is also responsible for scanning the file
     and creating a) an RsyncSum64 of the first blockLength bytes (as
     determined by JigdoCache). Caching is done in a "lazy" manner;
-    first, only the file size and name is read, then the MD5 sum of
+    first, only the file size and name is read, then the checksum of
     the file's first block as well as the Rsync sum is calculated, and
-    only when the 2nd MD5 sum or the entire file's sum is requested,
+    only when the 2nd checksum or the entire file's sum is requested,
     the file is scanned til EOF. */
 class FilePart {
   /** Objects are only created by JigdoCache */
@@ -82,9 +83,14 @@ public:
   inline time_t mtime() const;
   /** Returns null ptr if error and you don't throw it in your
       JigdoCache error handler */
-  inline const MD5* getSums(JigdoCache* c, size_t blockNr);
+  inline const MD5* getMD5Sums(JigdoCache* c, size_t blockNr);
   /** Returns null ptr if error and you don't throw it */
   inline const MD5Sum* getMD5Sum(JigdoCache* c);
+  /** Returns null ptr if error and you don't throw it in your
+      JigdoCache error handler */
+  inline const SHA256* getSHA256Sums(JigdoCache* c, size_t blockNr);
+  /** Returns null ptr if error and you don't throw it */
+  inline const SHA256Sum* getSHA256Sum(JigdoCache* c);
   /** Returns null ptr if error and you don't throw it */
   inline const RsyncSum64* getRsyncSum(JigdoCache* c);
 
@@ -110,16 +116,18 @@ private:
   inline FilePart(LocationPathSet::iterator p, string rest, uint64 fSize,
                   time_t fMtime);
 
-  /* Called when the methods getSums/getMD5Sum() need data read from
-     file. Might return null. */
-  const MD5* getSumsRead(JigdoCache* c, size_t blockNr);
+  /* Called when the methods getMD5Sums/getMD5Sum() need data read from
+     file. Might return false on failure. */
+  bool getChecksumsRead(JigdoCache* c, size_t blockNr);
+
   const MD5Sum* getMD5SumRead(JigdoCache* c);
+  const SHA256Sum* getSHA256SumRead(JigdoCache* c);
   //__________
 
   /* There are 3 states of a FilePart:
-     a) sums.empty(): File has not been read from so far
-     b) !sums.empty() && !mdValid: sums[0] and rsyncSum are valid
-     c) !sums.empty() && mdValid: all sums[] and rsyncSum and md5Sum valid*/
+     a) MD5sums.empty(): File has not been read from so far
+     b) !MD5sums.empty() && !mdValid: MD5sums[0] and rsyncSum are valid
+     c) !MD5sums.empty() && mdValid: all MD5sums[] and rsyncSum and md5Sum valid*/
 
   LocationPathSet::iterator path;
   string pathRest; // further dir names after "path", and leafname of file
@@ -129,22 +137,25 @@ private:
   /* RsyncSum64 of the first MkTemplate::blockLength bytes of the
      file. */
   RsyncSum64 rsyncSum;
-  bool rsyncValid() const { return sums.size() > 0; }
+  bool rsyncValid() const { return MD5sums.size() > 0; }
 
-  /* File is split up into chunks of length md5BlockLength (the last
+  /* File is split up into chunks of length csumBlockLength (the last
      one may be smaller) and the MD5 checksum of each is
      calculated. */
-  vector<MD5> sums;
+  vector<MD5> MD5sums;
+  vector<SHA256> SHA256sums;
 
   /* Hash of complete file contents. mdValid is true iff md5Sum is
      cached and valid. NB, it is possible that mdValid==true, but
-     sums.size()==0, after md5BlockSize has been changed. If
-     sums.size()==fileSize/md5ChunkSize, then not necessarily
+     MD5sums.size()==0, after csumBlockLength has been changed. If
+     MD5sums.size()==fileSize/csumBlockLength, then not necessarily
      mdValid==true */
   MD5Sum md5Sum;
+  SHA256Sum sha256Sum;
+
   enum Flags {
     EMPTY = 0,
-    // Bit flag is set iff md5Sum contains the whole file's md5 sum
+    // Bit flag is set iff md5Sum and sha256Sum contains the whole file's checksum
     MD_VALID = 1,
     /* This file was looked up in the cache file (whether successfully
        or not doesn't matter) - don't look it up again. */
@@ -161,12 +172,19 @@ private:
 # if HAVE_LIBDB
   // Offsets for binary representation in database (see cachefile.hh)
   enum {
-    BLOCKLEN = 0, MD5BLOCKLEN = 4, MD5BLOCKS = 8, RSYNCSUM = 12,
-    FILE_MD5SUM = 20, PART_MD5SUM = 36
+    BLOCKLEN = 0,
+    CSUMBLOCKLEN = 4,
+    CSUMBLOCKS = 8,
+    RSYNCSUM = 12,
+    FILE_MD5SUM = 20,
+    FILE_SHA256SUM = 36,
+    PART_MD5SUMS = 68
+    // Can't point directly to the offset for PART_SHA256SUM, as
+    // things are dynamic after PART_MD5SUMS
   };
 
   size_t unserializeCacheEntry(const byte* data, size_t dataSize,
-      size_t md5BlockLength); // Byte stream => FilePart
+      size_t csumBlockLength); // Byte stream => FilePart
   struct SerializeCacheEntry; // FilePart => byte stream
   friend struct SerializeCacheEntry;
 # endif
@@ -203,21 +221,21 @@ public:
   ~JigdoCache();
 
   /** Read a list of filenames from the object and store them in the
-      JigdoCache. Only the file size is read during the call, Rsync/MD5
-      sums are calculated later, if/when needed. */
+      JigdoCache. Only the file size is read during the call,
+      Checksums are calculated later, if/when needed. */
   template <class RecurseDir>
   inline void readFilenames(RecurseDir& rd);
-  /** Set the sizes of cache's blockLength and md5BlockLength
+  /** Set the sizes of cache's blockLength and csumBlockLength
       parameters. This means that the checksum returned by a
       FilePart's getRsyncSum() method will cover the specified size.
       NB: If the cache already contains files and the new parameters
       are not the same as the old, the start of the files will
       (eventually) have to be re-read to re-calculate the checksums
       for blockLength, and the *whole* file will have to be re-read
-      for a changed md5BlockLength. */
-  void setParams(size_t blockLen,size_t md5BlockLen);
+      for a changed csumBlockLength. */
+  void setParams(size_t blockLen,size_t csumBlockLen);
   size_t getBlockLen() const { return blockLength; }
-  size_t getMD5BlockLen() const { return md5BlockLength; }
+  size_t getMD5BlockLen() const { return csumBlockLength; }
 
   /** Amount of data that JigdoCache will attempt to read per call to
       ifstream::read(), and size of buffer allocated. Minimum: 64k */
@@ -291,7 +309,7 @@ private:
   /// Default reporter: Only prints error messages to stderr
   static ProgressReporter noReport;
 
-  size_t blockLength, md5BlockLength;
+  size_t blockLength, csumBlockLength;
 
   /* Check if files exist in the filesystem */
   bool checkFiles;
@@ -334,7 +352,7 @@ public:
       cerr */
   virtual void info(const string& message);
   /** Called when the individual files are read. JigdoCache sometimes
-      reads only the first md5BlockLength bytes and sometimes the
+      reads only the first csumBlockLength bytes and sometimes the
       whole file. This is *only* called when the whole file is read
       (if the file only consists of one MD5 block, it is also called).
       @param file File being scanned, or null if not applicable
@@ -349,7 +367,7 @@ public:
 FilePart::FilePart(LocationPathSet::iterator p, string rest, uint64 fSize,
                    time_t fMtime)
   : path(p), pathRest(rest), fileSize(fSize), fileMtime(fMtime),
-    rsyncSum(), sums(), md5Sum(), flags(EMPTY) {
+    rsyncSum(), MD5sums(), md5Sum(), flags(EMPTY) {
   //pathRest.reserve(0);
 }
 
@@ -373,29 +391,51 @@ time_t FilePart::mtime() const {
   return fileMtime;
 }
 
-const MD5* FilePart::getSums(JigdoCache* c, size_t blockNr) {
+const MD5* FilePart::getMD5Sums(JigdoCache* c, size_t blockNr) {
   Paranoid(!deleted());
-  if (mdValid() || (blockNr == 0 && !sums.empty())) return &sums[blockNr];
-  else return getSumsRead(c, blockNr);
+  if ( ((blockNr > 0) || MD5sums.empty()) && !mdValid() )
+    if (!getChecksumsRead(c, blockNr))
+      return 0;
+  return &MD5sums[blockNr];
 }
 
 const MD5Sum* FilePart::getMD5Sum(JigdoCache* c) {
   Paranoid(!deleted());
-  if (mdValid()) return &md5Sum;
-  else return getMD5SumRead(c);
+  if (mdValid())
+    return &md5Sum;
+  else
+    return getMD5SumRead(c);
+}
+
+const SHA256* FilePart::getSHA256Sums(JigdoCache* c, size_t blockNr) {
+  Paranoid(!deleted());
+  if ( ((blockNr > 0) || SHA256sums.empty()) && !mdValid() )
+    if (!getChecksumsRead(c, blockNr))
+      return 0;
+  return &SHA256sums[blockNr];
+}
+
+const SHA256Sum* FilePart::getSHA256Sum(JigdoCache* c) {
+  Paranoid(!deleted());
+  if (mdValid()) return &sha256Sum;
+  else return getSHA256SumRead(c);
 }
 
 const RsyncSum64* FilePart::getRsyncSum(JigdoCache* c) {
   Paranoid(!deleted());
   if (!rsyncValid()) {
-    if (getSumsRead(c, 0) == 0) return 0;
+    if (! getChecksumsRead(c, 0))
+      return 0;
   }
   Paranoid(rsyncValid());
   return &rsyncSum;
 }
 
 void FilePart::markAsDeleted(JigdoCache* c) {
-  fileSize = 0; sums.resize(0); --(c->nrOfFiles);
+  fileSize = 0;
+  MD5sums.resize(0);
+  SHA256sums.resize(0);
+  --(c->nrOfFiles);
 }
 
 void FilePart::setFlag(Flags f) {
index d7df911..12a0808 100644 (file)
@@ -465,12 +465,12 @@ int main(int argc, char* argv[]) {
       // Run MkTemplate operation over it
       // Try default parameters plus 2 other random cases
       size_t blockLen = 4096;
-      size_t md5BlockLen = 128*1024U - 55;
+      size_t csumBlockLen = 128*1024U - 55;
       size_t readAmount = 128U*1024;
       for (int i = 0; i < 2; ++i) {
         cerr << "     case=" << tc << static_cast<char>('a' + i)
              << " blockLen=" << blockLen
-             << " md5BlockLen=" << md5BlockLen
+             << " csumBlockLen=" << csumBlockLen
              << " readAmount=" << readAmount << endl;
         cheatMrNice();
         TortureReport reporter;
@@ -488,7 +488,7 @@ int main(int argc, char* argv[]) {
           fileNames.addFile(f);
         }
         JigdoCache cache(cacheFile, 60*60*24, readAmount);
-        cache.setParams(blockLen, md5BlockLen);
+        cache.setParams(blockLen, csumBlockLen);
         while (true) {
           try { cache.readFilenames(fileNames); }
           catch (RecurseError e) { cerr << e.message << endl; continue; }
@@ -614,12 +614,12 @@ int main(int argc, char* argv[]) {
 
         report << (allChecksOK ? "OK" : "FAIL") << " case=" << tc
                << ", blockLen=" << blockLen
-               << ", md5BlockLen=" << md5BlockLen
+               << ", csumBlockLen=" << csumBlockLen
                << ", readAmount=" << readAmount << endl;
 
         blockLen = 1024 + rand.get(16);
-        md5BlockLen = 1024 + rand.get(18);
-        if (md5BlockLen <= blockLen) md5BlockLen = blockLen + 1;
+        csumBlockLen = 1024 + rand.get(18);
+        if (csumBlockLen <= blockLen) csumBlockLen = blockLen + 1;
         readAmount = 16384 + rand.get(19);
       }
     }
diff --git a/src/util/glibc-sha256.cc b/src/util/glibc-sha256.cc
new file mode 100644 (file)
index 0000000..8f78571
--- /dev/null
@@ -0,0 +1,312 @@
+// Taken from glibc 2.30
+
+/* Functions to compute SHA256 message digest of files or memory blocks.
+   according to the definition of SHA256 in FIPS 180-2.
+   Copyright (C) 2007-2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* Written by Ulrich Drepper <drepper@redhat.com>, 2007.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <endian.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+// #include "sha256.h" - SAM
+#include <sha256sum.hh>
+#include <glibc-sha256.hh>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# ifdef _LIBC
+#  include <byteswap.h>
+#  define SWAP(n) bswap_32 (n)
+#  define SWAP64(n) bswap_64 (n)
+# else
+#  define SWAP(n) \
+    (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#  define SWAP64(n) \
+  (((n) << 56)                                 \
+   | (((n) & 0xff00) << 40)                    \
+   | (((n) & 0xff0000) << 24)                  \
+   | (((n) & 0xff000000) << 8)                 \
+   | (((n) >> 8) & 0xff000000)                 \
+   | (((n) >> 24) & 0xff0000)                  \
+   | (((n) >> 40) & 0xff00)                    \
+   | ((n) >> 56))
+# endif
+#else
+# define SWAP(n) (n)
+# define SWAP64(n) (n)
+#endif
+
+
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (FIPS 180-2:5.1.1)  */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */ };
+
+
+/* Constants for SHA256 from FIPS 180-2:4.2.2.  */
+static const uint32_t K[64] =
+  {
+    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+  };
+
+void __sha256_process_block (const void *, size_t, struct sha256_ctx *);
+
+/* Initialize structure containing state of computation.
+   (FIPS 180-2:5.3.2)  */
+void
+SHA256Sum::sha256_init_ctx (sha256_ctx *ctx)
+{
+  ctx->H[0] = 0x6a09e667;
+  ctx->H[1] = 0xbb67ae85;
+  ctx->H[2] = 0x3c6ef372;
+  ctx->H[3] = 0xa54ff53a;
+  ctx->H[4] = 0x510e527f;
+  ctx->H[5] = 0x9b05688c;
+  ctx->H[6] = 0x1f83d9ab;
+  ctx->H[7] = 0x5be0cd19;
+
+  ctx->total64 = 0;
+  ctx->buflen = 0;
+}
+
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+byte*
+SHA256Sum::sha256_finish_ctx (sha256_ctx *ctx, byte* resbuf)
+{
+  /* Take yet unprocessed bytes into account.  */
+  uint32_t bytes = ctx->buflen;
+  size_t pad;
+
+  /* Now count remaining bytes.  */
+  ctx->total64 += bytes;
+
+  pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+  memcpy (&ctx->buffer[bytes], fillbuf, pad);
+
+  /* Put the 64-bit file length in *bits* at the end of the buffer.  */
+#if _STRING_ARCH_unaligned
+  ctx->buffer64[(bytes + pad) / 8] = SWAP64 (ctx->total64 << 3);
+#else
+  ctx->buffer32[(bytes + pad + 4) / 4] = SWAP (ctx->total[TOTAL64_low] << 3);
+  ctx->buffer32[(bytes + pad) / 4] = SWAP ((ctx->total[TOTAL64_high] << 3)
+                                          | (ctx->total[TOTAL64_low] >> 29));
+#endif
+
+  /* Process last bytes.  */
+  sha256_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+  /* Put result from CTX in first 32 bytes following RESBUF.  */
+  for (unsigned int i = 0; i < 8; ++i)
+    ((uint32_t *) resbuf)[i] = SWAP (ctx->H[i]);
+
+  return resbuf;
+}
+
+void 
+SHA256Sum::sha256_process_bytes (const void *buffer, size_t len, struct sha256_ctx *ctx)
+{
+  /* When we already have some bits in our internal buffer concatenate
+     both inputs first.  */
+  if (ctx->buflen != 0)
+    {
+      size_t left_over = ctx->buflen;
+      size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+      memcpy (&ctx->buffer[left_over], buffer, add);
+      ctx->buflen += (uint32_t)add;
+
+      if (ctx->buflen > 64)
+       {
+         sha256_process_block (ctx->buffer, ctx->buflen & ~63, ctx);
+
+         ctx->buflen &= 63;
+         /* The regions in the following copy operation cannot overlap.  */
+         memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+                 ctx->buflen);
+       }
+
+      buffer = (const char *) buffer + add;
+      len -= add;
+    }
+
+  /* Process available complete blocks.  */
+  if (len >= 64)
+    {
+#if !_STRING_ARCH_unaligned
+/* To check alignment gcc has an appropriate operator.  Other
+   compilers don't.  */
+# if __GNUC__ >= 2
+#  define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__ (uint32_t) != 0)
+# else
+#  define UNALIGNED_P(p) (((uintptr_t) p) % sizeof (uint32_t) != 0)
+# endif
+      if (UNALIGNED_P (buffer))
+       while (len > 64)
+         {
+           sha256_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx);
+           buffer = (const char *) buffer + 64;
+           len -= 64;
+         }
+      else
+#endif
+       {
+         sha256_process_block (buffer, len & ~63, ctx);
+         buffer = (const char *) buffer + (len & ~63);
+         len &= 63;
+       }
+    }
+
+  /* Move remaining bytes into internal buffer.  */
+  if (len > 0)
+    {
+      size_t left_over = ctx->buflen;
+
+      memcpy (&ctx->buffer[left_over], buffer, len);
+      left_over += len;
+      if (left_over >= 64)
+       {
+         sha256_process_block (ctx->buffer, 64, ctx);
+         left_over -= 64;
+         memcpy (ctx->buffer, &ctx->buffer[64], left_over);
+       }
+      ctx->buflen = (uint32_t)left_over;
+    }
+}
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+   It is assumed that LEN % 64 == 0.  */
+void
+SHA256Sum::sha256_process_block (const void *buffer, size_t len, struct sha256_ctx *ctx)
+{
+  const uint32_t *words = (uint32 *)buffer;
+  size_t nwords = len / sizeof (uint32_t);
+  uint32_t a = ctx->H[0];
+  uint32_t b = ctx->H[1];
+  uint32_t c = ctx->H[2];
+  uint32_t d = ctx->H[3];
+  uint32_t e = ctx->H[4];
+  uint32_t f = ctx->H[5];
+  uint32_t g = ctx->H[6];
+  uint32_t h = ctx->H[7];
+
+  /* First increment the byte count.  FIPS 180-2 specifies the possible
+     length of the file up to 2^64 bits.  Here we only compute the
+     number of bytes.  */
+  ctx->total64 += len;
+
+  /* Process all bytes in the buffer with 64 bytes in each round of
+     the loop.  */
+  while (nwords > 0)
+    {
+      uint32_t W[64];
+      uint32_t a_save = a;
+      uint32_t b_save = b;
+      uint32_t c_save = c;
+      uint32_t d_save = d;
+      uint32_t e_save = e;
+      uint32_t f_save = f;
+      uint32_t g_save = g;
+      uint32_t h_save = h;
+
+      /* Operators defined in FIPS 180-2:4.1.2.  */
+#define Ch(x, y, z) ((x & y) ^ (~x & z))
+#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define S0(x) (CYCLIC (x, 2) ^ CYCLIC (x, 13) ^ CYCLIC (x, 22))
+#define S1(x) (CYCLIC (x, 6) ^ CYCLIC (x, 11) ^ CYCLIC (x, 25))
+#define R0(x) (CYCLIC (x, 7) ^ CYCLIC (x, 18) ^ (x >> 3))
+#define R1(x) (CYCLIC (x, 17) ^ CYCLIC (x, 19) ^ (x >> 10))
+
+      /* It is unfortunate that C does not provide an operator for
+        cyclic rotation.  Hope the C compiler is smart enough.  */
+#define CYCLIC(w, s) ((w >> s) | (w << (32 - s)))
+
+      /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2.  */
+      for (unsigned int t = 0; t < 16; ++t)
+       {
+         W[t] = SWAP (*words);
+         ++words;
+       }
+      for (unsigned int t = 16; t < 64; ++t)
+       W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16];
+
+      /* The actual computation according to FIPS 180-2:6.2.2 step 3.  */
+      for (unsigned int t = 0; t < 64; ++t)
+       {
+         uint32_t T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t];
+         uint32_t T2 = S0 (a) + Maj (a, b, c);
+         h = g;
+         g = f;
+         f = e;
+         e = d + T1;
+         d = c;
+         c = b;
+         b = a;
+         a = T1 + T2;
+       }
+
+      /* Add the starting values of the context according to FIPS 180-2:6.2.2
+        step 4.  */
+      a += a_save;
+      b += b_save;
+      c += c_save;
+      d += d_save;
+      e += e_save;
+      f += f_save;
+      g += g_save;
+      h += h_save;
+
+      /* Prepare for the next round.  */
+      nwords -= 16;
+    }
+
+  /* Put checksum in context given as argument.  */
+  ctx->H[0] = a;
+  ctx->H[1] = b;
+  ctx->H[2] = c;
+  ctx->H[3] = d;
+  ctx->H[4] = e;
+  ctx->H[5] = f;
+  ctx->H[6] = g;
+  ctx->H[7] = h;
+}
diff --git a/src/util/glibc-sha256.hh b/src/util/glibc-sha256.hh
new file mode 100644 (file)
index 0000000..c9e0ee0
--- /dev/null
@@ -0,0 +1,76 @@
+/* Declaration of functions and data types used for SHA256 sum computing
+   library functions.
+   Copyright (C) 2007-2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _SHA256_H
+#define _SHA256_H 1
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <endian.h>
+
+
+/* Structure to save state of computation between the single steps.  */
+/* SAM moved this to sha256sum.hh
+struct sha256_ctx
+{
+  uint32_t H[8];
+
+  union
+  {
+    uint64_t total64;
+#define TOTAL64_low (1 - (BYTE_ORDER == LITTLE_ENDIAN))
+#define TOTAL64_high (BYTE_ORDER == LITTLE_ENDIAN)
+    uint32_t total[2];
+  };
+  uint32_t buflen;
+  union
+  {
+    char buffer[128];
+    uint32_t buffer32[32];
+    uint64_t buffer64[16];
+  };
+};
+*/
+
+/* Initialize structure containing state of computation.
+   (FIPS 180-2: 5.3.2)  */
+// SAM extern void __sha256_init_ctx (struct sha256_ctx *ctx) __THROW;
+extern void sha256_init_ctx (struct sha256_ctx *ctx) __THROW;
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is NOT required that LEN is a multiple of 64.  */
+// SAM extern void __sha256_process_bytes (const void *buffer, size_t len,
+//                                 struct sha256_ctx *ctx) __THROW;
+extern void sha256_process_bytes (const void *buffer, size_t len,
+                                   struct sha256_ctx *ctx) __THROW;
+
+/* Process the remaining bytes in the buffer and put result from CTX
+   in first 32 bytes following RESBUF.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+// SAM extern void *__sha256_finish_ctx (struct sha256_ctx *ctx, void *resbuf)
+//     __THROW;
+extern void *sha256_finish_ctx (struct sha256_ctx *ctx, void *resbuf)
+  __THROW;
+
+#endif /* sha256.h */
index 751ebf4..1a4c65b 100644 (file)
@@ -29,7 +29,7 @@ void MD5Sum::ProgressReporter::error(const string& message) {
 void MD5Sum::ProgressReporter::info(const string& message) {
   cerr << message << endl;
 }
-void MD5Sum::ProgressReporter::readingMD5(uint64, uint64) { }
+void MD5Sum::ProgressReporter::readingChecksum(uint64, uint64) { }
 
 MD5Sum::ProgressReporter MD5Sum::noReport;
 //______________________________________________________________________
@@ -120,7 +120,7 @@ uint64 MD5Sum::updateFromStream(bistream& s, uint64 size, size_t bufSize,
     bytesRead += n;
     toRead -= n;
     if (bytesRead >= nextReport) {
-      pr.readingMD5(bytesRead, size);
+      pr.readingChecksum(bytesRead, size);
       nextReport += REPORT_INTERVAL;
     }
   }
index fffecd4..c3af1f2 100644 (file)
@@ -198,7 +198,7 @@ public:
   /// Like error(), but for purely informational messages.
   virtual void info(const string& message);
   /// Called when data is read during updateFromStream()
-  virtual void readingMD5(uint64 offInStream, uint64 size);
+  virtual void readingChecksum(uint64 offInStream, uint64 size);
 };
 //______________________________________________________________________
 
diff --git a/src/util/sha256sum-test.cc b/src/util/sha256sum-test.cc
new file mode 100644 (file)
index 0000000..49d7784
--- /dev/null
@@ -0,0 +1,145 @@
+/* $Id: sha256sum-test.cc,v 1.2 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*-
+  __   _
+  |_) /|  Copyright (C) 2000-2002  |  richard@
+  | \/¯|  Richard Atterer          |  atterer.org
+  ¯ '` ¯
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License, version 2. See
+  the file COPYING for details.
+
+  Quite secure 256-bit checksum
+
+  #test-deps util/glibc-sha256.o util/sha256sum.o
+
+*/
+
+#include <config.h>
+
+#include <iomanip>
+#include <iostream>
+#include <fstream>
+
+#include <bstream.hh>
+#include <log.hh>
+#include <sha256sum.hh>
+#include <mimestream.hh>
+//______________________________________________________________________
+
+namespace {
+
+int returnCode = 0;
+
+// SHA256 test suite (see end of RFC1321)
+
+const byte t1[] = "";
+const byte s1[] =
+"\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\x09\x98\xec\xf8\x42\x7e";
+const byte t2[] = "a";
+const byte s2[] =
+"\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8\x31\xc3\x99\xe2\x69\x77\x26\x61";
+const byte t3[] = "abc";
+const byte s3[] =
+"\x90\x01\x50\x98\x3c\xd2\x4f\xb0\xd6\x96\x3f\x7d\x28\xe1\x7f\x72";
+const byte t4[] = "message digest";
+const byte s4[] =
+"\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d\x52\x5a\x2f\x31\xaa\xf1\x61\xd0";
+const byte t5[] = "abcdefghijklmnopqrstuvwxyz";
+const byte s5[] =
+"\xc3\xfc\xd3\xd7\x61\x92\xe4\x00\x7d\xfb\x49\x6c\xca\x67\xe1\x3b";
+const byte t6[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+const byte s6[] =
+"\xd1\x74\xab\x98\xd2\x77\xd9\xf5\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f";
+const byte t7[] = "12345678901234567890123456789012345678901234567890123456789012345678901234567890";
+const byte s7[] =
+"\x57\xed\xf4\xa2\x2b\xe3\xc9\x55\xac\x49\xda\x2e\x21\x07\xb6\x7a";
+const byte sAll[] =
+"\x6f\xec\x75\xd4\xe7\xfc\xd7\xe9\x66\x46\xb4\xc7\xaf\x96\xbc\xe2";
+
+const char* const hexDigits = "0123456789abcdef";
+
+}
+//______________________________________________________________________
+
+string toHex(const byte* sum) {
+  string result;
+  for (int i = 0; i < 32; ++i) {
+    result += hexDigits[sum[i] >> 4];
+    result += hexDigits[sum[i] & 0xfU];
+  }
+  return result;
+}
+
+void compare(const byte* suite, const byte* mine) {
+  for (int i = 0; i < 32; ++i) {
+    if (suite[i] != mine[i]) {
+      msg("ERROR: Expected %1, but got %2", toHex(suite), toHex(mine));
+      returnCode = 1;
+      return;
+    }
+  }
+  msg("OK: %1", toHex(suite));
+}
+
+void printBlockSums(size_t blockSize, const char* fileName) {
+  bifstream file(fileName, ios::binary);
+  byte buf[blockSize];
+  byte* bufEnd = buf + blockSize;
+
+  while (file) {
+    // read another block
+    byte* cur = buf;
+    while (cur < bufEnd && file) {
+      readBytes(file, cur, bufEnd - cur); // Fill buffer
+      cur += file.gcount();
+    }
+    SHA256Sum sum;
+    sum.update(buf, cur - buf).finishForReuse();
+    cout << ' ' << sum << endl;
+  }
+  exit(0);
+}
+
+int main(int argc, char* argv[]) {
+  if (argc == 2) Logger::scanOptions(argv[1], argv[0]);
+  if (argc == 3) {
+    // 2 cmdline args, blocksize and filename. Print RsyncSums of all blocks
+    printBlockSums(atoi(argv[1]), argv[2]);
+    exit(0);
+  }
+  const byte* sum;
+  SHA256Sum x;
+  SHA256Sum all;
+
+  sum = x.reset().update(t1, sizeof(t1) - 1).finish().digest();
+  compare(s1, sum);
+  all.update(t1, sizeof(t1) - 1);
+
+  sum = x.reset().update(t2, sizeof(t2) - 1).finish().digest();
+  compare(s2, sum);
+  all.update(t2, sizeof(t2) - 1);
+
+  sum = x.reset().update(t3, sizeof(t3) - 1).finish().digest();
+  compare(s3, sum);
+  all.update(t3, sizeof(t3) - 1);
+
+  sum = x.reset().update(t4, sizeof(t4) - 1).finish().digest();
+  compare(s4, sum);
+  all.update(t4, sizeof(t4) - 1);
+
+  sum = x.reset().update(t5, sizeof(t5) - 1).finish().digest();
+  compare(s5, sum);
+  all.update(t5, sizeof(t5) - 1);
+
+  sum = x.reset().update(t6, sizeof(t6) - 1).finish().digest();
+  compare(s6, sum);
+  all.update(t6, sizeof(t6) - 1);
+
+  sum = x.reset().update(t7, sizeof(t7) - 1).finish().digest();
+  compare(s7, sum);
+  all.update(t7, sizeof(t7) - 1);
+
+  sum = all.finish().digest();
+  compare(sAll, sum);
+
+  return returnCode;
+}
diff --git a/src/util/sha256sum.cc b/src/util/sha256sum.cc
new file mode 100644 (file)
index 0000000..e7d6110
--- /dev/null
@@ -0,0 +1,160 @@
+/* $Id: sha256sum.cc,v 1.4 2004/06/20 20:35:15 atterer Exp $ -*- C++ -*-
+  __   _
+  |_) /|  Copyright (C) 2000-2004  |  richard@
+  | \/¯|  Richard Atterer          |  atterer.org
+  ¯ '` ¯
+  "Ported" to C++ by RA. Uses glibc code for the actual algorithm.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License, version 2. See
+  the file COPYING for details.
+
+  Quite secure 128-bit checksum
+
+*/
+
+#include <config.h>
+
+#include <iostream>
+#include <vector>
+
+#include <glibc-sha256.hh>
+#include <sha256sum.hh>
+#include <sha256sum.ih>
+//______________________________________________________________________
+
+void SHA256Sum::ProgressReporter::error(const string& message) {
+  cerr << message << endl;
+}
+void SHA256Sum::ProgressReporter::info(const string& message) {
+  cerr << message << endl;
+}
+void SHA256Sum::ProgressReporter::readingSHA256(uint64, uint64) { }
+
+SHA256Sum::ProgressReporter SHA256Sum::noReport;
+//______________________________________________________________________
+
+SHA256Sum::SHA256Sum(const SHA256Sum& md) {
+  if (md.p == 0) {
+    p = 0;
+    for (int i = 0; i < 32; ++i) sum[i] = md.sum[i];
+  } else {
+    p = new sha256_ctx();
+    *p = *md.p;
+  }
+}
+//________________________________________
+
+// NB must work with self-assign
+SHA256Sum& SHA256Sum::operator=(const SHA256Sum& md) {
+# if DEBUG
+  finished = md.finished;
+# endif
+  if (md.p == 0) {
+    delete p;
+    p = 0;
+    for (int i = 0; i < 32; ++i) sum[i] = md.sum[i];
+  } else {
+    if (p == 0) p = new sha256_ctx();
+    *p = *md.p;
+  }
+  return *this;
+}
+//______________________________________________________________________
+
+string SHA256::toString() const {
+  Base64String m;
+  m.write(sum, 32).flush();
+  return m.result();
+}
+//______________________________________________________________________
+
+bool SHA256::operator_less2(const SHA256& x) const {
+  if (sum[1] < x.sum[1]) return true;
+  if (sum[1] > x.sum[1]) return false;
+  if (sum[2] < x.sum[2]) return true;
+  if (sum[2] > x.sum[2]) return false;
+  if (sum[3] < x.sum[3]) return true;
+  if (sum[3] > x.sum[3]) return false;
+  if (sum[4] < x.sum[4]) return true;
+  if (sum[4] > x.sum[4]) return false;
+  if (sum[5] < x.sum[5]) return true;
+  if (sum[5] > x.sum[5]) return false;
+  if (sum[6] < x.sum[6]) return true;
+  if (sum[6] > x.sum[6]) return false;
+  if (sum[7] < x.sum[7]) return true;
+  if (sum[7] > x.sum[7]) return false;
+  if (sum[8] < x.sum[8]) return true;
+  if (sum[8] > x.sum[8]) return false;
+  if (sum[9] < x.sum[9]) return true;
+  if (sum[9] > x.sum[9]) return false;
+  if (sum[10] < x.sum[10]) return true;
+  if (sum[10] > x.sum[10]) return false;
+  if (sum[11] < x.sum[11]) return true;
+  if (sum[11] > x.sum[11]) return false;
+  if (sum[12] < x.sum[12]) return true;
+  if (sum[12] > x.sum[12]) return false;
+  if (sum[13] < x.sum[13]) return true;
+  if (sum[13] > x.sum[13]) return false;
+  if (sum[14] < x.sum[14]) return true;
+  if (sum[14] > x.sum[14]) return false;
+  if (sum[15] < x.sum[15]) return true;
+  if (sum[15] > x.sum[15]) return false;
+  if (sum[16] < x.sum[16]) return true;
+  if (sum[16] > x.sum[16]) return false;
+  if (sum[17] < x.sum[17]) return true;
+  if (sum[17] > x.sum[17]) return false;
+  if (sum[18] < x.sum[18]) return true;
+  if (sum[18] > x.sum[18]) return false;
+  if (sum[19] < x.sum[19]) return true;
+  if (sum[19] > x.sum[19]) return false;
+  if (sum[20] < x.sum[20]) return true;
+  if (sum[20] > x.sum[20]) return false;
+  if (sum[21] < x.sum[21]) return true;
+  if (sum[21] > x.sum[21]) return false;
+  if (sum[22] < x.sum[22]) return true;
+  if (sum[22] > x.sum[22]) return false;
+  if (sum[23] < x.sum[23]) return true;
+  if (sum[23] > x.sum[23]) return false;
+  if (sum[24] < x.sum[24]) return true;
+  if (sum[24] > x.sum[24]) return false;
+  if (sum[25] < x.sum[25]) return true;
+  if (sum[25] > x.sum[25]) return false;
+  if (sum[26] < x.sum[26]) return true;
+  if (sum[26] > x.sum[26]) return false;
+  if (sum[27] < x.sum[27]) return true;
+  if (sum[27] > x.sum[27]) return false;
+  if (sum[28] < x.sum[28]) return true;
+  if (sum[28] > x.sum[28]) return false;
+  if (sum[29] < x.sum[29]) return true;
+  if (sum[29] > x.sum[29]) return false;
+  if (sum[30] < x.sum[30]) return true;
+  if (sum[30] > x.sum[30]) return false;
+  if (sum[31] < x.sum[31]) return true;
+  return false;
+}
+//______________________________________________________________________
+
+uint64 SHA256Sum::updateFromStream(bistream& s, uint64 size, size_t bufSize,
+                                ProgressReporter& pr) {
+  uint64 nextReport = REPORT_INTERVAL; // When next to call reporter
+  uint64 toRead = size;
+  uint64 bytesRead = 0;
+  vector<byte> buffer;
+  buffer.resize(bufSize);
+  byte* buf = &buffer[0];
+  // Read from stream and update *this
+  while (s && !s.eof() && toRead > 0) {
+    size_t n = (toRead < bufSize ? toRead : bufSize);
+    readBytes(s, buf, n);
+    n = s.gcount();
+    update(buf, n);
+    bytesRead += n;
+    toRead -= n;
+    if (bytesRead >= nextReport) {
+      pr.readingSHA256(bytesRead, size);
+      nextReport += REPORT_INTERVAL;
+    }
+  }
+  return bytesRead;
+}
diff --git a/src/util/sha256sum.fh b/src/util/sha256sum.fh
new file mode 100644 (file)
index 0000000..9df63fe
--- /dev/null
@@ -0,0 +1,17 @@
+/* $Id: sha256sum.fh,v 1.2 2003/09/27 21:31:04 atterer Exp $ -*- C++ -*-
+  __   _
+  |_) /|  Copyright (C) 2000-2002  |  richard@
+  | \/¯|  Richard Atterer          |  atterer.org
+  ¯ '` ¯
+  "Ported" to C++ by RA. Actual SHA256 code taken from glibc
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License, version 2. See
+  the file COPYING for details.
+
+  Quite secure 256-bit checksum
+
+*/
+
+class SHA256;
+class SHA256Sum;
diff --git a/src/util/sha256sum.hh b/src/util/sha256sum.hh
new file mode 100644 (file)
index 0000000..dacf465
--- /dev/null
@@ -0,0 +1,368 @@
+/* $Id: sha256sum.hh,v 1.5 2005/04/09 23:09:52 atterer Exp $ -*- C++ -*-
+  __   _
+  |_) /|  Copyright (C) 2000-2004  |  richard@
+  | \/¯|  Richard Atterer          |  atterer.org
+  ¯ '` ¯
+  "Ported" to C++ by RA. Actual SHA256 code taken from glibc
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License, version 2. See
+  the file COPYING for details.
+
+*//** @file
+
+  128-bit checksum, secure enough for our purposes
+
+*/
+
+#ifndef SHA256SUM_HH
+#define SHA256SUM_HH
+
+#ifndef INLINE
+#  ifdef NOINLINE
+#    define INLINE
+#    else
+#    define INLINE inline
+#  endif
+#endif
+
+#include <config.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <iosfwd>
+#include <string>
+
+#include <bstream.hh>
+#include <debug.hh>
+#include <sha256sum.fh>
+//______________________________________________________________________
+
+/** Container for an already computed SHA256Sum.
+
+    Objects of this class are smaller than SHA256Sum objects by one
+    pointer. As soon as the checksum calculation of an SHA256Sum object
+    has finish()ed, the pointer is no longer needed. If you need to
+    store a large number of calculated SHA256Sums, it may be beneficial
+    to assign the SHA256Sum to an SHA256 to save space. */
+class SHA256 {
+public:
+  SHA256() { }
+  inline SHA256(const SHA256Sum& md);
+  /** 32 bytes of SHA256 checksum */
+  byte sum[32];
+  /** Allows you to treat the object exactly like a pointer to a byte
+      array */
+  operator byte*() { return sum; }
+  operator const byte*() const { return sum; }
+  /** Assign an SHA256Sum */
+  inline SHA256& operator=(const SHA256Sum& md);
+  inline bool operator<(const SHA256& x) const;
+  /** Clear contents to zero */
+  inline SHA256& clear();
+  /** Convert to string */
+  string toString() const;
+
+  template<class Iterator>
+  inline Iterator serialize(Iterator i) const;
+  template<class ConstIterator>
+  inline ConstIterator unserialize(ConstIterator i);
+  inline size_t serialSizeOf() const { return 32; }
+
+  // Default copy ctor
+private:
+  bool operator_less2(const SHA256& x) const;
+  static const byte zero[32];
+};
+
+inline bool operator==(const SHA256& a, const SHA256& b);
+inline bool operator!=(const SHA256& a, const SHA256& b) { return !(a == b); }
+
+/// Output SHA256 as Base64 digest
+INLINE ostream& operator<<(ostream& s, const SHA256& r);
+//______________________________________________________________________
+
+/** A 256-bit, cryptographically strong message digest algorithm.
+
+    Unless described otherwise, if a method returns an SHA256Sum&, then
+    this is a reference to the object itself, to allow chaining of
+    calls. */
+class SHA256Sum {
+  friend class SHA256;
+public:
+  class ProgressReporter;
+
+  /** Initialise the checksum */
+  inline SHA256Sum();
+  /** Initialise with another checksum instance */
+  SHA256Sum(const SHA256Sum& md);
+  ~SHA256Sum() { delete p; }
+  /** Assign another checksum instance */
+  SHA256Sum& operator=(const SHA256Sum& md);
+  /** Tests for equality. Note: Will only return true if both message
+      digest operations have been finished and their SHA256 sums are the
+      same. */
+  inline bool operator==(const SHA256Sum& md) const;
+  inline bool operator!=(const SHA256Sum& md) const;
+  inline bool operator==(const SHA256& md) const { return sum == md; }
+  inline bool operator!=(const SHA256& md) const { return sum != md; }
+  /** Reset checksum object to the same state as immediately after its
+      creation. You must call when reusing an SHA256Sum object - call it
+      just before the first update() for the new checksum. */
+  inline SHA256Sum& reset();
+  /** Process bytes with the checksum algorithm. May lead to some
+      bytes being temporarily buffered internally. */
+  inline SHA256Sum& update(const byte* mem, size_t len);
+  /// Add a single byte. NB, not implemented efficiently ATM
+  inline SHA256Sum& update(byte x) { update(&x, 1); return *this; }
+  /** Process remaining bytes in internal buffer and create the final
+      checksum.
+      @return Pointer to the 32-byte checksum. */
+  inline SHA256Sum& finish();
+  /** Exactly the same behaviour as finish(), but is more efficient if
+      you are going to call reset() again in the near future to re-use
+      the SHA256Sum object.
+      @return Pointer to the 32-byte checksum. */
+  inline SHA256Sum& finishForReuse();
+  /** Deallocate buffers like finish(), but don't generate the final
+      sum */
+  inline SHA256Sum& abort();
+  /** Return 32 byte buffer with checksum. Warning: Returns junk if
+      checksum not yet finish()ed or flush()ed. */
+  inline const byte* digest() const;
+
+  /** Convert to string */
+  INLINE string toString() const;
+
+  /** Read data from file and update() this checksum with it.
+      @param s The stream to read from
+      @param size Total number of bytes to read
+      @param bufSize Size of temporary read buffer
+      @param pr Reporter object
+      @return Number of bytes read (==size if no error) */
+  uint64 updateFromStream(bistream& s, uint64 size,
+      size_t bufSize = 128*1024, ProgressReporter& pr = noReport);
+
+  /* Serializing an SHA256Sum is only allowed after finish(). The
+     serialization is compatible with that of SHA256. */
+  template<class Iterator>
+  inline Iterator serialize(Iterator i) const;
+  template<class ConstIterator>
+  inline ConstIterator unserialize(ConstIterator i);
+  inline size_t serialSizeOf() const { return sum.serialSizeOf(); }
+
+private:
+/* Structure to save state of computation between the single steps.  */
+  struct sha256_ctx
+  {
+    uint32_t H[8];
+    union
+    {
+      uint64_t total64;
+#define TOTAL64_low (1 - (BYTE_ORDER == LITTLE_ENDIAN))
+#define TOTAL64_high (BYTE_ORDER == LITTLE_ENDIAN)
+      uint32_t total[2];
+    };
+    uint32_t buflen;
+    union
+    {
+      char buffer[128];
+      uint32_t buffer32[32];
+      uint64_t buffer64[16];
+    };
+  };
+
+  /// Default reporter: Only prints error messages to stderr
+  static ProgressReporter noReport;
+
+  // These functions are the original glibc API
+  static void sha256_init_ctx(sha256_ctx* ctx);
+  static void sha256_process_bytes(const void* buffer, size_t len,
+                                struct sha256_ctx* ctx);
+  static byte* sha256_finish_ctx(struct sha256_ctx* ctx, byte* resbuf);
+  static byte* sha256_read_ctx(const sha256_ctx *ctx, byte* resbuf);
+  static void sha256_process_block(const void* buffer, size_t len,
+                                sha256_ctx* ctx);
+  SHA256 sum;
+  struct sha256_ctx* p; // null once MD creation is finished
+
+# if DEBUG
+  /* After finish(ForReuse)(), must call reset() before the next
+     update(). OTOH, must only call digest() after finish(). Enforce
+     this rule by keeping track of the state. */
+  bool finished;
+# endif
+};
+
+inline bool operator==(const SHA256& a, const SHA256Sum& b) { return b == a; }
+inline bool operator!=(const SHA256& a, const SHA256Sum& b) { return b != a; }
+
+/// Output SHA256Sum as Base64 digest
+INLINE ostream& operator<<(ostream& s, const SHA256Sum& r);
+//______________________________________________________________________
+
+/** Class allowing JigdoCache to convey information back to the
+    creator of a JigdoCache object. */
+class SHA256Sum::ProgressReporter {
+public:
+  virtual ~ProgressReporter() { }
+  /// General-purpose error reporting.
+  virtual void error(const string& message);
+  /// Like error(), but for purely informational messages.
+  virtual void info(const string& message);
+  /// Called when data is read during updateFromStream()
+  virtual void readingSHA256(uint64 offInStream, uint64 size);
+};
+//______________________________________________________________________
+
+bool SHA256Sum::operator==(const SHA256Sum& md) const {
+# if DEBUG
+  Paranoid(this->finished && md.finished);
+# endif
+  return sum == md.sum;
+}
+bool SHA256Sum::operator!=(const SHA256Sum& md) const {
+# if DEBUG
+  Paranoid(this->finished && md.finished);
+# endif
+  return sum != md.sum;
+}
+
+SHA256Sum::SHA256Sum() {
+  p = new sha256_ctx();
+  sha256_init_ctx(p);
+# if DEBUG
+  finished = false;
+# endif
+}
+
+SHA256Sum& SHA256Sum::reset() {
+  if (p == 0) p = new sha256_ctx();
+  sha256_init_ctx(p);
+# if DEBUG
+  finished = false;
+# endif
+  return *this;
+}
+
+SHA256Sum& SHA256Sum::update(const byte* mem, size_t len) {
+  Paranoid(p != 0);
+# if DEBUG
+  Paranoid(!finished); // Don't forget to call reset() before update()
+# endif
+  sha256_process_bytes(mem, len, p);
+  return *this;
+}
+
+SHA256Sum& SHA256Sum::finish() {
+  Paranoid(p != 0 );
+  sha256_finish_ctx(p, sum);
+  delete p;
+  p = 0;
+# if DEBUG
+  finished = true;
+# endif
+  return *this;
+}
+
+SHA256Sum& SHA256Sum::finishForReuse() {
+  Paranoid(p != 0  );
+  sha256_finish_ctx(p, sum);
+# if DEBUG
+  finished = true;
+# endif
+  return *this;
+}
+
+SHA256Sum& SHA256Sum::abort() {
+  delete p;
+  p = 0;
+# if DEBUG
+  finished = false;
+# endif
+  return *this;
+}
+
+const byte* SHA256Sum::digest() const {
+# if DEBUG
+  Paranoid(finished); // Call finish() first
+# endif
+  return sum.sum;
+}
+
+template<class Iterator>
+inline Iterator SHA256Sum::serialize(Iterator i) const {
+# if DEBUG
+  Paranoid(finished ); // Call finish() first
+# endif
+  return sum.serialize(i);
+}
+template<class ConstIterator>
+inline ConstIterator SHA256Sum::unserialize(ConstIterator i) {
+# if DEBUG
+  finished = true;
+# endif
+  return sum.unserialize(i);
+}
+//____________________
+
+SHA256& SHA256::operator=(const SHA256Sum& md) {
+# if DEBUG
+  Paranoid(md.finished); // Call finish() first
+# endif
+  *this = md.sum;
+  return *this;
+}
+
+bool SHA256::operator<(const SHA256& x) const {
+  if (sum[0] < x.sum[0]) return true;
+  if (sum[0] > x.sum[0]) return false;
+  return operator_less2(x);
+}
+
+// inline bool operator<(const SHA256& a, const SHA256& b) {
+//   return a.operator<(b);
+// }
+
+SHA256::SHA256(const SHA256Sum& md) { *this = md.sum; }
+
+bool operator==(const SHA256& a, const SHA256& b) {
+  // How portable is this?
+  return memcmp(a.sum, b.sum, 32 * sizeof(byte)) == 0;
+# if 0
+  return a.sum[0] == b.sum[0] && a.sum[1] == b.sum[1]
+    && a.sum[2] == b.sum[2] && a.sum[3] == b.sum[3]
+    && a.sum[4] == b.sum[4] && a.sum[5] == b.sum[5]
+    && a.sum[6] == b.sum[6] && a.sum[7] == b.sum[7]
+    && a.sum[8] == b.sum[8] && a.sum[9] == b.sum[9]
+    && a.sum[10] == b.sum[10] && a.sum[11] == b.sum[11]
+    && a.sum[12] == b.sum[12] && a.sum[13] == b.sum[13]
+    && a.sum[14] == b.sum[14] && a.sum[15] == b.sum[15];
+# endif
+}
+
+SHA256& SHA256::clear() {
+  byte* x = sum;
+  *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0;
+  *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0;
+  *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0;
+  *x++ = 0; *x++ = 0; *x++ = 0; *x++ = 0;
+  return *this;
+}
+
+template<class Iterator>
+inline Iterator SHA256::serialize(Iterator i) const {
+  for (int j = 0; j < 32; ++j) { *i = sum[j]; ++i; }
+  return i;
+}
+template<class ConstIterator>
+inline ConstIterator SHA256::unserialize(ConstIterator i) {
+  for (int j = 0; j < 32; ++j) { sum[j] = *i; ++i; }
+  return i;
+}
+
+#ifndef NOINLINE
+#  include <sha256sum.ih> /* NOINLINE */
+#endif
+
+#endif
diff --git a/src/util/sha256sum.ih b/src/util/sha256sum.ih
new file mode 100644 (file)
index 0000000..e46eeda
--- /dev/null
@@ -0,0 +1,50 @@
+/* $Id: sha256sum.ih,v 1.3 2004/06/20 20:35:15 atterer Exp $ -*- C++ -*-
+  __   _
+  |_) /|  Copyright (C) 2000-2004  |  richard@
+  | \/¯|  Richard Atterer          |  atterer.org
+  ¯ '` ¯
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License, version 2. See
+  the file COPYING for details.
+
+  Quite secure 256-bit checksum
+
+*/
+
+#ifndef SHA256SUM_IH
+#define SHA256SUM_IH
+
+#include <mimestream.hh>
+//______________________________________________________________________
+
+ostream& operator<<(ostream& s, const SHA256& r) {
+  Base64String m;
+  m.write(r.sum, 32).flush();
+  s << m.result();
+  return s;
+}
+
+string SHA256Sum::toString() const {
+  const byte* d = digest();
+  if (d == 0) {
+    return "[SHA256_creation_not_finished]";
+  } else {
+    Base64String m;
+    m.write(d, 32).flush();
+    return m.result();
+  }
+}
+
+ostream& operator<<(ostream& s, const SHA256Sum& r) {
+  const byte* d = r.digest();
+  if (d == 0) {
+    s << "[SHA256_creation_not_finished]";
+  } else {
+    Base64String m;
+    m.write(d, 32).flush();
+    s << m.result();
+  }
+  return s;
+}
+
+#endif