]> git.saurik.com Git - apt.git/blobdiff - ftparchive/writer.cc
* apt-pkg/deb/dpkgpm.cc:
[apt.git] / ftparchive / writer.cc
index fc9ea27d7e52af22d10f5a8d8b46e6c21edead2e..18a3de0c250ac1f3f0ea252bb54499438c2b19a7 100644 (file)
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
-#ifdef __GNUG__
-#pragma implementation "writer.h"
-#endif
-
 #include "writer.h"
     
 #include <apti18n.h>
@@ -23,6 +19,7 @@
 #include <apt-pkg/configuration.h>
 #include <apt-pkg/md5.h>
 #include <apt-pkg/sha1.h>
+#include <apt-pkg/sha256.h>
 #include <apt-pkg/deblistparser.h>
 
 #include <sys/types.h>
@@ -31,6 +28,7 @@
 #include <ftw.h>
 #include <fnmatch.h>
 #include <iostream>
+#include <memory>
     
 #include "cachedb.h"
 #include "apt-ftparchive.h"
@@ -60,17 +58,13 @@ FTWScanner::FTWScanner()
 {
    ErrorPrinted = false;
    NoLinkAct = !_config->FindB("APT::FTPArchive::DeLinkAct",true);
-   RealPath = 0;
-   long PMax = pathconf(".",_PC_PATH_MAX);
-   if (PMax > 0)
-      RealPath = new char[PMax];
 }
                                                                        /*}}}*/
 // FTWScanner::Scanner - FTW Scanner                                   /*{{{*/
 // ---------------------------------------------------------------------
 /* This is the FTW scanner, it processes each directory element in the 
    directory tree. */
-int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
+int FTWScanner::ScannerFTW(const char *File,const struct stat *sb,int Flag)
 {
    if (Flag == FTW_DNR)
    {
@@ -85,13 +79,23 @@ int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
    if (Flag != FTW_F)
       return 0;
 
+   return ScannerFile(File, true);
+}
+                                                                       /*}}}*/
+// FTWScanner::ScannerFile - File Scanner                              /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+int FTWScanner::ScannerFile(const char *File, bool const &ReadLink)
+{
    const char *LastComponent = strrchr(File, '/');
+   char *RealPath = NULL;
+
    if (LastComponent == NULL)
       LastComponent = File;
    else
       LastComponent++;
 
-   vector<string>::iterator I;
+   vector<string>::const_iterator I;
    for(I = Owner->Patterns.begin(); I != Owner->Patterns.end(); ++I)
    {
       if (fnmatch((*I).c_str(), LastComponent, 0) == 0)
@@ -105,9 +109,13 @@ int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
       given are not links themselves. */
    char Jnk[2];
    Owner->OriginalPath = File;
-   if (Owner->RealPath != 0 && readlink(File,Jnk,sizeof(Jnk)) != -1 &&
-       realpath(File,Owner->RealPath) != 0)
-      Owner->DoPackage(Owner->RealPath);
+   if (ReadLink &&
+       readlink(File,Jnk,sizeof(Jnk)) != -1 &&
+       (RealPath = realpath(File,NULL)) != 0)
+   {
+      Owner->DoPackage(RealPath);
+      free(RealPath);
+   }
    else
       Owner->DoPackage(File);
    
@@ -120,7 +128,7 @@ int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
       {
         Owner->NewLine(1);
         
-        bool Type = _error->PopMessage(Err);
+        bool const Type = _error->PopMessage(Err);
         if (Type == true)
            cerr << _("E: ") << Err << endl;
         else
@@ -141,20 +149,22 @@ int FTWScanner::Scanner(const char *File,const struct stat *sb,int Flag)
 // FTWScanner::RecursiveScan - Just scan a directory tree              /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-bool FTWScanner::RecursiveScan(string Dir)
+bool FTWScanner::RecursiveScan(string const &Dir)
 {
+   char *RealPath = NULL;
    /* If noprefix is set then jam the scan root in, so we don't generate
       link followed paths out of control */
    if (InternalPrefix.empty() == true)
    {
-      if (realpath(Dir.c_str(),RealPath) == 0)
+      if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
         return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
-      InternalPrefix = RealPath;      
+      InternalPrefix = RealPath;
+      free(RealPath);
    }
    
    // Do recursive directory searching
    Owner = this;
-   int Res = ftw(Dir.c_str(),Scanner,30);
+   int const Res = ftw(Dir.c_str(),ScannerFTW,30);
    
    // Error treewalking?
    if (Res != 0)
@@ -171,15 +181,17 @@ bool FTWScanner::RecursiveScan(string Dir)
 // ---------------------------------------------------------------------
 /* This is an alternative to using FTW to locate files, it reads the list
    of files from another file. */
-bool FTWScanner::LoadFileList(string Dir,string File)
+bool FTWScanner::LoadFileList(string const &Dir, string const &File)
 {
+   char *RealPath = NULL;
    /* If noprefix is set then jam the scan root in, so we don't generate
       link followed paths out of control */
    if (InternalPrefix.empty() == true)
    {
-      if (realpath(Dir.c_str(),RealPath) == 0)
+      if ((RealPath = realpath(Dir.c_str(),NULL)) == 0)
         return _error->Errno("realpath",_("Failed to resolve %s"),Dir.c_str());
       InternalPrefix = RealPath;      
+      free(RealPath);
    }
    
    Owner = this;
@@ -209,12 +221,14 @@ bool FTWScanner::LoadFileList(string Dir,string File)
         FileName = Line;
       }
       
+#if 0
       struct stat St;
       int Flag = FTW_F;
       if (stat(FileName,&St) != 0)
         Flag = FTW_NS;
+#endif
 
-      if (Scanner(FileName,&St,Flag) != 0)
+      if (ScannerFile(FileName, false) != 0)
         break;
    }
   
@@ -227,7 +241,7 @@ bool FTWScanner::LoadFileList(string Dir,string File)
 /* */
 bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
                        unsigned long &DeLinkBytes,
-                       struct stat &St)
+                       off_t const &FileSize)
 {
    // See if this isn't an internaly prefix'd file name.
    if (InternalPrefix.empty() == false &&
@@ -243,7 +257,7 @@ bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
         
         NewLine(1);
         ioprintf(c1out, _(" DeLink %s [%s]\n"), (OriginalPath + InternalPrefix.length()),
-                   SizeToStr(St.st_size).c_str());
+                   SizeToStr(FileSize).c_str());
         c1out << flush;
         
         if (NoLinkAct == false)
@@ -269,7 +283,7 @@ bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
            }       
         }
         
-        DeLinkBytes += St.st_size;
+        DeLinkBytes += FileSize;
         if (DeLinkBytes/1024 >= DeLinkLimit)
            ioprintf(c1out, _(" DeLink limit of %sB hit.\n"), SizeToStr(DeLinkBytes).c_str());      
       }
@@ -284,8 +298,8 @@ bool FTWScanner::Delink(string &FileName,const char *OriginalPath,
 // PackagesWriter::PackagesWriter - Constructor                                /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-PackagesWriter::PackagesWriter(string DB,string Overrides,string ExtOverrides,
-                              string aArch) :
+PackagesWriter::PackagesWriter(string const &DB,string const &Overrides,string const &ExtOverrides,
+                              string const &aArch) :
    Db(DB),Stats(Db.Stats), Arch(aArch)
 {
    Output = stdout;
@@ -295,8 +309,12 @@ PackagesWriter::PackagesWriter(string DB,string Overrides,string ExtOverrides,
    
    // Process the command line options
    DoMD5 = _config->FindB("APT::FTPArchive::MD5",true);
+   DoSHA1 = _config->FindB("APT::FTPArchive::SHA1",true);
+   DoSHA256 = _config->FindB("APT::FTPArchive::SHA256",true);
+   DoAlwaysStat = _config->FindB("APT::FTPArchive::AlwaysStat", false);
    DoContents = _config->FindB("APT::FTPArchive::Contents",true);
    NoOverride = _config->FindB("APT::FTPArchive::NoOverrideMsg",false);
+   LongDescription = _config->FindB("APT::FTPArchive::LongDescription",true);
 
    if (Db.Loaded() == false)
       DoContents = false;
@@ -316,7 +334,7 @@ PackagesWriter::PackagesWriter(string DB,string Overrides,string ExtOverrides,
 // FTWScanner::SetExts - Set extensions to support                      /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-bool FTWScanner::SetExts(string Vals)
+bool FTWScanner::SetExts(string const &Vals)
 {
    ClearPatterns();
    string::size_type Start = 0;
@@ -343,29 +361,19 @@ bool FTWScanner::SetExts(string Vals)
 // PackagesWriter::DoPackage - Process a single package                        /*{{{*/
 // ---------------------------------------------------------------------
 /* This method takes a package and gets its control information and 
-   MD5 then writes out a control record with the proper fields rewritten
-   and the path/size/hash appended. */
+   MD5, SHA1 and SHA256 then writes out a control record with the proper fields 
+   rewritten and the path/size/hash appended. */
 bool PackagesWriter::DoPackage(string FileName)
 {      
-   // Open the archive
-   FileFd F(FileName,FileFd::ReadOnly);
-   if (_error->PendingError() == true)
-      return false;
-   
-   // Stat the file for later
-   struct stat St;
-   if (fstat(F.Fd(),&St) != 0)
-      return _error->Errno("fstat",_("Failed to stat %s"),FileName.c_str());
-
    // Pull all the data we need form the DB
-   string MD5Res;
-   if (Db.SetFile(FileName,St,&F) == false ||
-       Db.LoadControl() == false ||
-       (DoContents == true && Db.LoadContents(true) == false) ||
-       (DoMD5 == true && Db.GetMD5(MD5Res,false) == false))
+   if (Db.GetFileInfo(FileName, true, DoContents, true, DoMD5, DoSHA1, DoSHA256, DoAlwaysStat)
+                 == false)
+   {
       return false;
+   }
 
-   if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,St) == false)
+   off_t FileSize = Db.GetFileSize();
+   if (Delink(FileName,OriginalPath,Stats.DeLinkBytes,FileSize) == false)
       return false;
    
    // Lookup the overide information
@@ -400,7 +408,7 @@ bool PackagesWriter::DoPackage(string FileName)
    }
 
    char Size[40];
-   sprintf(Size,"%lu",St.st_size);
+   sprintf(Size,"%lu", (unsigned long) FileSize);
    
    // Strip the DirStrip prefix from the FileName and add the PathPrefix
    string NewFileName;
@@ -413,19 +421,37 @@ bool PackagesWriter::DoPackage(string FileName)
       NewFileName = FileName;
    if (PathPrefix.empty() == false)
       NewFileName = flCombine(PathPrefix,NewFileName);
-          
+
+   /* Configuration says we don't want to include the long Description
+      in the package file - instead we want to ship a separated file */
+   string desc;
+   if (LongDescription == false) {
+      desc = Tags.FindS("Description").append("\n");
+      OverItem->FieldOverride["Description"] = desc.substr(0, desc.find('\n')).c_str();
+   }
+
    // This lists all the changes to the fields we are going to make.
    // (7 hardcoded + maintainer + suggests + end marker)
-   TFRewriteData Changes[6+2+OverItem->FieldOverride.size()+1];
+   TFRewriteData Changes[6+2+OverItem->FieldOverride.size()+1+1];
 
    unsigned int End = 0;
    SetTFRewriteData(Changes[End++], "Size", Size);
-   SetTFRewriteData(Changes[End++], "MD5sum", MD5Res.c_str());
+   SetTFRewriteData(Changes[End++], "MD5sum", Db.MD5Res.c_str());
+   SetTFRewriteData(Changes[End++], "SHA1", Db.SHA1Res.c_str());
+   SetTFRewriteData(Changes[End++], "SHA256", Db.SHA256Res.c_str());
    SetTFRewriteData(Changes[End++], "Filename", NewFileName.c_str());
    SetTFRewriteData(Changes[End++], "Priority", OverItem->Priority.c_str());
    SetTFRewriteData(Changes[End++], "Status", 0);
    SetTFRewriteData(Changes[End++], "Optional", 0);
 
+   string DescriptionMd5;
+   if (LongDescription == false) {
+      MD5Summation descmd5;
+      descmd5.Add(desc.c_str());
+      DescriptionMd5 = descmd5.Result().Value();
+      SetTFRewriteData(Changes[End++], "Description-md5", DescriptionMd5.c_str());
+   }
+
    // Rewrite the maintainer field if necessary
    bool MaintFailed;
    string NewMaint = OverItem->SwapMaint(Tags.FindS("Maintainer"),MaintFailed);
@@ -443,7 +469,7 @@ bool PackagesWriter::DoPackage(string FileName)
       SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
    
    /* Get rid of the Optional tag. This is an ugly, ugly, ugly hack that
-      dpkg-scanpackages does.. Well sort of. dpkg-scanpackages just does renaming
+      dpkg-scanpackages does. Well sort of. dpkg-scanpackages just does renaming
       but dpkg does this append bit. So we do the append bit, at least that way the
       status file and package file will remain similar. There are other transforms
       but optional is the only legacy one still in use for some lazy reason. */
@@ -455,7 +481,7 @@ bool PackagesWriter::DoPackage(string FileName)
       SetTFRewriteData(Changes[End++], "Suggests", OptionalStr.c_str());
    }
 
-   for (map<string,string>::iterator I = OverItem->FieldOverride.begin(); 
+   for (map<string,string>::const_iterator I = OverItem->FieldOverride.begin(); 
         I != OverItem->FieldOverride.end(); I++) 
       SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
 
@@ -473,8 +499,8 @@ bool PackagesWriter::DoPackage(string FileName)
 // SourcesWriter::SourcesWriter - Constructor                          /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-SourcesWriter::SourcesWriter(string BOverrides,string SOverrides,
-                            string ExtOverrides)
+SourcesWriter::SourcesWriter(string const &BOverrides,string const &SOverrides,
+                            string const &ExtOverrides)
 {
    Output = stdout;
    AddPattern("*.dsc");
@@ -491,6 +517,10 @@ SourcesWriter::SourcesWriter(string BOverrides,string SOverrides,
    else
       NoOverride = true;
 
+   // WTF?? The logic above: if we can't read binary overrides, don't even try
+   // reading source overrides. if we can read binary overrides, then say there
+   // are no overrides. THIS MAKES NO SENSE! -- ajt@d.o, 2006/02/28
+
    if (ExtOverrides.empty() == false)
       SOver.ReadExtraOverride(ExtOverrides);
    
@@ -530,7 +560,12 @@ bool SourcesWriter::DoPackage(string FileName)
    char *BlkEnd = Buffer + St.st_size;
    MD5Summation MD5;
    MD5.Add((unsigned char *)Start,BlkEnd - Start);
-      
+
+   SHA1Summation SHA1;
+   SHA256Summation SHA256;
+   SHA1.Add((unsigned char *)Start,BlkEnd - Start);
+   SHA256.Add((unsigned char *)Start,BlkEnd - Start);
+
    // Add an extra \n to the end, just in case
    *BlkEnd++ = '\n';
    
@@ -607,24 +642,39 @@ bool SourcesWriter::DoPackage(string FileName)
    }
    
    auto_ptr<Override::Item> SOverItem(SOver.GetItem(Tags.FindS("Source")));
-   const auto_ptr<Override::Item> autoSOverItem(SOverItem);
+   // const auto_ptr<Override::Item> autoSOverItem(SOverItem);
    if (SOverItem.get() == 0)
    {
+      ioprintf(c1out, _("  %s has no source override entry\n"), Tags.FindS("Source").c_str());
       SOverItem = auto_ptr<Override::Item>(BOver.GetItem(Tags.FindS("Source")));
       if (SOverItem.get() == 0)
       {
+        ioprintf(c1out, _("  %s has no binary override entry either\n"), Tags.FindS("Source").c_str());
         SOverItem = auto_ptr<Override::Item>(new Override::Item);
         *SOverItem = *OverItem;
       }
    }
    
    // Add the dsc to the files hash list
+   string const strippedName = flNotDir(FileName);
    char Files[1000];
    snprintf(Files,sizeof(Files),"\n %s %lu %s\n %s",
            string(MD5.Result()).c_str(),St.st_size,
-           flNotDir(FileName).c_str(),
+           strippedName.c_str(),
            Tags.FindS("Files").c_str());
-   
+
+   char ChecksumsSha1[1000];
+   snprintf(ChecksumsSha1,sizeof(ChecksumsSha1),"\n %s %lu %s\n %s",
+           string(SHA1.Result()).c_str(),St.st_size,
+           strippedName.c_str(),
+           Tags.FindS("Checksums-Sha1").c_str());
+
+   char ChecksumsSha256[1000];
+   snprintf(ChecksumsSha256,sizeof(ChecksumsSha256),"\n %s %lu %s\n %s",
+           string(SHA256.Result()).c_str(),St.st_size,
+           strippedName.c_str(),
+           Tags.FindS("Checksums-Sha256").c_str());
+
    // Strip the DirStrip prefix from the FileName and add the PathPrefix
    string NewFileName;
    if (DirStrip.empty() == false &&
@@ -642,6 +692,7 @@ bool SourcesWriter::DoPackage(string FileName)
    // Perform the delinking operation over all of the files
    string ParseJnk;
    const char *C = Files;
+   char *RealPath = NULL;
    for (;isspace(*C); C++);
    while (*C != 0)
    {   
@@ -653,11 +704,12 @@ bool SourcesWriter::DoPackage(string FileName)
       
       char Jnk[2];
       string OriginalPath = Directory + ParseJnk;
-      if (RealPath != 0 && readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
-         realpath(OriginalPath.c_str(),RealPath) != 0)
+      if (readlink(OriginalPath.c_str(),Jnk,sizeof(Jnk)) != -1 &&
+         (RealPath = realpath(OriginalPath.c_str(),NULL)) != 0)
       {
         string RP = RealPath;
-        if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St) == false)
+        free(RealPath);
+        if (Delink(RP,OriginalPath.c_str(),Stats.DeLinkBytes,St.st_size) == false)
            return false;
       }
    }
@@ -667,12 +719,14 @@ bool SourcesWriter::DoPackage(string FileName)
       Directory.erase(Directory.end()-1);
 
    // This lists all the changes to the fields we are going to make.
-   // (5 hardcoded + maintainer + end marker)
-   TFRewriteData Changes[5+1+SOverItem->FieldOverride.size()+1];
+   // (5 hardcoded + checksums + maintainer + end marker)
+   TFRewriteData Changes[5+2+1+SOverItem->FieldOverride.size()+1];
 
    unsigned int End = 0;
    SetTFRewriteData(Changes[End++],"Source",Package.c_str(),"Package");
    SetTFRewriteData(Changes[End++],"Files",Files);
+   SetTFRewriteData(Changes[End++],"Checksums-Sha1",ChecksumsSha1);
+   SetTFRewriteData(Changes[End++],"Checksums-Sha256",ChecksumsSha256);
    if (Directory != "./")
       SetTFRewriteData(Changes[End++],"Directory",Directory.c_str());
    SetTFRewriteData(Changes[End++],"Priority",BestPrio.c_str());
@@ -693,7 +747,7 @@ bool SourcesWriter::DoPackage(string FileName)
    if (NewMaint.empty() == false)
       SetTFRewriteData(Changes[End++], "Maintainer", NewMaint.c_str());
    
-   for (map<string,string>::iterator I = SOverItem->FieldOverride.begin(); 
+   for (map<string,string>::const_iterator I = SOverItem->FieldOverride.begin(); 
         I != SOverItem->FieldOverride.end(); I++) 
       SetTFRewriteData(Changes[End++],I->first.c_str(),I->second.c_str());
 
@@ -713,7 +767,7 @@ bool SourcesWriter::DoPackage(string FileName)
 // ContentsWriter::ContentsWriter - Constructor                                /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-ContentsWriter::ContentsWriter(string DB) : 
+ContentsWriter::ContentsWriter(string const &DB) : 
                    Db(DB), Stats(Db.Stats)
 
 {
@@ -725,28 +779,16 @@ ContentsWriter::ContentsWriter(string DB) :
 // ---------------------------------------------------------------------
 /* If Package is the empty string the control record will be parsed to
    determine what the package name is. */
-bool ContentsWriter::DoPackage(string FileName,string Package)
+bool ContentsWriter::DoPackage(string FileName, string Package)
 {
-   // Open the archive
-   FileFd F(FileName,FileFd::ReadOnly);
-   if (_error->PendingError() == true)
-      return false;
-   
-   // Stat the file for later
-   struct stat St;
-   if (fstat(F.Fd(),&St) != 0)
-      return _error->Errno("fstat","Failed too stat %s",FileName.c_str());
-
-   // Ready the DB
-   if (Db.SetFile(FileName,St,&F) == false || 
-       Db.LoadContents(false) == false)
+   if (!Db.GetFileInfo(FileName, Package.empty(), true, false, false, false, false, false))
+   {
       return false;
+   }
 
    // Parse the package name
    if (Package.empty() == true)
    {
-      if (Db.LoadControl() == false)
-        return false;
       Package = Db.Control.Section.FindS("Package");
    }
 
@@ -758,7 +800,7 @@ bool ContentsWriter::DoPackage(string FileName,string Package)
 // ContentsWriter::ReadFromPkgs - Read from a packages file            /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-bool ContentsWriter::ReadFromPkgs(string PkgFile,string PkgCompress)
+bool ContentsWriter::ReadFromPkgs(string const &PkgFile,string const &PkgCompress)
 {
    MultiCompress Pkgs(PkgFile,PkgCompress,0,false);
    if (_error->PendingError() == true)
@@ -813,19 +855,21 @@ bool ContentsWriter::ReadFromPkgs(string PkgFile,string PkgCompress)
 // ReleaseWriter::ReleaseWriter - Constructor                          /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-ReleaseWriter::ReleaseWriter(string DB)
+ReleaseWriter::ReleaseWriter(string const &DB)
 {
    AddPattern("Packages");
    AddPattern("Packages.gz");
    AddPattern("Packages.bz2");
+   AddPattern("Packages.lzma");
    AddPattern("Sources");
    AddPattern("Sources.gz");
    AddPattern("Sources.bz2");
+   AddPattern("Sources.lzma");
    AddPattern("Release");
    AddPattern("md5sum.txt");
 
    Output = stdout;
-   time_t now = time(NULL);
+   time_t const now = time(NULL);
    char datestr[128];
    if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S UTC",
                 gmtime(&now)) == 0)
@@ -896,6 +940,11 @@ bool ReleaseWriter::DoPackage(string FileName)
    SHA1.AddFD(fd.Fd(), fd.Size());
    CheckSums[NewFileName].SHA1 = SHA1.Result();
 
+   fd.Seek(0);
+   SHA256Summation SHA256;
+   SHA256.AddFD(fd.Fd(), fd.Size());
+   CheckSums[NewFileName].SHA256 = SHA256.Result();
+
    fd.Close();
    
    return true;
@@ -907,7 +956,7 @@ bool ReleaseWriter::DoPackage(string FileName)
 void ReleaseWriter::Finish()
 {
    fprintf(Output, "MD5Sum:\n");
-   for(map<string,struct CheckSum>::iterator I = CheckSums.begin();
+   for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
        I != CheckSums.end();
        ++I)
    {
@@ -918,7 +967,7 @@ void ReleaseWriter::Finish()
    }
 
    fprintf(Output, "SHA1:\n");
-   for(map<string,struct CheckSum>::iterator I = CheckSums.begin();
+   for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
        I != CheckSums.end();
        ++I)
    {
@@ -927,5 +976,16 @@ void ReleaseWriter::Finish()
               (*I).second.size,
               (*I).first.c_str());
    }
+
+   fprintf(Output, "SHA256:\n");
+   for(map<string,struct CheckSum>::const_iterator I = CheckSums.begin();
+       I != CheckSums.end();
+       ++I)
+   {
+      fprintf(Output, " %s %16ld %s\n",
+              (*I).second.SHA256.c_str(),
+              (*I).second.size,
+              (*I).first.c_str());
+   }
 }