]> git.saurik.com Git - apt.git/commitdiff
Merge remote-tracking branch 'upstream/debian/experimental' into feature/acq-trans
authorMichael Vogt <mvo@ubuntu.com>
Fri, 5 Sep 2014 12:47:22 +0000 (14:47 +0200)
committerMichael Vogt <mvo@ubuntu.com>
Fri, 5 Sep 2014 12:47:22 +0000 (14:47 +0200)
13 files changed:
apt-pkg/acquire-item.cc
apt-pkg/acquire-item.h
apt-pkg/acquire.cc
apt-pkg/acquire.h
apt-pkg/deb/debmetaindex.cc
methods/copy.cc
methods/gpgv.cc
test/integration/test-apt-update-ims [new file with mode: 0755]
test/integration/test-apt-update-rollback [new file with mode: 0755]
test/integration/test-apt-update-transactions [new file with mode: 0755]
test/integration/test-hashsum-verification
test/integration/test-ubuntu-bug-346386-apt-get-update-paywall
test/integration/test-ubuntu-bug-784473-InRelease-one-message-only

index 3feb17ffa841c6f56e3851df8f38e4a801b1fd3f..eee1097e918125a1a3e15e3e9c60b9fa25f58ca2 100644 (file)
@@ -64,10 +64,12 @@ static void printHashSumComparision(std::string const &URI, HashStringList const
                                                                        /*}}}*/
 
 // Acquire::Item::Item - Constructor                                   /*{{{*/
-pkgAcquire::Item::Item(pkgAcquire *Owner, HashStringList const &ExpectedHashes) :
-   Owner(Owner), FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false),
-   Local(false), QueueCounter(0), ExpectedAdditionalItems(0),
-   ExpectedHashes(ExpectedHashes)
+pkgAcquire::Item::Item(pkgAcquire *Owner,
+                       HashStringList const &ExpectedHashes,
+                       unsigned long TransactionID)
+   : Owner(Owner), FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false),
+     Local(false), QueueCounter(0), TransactionID(TransactionID),
+     ExpectedAdditionalItems(0), ExpectedHashes(ExpectedHashes)
 {
    Owner->Add(this);
    Status = StatIdle;
@@ -88,7 +90,8 @@ pkgAcquire::Item::~Item()
 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 {
    Status = StatIdle;
-   ErrorText = LookupTag(Message,"Message");
+   if(ErrorText == "")
+      ErrorText = LookupTag(Message,"Message");
    UsedMirror =  LookupTag(Message,"UsedMirror");
    if (QueueCounter <= 1)
    {
@@ -134,7 +137,7 @@ void pkgAcquire::Item::Done(string Message,unsigned long long Size,HashStringLis
 {
    // We just downloaded something..
    string FileName = LookupTag(Message,"Filename");
-   UsedMirror =  LookupTag(Message,"UsedMirror");
+   UsedMirror = LookupTag(Message,"UsedMirror");
    if (Complete == false && !Local && FileName == DestFile)
    {
       if (Owner->Log != 0)
@@ -238,10 +241,12 @@ void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
 // ---------------------------------------------------------------------
 /* Get a sub-index file based on checksums from a 'master' file and
    possibly query additional files */
-pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, string const &URI,
-                                string const &URIDesc, string const &ShortDesc,
-                                HashStringList const &ExpectedHashes)
-   : Item(Owner, ExpectedHashes)
+pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, 
+                               unsigned long TransactionID,
+                               string const &URI,
+                               string const &URIDesc, string const &ShortDesc,
+                               HashStringList const &ExpectedHashes)
+   : Item(Owner, ExpectedHashes, TransactionID)
 {
    /* XXX: Beware: Currently this class does nothing (of value) anymore ! */
    Debug = _config->FindB("Debug::pkgAcquire::SubIndex",false);
@@ -354,10 +359,12 @@ bool pkgAcqSubIndex::ParseIndex(string const &IndexFile)          /*{{{*/
  * the original packages file
  */
 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
+                                 unsigned long TransactionID,
                                  IndexTarget const * const Target,
                                 HashStringList const &ExpectedHashes,
                                  indexRecords *MetaIndexParser)
-   : pkgAcqBaseIndex(Owner, Target, ExpectedHashes, MetaIndexParser)
+   : pkgAcqBaseIndex(Owner, TransactionID, Target, ExpectedHashes, 
+                     MetaIndexParser)
 {
    
    Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
@@ -455,7 +462,8 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile)          /*{{{*/
            std::clog << "Package file is up-to-date" << std::endl;
         // list cleanup needs to know that this file as well as the already
         // present index is ours, so we create an empty diff to save it for us
-        new pkgAcqIndexDiffs(Owner, Target, ExpectedHashes, MetaIndexParser, 
+        new pkgAcqIndexDiffs(Owner, TransactionID, Target, 
+                              ExpectedHashes, MetaIndexParser, 
                               ServerSha1, available_patches);
         return true;
       }
@@ -542,14 +550,17 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile)                /*{{{*/
 
         if (pdiff_merge == false)
          {
-           new pkgAcqIndexDiffs(Owner, Target, ExpectedHashes, MetaIndexParser,
+           new pkgAcqIndexDiffs(Owner, TransactionID, Target, ExpectedHashes, 
+                                 MetaIndexParser,
                                  ServerSha1, available_patches);
          }
          else
         {
            std::vector<pkgAcqIndexMergeDiffs*> *diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
            for(size_t i = 0; i < available_patches.size(); ++i)
-              (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, Target,
+              (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner,
+                                                       TransactionID,
+                                                       Target,
                                                        ExpectedHashes,
                                                        MetaIndexParser,
                                                        available_patches[i],
@@ -577,7 +588,7 @@ void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/
       std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
                << "Falling back to normal index file acquire" << std::endl;
 
-   new pkgAcqIndex(Owner, Target, ExpectedHashes, MetaIndexParser);
+   new pkgAcqIndex(Owner, TransactionID, Target, ExpectedHashes, MetaIndexParser);
 
    Complete = false;
    Status = StatDone;
@@ -620,12 +631,13 @@ void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,HashStringList
  * for each diff and the index
  */
 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
+                                   unsigned long TransactionID,
                                    struct IndexTarget const * const Target,
                                    HashStringList const &ExpectedHashes,
                                    indexRecords *MetaIndexParser,
                                   string ServerSha1,
                                   vector<DiffInfo> diffs)
-   : pkgAcqBaseIndex(Owner, Target, ExpectedHashes, MetaIndexParser),
+   : pkgAcqBaseIndex(Owner, TransactionID, Target, ExpectedHashes, MetaIndexParser),
      available_patches(diffs), ServerSha1(ServerSha1)
 {
    
@@ -657,19 +669,28 @@ void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)
    if(Debug)
       std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
                << "Falling back to normal index file acquire" << std::endl;
-   new pkgAcqIndex(Owner, Target, ExpectedHashes, MetaIndexParser);
+   new pkgAcqIndex(Owner, TransactionID, Target, ExpectedHashes, MetaIndexParser);
    Finish();
 }
                                                                        /*}}}*/
 // Finish - helper that cleans the item out of the fetcher queue       /*{{{*/
 void pkgAcqIndexDiffs::Finish(bool allDone)
 {
+   if(Debug)
+      std::clog << "pkgAcqIndexDiffs::Finish(): " 
+                << allDone << " "
+                << Desc.URI << std::endl;
+
    // we restore the original name, this is required, otherwise
    // the file will be cleaned
    if(allDone) 
    {
       DestFile = _config->FindDir("Dir::State::lists");
       DestFile += URItoFileName(RealURI);
+      
+      // FIXME: we want the rred stuff to use the real transactional update
+      //        this is just a workaround
+      PartialFile = DestFile;
 
       if(HashSums().usable() && !HashSums().VerifyFile(DestFile))
       {
@@ -797,7 +818,7 @@ void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size, HashStringLi
 
       // see if there is more to download
       if(available_patches.empty() == false) {
-        new pkgAcqIndexDiffs(Owner, Target,
+        new pkgAcqIndexDiffs(Owner, TransactionID, Target,
                              ExpectedHashes, MetaIndexParser,
                               ServerSha1, available_patches);
         return Finish();
@@ -808,12 +829,13 @@ void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size, HashStringLi
                                                                        /*}}}*/
 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor                        /*{{{*/
 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
+                                             unsigned long TransactionID,
                                              struct IndexTarget const * const Target,
                                              HashStringList const &ExpectedHashes,
                                              indexRecords *MetaIndexParser,
                                              DiffInfo const &patch,
                                              std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
-   : pkgAcqBaseIndex(Owner, Target, ExpectedHashes, MetaIndexParser),
+  : pkgAcqBaseIndex(Owner, TransactionID, Target, ExpectedHashes, MetaIndexParser),
      patch(patch), allPatches(allPatches), State(StateFetchDiff)
 {
 
@@ -856,7 +878,7 @@ void pkgAcqIndexMergeDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*C
    // first failure means we should fallback
    State = StateErrorDiff;
    std::clog << "Falling back to normal index file acquire" << std::endl;
-   new pkgAcqIndex(Owner, Target, ExpectedHashes, MetaIndexParser);
+   new pkgAcqIndex(Owner, TransactionID, Target, ExpectedHashes, MetaIndexParser);
 }
                                                                        /*}}}*/
 void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,HashStringList const &Hashes,  /*{{{*/
@@ -916,6 +938,8 @@ void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,HashStri
 
       // otherwise lists cleanup will eat the file
       DestFile = FinalFile;
+      // FIXME: make the merged rred code really transactional
+      PartialFile = FinalFile;
 
       // ensure the ed's are gone regardless of list-cleanup
       for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
@@ -939,28 +963,39 @@ void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,HashStri
 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
                         string URI,string URIDesc,string ShortDesc,
                         HashStringList const  &ExpectedHash, string comprExt)
-   : pkgAcqBaseIndex(Owner, NULL, ExpectedHash, NULL), RealURI(URI)
+   : pkgAcqBaseIndex(Owner, 0, NULL, ExpectedHash, NULL), RealURI(URI)
 {
-   if(comprExt.empty() == true)
-   {
-      // autoselect the compression method
-      std::vector<std::string> types = APT::Configuration::getCompressionTypes();
-      for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
-        comprExt.append(*t).append(" ");
-      if (comprExt.empty() == false)
-        comprExt.erase(comprExt.end()-1);
-   }
-   CompressionExtension = comprExt;
-
+   AutoSelectCompression();
    Init(URI, URIDesc, ShortDesc);
+
+   if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+      std::clog << "New pkgIndex with TransactionID "
+                << TransactionID << std::endl;
 }
-pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const *Target,
+                                                                       /*}}}*/
+// AcqIndex::AcqIndex - Constructor                                    /*{{{*/
+// ---------------------------------------------------------------------
+pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
+                         unsigned long TransactionID,
+                         IndexTarget const *Target,
                         HashStringList const &ExpectedHash, 
                          indexRecords *MetaIndexParser)
-   : pkgAcqBaseIndex(Owner, Target, ExpectedHash, MetaIndexParser)
-     RealURI(Target->URI)
+   : pkgAcqBaseIndex(Owner, TransactionID, Target, ExpectedHash
+                     MetaIndexParser), RealURI(Target->URI)
 {
    // autoselect the compression method
+   AutoSelectCompression();
+   Init(Target->URI, Target->Description, Target->ShortDesc);
+
+   if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+      std::clog << "New pkgIndex with TransactionID "
+                << TransactionID << std::endl;
+}
+                                                                       /*}}}*/
+// AcqIndex::AutoSelectCompression - Select compression                        /*{{{*/
+// ---------------------------------------------------------------------
+void pkgAcqIndex::AutoSelectCompression()
+{
    std::vector<std::string> types = APT::Configuration::getCompressionTypes();
    CompressionExtension = "";
    if (ExpectedHashes.usable())
@@ -976,10 +1011,7 @@ pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const *Target,
    }
    if (CompressionExtension.empty() == false)
       CompressionExtension.erase(CompressionExtension.end()-1);
-
-   Init(Target->URI, Target->Description, Target->ShortDesc);
 }
-                                                                       /*}}}*/
 // AcqIndex::Init - defered Constructor                                        /*{{{*/
 void pkgAcqIndex::Init(string const &URI, string const &URIDesc, string const &ShortDesc) {
    Decompression = false;
@@ -989,7 +1021,6 @@ void pkgAcqIndex::Init(string const &URI, string const &URIDesc, string const &S
    DestFile += URItoFileName(URI);
 
    std::string const comprExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
-   std::string MetaKey;
    if (comprExt == "uncompressed")
    {
       Desc.URI = URI;
@@ -1074,6 +1105,9 @@ string pkgAcqIndex::Custom600Headers() const
    return msg;
 }
                                                                        /*}}}*/
+// pkgAcqIndex::Failed - getting the indexfile failed                  /*{{{*/
+// ---------------------------------------------------------------------
+/* */
 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
 {
    size_t const nextExt = CompressionExtension.find(' ');
@@ -1092,19 +1126,52 @@ void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)  /*{{{*/
    }
 
    Item::Failed(Message,Cnf);
+
+   /// cancel the entire transaction
+   Owner->AbortTransaction(TransactionID);
 }
                                                                        /*}}}*/
+// pkgAcqIndex::GetFinalFilename - Return the full final file path      /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+std::string pkgAcqIndex::GetFinalFilename(std::string const &URI,
+                                          std::string const &compExt)
+{
+   std::string FinalFile = _config->FindDir("Dir::State::lists");
+   FinalFile += URItoFileName(URI);
+   if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
+      FinalFile += ".gz";
+   return FinalFile;
+}
+                                                                       /*}}}*/
+// AcqIndex::ReverifyAfterIMS - Reverify index after an ims-hit        /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void pkgAcqIndex::ReverifyAfterIMS(std::string const &FileName)
+{
+   std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
+   if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
+      DestFile += ".gz";
+
+   // copy FinalFile into partial/ so that we check the hash again
+   string FinalFile = GetFinalFilename(RealURI, compExt);
+   Decompression = true;
+   Desc.URI = "copy:" + FinalFile;
+   QueueURI(Desc);
+}
+                                                                       /*}}}*/
 // AcqIndex::Done - Finished a fetch                                   /*{{{*/
 // ---------------------------------------------------------------------
 /* This goes through a number of states.. On the initial fetch the
    method could possibly return an alternate filename which points
    to the uncompressed version of the file. If this is so the file
    is copied into the partial directory. In all other cases the file
-   is decompressed with a gzip uri. */
+   is decompressed with a compressed uri. */
 void pkgAcqIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes,
                       pkgAcquire::MethodConfig *Cfg)
 {
    Item::Done(Message,Size,Hashes,Cfg);
+   std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
 
    if (Decompression == true)
    {
@@ -1112,6 +1179,7 @@ void pkgAcqIndex::Done(string Message,unsigned long long Size,HashStringList con
       {
         RenameOnError(HashSumMismatch);
         printHashSumComparision(RealURI, ExpectedHashes, Hashes);
+         Failed(Message, Cfg);
          return;
       }
 
@@ -1121,9 +1189,10 @@ void pkgAcqIndex::Done(string Message,unsigned long long Size,HashStringList con
       /* Always verify the index file for correctness (all indexes must
        * have a Package field) (LP: #346386) (Closes: #627642) 
        */
-      FileFd fd(DestFile, FileFd::ReadOnly);
-      // Only test for correctness if the file is not empty (empty is ok)
-      if (fd.FileSize() > 0)
+      FileFd fd(DestFile, FileFd::ReadOnly, FileFd::Extension);
+      // Only test for correctness if the content of the file is not empty
+      // (empty is ok)
+      if (fd.Size() > 0)
       {
          pkgTagSection sec;
          pkgTagFile tag(&fd);
@@ -1132,27 +1201,38 @@ void pkgAcqIndex::Done(string Message,unsigned long long Size,HashStringList con
          if (_error->PendingError() == true || tag.Step(sec) == false || sec.Exists("Package") == false)
          {
             RenameOnError(InvalidFormat);
+            Failed(Message, Cfg);
             return;
          }
       }
        
-      // Done, move it into position
-      string FinalFile = _config->FindDir("Dir::State::lists");
-      FinalFile += URItoFileName(RealURI);
-      Rename(DestFile,FinalFile);
-      chmod(FinalFile.c_str(),0644);
-
-      /* We restore the original name to DestFile so that the clean operation
-         will work OK */
-      DestFile = _config->FindDir("Dir::State::lists") + "partial/";
-      DestFile += URItoFileName(RealURI);
-      
+      // FIXME: can we void the "Erase" bool here as its very non-local?
+      std::string CompressedFile = _config->FindDir("Dir::State::lists") + "partial/";
+      CompressedFile += URItoFileName(RealURI);
       // Remove the compressed version.
       if (Erase == true)
-        unlink(DestFile.c_str());
+        unlink(CompressedFile.c_str());
+
+      // Done, queue for rename on transaction finished
+      PartialFile = DestFile;
+      DestFile = GetFinalFilename(RealURI, compExt);
 
       return;
    }
+   
+   // FIXME: use the same method to find 
+   // check the compressed hash too
+   if(MetaKey != "" && Hashes.size() > 0)
+   {
+      indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
+      if(Record && Record->Hashes.usable() && Hashes != Record->Hashes)
+      {
+         RenameOnError(HashSumMismatch);
+         printHashSumComparision(RealURI, Record->Hashes, Hashes);
+         Failed(Message, Cfg);
+         return;
+      }
+   }
 
    Erase = false;
    Complete = true;
@@ -1163,7 +1243,10 @@ void pkgAcqIndex::Done(string Message,unsigned long long Size,HashStringList con
    {
       // The files timestamp matches
       if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
+      {
+         ReverifyAfterIMS(FileName);
         return;
+      }
       Decompression = true;
       Local = true;
       DestFile += ".decomp";
@@ -1180,13 +1263,10 @@ void pkgAcqIndex::Done(string Message,unsigned long long Size,HashStringList con
       ErrorText = "Method gave a blank filename";
    }
 
-   std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
-
    // The files timestamp matches
-   if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) {
-       if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
-         // Update DestFile for .gz suffix so that the clean operation keeps it
-         DestFile += ".gz";
+   if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
+   {
+      ReverifyAfterIMS(FileName);
       return;
     }
 
@@ -1199,14 +1279,9 @@ void pkgAcqIndex::Done(string Message,unsigned long long Size,HashStringList con
 
    // If we enable compressed indexes and already have gzip, keep it
    if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz" && !Local) {
-      string FinalFile = _config->FindDir("Dir::State::lists");
-      FinalFile += URItoFileName(RealURI) + ".gz";
-      Rename(DestFile,FinalFile);
-      chmod(FinalFile.c_str(),0644);
-      
-      // Update DestFile for .gz suffix so that the clean operation keeps it
-      DestFile = _config->FindDir("Dir::State::lists") + "partial/";
-      DestFile += URItoFileName(RealURI) + ".gz";
+      // Done, queue for rename on transaction finished
+      PartialFile = DestFile;
+      DestFile = GetFinalFilename(RealURI, compExt);
       return;
     }
 
@@ -1237,9 +1312,13 @@ pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
   : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashStringList(), "")
 {
 }
-pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const * const Target,
-                        HashStringList const &ExpectedHashes, indexRecords *MetaIndexParser)
-  : pkgAcqIndex(Owner, Target, ExpectedHashes, MetaIndexParser)
+                                                                       /*}}}*/
+pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, 
+                                   unsigned long TransactionID, 
+                                   IndexTarget const * const Target,
+                                   HashStringList const &ExpectedHashes, 
+                                   indexRecords *MetaIndexParser)
+   : pkgAcqIndex(Owner, TransactionID, Target, ExpectedHashes, MetaIndexParser)
 {
    // load the filesize
    indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
@@ -1274,6 +1353,7 @@ void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
       return;
    }
 
+   // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
    if (Cnf->LocalOnly == true || 
        StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
    {      
@@ -1287,15 +1367,58 @@ void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
    Item::Failed(Message,Cnf);
 }
                                                                        /*}}}*/
-pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,                                /*{{{*/
+
+pkgAcqMetaSigBase::pkgAcqMetaSigBase(pkgAcquire *Owner,
+                                     HashStringList const &ExpectedHashes,
+                                     unsigned long TransactionID)
+   : Item(Owner, ExpectedHashes, TransactionID)
+{
+}
+                                                                       /*{{{*/
+bool pkgAcqMetaSigBase::GenerateAuthWarning(const std::string &RealURI,
+                                            const std::string &Message)
+{
+   string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
+   
+   if(FileExists(Final))
+   {
+      Status = StatTransientNetworkError;
+      _error->Warning(_("An error occurred during the signature "
+                        "verification. The repository is not updated "
+                        "and the previous index files will be used. "
+                        "GPG error: %s: %s\n"),
+                      Desc.Description.c_str(),
+                      LookupTag(Message,"Message").c_str());
+      RunScripts("APT::Update::Auth-Failure");
+      return true;
+   } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
+      /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
+      _error->Error(_("GPG error: %s: %s"),
+                    Desc.Description.c_str(),
+                    LookupTag(Message,"Message").c_str());
+      Status = StatError;
+      return true;
+   } else {
+      _error->Warning(_("GPG error: %s: %s"),
+                      Desc.Description.c_str(),
+                      LookupTag(Message,"Message").c_str());
+   }
+   // gpgv method failed 
+   ReportMirrorFailure("GPGFailure");
+   return false;
+}
+                                                                       /*}}}*/
+
+
+pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,                        /*{{{*/
+                             unsigned long TransactionID,
                             string URI,string URIDesc,string ShortDesc,
-                            string MetaIndexURI, string MetaIndexURIDesc,
-                            string MetaIndexShortDesc,
+                             string MetaIndexFile,
                             const vector<IndexTarget*>* IndexTargets,
                             indexRecords* MetaIndexParser) :
-   Item(Owner, HashStringList()), RealURI(URI), MetaIndexURI(MetaIndexURI),
-   MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
-   MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
+   pkgAcqMetaSigBase(Owner, HashStringList(), TransactionID), RealURI(URI), 
+   MetaIndexParser(MetaIndexParser), MetaIndexFile(MetaIndexFile),
+   IndexTargets(IndexTargets), AuthPass(false), IMSHit(false)
 {
    DestFile = _config->FindDir("Dir::State::lists") + "partial/";
    DestFile += URItoFileName(URI);
@@ -1305,42 +1428,22 @@ pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,                         /*{{{*/
    // partial download anyway
    unlink(DestFile.c_str());
 
+   // set the TransactionID
+   if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+      std::clog << "New pkgAcqMetaSig with TransactionID "
+                << TransactionID << std::endl;
+
    // Create the item
    Desc.Description = URIDesc;
    Desc.Owner = this;
    Desc.ShortDesc = ShortDesc;
    Desc.URI = URI;
-      
-   string Final = _config->FindDir("Dir::State::lists");
-   Final += URItoFileName(RealURI);
-   if (RealFileExists(Final) == true)
-   {
-      // File was already in place.  It needs to be re-downloaded/verified
-      // because Release might have changed, we do give it a different
-      // name than DestFile because otherwise the http method will
-      // send If-Range requests and there are too many broken servers
-      // out there that do not understand them
-      LastGoodSig = DestFile+".reverify";
-      Rename(Final,LastGoodSig);
-   }
-
-   // we expect the indextargets + one additional Release file
-   ExpectedAdditionalItems = IndexTargets->size() + 1;
 
    QueueURI(Desc);
 }
                                                                        /*}}}*/
 pkgAcqMetaSig::~pkgAcqMetaSig()                                                /*{{{*/
 {
-   // if the file was never queued undo file-changes done in the constructor
-   if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false &&
-        LastGoodSig.empty() == false)
-   {
-      string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
-      if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
-        Rename(LastGoodSig, Final);
-   }
-
 }
                                                                        /*}}}*/
 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers     /*{{{*/
@@ -1348,8 +1451,11 @@ pkgAcqMetaSig::~pkgAcqMetaSig()                                          /*{{{*/
 /* The only header we use is the last-modified header. */
 string pkgAcqMetaSig::Custom600Headers() const
 {
+   string FinalFile = _config->FindDir("Dir::State::lists");
+   FinalFile += URItoFileName(RealURI);
+
    struct stat Buf;
-   if (stat(LastGoodSig.c_str(),&Buf) != 0)
+   if (stat(FinalFile.c_str(),&Buf) != 0)
       return "\nIndex-File: true";
 
    return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
@@ -1377,21 +1483,40 @@ void pkgAcqMetaSig::Done(string Message,unsigned long long Size, HashStringList
       return;
    }
 
-   Complete = true;
+   if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
+      IMSHit = true;
 
-   // at this point pkgAcqMetaIndex takes over
-   ExpectedAdditionalItems = 0;
+   // adjust paths if its a ims-hit
+   if(IMSHit)
+   {
+      string FinalFile = _config->FindDir("Dir::State::lists");
+      FinalFile += URItoFileName(RealURI);
+         
+      DestFile = PartialFile = FinalFile;
+   }
 
-   // put the last known good file back on i-m-s hit (it will
-   // be re-verified again)
-   // Else do nothing, we have the new file in DestFile then
-   if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
-      Rename(LastGoodSig, DestFile);
+   // queue for verify
+   if(AuthPass == false)
+   {
+      AuthPass = true;
+      Desc.URI = "gpgv:" + DestFile;
+      DestFile = MetaIndexFile;
+      QueueURI(Desc);
+      return;
+   }
+
+   // queue to copy the file in place if it was not a ims hit, on ims
+   // hit the file is already at the right place
+   if(IMSHit == false)
+   {
+      PartialFile = _config->FindDir("Dir::State::lists") + "partial/";
+      PartialFile += URItoFileName(RealURI);
+      
+      DestFile = _config->FindDir("Dir::State::lists");
+      DestFile += URItoFileName(RealURI);
+   }
 
-   // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
-   new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, 
-                      MetaIndexShortDesc,  DestFile, IndexTargets, 
-                      MetaIndexParser);
+   Complete = true;
 
 }
                                                                        /*}}}*/
@@ -1399,29 +1524,21 @@ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
 {
    string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
 
-   // at this point pkgAcqMetaIndex takes over
-   ExpectedAdditionalItems = 0;
+   // this ensures that any file in the lists/ dir is removed by the
+   // transaction
+   DestFile =  _config->FindDir("Dir::State::lists") + "partial/";
+   DestFile += URItoFileName(RealURI);
+   PartialFile = "";
 
-   // if we get a network error we fail gracefully
-   if(Status == StatTransientNetworkError)
+   // FIXME: duplicated code from pkgAcqMetaIndex
+   if (AuthPass == true)
    {
-      Item::Failed(Message,Cnf);
-      // move the sigfile back on transient network failures 
-      if(FileExists(LastGoodSig))
-        Rename(LastGoodSig,Final);
-
-      // set the status back to , Item::Failed likes to reset it
-      Status = pkgAcquire::Item::StatTransientNetworkError;
-      return;
+      bool Stop = GenerateAuthWarning(RealURI, Message);
+      if(Stop)
+         return;
    }
 
-   // Delete any existing sigfile when the acquire failed
-   unlink(Final.c_str());
-
-   // queue a pkgAcqMetaIndex with no sigfile
-   new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
-                      "", IndexTargets, MetaIndexParser);
-
+   // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
    if (Cnf->LocalOnly == true || 
        StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
    {      
@@ -1431,33 +1548,46 @@ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
       Dequeue();
       return;
    }
-   
    Item::Failed(Message,Cnf);
 }
                                                                        /*}}}*/
 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner,                    /*{{{*/
+                                 unsigned long TransactionID,
                                 string URI,string URIDesc,string ShortDesc,
-                                string SigFile,
+                                 string MetaIndexSigURI,string MetaIndexSigURIDesc, string MetaIndexSigShortDesc,
                                 const vector<IndexTarget*>* IndexTargets,
                                 indexRecords* MetaIndexParser) :
-   Item(Owner, HashStringList()), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
-   MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
+   pkgAcqMetaSigBase(Owner, HashStringList(), TransactionID), RealURI(URI), IndexTargets(IndexTargets),
+   MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false),
+   MetaIndexSigURI(MetaIndexSigURI), MetaIndexSigURIDesc(MetaIndexSigURIDesc),
+   MetaIndexSigShortDesc(MetaIndexSigShortDesc)
+{
+   if(TransactionID == 0)
+      this->TransactionID = (unsigned long)this;
+
+   if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+      std::clog << "New pkgAcqMetaIndex with TransactionID "
+                << TransactionID << std::endl;
+
+   Init(URIDesc, ShortDesc);
+}
+                                                                       /*}}}*/
+// pkgAcqMetaIndex::Init - Delayed constructor                         /*{{{*/
+void pkgAcqMetaIndex::Init(std::string URIDesc, std::string ShortDesc)
 {
    DestFile = _config->FindDir("Dir::State::lists") + "partial/";
-   DestFile += URItoFileName(URI);
+   DestFile += URItoFileName(RealURI);
 
    // Create the item
    Desc.Description = URIDesc;
    Desc.Owner = this;
    Desc.ShortDesc = ShortDesc;
-   Desc.URI = URI;
+   Desc.URI = RealURI;
 
    // we expect more item
    ExpectedAdditionalItems = IndexTargets->size();
-
    QueueURI(Desc);
 }
-                                                                       /*}}}*/
 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers   /*{{{*/
 // ---------------------------------------------------------------------
 /* The only header we use is the last-modified header. */
@@ -1498,25 +1628,12 @@ void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,HashStringList
 
       if (SigFile == "")
       {
-         // There was no signature file, so we are finished.  Download
-         // the indexes and do only hashsum verification if possible
+         // load indexes, the signature will downloaded afterwards
          MetaIndexParser->Load(DestFile);
-         QueueIndexes(false);
+         QueueIndexes(true);
       }
       else
       {
-         // FIXME: move this into pkgAcqMetaClearSig::Done on the next
-         //        ABI break
-
-         // if we expect a ClearTextSignature (InRelase), ensure that
-         // this is what we get and if not fail to queue a 
-         // Release/Release.gpg, see #346386
-         if (SigFile == DestFile && !StartsWithGPGClearTextSignature(DestFile))
-         {
-            Failed(Message, Cfg);
-            return;
-         }
-
          // There was a signature file, so pass it to gpgv for
          // verification
          if (_config->FindB("Debug::pkgAcquire::Auth", false))
@@ -1536,8 +1653,8 @@ void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,HashStringList
       FinalFile += URItoFileName(RealURI);
       if (SigFile == DestFile)
         SigFile = FinalFile;
-      Rename(DestFile,FinalFile);
-      chmod(FinalFile.c_str(),0644);
+      // queue for copy in place
+      PartialFile = DestFile;
       DestFile = FinalFile;
    }
 }
@@ -1572,14 +1689,24 @@ void pkgAcqMetaIndex::RetrievalDone(string Message)                     /*{{{*/
       if (SigFile == DestFile)
       {
         SigFile = FinalFile;
+#if 0
         // constructor of pkgAcqMetaClearSig moved it out of the way,
         // now move it back in on IMS hit for the 'old' file
         string const OldClearSig = DestFile + ".reverify";
         if (RealFileExists(OldClearSig) == true)
            Rename(OldClearSig, FinalFile);
+#endif
       }
       DestFile = FinalFile;
    }
+
+   // queue a signature
+   if(SigFile != DestFile)
+      new pkgAcqMetaSig(Owner, TransactionID, 
+                        MetaIndexSigURI, MetaIndexSigURIDesc,
+                        MetaIndexSigShortDesc, DestFile, IndexTargets, 
+                        MetaIndexParser);
+
    Complete = true;
 }
                                                                        /*}}}*/
@@ -1609,6 +1736,7 @@ void pkgAcqMetaIndex::AuthDone(string Message)                            /*{{{*/
    // Download further indexes with verification
    QueueIndexes(true);
 
+#if 0
    // is it a clearsigned MetaIndex file?
    if (DestFile == SigFile)
       return;
@@ -1618,20 +1746,11 @@ void pkgAcqMetaIndex::AuthDone(string Message)                          /*{{{*/
       URItoFileName(RealURI) + ".gpg";
    Rename(SigFile,VerifiedSigFile);
    chmod(VerifiedSigFile.c_str(),0644);
+#endif
 }
                                                                        /*}}}*/
 void pkgAcqMetaIndex::QueueIndexes(bool verify)                                /*{{{*/
 {
-#if 0
-   /* Reject invalid, existing Release files (LP: #346386) (Closes: #627642)
-    * FIXME: Disabled; it breaks unsigned repositories without hashes */
-   if (!verify && FileExists(DestFile) && !MetaIndexParser->Load(DestFile))
-   {
-      Status = StatError;
-      ErrorText = MetaIndexParser->ErrorText;
-      return;
-   }
-#endif
    bool transInRelease = false;
    {
       std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
@@ -1694,15 +1813,16 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify)                         /*{{{*/
       if ((*Target)->IsOptional() == true)
       {
         if ((*Target)->IsSubIndex() == true)
-           new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description,
+           new pkgAcqSubIndex(Owner, TransactionID, 
+                               (*Target)->URI, (*Target)->Description,
                                (*Target)->ShortDesc, ExpectedIndexHashes);
         else if (transInRelease == false || Record != NULL || compressedAvailable == true)
         {
            if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
                MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
-              new pkgAcqDiffIndex(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
+              new pkgAcqDiffIndex(Owner, TransactionID, *Target, ExpectedIndexHashes, MetaIndexParser);
            else
-              new pkgAcqIndexTrans(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
+              new pkgAcqIndexTrans(Owner, TransactionID, *Target, ExpectedIndexHashes, MetaIndexParser);
         }
         continue;
       }
@@ -1713,9 +1833,9 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify)                           /*{{{*/
          instead, but passing the required info to it is to much hassle */
       if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
          MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
-        new pkgAcqDiffIndex(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
+        new pkgAcqDiffIndex(Owner, TransactionID, *Target, ExpectedIndexHashes, MetaIndexParser);
       else
-        new pkgAcqIndex(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
+        new pkgAcqIndex(Owner, TransactionID, *Target, ExpectedIndexHashes, MetaIndexParser);
    }
 }
                                                                        /*}}}*/
@@ -1799,44 +1919,16 @@ bool pkgAcqMetaIndex::VerifyVendor(string Message)                      /*{{{*/
 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present      /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)
+void pkgAcqMetaIndex::Failed(string Message,
+                             pkgAcquire::MethodConfig * /*Cnf*/)
 {
+   string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
+
    if (AuthPass == true)
    {
-      // gpgv method failed, if we have a good signature 
-      string LastGoodSigFile = _config->FindDir("Dir::State::lists").append("partial/").append(URItoFileName(RealURI));
-      if (DestFile != SigFile)
-        LastGoodSigFile.append(".gpg");
-      LastGoodSigFile.append(".reverify");
-
-      if(FileExists(LastGoodSigFile))
-      {
-        string VerifiedSigFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
-        if (DestFile != SigFile)
-           VerifiedSigFile.append(".gpg");
-        Rename(LastGoodSigFile, VerifiedSigFile);
-        Status = StatTransientNetworkError;
-        _error->Warning(_("An error occurred during the signature "
-                          "verification. The repository is not updated "
-                          "and the previous index files will be used. "
-                          "GPG error: %s: %s\n"),
-                        Desc.Description.c_str(),
-                        LookupTag(Message,"Message").c_str());
-        RunScripts("APT::Update::Auth-Failure");
-        return;
-      } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
-        /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
-        _error->Error(_("GPG error: %s: %s"),
-                        Desc.Description.c_str(),
-                        LookupTag(Message,"Message").c_str());
-        return;
-      } else {
-        _error->Warning(_("GPG error: %s: %s"),
-                        Desc.Description.c_str(),
-                        LookupTag(Message,"Message").c_str());
-      }
-      // gpgv method failed 
-      ReportMirrorFailure("GPGFailure");
+      bool Stop = GenerateAuthWarning(RealURI, Message);
+      if(Stop)
+         return;
    }
 
    /* Always move the meta index, even if gpgv failed. This ensures
@@ -1853,9 +1945,9 @@ void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)
                                       "Release");
         SigFile = FinalFile;
       }
-      Rename(DestFile,FinalFile);
-      chmod(FinalFile.c_str(),0644);
 
+      // Done, queue for rename on transaction finished
+      PartialFile = DestFile;
       DestFile = FinalFile;
    }
 
@@ -1864,22 +1956,33 @@ void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)
    QueueIndexes(false);
 }
                                                                        /*}}}*/
+
+void pkgAcqMetaIndex::Finished()
+{
+   if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+      std::clog << "Finished: " << DestFile <<std::endl;
+   if(Owner->TransactionHasError(TransactionID) == false && 
+      TransactionID > 0)
+      Owner->CommitTransaction(TransactionID);
+}
+
+
 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner,              /*{{{*/
                string const &URI, string const &URIDesc, string const &ShortDesc,
                string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
                string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
                const vector<IndexTarget*>* IndexTargets,
                indexRecords* MetaIndexParser) :
-       pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser),
-       MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
-       MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
+   pkgAcqMetaIndex(Owner, (unsigned long)this, URI, URIDesc, ShortDesc, MetaSigURI, MetaSigURIDesc,MetaSigShortDesc, IndexTargets, MetaIndexParser),
+       MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
+       MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
 {
    SigFile = DestFile;
 
    // index targets + (worst case:) Release/Release.gpg
    ExpectedAdditionalItems = IndexTargets->size() + 2;
 
-
+#if 0
    // keep the old InRelease around in case of transistent network errors
    string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
    if (RealFileExists(Final) == true)
@@ -1887,10 +1990,12 @@ pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner,               /*{{{*/
       string const LastGoodSig = DestFile + ".reverify";
       Rename(Final,LastGoodSig);
    }
+#endif
 }
                                                                        /*}}}*/
 pkgAcqMetaClearSig::~pkgAcqMetaClearSig()                              /*{{{*/
 {
+#if 0
    // if the file was never queued undo file-changes done in the constructor
    if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false)
    {
@@ -1899,6 +2004,7 @@ pkgAcqMetaClearSig::~pkgAcqMetaClearSig()                         /*{{{*/
       if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
         Rename(LastGoodSig, Final);
    }
+#endif
 }
                                                                        /*}}}*/
 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers        /*{{{*/
@@ -1912,7 +2018,6 @@ string pkgAcqMetaClearSig::Custom600Headers() const
    struct stat Buf;
    if (stat(Final.c_str(),&Buf) != 0)
    {
-      Final = DestFile + ".reverify";
       if (stat(Final.c_str(),&Buf) != 0)
         return "\nIndex-File: true\nFail-Ignore: true\n";
    }
@@ -1920,6 +2025,24 @@ string pkgAcqMetaClearSig::Custom600Headers() const
    return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
 }
                                                                        /*}}}*/
+// pkgAcqMetaClearSig::Done - We got a file                            /*{{{*/
+// ---------------------------------------------------------------------
+void pkgAcqMetaClearSig::Done(std::string Message,unsigned long long Size, 
+                              HashStringList const &Hashes,
+                              pkgAcquire::MethodConfig *Cnf)
+{
+   // if we expect a ClearTextSignature (InRelase), ensure that
+   // this is what we get and if not fail to queue a 
+   // Release/Release.gpg, see #346386
+   if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
+   {
+      pkgAcquire::Item::Failed(Message, Cnf);
+      ErrorText = _("Does not start with a cleartext signature");
+      return;
+   }
+   pkgAcqMetaIndex::Done(Message, Size, Hashes, Cnf);
+}
+                                                                       /*}}}*/
 void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
 {
    // we failed, we will not get additional items from this method
@@ -1927,16 +2050,17 @@ void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*
 
    if (AuthPass == false)
    {
-      // Remove the 'old' InRelease file if we try Release.gpg now as otherwise
-      // the file will stay around and gives a false-auth impression (CVE-2012-0214)
+      // Queue the 'old' InRelease file for removal if we try Release.gpg
+      // as otherwise the file will stay around and gives a false-auth
+      // impression (CVE-2012-0214)
       string FinalFile = _config->FindDir("Dir::State::lists");
       FinalFile.append(URItoFileName(RealURI));
-      if (FileExists(FinalFile))
-        unlink(FinalFile.c_str());
+      PartialFile = "";
+      DestFile = FinalFile;
 
-      new pkgAcqMetaSig(Owner,
-                       MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
+      new pkgAcqMetaIndex(Owner, TransactionID,
                        MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
+                       MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
                        IndexTargets, MetaIndexParser);
       if (Cnf->LocalOnly == true ||
          StringToBool(LookupTag(Message, "Transient-Failure"), false) == false)
index 0500a362713c5de4a2defcee56e7bfe020aac5e0..ae93ea3116179e85697df7ad1d5d1c32ec9dd8f0 100644 (file)
@@ -47,6 +47,7 @@ class indexRecords;
 class pkgRecords;
 class pkgSourceList;
 class IndexTarget;
+class pkgAcqMetaIndex;
 
 /** \brief Represents the process by which a pkgAcquire object should  {{{
  *  retrieve a file or a collection of files.
@@ -116,7 +117,7 @@ class pkgAcquire::Item : public WeakPointable
        /** \brief The item was could not be downloaded because of 
        *  a transient network error (e.g. network down)
        */
-       StatTransientNetworkError
+       StatTransientNetworkError,
      } Status;
 
    /** \brief Contains a textual description of the error encountered
@@ -168,6 +169,9 @@ class pkgAcquire::Item : public WeakPointable
     */
    unsigned int QueueCounter;
 
+   /** \brief TransactionID */
+   unsigned long TransactionID;
+
    /** \brief The number of additional fetch items that are expected
     *  once this item is done.
     *
@@ -183,6 +187,9 @@ class pkgAcquire::Item : public WeakPointable
     */
    std::string DestFile;
 
+   /** \brief storge name until a transaction is finished */
+   std::string PartialFile;
+
    /** \brief Invoked by the acquire worker when the object couldn't
     *  be fetched.
     *
@@ -290,7 +297,8 @@ class pkgAcquire::Item : public WeakPointable
     *  \param ExpectedHashes of the file represented by this item
     */
    Item(pkgAcquire *Owner,
-        HashStringList const &ExpectedHashes=HashStringList());
+        HashStringList const &ExpectedHashes=HashStringList(),
+        unsigned long TransactionID=0);
 
    /** \brief Remove this item from its owner's queue by invoking
     *  pkgAcquire::Remove.
@@ -363,11 +371,171 @@ class pkgAcqSubIndex : public pkgAcquire::Item
     *
     *  \param ExpectedHashes The list file's hashsums which are expected.
     */
-   pkgAcqSubIndex(pkgAcquire *Owner, std::string const &URI,std::string const &URIDesc,
+   pkgAcqSubIndex(pkgAcquire *Owner, 
+                  unsigned long TransactionID,
+                  std::string const &URI,std::string const &URIDesc,
                   std::string const &ShortDesc, HashStringList const &ExpectedHashes);
 };
                                                                        /*}}}*/
 
+class pkgAcqMetaSigBase : public pkgAcquire::Item
+{
+ protected:
+   bool GenerateAuthWarning(const std::string &RealURI,
+                            const std::string &Message);
+
+ public:
+   pkgAcqMetaSigBase(pkgAcquire *Owner,
+                     HashStringList const &ExpectedHashes=HashStringList(),
+                     unsigned long TransactionID=0);
+};
+
+/** \brief An item that is responsible for downloading the meta-index  {{{
+ *  file (i.e., Release) itself and verifying its signature.
+ *
+ *  Once the download and verification are complete, the downloads of
+ *  the individual index files are queued up using pkgAcqDiffIndex.
+ *  If the meta-index file had a valid signature, the expected hashsums
+ *  of the index files will be the md5sums listed in the meta-index;
+ *  otherwise, the expected hashsums will be "" (causing the
+ *  authentication of the index files to be bypassed).
+ */
+class pkgAcqMetaIndex : public pkgAcqMetaSigBase
+{
+   protected:
+   /** \brief The URI that is actually being downloaded; never
+    *  modified by pkgAcqMetaIndex.
+    */
+   std::string RealURI;
+
+   /** \brief The file in which the signature for this index was stored.
+    *
+    *  If empty, the signature and the md5sums of the individual
+    *  indices will not be checked.
+    */
+   std::string SigFile;
+
+   /** \brief The index files to download. */
+   const std::vector<IndexTarget*>* IndexTargets;
+
+   /** \brief The parser for the meta-index file. */
+   indexRecords* MetaIndexParser;
+
+   /** \brief If \b true, the index's signature is currently being verified.
+    */
+   bool AuthPass;
+   // required to deal gracefully with problems caused by incorrect ims hits
+   bool IMSHit; 
+
+   /** \brief Check that the release file is a release file for the
+    *  correct distribution.
+    *
+    *  \return \b true if no fatal errors were encountered.
+    */
+   bool VerifyVendor(std::string Message);
+
+   /** \brief Called when a file is finished being retrieved.
+    *
+    *  If the file was not downloaded to DestFile, a copy process is
+    *  set up to copy it to DestFile; otherwise, Complete is set to \b
+    *  true and the file is moved to its final location.
+    *
+    *  \param Message The message block received from the fetch
+    *  subprocess.
+    */
+   void RetrievalDone(std::string Message);
+
+   /** \brief Called when authentication succeeded.
+    *
+    *  Sanity-checks the authenticated file, queues up the individual
+    *  index files for download, and saves the signature in the lists
+    *  directory next to the authenticated list file.
+    *
+    *  \param Message The message block received from the fetch
+    *  subprocess.
+    */
+   void AuthDone(std::string Message);
+
+   /** \brief Starts downloading the individual index files.
+    *
+    *  \param verify If \b true, only indices whose expected hashsum
+    *  can be determined from the meta-index will be downloaded, and
+    *  the hashsums of indices will be checked (reporting
+    *  #StatAuthError if there is a mismatch).  If verify is \b false,
+    *  no hashsum checking will be performed.
+    */
+   void QueueIndexes(bool verify);
+
+   /** \brief The URI of the meta-index file for the detached signature */
+   std::string MetaIndexSigURI;
+
+   /** \brief A "URI-style" description of the meta-index file */
+   std::string MetaIndexSigURIDesc;
+
+   /** \brief A brief description of the meta-index file */
+   std::string MetaIndexSigShortDesc;
+
+   /** \brief delayed constructor */
+   void Init(std::string URIDesc, std::string ShortDesc);
+   
+   public:
+   
+   // Specialized action members
+   virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
+                    pkgAcquire::MethodConfig *Cnf);
+   virtual std::string Custom600Headers() const;
+   virtual std::string DescURI() const {return RealURI; };
+   virtual void Finished();
+
+   /** \brief Create a new pkgAcqMetaIndex. */
+   pkgAcqMetaIndex(pkgAcquire *Owner,
+                   unsigned long TransactionID,
+                  std::string URI,std::string URIDesc, std::string ShortDesc,
+                   std::string MetaIndexSigURI, std::string MetaIndexSigURIDesc, std::string MetaIndexSigShortDesc,
+                  const std::vector<IndexTarget*>* IndexTargets,
+                  indexRecords* MetaIndexParser);
+};
+                                                                       /*}}}*/
+/** \brief An item repsonsible for downloading clearsigned metaindexes {{{*/
+class pkgAcqMetaClearSig : public pkgAcqMetaIndex
+{
+   /** \brief The URI of the meta-index file for the detached signature */
+   std::string MetaIndexURI;
+
+   /** \brief A "URI-style" description of the meta-index file */
+   std::string MetaIndexURIDesc;
+
+   /** \brief A brief description of the meta-index file */
+   std::string MetaIndexShortDesc;
+
+   /** \brief The URI of the detached meta-signature file if the clearsigned one failed. */
+   std::string MetaSigURI;
+
+   /** \brief A "URI-style" description of the meta-signature file */
+   std::string MetaSigURIDesc;
+
+   /** \brief A brief description of the meta-signature file */
+   std::string MetaSigShortDesc;
+
+public:
+   virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
+   virtual std::string Custom600Headers() const;
+   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
+                    pkgAcquire::MethodConfig *Cnf);
+
+   /** \brief Create a new pkgAcqMetaClearSig. */
+   pkgAcqMetaClearSig(pkgAcquire *Owner,
+               std::string const &URI, std::string const &URIDesc, std::string const &ShortDesc,
+               std::string const &MetaIndexURI, std::string const &MetaIndexURIDesc, std::string const &MetaIndexShortDesc,
+               std::string const &MetaSigURI, std::string const &MetaSigURIDesc, std::string const &MetaSigShortDesc,
+               const std::vector<IndexTarget*>* IndexTargets,
+               indexRecords* MetaIndexParser);
+   virtual ~pkgAcqMetaClearSig();
+};
+                                                                       /*}}}*/
+
+
 /** \brief Common base class for all classes that deal with fetching   {{{
            indexes
  */
@@ -378,14 +546,16 @@ class pkgAcqBaseIndex : public pkgAcquire::Item
     */
    const struct IndexTarget * Target;
    indexRecords *MetaIndexParser;
+   /** \brief The MetaIndex Key */
+   std::string MetaKey;
 
    pkgAcqBaseIndex(pkgAcquire *Owner,
+                   unsigned long TransactionID,
                    struct IndexTarget const * const Target,
                    HashStringList const &ExpectedHashes,
                    indexRecords *MetaIndexParser)
-      : Item(Owner, ExpectedHashes), Target(Target), 
+      : Item(Owner, ExpectedHashes, TransactionID), Target(Target), 
         MetaIndexParser(MetaIndexParser) {};
-
 };
                                                                        /*}}}*/
 /** \brief An item that is responsible for fetching an index file of   {{{
@@ -452,6 +622,7 @@ class pkgAcqDiffIndex : public pkgAcqBaseIndex
     *  \param ExpectedHashes The list file's hashsums which are expected.
     */
    pkgAcqDiffIndex(pkgAcquire *Owner,
+                   unsigned long TransactionID,
                    struct IndexTarget const * const Target,
                    HashStringList const &ExpectedHashes,
                    indexRecords *MetaIndexParser);
@@ -540,6 +711,7 @@ class pkgAcqIndexMergeDiffs : public pkgAcqBaseIndex
     *  check if it was the last one to complete the download step
     */
    pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
+                         unsigned long TransactionID,
                          struct IndexTarget const * const Target,
                          HashStringList const &ExpectedHash,
                          indexRecords *MetaIndexParser,
@@ -668,6 +840,7 @@ class pkgAcqIndexDiffs : public pkgAcqBaseIndex
     *  that depends on it.
     */
    pkgAcqIndexDiffs(pkgAcquire *Owner,
+                    unsigned long TransactionID,
                     struct IndexTarget const * const Target,
                     HashStringList const &ExpectedHash,
                     indexRecords *MetaIndexParser,
@@ -694,6 +867,7 @@ class pkgAcqIndex : public pkgAcqBaseIndex
     */
    bool Erase;
 
+   // FIXME:
    // Unused, used to be used to verify that "Packages: " header was there
    bool __DELME_ON_NEXT_ABI_BREAK_Verify;
 
@@ -710,6 +884,17 @@ class pkgAcqIndex : public pkgAcqBaseIndex
    /** \brief Do the changes needed to fetch via AptByHash (if needed) */
    void InitByHashIfNeeded(const std::string MetaKey);
 
+   /** \brief Auto select the right compression to use */
+   void AutoSelectCompression();
+
+   /** \brief Get the full pathname of the final file for the given URI
+    */
+   std::string GetFinalFilename(std::string const &URI,
+                                std::string const &compExt);
+
+   /** \brief Schedule file for verification after a IMS hit */
+   void ReverifyAfterIMS(std::string const &FileName);
+
    public:
    
    // Specialized action members
@@ -741,10 +926,11 @@ class pkgAcqIndex : public pkgAcqBaseIndex
    pkgAcqIndex(pkgAcquire *Owner,std::string URI,std::string URIDesc,
               std::string ShortDesc, HashStringList const &ExpectedHashes,
               std::string compressExt="");
-   pkgAcqIndex(pkgAcquire *Owner,
+   pkgAcqIndex(pkgAcquire *Owner, unsigned long TransactionID,
                IndexTarget const * const Target,
                HashStringList const &ExpectedHash,
                indexRecords *MetaIndexParser);
+               
    void Init(std::string const &URI, std::string const &URIDesc,
              std::string const &ShortDesc);
 };
@@ -774,10 +960,14 @@ class pkgAcqIndexTrans : public pkgAcqIndex
     *
     *  \param ShortDesc A brief description of this index file.
     */
-   pkgAcqIndexTrans(pkgAcquire *Owner,std::string URI,std::string URIDesc,
+   pkgAcqIndexTrans(pkgAcquire *Owner,
+                    std::string URI,std::string URIDesc,
                    std::string ShortDesc);
-   pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const * const Target,
-                   HashStringList const &ExpectedHashes, indexRecords *MetaIndexParser);
+   pkgAcqIndexTrans(pkgAcquire *Owner,
+                    unsigned long TransactionID,
+                    IndexTarget const * const Target,
+                    HashStringList const &ExpectedHashes,
+                    indexRecords *MetaIndexParser);
 };
                                                                        /*}}}*/
 /** \brief Information about an index file. */                         /*{{{*/
@@ -839,7 +1029,7 @@ class OptionalSubIndexTarget : public OptionalIndexTarget
  *
  *  \sa pkgAcqMetaIndex
  */
-class pkgAcqMetaSig : public pkgAcquire::Item
+class pkgAcqMetaSig : public pkgAcqMetaSigBase
 {
    protected:
    /** \brief The last good signature file */
@@ -851,22 +1041,12 @@ class pkgAcqMetaSig : public pkgAcquire::Item
     */
    std::string RealURI;
 
-   /** \brief The URI of the meta-index file to be fetched after the signature. */
-   std::string MetaIndexURI;
-
-   /** \brief A "URI-style" description of the meta-index file to be
-    *  fetched after the signature.
-    */
-   std::string MetaIndexURIDesc;
-
-   /** \brief A brief description of the meta-index file to be fetched
-    *  after the signature.
-    */
-   std::string MetaIndexShortDesc;
-
    /** \brief A package-system-specific parser for the meta-index file. */
    indexRecords* MetaIndexParser;
 
+   /** \brief The file we need to verify */
+   std::string MetaIndexFile;
+
    /** \brief The index files which should be looked up in the meta-index
     *  and then downloaded.
     *
@@ -874,6 +1054,12 @@ class pkgAcqMetaSig : public pkgAcquire::Item
     */
    const std::vector<IndexTarget*>* IndexTargets;
 
+   /** \brief If we are in fetching or download state */
+   bool AuthPass;
+
+   /** \brief Was this file already on disk */
+   bool IMSHit;
+
    public:
    
    // Specialized action members
@@ -884,141 +1070,15 @@ class pkgAcqMetaSig : public pkgAcquire::Item
    virtual std::string DescURI() const {return RealURI; };
 
    /** \brief Create a new pkgAcqMetaSig. */
-   pkgAcqMetaSig(pkgAcquire *Owner,std::string URI,std::string URIDesc, std::string ShortDesc,
-                std::string MetaIndexURI, std::string MetaIndexURIDesc, std::string MetaIndexShortDesc,
+   pkgAcqMetaSig(pkgAcquire *Owner,
+                 unsigned long TransactionID,
+                 std::string URI,std::string URIDesc, std::string ShortDesc,
+                 std::string MetaIndexFile,
                 const std::vector<IndexTarget*>* IndexTargets,
                 indexRecords* MetaIndexParser);
    virtual ~pkgAcqMetaSig();
 };
                                                                        /*}}}*/
-/** \brief An item that is responsible for downloading the meta-index  {{{
- *  file (i.e., Release) itself and verifying its signature.
- *
- *  Once the download and verification are complete, the downloads of
- *  the individual index files are queued up using pkgAcqDiffIndex.
- *  If the meta-index file had a valid signature, the expected hashsums
- *  of the index files will be the md5sums listed in the meta-index;
- *  otherwise, the expected hashsums will be "" (causing the
- *  authentication of the index files to be bypassed).
- */
-class pkgAcqMetaIndex : public pkgAcquire::Item
-{
-   protected:
-   /** \brief The URI that is actually being downloaded; never
-    *  modified by pkgAcqMetaIndex.
-    */
-   std::string RealURI;
-
-   /** \brief The file in which the signature for this index was stored.
-    *
-    *  If empty, the signature and the md5sums of the individual
-    *  indices will not be checked.
-    */
-   std::string SigFile;
-
-   /** \brief The index files to download. */
-   const std::vector<IndexTarget*>* IndexTargets;
-
-   /** \brief The parser for the meta-index file. */
-   indexRecords* MetaIndexParser;
-
-   /** \brief If \b true, the index's signature is currently being verified.
-    */
-   bool AuthPass;
-   // required to deal gracefully with problems caused by incorrect ims hits
-   bool IMSHit; 
-
-   /** \brief Check that the release file is a release file for the
-    *  correct distribution.
-    *
-    *  \return \b true if no fatal errors were encountered.
-    */
-   bool VerifyVendor(std::string Message);
-
-   /** \brief Called when a file is finished being retrieved.
-    *
-    *  If the file was not downloaded to DestFile, a copy process is
-    *  set up to copy it to DestFile; otherwise, Complete is set to \b
-    *  true and the file is moved to its final location.
-    *
-    *  \param Message The message block received from the fetch
-    *  subprocess.
-    */
-   void RetrievalDone(std::string Message);
-
-   /** \brief Called when authentication succeeded.
-    *
-    *  Sanity-checks the authenticated file, queues up the individual
-    *  index files for download, and saves the signature in the lists
-    *  directory next to the authenticated list file.
-    *
-    *  \param Message The message block received from the fetch
-    *  subprocess.
-    */
-   void AuthDone(std::string Message);
-
-   /** \brief Starts downloading the individual index files.
-    *
-    *  \param verify If \b true, only indices whose expected hashsum
-    *  can be determined from the meta-index will be downloaded, and
-    *  the hashsums of indices will be checked (reporting
-    *  #StatAuthError if there is a mismatch).  If verify is \b false,
-    *  no hashsum checking will be performed.
-    */
-   void QueueIndexes(bool verify);
-   
-   public:
-   
-   // Specialized action members
-   virtual void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual void Done(std::string Message,unsigned long long Size, HashStringList const &Hashes,
-                    pkgAcquire::MethodConfig *Cnf);
-   virtual std::string Custom600Headers() const;
-   virtual std::string DescURI() const {return RealURI; };
-
-   /** \brief Create a new pkgAcqMetaIndex. */
-   pkgAcqMetaIndex(pkgAcquire *Owner,
-                  std::string URI,std::string URIDesc, std::string ShortDesc,
-                  std::string SigFile,
-                  const std::vector<IndexTarget*>* IndexTargets,
-                  indexRecords* MetaIndexParser);
-};
-                                                                       /*}}}*/
-/** \brief An item repsonsible for downloading clearsigned metaindexes {{{*/
-class pkgAcqMetaClearSig : public pkgAcqMetaIndex
-{
-   /** \brief The URI of the meta-index file for the detached signature */
-   std::string MetaIndexURI;
-
-   /** \brief A "URI-style" description of the meta-index file */
-   std::string MetaIndexURIDesc;
-
-   /** \brief A brief description of the meta-index file */
-   std::string MetaIndexShortDesc;
-
-   /** \brief The URI of the detached meta-signature file if the clearsigned one failed. */
-   std::string MetaSigURI;
-
-   /** \brief A "URI-style" description of the meta-signature file */
-   std::string MetaSigURIDesc;
-
-   /** \brief A brief description of the meta-signature file */
-   std::string MetaSigShortDesc;
-
-public:
-   void Failed(std::string Message,pkgAcquire::MethodConfig *Cnf);
-   virtual std::string Custom600Headers() const;
-
-   /** \brief Create a new pkgAcqMetaClearSig. */
-   pkgAcqMetaClearSig(pkgAcquire *Owner,
-               std::string const &URI, std::string const &URIDesc, std::string const &ShortDesc,
-               std::string const &MetaIndexURI, std::string const &MetaIndexURIDesc, std::string const &MetaIndexShortDesc,
-               std::string const &MetaSigURI, std::string const &MetaSigURIDesc, std::string const &MetaSigShortDesc,
-               const std::vector<IndexTarget*>* IndexTargets,
-               indexRecords* MetaIndexParser);
-   virtual ~pkgAcqMetaClearSig();
-};
-                                                                       /*}}}*/
 /** \brief An item that is responsible for fetching a package file.    {{{
  *
  *  If the package file already exists in the cache, nothing will be
index 8467dab5b6086e1c2fe018debe36ab0d748dcd27..be4e494e0d5f5caf144be6fd558862fb9bee879f 100644 (file)
@@ -37,6 +37,7 @@
 #include <sys/time.h>
 #include <sys/select.h>
 #include <errno.h>
+#include <sys/stat.h>
 
 #include <apti18n.h>
                                                                        /*}}}*/
@@ -168,6 +169,75 @@ void pkgAcquire::Remove(Item *Itm)
    }
 }
                                                                        /*}}}*/
+// Acquire::AbortTransaction - Remove a transaction                    /*{{{*/
+void pkgAcquire::AbortTransaction(unsigned long TransactionID)
+{
+   if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+      std::clog << "AbortTransaction: " << TransactionID << std::endl;
+
+   std::vector<Item*> Transaction;
+   for (ItemIterator I = Items.begin(); I != Items.end(); ++I)
+      if((*I)->TransactionID == TransactionID)
+         Transaction.push_back(*I);
+   
+   for (std::vector<Item*>::iterator I = Transaction.begin();
+        I != Transaction.end(); ++I)
+   {
+      if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+         std::clog << "  Cancel: " << (*I)->DestFile << std::endl;
+      // the transaction will abort, so stop anything that is idle
+      if ((*I)->Status == pkgAcquire::Item::StatIdle)
+         (*I)->Status = pkgAcquire::Item::StatDone;
+   }
+}
+                                                                       /*}}}*/
+bool pkgAcquire::TransactionHasError(unsigned long TransactionID)
+{
+   std::vector<Item*> Transaction;
+   for (ItemIterator I = Items.begin(); I != Items.end(); ++I)
+      if((*I)->TransactionID == TransactionID)
+         if((*I)->Status != pkgAcquire::Item::StatDone &&
+            (*I)->Status != pkgAcquire::Item::StatIdle)
+            return true;
+
+   return false;
+}
+// Acquire::CommitTransaction - Commit a transaction                   /*{{{*/
+void pkgAcquire::CommitTransaction(unsigned long TransactionID)
+{
+   if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+      std::clog << "CommitTransaction: " << TransactionID << std::endl;
+
+   std::vector<Item*> Transaction;
+   for (ItemIterator I = Items.begin(); I != Items.end(); ++I)
+      if((*I)->TransactionID == TransactionID)
+         Transaction.push_back(*I);
+   
+   // move new files into place *and* remove files that are not
+   // part of the transaction but are still on disk
+   for (std::vector<Item*>::iterator I = Transaction.begin();
+        I != Transaction.end(); ++I)
+   {
+      if((*I)->PartialFile != "")
+      {
+         if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+            std::clog << "mv " 
+                      << (*I)->PartialFile << " -> " 
+                      <<  (*I)->DestFile << std::endl;
+         Rename((*I)->PartialFile, (*I)->DestFile);
+         chmod((*I)->DestFile.c_str(),0644);
+      } else {
+         if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+            std::clog << "rm " 
+                      <<  (*I)->DestFile << std::endl;
+         unlink((*I)->DestFile.c_str());
+      }
+      // mark that this transaction is finished
+      (*I)->TransactionID = 0;
+   }
+}
+                                                                       /*}}}*/
+
 // Acquire::Add - Add a worker                                         /*{{{*/
 // ---------------------------------------------------------------------
 /* A list of workers is kept so that the select loop can direct their FD
index 0113021b21dce95c1a4cec534a61732c2e75d5c8..7f51dd8f57f1f044d808f7c6c3ef23eaad4fb0b3 100644 (file)
@@ -248,6 +248,11 @@ class pkgAcquire
    
    public:
 
+   /** \brief Abort a given transaction */
+   void AbortTransaction(unsigned long TransactionID);
+   void CommitTransaction(unsigned long TransactionID);
+   bool TransactionHasError(unsigned long TransactionID);
+
    /** \brief Retrieve information about a fetch method by name.
     *
     *  \param Access The name of the method to look up.
index 73010e8672100febca469f3bffd1c32b62d81c63..b1dc060fedfc27991d1dc924849ff8eabb8d3b96 100644 (file)
@@ -265,26 +265,27 @@ bool debReleaseIndex::GetIndexes(pkgAcquire *Owner, bool const &GetAll) const
       // this is normally created in pkgAcqMetaSig, but if we run
       // in --print-uris mode, we add it here
       if (tryInRelease == false)
-        new pkgAcqMetaIndex(Owner, MetaIndexURI("Release"),
-              MetaIndexInfo("Release"), "Release",
-              MetaIndexURI("Release.gpg"),
-              ComputeIndexTargets(),
-              new indexRecords (Dist));
+        new pkgAcqMetaIndex(Owner, 0,
+                             MetaIndexURI("Release"),
+                             MetaIndexInfo("Release"), "Release",
+                             MetaIndexURI("Release.gpg"), MetaIndexInfo("Release.gpg"), "Release.gpg",
+                             ComputeIndexTargets(),
+                             new indexRecords (Dist));
    }
 
    if (tryInRelease == true)
-      new pkgAcqMetaClearSig(Owner, MetaIndexURI("InRelease"),
-           MetaIndexInfo("InRelease"), "InRelease",
+      new pkgAcqMetaClearSig(Owner, 
+            MetaIndexURI("InRelease"), MetaIndexInfo("InRelease"), "InRelease",
            MetaIndexURI("Release"), MetaIndexInfo("Release"), "Release",
            MetaIndexURI("Release.gpg"), MetaIndexInfo("Release.gpg"), "Release.gpg",
            ComputeIndexTargets(),
            new indexRecords (Dist));
    else
-      new pkgAcqMetaSig(Owner, MetaIndexURI("Release.gpg"),
-           MetaIndexInfo("Release.gpg"), "Release.gpg",
-           MetaIndexURI("Release"), MetaIndexInfo("Release"), "Release",
-           ComputeIndexTargets(),
-           new indexRecords (Dist));
+      new pkgAcqMetaIndex(Owner, 0,
+          MetaIndexURI("Release"), MetaIndexInfo("Release"), "Release",
+          MetaIndexURI("Release.gpg"), MetaIndexInfo("Release.gpg"), "Release.gpg",
+          ComputeIndexTargets(),
+          new indexRecords (Dist));
 
    return true;
 }
index d59f032ffa741f3aeeb644130a92c63418dec2b9..8c797ff1fd6a183fd9aa0a59a55697bb241dad84 100644 (file)
@@ -16,6 +16,7 @@
 #include <apt-pkg/acquire-method.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/hashes.h>
+#include <apt-pkg/configuration.h>
 
 #include <string>
 #include <sys/stat.h>
 class CopyMethod : public pkgAcqMethod
 {
    virtual bool Fetch(FetchItem *Itm);
+   void CalculateHashes(FetchResult &Res);
    
    public:
    
-   CopyMethod() : pkgAcqMethod("1.0",SingleInstance) {};
+   CopyMethod() : pkgAcqMethod("1.0",SingleInstance|SendConfig) {};
 };
 
+void CopyMethod::CalculateHashes(FetchResult &Res)
+{
+   // For gzip indexes we need to look inside the gzip for the hash
+   // We can not use the extension here as its not used in partial 
+   // on a IMS hit
+   FileFd::OpenMode OpenMode = FileFd::ReadOnly;
+   if (_config->FindB("Acquire::GzipIndexes", false) == true)
+      OpenMode = FileFd::ReadOnlyGzip;
+
+   Hashes Hash;
+   FileFd Fd(Res.Filename, OpenMode);
+   Hash.AddFD(Fd);
+   Res.TakeHashes(Hash);
+}
+
 // CopyMethod::Fetch - Fetch a file                                    /*{{{*/
 // ---------------------------------------------------------------------
 /* */
@@ -53,6 +70,14 @@ bool CopyMethod::Fetch(FetchItem *Itm)
    Res.LastModified = Buf.st_mtime;
    Res.IMSHit = false;      
    URIStart(Res);
+
+   // when the files are identical, just compute the hashes
+   if(File == Itm->DestFile)
+   {
+      CalculateHashes(Res);
+      URIDone(Res);
+      return true;
+   }
    
    // See if the file exists
    FileFd From(File,FileFd::ReadOnly);
@@ -82,10 +107,7 @@ bool CopyMethod::Fetch(FetchItem *Itm)
    if (utimes(Res.Filename.c_str(), times) != 0)
       return _error->Errno("utimes",_("Failed to set modification time"));
 
-   Hashes Hash;
-   FileFd Fd(Res.Filename, FileFd::ReadOnly);
-   Hash.AddFD(Fd);
-   Res.TakeHashes(Hash);
+   CalculateHashes(Res);
 
    URIDone(Res);
    return true;
index ae521a2edbd6d831e6a1ecf52d20f080f306133a..30fd217bde9a382d709fa034ff366e3ef9a15de2 100644 (file)
@@ -159,7 +159,7 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
    waitpid(pid, &status, 0);
    if (Debug == true)
    {
-      std::clog << "gpgv exited\n";
+      ioprintf(std::clog, "gpgv exited with status %i\n", WEXITSTATUS(status));
    }
    
    if (WEXITSTATUS(status) == 0)
diff --git a/test/integration/test-apt-update-ims b/test/integration/test-apt-update-ims
new file mode 100755 (executable)
index 0000000..3bd6e84
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'amd64'
+
+buildsimplenativepackage 'unrelated' 'all' '0.5~squeeze1' 'unstable'
+
+setupaptarchive
+changetowebserver
+
+testsuccess aptget update
+
+# check that I-M-S header is kept in redirections
+testequal "Hit http://localhost:8080 unstable InRelease
+Hit http://localhost:8080 unstable/main Sources
+Hit http://localhost:8080 unstable/main amd64 Packages
+Hit http://localhost:8080 unstable/main Translation-en
+Reading package lists..." aptget update 
+
+# ensure that we still do a hash check on ims hit
+msgtest 'Test I-M-S reverify'
+aptget update -o Debug::pkgAcquire::Auth=1 2>&1 | grep -A1 'RecivedHash:' | grep -q -- '- SHA' && msgpass || msgfail
diff --git a/test/integration/test-apt-update-rollback b/test/integration/test-apt-update-rollback
new file mode 100755 (executable)
index 0000000..ccd7f57
--- /dev/null
@@ -0,0 +1,198 @@
+#!/bin/sh
+#
+# test that apt-get update is transactional
+# 
+set -e
+
+avoid_ims_hit() {
+    touch -d '+1hour' aptarchive/dists/unstable/main/binary-i386/Packages*
+    touch -d '+1hour' aptarchive/dists/unstable/main/source/Sources*
+    touch -d '+1hour' aptarchive/dists/unstable/*Release*
+
+    touch -d '-1hour' rootdir/var/lib/apt/lists/*
+}
+
+create_fresh_archive()
+{
+    rm -rf aptarchive/*
+    rm -f rootdir/var/lib/apt/lists/_* rootdir/var/lib/apt/lists/partial/*
+
+    insertpackage 'unstable' 'old' 'all' '1.0'
+
+    setupaptarchive
+}
+
+add_new_package() {
+    insertpackage "unstable" "new" "all" "1.0"
+    insertsource "unstable" "new" "all" "1.0"
+
+    setupaptarchive --no-update
+
+    avoid_ims_hit
+}
+
+break_repository_sources_index() {
+    printf "xxx" > $APTARCHIVE/dists/unstable/main/source/Sources
+    gzip -c $APTARCHIVE/dists/unstable/main/source/Sources > \
+            $APTARCHIVE/dists/unstable/main/source/Sources.gz
+    avoid_ims_hit
+}
+
+test_inrelease_to_new_inrelease() {
+    msgmsg "Test InRelease to new InRelease works fine"
+    create_fresh_archive
+    testequal "old/unstable 1.0 all" apt list -q
+
+    add_new_package
+
+    testsuccess aptget update -o Debug::Acquire::Transaction=1
+
+    testequal "new/unstable 1.0 all
+old/unstable 1.0 all" apt list -q
+}
+
+test_inrelease_to_broken_hash_reverts_all() {
+    msgmsg "Test InRelease to broken InRelease reverts everything"
+    create_fresh_archive
+    add_new_package
+    # break the Sources file
+    break_repository_sources_index
+
+    # test the error condition
+    testequal "W: Failed to fetch file:${APTARCHIVE}/dists/unstable/main/source/Sources  Hash Sum mismatch
+
+E: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq
+    # ensure that the Packages file is also rolled back
+    testequal "E: Unable to locate package new" aptget install new -s -qq
+}
+
+test_inreleae_to_valid_release() {
+    msgmsg "Test InRelease to valid Release"
+    create_fresh_archive
+    add_new_package
+    # switch to a unsinged repo now
+    rm $APTARCHIVE/dists/unstable/InRelease
+    rm $APTARCHIVE/dists/unstable/Release.gpg
+    avoid_ims_hit
+
+    # update works
+    testsuccess aptget update -o Debug::Acquire::Transaction=1
+
+    # test that we can install the new packages but do no longer have a sig
+    testsuccess aptget install old -s
+    testsuccess aptget install new -s
+    testfailure ls $ROOTDIR/var/lib/apt/lists/*_InRelease
+    testfailure ls $ROOTDIR/var/lib/apt/lists/*_Release.gpg
+    testsuccess ls $ROOTDIR/var/lib/apt/lists/*_Release
+}
+
+test_inreleae_to_release_reverts_all() {
+    msgmsg "Test InRelease to broken Release reverts everything"
+    create_fresh_archive
+
+    # switch to a unsinged repo now
+    add_new_package
+    rm $APTARCHIVE/dists/unstable/InRelease
+    rm $APTARCHIVE/dists/unstable/Release.gpg
+    # break it
+    break_repository_sources_index
+
+    # ensure error
+    testequal "W: Failed to fetch file:$APTARCHIVE/dists/unstable/main/source/Sources  Hash Sum mismatch
+
+E: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq # -o Debug::acquire::transaction=1
+
+    # ensure that the Packages file is also rolled back
+    testsuccess aptget install old -s
+    testfailure aptget install new -s
+    testsuccess ls $ROOTDIR/var/lib/apt/lists/*_InRelease
+    testfailure ls $ROOTDIR/var/lib/apt/lists/*_Release
+}
+
+test_unauthenticated_to_invalid_inrelease() {
+    msgmsg "Test UnAuthenticated to invalid InRelease reverts everything"
+    create_fresh_archive
+    rm $APTARCHIVE/dists/unstable/InRelease
+    rm $APTARCHIVE/dists/unstable/Release.gpg
+    avoid_ims_hit
+    
+    testsuccess aptget update -qq
+    testequal "WARNING: The following packages cannot be authenticated!
+  old
+E: There are problems and -y was used without --force-yes" aptget install -qq -y old
+    
+    # go to authenticated but not correct
+    add_new_package
+    break_repository_sources_index
+
+    testequal "W: Failed to fetch file:$APTARCHIVE/dists/unstable/main/source/Sources  Hash Sum mismatch
+
+E: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq
+
+    testfailure ls rootdir/var/lib/apt/lists/*_InRelease
+    testequal "WARNING: The following packages cannot be authenticated!
+  old
+E: There are problems and -y was used without --force-yes" aptget install -qq -y old
+}
+
+test_inrelease_to_unauth_inrelease() {
+    msgmsg "Test InRelease to InRelease without sig"
+    create_fresh_archive
+    signreleasefiles 'Marvin Paranoid'
+    avoid_ims_hit
+    
+    testequal "W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: file: unstable InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY E8525D47528144E2
+
+W: Failed to fetch file:$APTARCHIVE/dists/unstable/InRelease  
+
+W: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq
+
+    testsuccess ls rootdir/var/lib/apt/lists/*_InRelease
+}
+
+test_inrelease_to_broken_gzip() {
+    msgmsg "Test InRelease to broken gzip"
+    create_fresh_archive
+    # append junk at the end of the gzip, this
+    echo "lala" >> $APTARCHIVE/dists/unstable/main/source/Sources.gz
+    # remove uncompressed file, otherwise apt will just fallback fetching
+    # that
+    rm $APTARCHIVE/dists/unstable/main/source/Sources
+    avoid_ims_hit
+
+    testfailure aptget update
+}
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+
+setupenvironment
+configarchitecture "i386"
+
+# setup the archive and ensure we have a single package that installs fine
+setupaptarchive
+APTARCHIVE=$(readlink -f ./aptarchive)
+ROOTDIR=${TMPWORKINGDIRECTORY}/rootdir
+APTARCHIVE_LISTS="$(echo $APTARCHIVE | tr "/" "_" )"
+
+# test the following cases:
+# - InRelease -> broken InRelease revert to previous state 
+# - empty lists dir and broken remote leaves nothing on the system
+# - InRelease -> hashsum mismatch for one file reverts all files to previous state
+# - Release/Release.gpg -> hashsum mismatch
+# - InRelease -> Release with hashsum mismatch revert entire state and kills Release
+# - Release -> InRelease with broken Sig/Hash removes InRelease
+# going from Release/Release.gpg -> InRelease and vice versa
+# - unauthenticated -> invalid InRelease
+
+# stuff to do:
+# - ims-hit 
+# - gzip-index tests
+
+test_inrelease_to_new_inrelease
+test_inrelease_to_broken_hash_reverts_all
+test_inreleae_to_valid_release
+test_inreleae_to_release_reverts_all
+test_unauthenticated_to_invalid_inrelease
+test_inrelease_to_unauth_inrelease
+test_inrelease_to_broken_gzip
diff --git a/test/integration/test-apt-update-transactions b/test/integration/test-apt-update-transactions
new file mode 100755 (executable)
index 0000000..2473349
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+
+setupenvironment
+configarchitecture "i386"
+
+insertpackage 'unstable' 'foo' 'all' '1.0'
+
+setupaptarchive --no-update
+changetowebserver
+
+# break package file
+cat > aptarchive/dists/unstable/main/binary-i386/Packages <<EOF
+Package: bar
+EOF
+compressfile aptarchive/dists/unstable/main/binary-i386/Packages '+1hour'
+
+# ensure that a update will only succeed entirely or not at all
+testfailure aptget update
+testequal "partial" ls rootdir/var/lib/apt/lists
+
index e77efb46e37c5c2e353284190e38ef54788aae12..70bf1b4761b90d0c89181e1d4cd4919e1ed21cf6 100755 (executable)
@@ -75,5 +75,13 @@ runtest() {
 
 }
 
-runtest
-
+for COMPRESSEDINDEXES in 'false' 'true'; do
+       echo "Acquire::GzipIndexes \"$COMPRESSEDINDEXES\";" > rootdir/etc/apt/apt.conf.d/compressindexes
+       if $COMPRESSEDINDEXES; then
+               msgmsg 'Run tests with GzipIndexes enabled'
+       else
+               msgmsg 'Run tests with GzipIndexes disabled'
+       fi
+        runtest
+done
index f655ae2d8dba74972c62113a43632322f340758d..df2c69cf66a2c04368b066ec53eda55790e79498 100755 (executable)
@@ -36,8 +36,8 @@ ensure_n_canary_strings_in_dir() {
 
 LISTS='rootdir/var/lib/apt/lists'
 rm -rf rootdir/var/lib/apt/lists
-msgtest 'Got expected NODATA failure in' 'apt-get update'
-aptget update -qq 2>&1 | grep -q 'E: GPG error.*NODATA' && msgpass || msgfail
+msgtest 'Got expected failure message' 'apt-get update'
+aptget update -qq 2>&1 | grep -q 'W:.*Does not start with a cleartext signature' && msgpass || msgfail
 
 ensure_n_canary_strings_in_dir $LISTS 'ni ni ni' 0
 testequal 'partial' ls $LISTS
@@ -47,8 +47,8 @@ for f in Release Release.gpg main_binary-amd64_Packages main_source_Sources; do
     echo 'peng neee-wom' > $LISTS/localhost:8080_dists_stable_${f}
 done
 
-msgtest 'Got expected NODATA failure in' 'apt-get update'
-aptget update -qq 2>&1 | grep -q 'E: GPG error.*NODATA' && msgpass || msgfail
+msgtest 'Got expected failure message in' 'apt-get update'
+aptget update -qq 2>&1 | grep -q 'W:.*Does not start with a cleartext signature' && msgpass || msgfail
 
 ensure_n_canary_strings_in_dir $LISTS 'peng neee-wom' 4
 ensure_n_canary_strings_in_dir $LISTS 'ni ni ni' 0
@@ -57,7 +57,7 @@ ensure_n_canary_strings_in_dir $LISTS 'ni ni ni' 0
 echo 'peng neee-wom' > $LISTS/localhost:8080_dists_stable_InRelease
 rm -f $LISTS/localhost:8080_dists_stable_Release $LISTS/localhost:8080_dists_stable_Release.gpg
 msgtest 'excpected failure of' 'apt-get update'
-aptget update -qq 2>&1 | grep -q 'E: GPG error.*NODATA' && msgpass || msgfail
+aptget update -qq 2>&1 | grep -q 'W:.*Does not start with a cleartext signature' && msgpass || msgfail
 
 ensure_n_canary_strings_in_dir $LISTS 'peng neee-wom' 3
 ensure_n_canary_strings_in_dir $LISTS 'ni ni ni' 0
index 50ca2bf57666dc7f343570bd1080732fec367a47..09315868bd7a51833bac34ae0a14d5496706d3f9 100755 (executable)
@@ -28,12 +28,10 @@ MD5Sum:
 done
 
 msgtest 'The unsigned garbage before signed block is' 'ignored'
-testsuccess --nomsg aptget update
+aptget update -qq 2>&1 | grep -q 'W:.*Does not start with a cleartext signature' && msgpass || msgfail
 
 ROOTDIR="$(readlink -f .)"
 testequal "Package files:
  100 ${ROOTDIR}/rootdir/var/lib/dpkg/status
      release a=now
- 500 file:${ROOTDIR}/aptarchive/ unstable/main i386 Packages
-     release a=unstable,n=sid,c=main
 Pinned packages:" aptcache policy