Просмотр исходного кода

chown finished partial files earlier

partial files are chowned by the Item baseclass to let the methods work
with them. Now, this baseclass is also responsible for chowning the
files back to root instead of having various deeper levels do this.

The consequence is that all overloaded Failed() methods now call the
Item::Failed base as their first step. The same is done for Done().

The effect is that even in partial files usually don't belong to
_apt anymore, helping sneakernets and reducing possibilities of a bad
method modifying files not belonging to them.

The change is supported by the framework not only supporting being run
as root, but with proper permission management, too, so that privilege
dropping can be tested with them.
David Kalnischkies лет назад: 11
Родитель
Сommit
03aa08472d

+ 61 - 50
apt-pkg/acquire-item.cc

@@ -67,11 +67,11 @@ static void printHashSumComparision(std::string const &URI, HashStringList const
 									/*}}}*/
 									/*}}}*/
 static void ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode) /*{{{*/
 static void ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode) /*{{{*/
 {
 {
-   // ensure the file is owned by root and has good permissions
-   struct passwd const * const pw = getpwnam(user);
-   struct group const * const gr = getgrnam(group);
-   if (getuid() == 0) // if we aren't root, we can't chown, so don't try it
+   if (getuid() == 0 && strlen(user) != 0 && strlen(group) != 0) // if we aren't root, we can't chown, so don't try it
    {
    {
+      // ensure the file is owned by root and has good permissions
+      struct passwd const * const pw = getpwnam(user);
+      struct group const * const gr = getgrnam(group);
       if (pw != NULL && gr != NULL && chown(file, pw->pw_uid, gr->gr_gid) != 0)
       if (pw != NULL && gr != NULL && chown(file, pw->pw_uid, gr->gr_gid) != 0)
 	 _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
 	 _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
    }
    }
@@ -155,7 +155,10 @@ pkgAcquire::Item::~Item()
    fetch this object */
    fetch this object */
 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 {
 {
-   if(ErrorText == "")
+   if (RealFileExists(DestFile))
+      ChangeOwnerAndPermissionOfFile("Item::Failed", DestFile.c_str(), "root", "root", 0644);
+
+   if(ErrorText.empty())
       ErrorText = LookupTag(Message,"Message");
       ErrorText = LookupTag(Message,"Message");
    UsedMirror =  LookupTag(Message,"UsedMirror");
    UsedMirror =  LookupTag(Message,"UsedMirror");
    if (QueueCounter <= 1)
    if (QueueCounter <= 1)
@@ -179,9 +182,9 @@ void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
       Status = StatIdle;
       Status = StatIdle;
 
 
    // check fail reason
    // check fail reason
-   string FailReason = LookupTag(Message, "FailReason");
+   string const FailReason = LookupTag(Message, "FailReason");
    if(FailReason == "MaximumSizeExceeded")
    if(FailReason == "MaximumSizeExceeded")
-      Rename(DestFile, DestFile+".FAILED");
+      RenameOnError(MaximumSizeExceeded);
 
 
    // report mirror failure back to LP if we actually use a mirror
    // report mirror failure back to LP if we actually use a mirror
    if(FailReason.size() != 0)
    if(FailReason.size() != 0)
@@ -197,6 +200,7 @@ void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 void pkgAcquire::Item::Start(string /*Message*/,unsigned long long Size)
 void pkgAcquire::Item::Start(string /*Message*/,unsigned long long Size)
 {
 {
    Status = StatFetching;
    Status = StatFetching;
+   ErrorText.clear();
    if (FileSize == 0 && Complete == false)
    if (FileSize == 0 && Complete == false)
       FileSize = Size;
       FileSize = Size;
 }
 }
@@ -215,6 +219,8 @@ void pkgAcquire::Item::Done(string Message,unsigned long long Size,HashStringLis
       if (Owner->Log != 0)
       if (Owner->Log != 0)
 	 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
 	 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
    }
    }
+   if (RealFileExists(DestFile))
+      ChangeOwnerAndPermissionOfFile("Item::Done", DestFile.c_str(), "root", "root", 0644);
 
 
    if (FileSize == 0)
    if (FileSize == 0)
       FileSize= Size;
       FileSize= Size;
@@ -231,6 +237,7 @@ bool pkgAcquire::Item::Rename(string From,string To)
 {
 {
    if (rename(From.c_str(),To.c_str()) == 0)
    if (rename(From.c_str(),To.c_str()) == 0)
       return true;
       return true;
+   ChangeOwnerAndPermissionOfFile("Item::Failed", To.c_str(), "root", "root", 0644);
 
 
    std::string S;
    std::string S;
    strprintf(S, _("rename failed, %s (%s -> %s)."), strerror(errno),
    strprintf(S, _("rename failed, %s (%s -> %s)."), strerror(errno),
@@ -258,7 +265,7 @@ void pkgAcquire::Item::Dequeue()					/*{{{*/
 									/*}}}*/
 									/*}}}*/
 bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
 bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
 {
 {
-   if(FileExists(DestFile))
+   if (RealFileExists(DestFile))
       Rename(DestFile, DestFile + ".FAILED");
       Rename(DestFile, DestFile + ".FAILED");
 
 
    switch (error)
    switch (error)
@@ -283,7 +290,11 @@ bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const
 	 Status = StatError;
 	 Status = StatError;
 	 break;
 	 break;
       case NotClearsigned:
       case NotClearsigned:
-         ErrorText = _("Does not start with a cleartext signature");
+	 ErrorText = _("Does not start with a cleartext signature");
+	 Status = StatError;
+	 break;
+      case MaximumSizeExceeded:
+	 // the method is expected to report a good error for this
 	 Status = StatError;
 	 Status = StatError;
 	 break;
 	 break;
    }
    }
@@ -702,14 +713,14 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile)		/*{{{*/
 									/*}}}*/
 									/*}}}*/
 void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * Cnf)/*{{{*/
 void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * Cnf)/*{{{*/
 {
 {
+   Item::Failed(Message,Cnf);
+   Status = StatDone;
+
    if(Debug)
    if(Debug)
       std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
       std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
 		<< "Falling back to normal index file acquire" << std::endl;
 		<< "Falling back to normal index file acquire" << std::endl;
 
 
    new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
    new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
-
-   Item::Failed(Message,Cnf);
-   Status = StatDone;
 }
 }
 									/*}}}*/
 									/*}}}*/
 void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes,	/*{{{*/
 void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes,	/*{{{*/
@@ -792,8 +803,11 @@ pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
    }
    }
 }
 }
 									/*}}}*/
 									/*}}}*/
-void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
+void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * Cnf)/*{{{*/
 {
 {
+   Item::Failed(Message,Cnf);
+   Status = StatDone;
+
    if(Debug)
    if(Debug)
       std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
       std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
 		<< "Falling back to normal index file acquire" << std::endl;
 		<< "Falling back to normal index file acquire" << std::endl;
@@ -1281,11 +1295,14 @@ string pkgAcqIndex::Custom600Headers() const
 // pkgAcqIndex::Failed - getting the indexfile failed			/*{{{*/
 // pkgAcqIndex::Failed - getting the indexfile failed			/*{{{*/
 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 {
 {
+   Item::Failed(Message,Cnf);
+
    size_t const nextExt = CompressionExtensions.find(' ');
    size_t const nextExt = CompressionExtensions.find(' ');
    if (nextExt != std::string::npos)
    if (nextExt != std::string::npos)
    {
    {
       CompressionExtensions = CompressionExtensions.substr(nextExt+1);
       CompressionExtensions = CompressionExtensions.substr(nextExt+1);
       Init(RealURI, Desc.Description, Desc.ShortDesc);
       Init(RealURI, Desc.Description, Desc.ShortDesc);
+      Status = StatIdle;
       return;
       return;
    }
    }
 
 
@@ -1295,8 +1312,6 @@ void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
       unlink(EraseFileName.c_str());
       unlink(EraseFileName.c_str());
    }
    }
 
 
-   Item::Failed(Message,Cnf);
-
    /// cancel the entire transaction
    /// cancel the entire transaction
    TransactionManager->AbortTransaction();
    TransactionManager->AbortTransaction();
 }
 }
@@ -1521,6 +1536,8 @@ string pkgAcqIndexTrans::Custom600Headers() const
 // AcqIndexTrans::Failed - Silence failure messages for missing files	/*{{{*/
 // AcqIndexTrans::Failed - Silence failure messages for missing files	/*{{{*/
 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 {
 {
+   Item::Failed(Message,Cnf);
+
    size_t const nextExt = CompressionExtensions.find(' ');
    size_t const nextExt = CompressionExtensions.find(' ');
    if (nextExt != std::string::npos)
    if (nextExt != std::string::npos)
    {
    {
@@ -1530,8 +1547,6 @@ void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
       return;
       return;
    }
    }
 
 
-   Item::Failed(Message,Cnf);
-
    // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
    // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
    if (Cnf->LocalOnly == true ||
    if (Cnf->LocalOnly == true ||
        StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
        StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
@@ -1563,16 +1578,9 @@ void pkgAcqMetaBase::AbortTransaction()
       if ((*I)->Status == pkgAcquire::Item::StatIdle)
       if ((*I)->Status == pkgAcquire::Item::StatIdle)
          (*I)->Status = pkgAcquire::Item::StatDone;
          (*I)->Status = pkgAcquire::Item::StatDone;
 
 
-      // kill failed files in partial
-      if ((*I)->Status == pkgAcquire::Item::StatError)
-      {
-         std::string const PartialFile = GetPartialFileName(flNotDir((*I)->DestFile));
-         if(FileExists(PartialFile))
-            Rename(PartialFile, PartialFile + ".FAILED");
-      }
-      // fix permissions for existing files which were part of a reverify
-      // like InRelease files or files in partial we might work with next time
-      else if (FileExists((*I)->DestFile))
+      // reverify might act on a file outside of partial
+      // (as it itself is good, but needed to verify others, like Release)
+      if ((*I)->DestFile == (*I)->PartialFile && RealFileExists((*I)->DestFile))
 	 ChangeOwnerAndPermissionOfFile("AbortTransaction", (*I)->DestFile.c_str(), "root", "root", 0644);
 	 ChangeOwnerAndPermissionOfFile("AbortTransaction", (*I)->DestFile.c_str(), "root", "root", 0644);
    }
    }
    Transaction.clear();
    Transaction.clear();
@@ -1608,8 +1616,6 @@ void pkgAcqMetaBase::CommitTransaction()
 	       << (*I)->DescURI() << std::endl;
 	       << (*I)->DescURI() << std::endl;
 
 
 	 Rename((*I)->PartialFile, (*I)->DestFile);
 	 Rename((*I)->PartialFile, (*I)->DestFile);
-	 ChangeOwnerAndPermissionOfFile("CommitTransaction", (*I)->DestFile.c_str(), "root", "root", 0644);
-
       } else {
       } else {
          if(_config->FindB("Debug::Acquire::Transaction", false) == true)
          if(_config->FindB("Debug::Acquire::Transaction", false) == true)
             std::clog << "rm "
             std::clog << "rm "
@@ -1758,16 +1764,17 @@ void pkgAcqMetaSig::Done(string Message,unsigned long long Size,
 									/*}}}*/
 									/*}}}*/
 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
 {
 {
-   string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
+   Item::Failed(Message,Cnf);
 
 
    // check if we need to fail at this point 
    // check if we need to fail at this point 
    if (AuthPass == true && CheckStopAuthentication(RealURI, Message))
    if (AuthPass == true && CheckStopAuthentication(RealURI, Message))
          return;
          return;
 
 
    // FIXME: meh, this is not really elegant
    // FIXME: meh, this is not really elegant
-   string InReleaseURI = RealURI.replace(RealURI.rfind("Release.gpg"), 12,
+   string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
+   string const InReleaseURI = RealURI.replace(RealURI.rfind("Release.gpg"), 12,
                                          "InRelease");
                                          "InRelease");
-   string FinalInRelease = _config->FindDir("Dir::State::lists") + URItoFileName(InReleaseURI);
+   string const FinalInRelease = _config->FindDir("Dir::State::lists") + URItoFileName(InReleaseURI);
 
 
    if (RealFileExists(Final) || RealFileExists(FinalInRelease))
    if (RealFileExists(Final) || RealFileExists(FinalInRelease))
    {
    {
@@ -1782,7 +1789,7 @@ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
          _error->Warning(_("This is normally not allowed, but the option "
          _error->Warning(_("This is normally not allowed, but the option "
                            "Acquire::AllowDowngradeToInsecureRepositories was "
                            "Acquire::AllowDowngradeToInsecureRepositories was "
                            "given to override it."));
                            "given to override it."));
-         
+         Status = StatDone;
       } else {
       } else {
          _error->Error("%s", downgrade_msg.c_str());
          _error->Error("%s", downgrade_msg.c_str());
          Rename(MetaIndexFile, MetaIndexFile+".FAILED");
          Rename(MetaIndexFile, MetaIndexFile+".FAILED");
@@ -1810,8 +1817,6 @@ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
       QueueIndexes(true);
       QueueIndexes(true);
    }
    }
 
 
-   Item::Failed(Message,Cnf);
-
    // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
    // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
    if (Cnf->LocalOnly == true ||
    if (Cnf->LocalOnly == true ||
        StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
        StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
@@ -2226,10 +2231,12 @@ string pkgAcqMetaClearSig::Custom600Headers() const
 									/*}}}*/
 									/*}}}*/
 // pkgAcqMetaClearSig::Done - We got a file                     	/*{{{*/
 // pkgAcqMetaClearSig::Done - We got a file                     	/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
-void pkgAcqMetaClearSig::Done(std::string Message,unsigned long long /*Size*/,
-                              HashStringList const &/*Hashes*/,
+void pkgAcqMetaClearSig::Done(std::string Message,unsigned long long Size,
+                              HashStringList const &Hashes,
                               pkgAcquire::MethodConfig *Cnf)
                               pkgAcquire::MethodConfig *Cnf)
 {
 {
+   Item::Done(Message, Size, Hashes, Cnf);
+
    // if we expect a ClearTextSignature (InRelase), ensure that
    // if we expect a ClearTextSignature (InRelase), ensure that
    // this is what we get and if not fail to queue a 
    // this is what we get and if not fail to queue a 
    // Release/Release.gpg, see #346386
    // Release/Release.gpg, see #346386
@@ -2567,7 +2574,6 @@ void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList
    string FinalFile = _config->FindDir("Dir::Cache::Archives");
    string FinalFile = _config->FindDir("Dir::Cache::Archives");
    FinalFile += flNotDir(StoreFilename);
    FinalFile += flNotDir(StoreFilename);
    Rename(DestFile,FinalFile);
    Rename(DestFile,FinalFile);
-   ChangeOwnerAndPermissionOfFile("pkgAcqArchive::Done", FinalFile.c_str(), "root", "root", 0644);
    StoreFilename = DestFile = FinalFile;
    StoreFilename = DestFile = FinalFile;
    Complete = true;
    Complete = true;
 }
 }
@@ -2577,8 +2583,8 @@ void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList
 /* Here we try other sources */
 /* Here we try other sources */
 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 {
 {
-   ErrorText = LookupTag(Message,"Message");
-   
+   Item::Failed(Message,Cnf);
+
    /* We don't really want to retry on failed media swaps, this prevents 
    /* We don't really want to retry on failed media swaps, this prevents 
       that. An interesting observation is that permanent failures are not
       that. An interesting observation is that permanent failures are not
       recorded. */
       recorded. */
@@ -2588,10 +2594,10 @@ void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
       // Vf = Version.FileList();
       // Vf = Version.FileList();
       while (Vf.end() == false) ++Vf;
       while (Vf.end() == false) ++Vf;
       StoreFilename = string();
       StoreFilename = string();
-      Item::Failed(Message,Cnf);
       return;
       return;
    }
    }
-   
+
+   Status = StatIdle;
    if (QueueNext() == false)
    if (QueueNext() == false)
    {
    {
       // This is the retry counter
       // This is the retry counter
@@ -2604,9 +2610,9 @@ void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 	 if (QueueNext() == true)
 	 if (QueueNext() == true)
 	    return;
 	    return;
       }
       }
-      
+
       StoreFilename = string();
       StoreFilename = string();
-      Item::Failed(Message,Cnf);
+      Status = StatError;
    }
    }
 }
 }
 									/*}}}*/
 									/*}}}*/
@@ -2726,7 +2732,12 @@ void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList cons
       // Symlink the file
       // Symlink the file
       if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
       if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
       {
       {
-	 ErrorText = "Link to " + DestFile + " failure ";
+	 _error->PushToStack();
+	 _error->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile.c_str());
+	 std::stringstream msg;
+	 _error->DumpErrors(msg);
+	 _error->RevertToStack();
+	 ErrorText = msg.str();
 	 Status = StatError;
 	 Status = StatError;
 	 Complete = false;
 	 Complete = false;
       }      
       }      
@@ -2738,19 +2749,19 @@ void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList cons
 /* Here we try other sources */
 /* Here we try other sources */
 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
 {
 {
-   ErrorText = LookupTag(Message,"Message");
-   
+   Item::Failed(Message,Cnf);
+
    // This is the retry counter
    // This is the retry counter
    if (Retries != 0 &&
    if (Retries != 0 &&
        Cnf->LocalOnly == false &&
        Cnf->LocalOnly == false &&
        StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
        StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
    {
    {
-      Retries--;
+      --Retries;
       QueueURI(Desc);
       QueueURI(Desc);
+      Status = StatIdle;
       return;
       return;
    }
    }
-   
-   Item::Failed(Message,Cnf);
+
 }
 }
 									/*}}}*/
 									/*}}}*/
 // AcqIndex::Custom600Headers - Insert custom request headers		/*{{{*/
 // AcqIndex::Custom600Headers - Insert custom request headers		/*{{{*/

+ 1 - 0
apt-pkg/acquire-item.h

@@ -323,6 +323,7 @@ class pkgAcquire::Item : public WeakPointable
       InvalidFormat,
       InvalidFormat,
       SignatureError,
       SignatureError,
       NotClearsigned,
       NotClearsigned,
+      MaximumSizeExceeded
    };
    };
 
 
    /** \brief Rename failed file and set error
    /** \brief Rename failed file and set error

+ 25 - 15
apt-pkg/acquire.cc

@@ -54,23 +54,38 @@ pkgAcquire::pkgAcquire() : LockFD(-1), Queues(0), Workers(0), Configs(0), Log(NU
 			   Debug(_config->FindB("Debug::pkgAcquire",false)),
 			   Debug(_config->FindB("Debug::pkgAcquire",false)),
 			   Running(false)
 			   Running(false)
 {
 {
-   string const Mode = _config->Find("Acquire::Queue-Mode","host");
-   if (strcasecmp(Mode.c_str(),"host") == 0)
-      QueueMode = QueueHost;
-   if (strcasecmp(Mode.c_str(),"access") == 0)
-      QueueMode = QueueAccess;
+   Initialize();
 }
 }
 pkgAcquire::pkgAcquire(pkgAcquireStatus *Progress) : LockFD(-1), Queues(0), Workers(0),
 pkgAcquire::pkgAcquire(pkgAcquireStatus *Progress) : LockFD(-1), Queues(0), Workers(0),
 			   Configs(0), Log(NULL), ToFetch(0),
 			   Configs(0), Log(NULL), ToFetch(0),
 			   Debug(_config->FindB("Debug::pkgAcquire",false)),
 			   Debug(_config->FindB("Debug::pkgAcquire",false)),
 			   Running(false)
 			   Running(false)
+{
+   Initialize();
+   SetLog(Progress);
+}
+void pkgAcquire::Initialize()
 {
 {
    string const Mode = _config->Find("Acquire::Queue-Mode","host");
    string const Mode = _config->Find("Acquire::Queue-Mode","host");
    if (strcasecmp(Mode.c_str(),"host") == 0)
    if (strcasecmp(Mode.c_str(),"host") == 0)
       QueueMode = QueueHost;
       QueueMode = QueueHost;
    if (strcasecmp(Mode.c_str(),"access") == 0)
    if (strcasecmp(Mode.c_str(),"access") == 0)
       QueueMode = QueueAccess;
       QueueMode = QueueAccess;
-   SetLog(Progress);
+
+   // chown the auth.conf file as it will be accessed by our methods
+   std::string const SandboxUser = _config->Find("APT::Sandbox::User");
+   if (getuid() == 0 && SandboxUser.empty() == false) // if we aren't root, we can't chown, so don't try it
+   {
+      struct passwd const * const pw = getpwnam(SandboxUser.c_str());
+      struct group const * const gr = getgrnam("root");
+      if (pw != NULL && gr != NULL)
+      {
+	 std::string const AuthConf = _config->FindFile("Dir::Etc::netrc");
+	 if(AuthConf.empty() == false && RealFileExists(AuthConf) &&
+	       chown(AuthConf.c_str(), pw->pw_uid, gr->gr_gid) != 0)
+	    _error->WarningE("SetupAPTPartialDirectory", "chown to %s:root of file %s failed", SandboxUser.c_str(), AuthConf.c_str());
+      }
+   }
 }
 }
 									/*}}}*/
 									/*}}}*/
 // Acquire::GetLock - lock directory and prepare for action		/*{{{*/
 // Acquire::GetLock - lock directory and prepare for action		/*{{{*/
@@ -81,21 +96,16 @@ static bool SetupAPTPartialDirectory(std::string const &grand, std::string const
 	 CreateAPTDirectoryIfNeeded(parent, partial) == false)
 	 CreateAPTDirectoryIfNeeded(parent, partial) == false)
       return false;
       return false;
 
 
-   if (getuid() == 0) // if we aren't root, we can't chown, so don't try it
+   std::string const SandboxUser = _config->Find("APT::Sandbox::User");
+   if (getuid() == 0 && SandboxUser.empty() == false) // if we aren't root, we can't chown, so don't try it
    {
    {
-      std::string SandboxUser = _config->Find("APT::Sandbox::User");
-      struct passwd *pw = getpwnam(SandboxUser.c_str());
-      struct group *gr = getgrnam("root");
+      struct passwd const * const pw = getpwnam(SandboxUser.c_str());
+      struct group const * const gr = getgrnam("root");
       if (pw != NULL && gr != NULL)
       if (pw != NULL && gr != NULL)
       {
       {
          // chown the partial dir
          // chown the partial dir
          if(chown(partial.c_str(), pw->pw_uid, gr->gr_gid) != 0)
          if(chown(partial.c_str(), pw->pw_uid, gr->gr_gid) != 0)
             _error->WarningE("SetupAPTPartialDirectory", "chown to %s:root of directory %s failed", SandboxUser.c_str(), partial.c_str());
             _error->WarningE("SetupAPTPartialDirectory", "chown to %s:root of directory %s failed", SandboxUser.c_str(), partial.c_str());
-         // chown the auth.conf file
-         std::string const AuthConf = _config->FindFile("Dir::Etc::netrc");
-	 if(AuthConf.empty() == false && RealFileExists(AuthConf) &&
-	       chown(AuthConf.c_str(), pw->pw_uid, gr->gr_gid) != 0)
-	    _error->WarningE("SetupAPTPartialDirectory", "chown to %s:root of file %s failed", SandboxUser.c_str(), AuthConf.c_str());
       }
       }
    }
    }
    if (chmod(partial.c_str(), 0700) != 0)
    if (chmod(partial.c_str(), 0700) != 0)

+ 2 - 0
apt-pkg/acquire.h

@@ -378,6 +378,8 @@ class pkgAcquire
     */
     */
    virtual ~pkgAcquire();
    virtual ~pkgAcquire();
 
 
+   private:
+   APT_HIDDEN void Initialize();
 };
 };
 
 
 /** \brief Represents a single download source from which an item
 /** \brief Represents a single download source from which an item

+ 9 - 2
apt-private/private-download.cc

@@ -49,16 +49,23 @@ bool CheckDropPrivsMustBeDisabled(pkgAcquire &Fetcher)			/*{{{*/
    for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin();
    for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin();
 	I != Fetcher.ItemsEnd() && res == true; ++I)
 	I != Fetcher.ItemsEnd() && res == true; ++I)
    {
    {
-      int fd = open((*I)->DestFile.c_str(), O_CREAT | O_RDWR, 0600);
+      if ((*I)->DestFile.empty())
+	 continue;
+      // we assume that an existing (partial) file means that we have sufficient rights
+      if (RealFileExists((*I)->DestFile))
+	 continue;
+      int fd = open((*I)->DestFile.c_str(), O_CREAT | O_EXCL | O_RDWR, 0600);
       if (fd < 0)
       if (fd < 0)
       {
       {
 	 res = false;
 	 res = false;
 	 std::string msg;
 	 std::string msg;
 	 strprintf(msg, _("Can't drop privileges for downloading as file '%s' couldn't be accessed by user '%s'."),
 	 strprintf(msg, _("Can't drop privileges for downloading as file '%s' couldn't be accessed by user '%s'."),
 	       (*I)->DestFile.c_str(), SandboxUser.c_str());
 	       (*I)->DestFile.c_str(), SandboxUser.c_str());
-	 c0out << msg << std::endl;
+	 std::cerr << "W: " << msg << std::endl;
 	 _config->Set("APT::Sandbox::User", "");
 	 _config->Set("APT::Sandbox::User", "");
+	 break;
       }
       }
+      unlink((*I)->DestFile.c_str());
       close(fd);
       close(fd);
    }
    }
 
 

+ 27 - 6
cmdline/apt-get.cc

@@ -80,6 +80,9 @@
 #include <sys/stat.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
 #include <algorithm>
 #include <algorithm>
 #include <fstream>
 #include <fstream>
 #include <iostream>
 #include <iostream>
@@ -659,6 +662,8 @@ static bool DoDownload(CommandLine &CmdL)
 	 Ver != verset.end(); ++Ver, ++i)
 	 Ver != verset.end(); ++Ver, ++i)
    {
    {
       pkgAcquire::Item *I = new pkgAcqArchive(&Fetcher, SrcList, &Recs, *Ver, storefile[i]);
       pkgAcquire::Item *I = new pkgAcqArchive(&Fetcher, SrcList, &Recs, *Ver, storefile[i]);
+      if (storefile[i].empty())
+	 continue;
       std::string const filename = cwd + flNotDir(storefile[i]);
       std::string const filename = cwd + flNotDir(storefile[i]);
       storefile[i].assign(filename);
       storefile[i].assign(filename);
       I->DestFile.assign(filename);
       I->DestFile.assign(filename);
@@ -1481,7 +1486,10 @@ static bool DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher,
                           "http://packages.debian.org/changelogs");
                           "http://packages.debian.org/changelogs");
    string const path = GetChangelogPath(CacheFile, Ver);
    string const path = GetChangelogPath(CacheFile, Ver);
    string changelog_uri;
    string changelog_uri;
-   strprintf(changelog_uri, "%s/%s/changelog", server.c_str(), path.c_str());
+   if (APT::String::Endswith(server, "/") == true)
+      strprintf(changelog_uri, "%s%s/changelog", server.c_str(), path.c_str());
+   else
+      strprintf(changelog_uri, "%s/%s/changelog", server.c_str(), path.c_str());
    if (_config->FindB("APT::Get::Print-URIs", false) == true)
    if (_config->FindB("APT::Get::Print-URIs", false) == true)
    {
    {
       std::cout << '\'' << changelog_uri << '\'' << std::endl;
       std::cout << '\'' << changelog_uri << '\'' << std::endl;
@@ -1492,7 +1500,7 @@ static bool DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher,
    string descr;
    string descr;
    strprintf(descr, _("Changelog for %s (%s)"), Pkg.Name(), changelog_uri.c_str());
    strprintf(descr, _("Changelog for %s (%s)"), Pkg.Name(), changelog_uri.c_str());
    // queue it
    // queue it
-   new pkgAcqFile(&Fetcher, changelog_uri, "", 0, descr, Pkg.Name(), "ignored", targetfile);
+   pkgAcquire::Item const * itm = new pkgAcqFile(&Fetcher, changelog_uri, "", 0, descr, Pkg.Name(), "ignored", targetfile);
 
 
    // Disable drop-privs if "_apt" can not write to the target dir
    // Disable drop-privs if "_apt" can not write to the target dir
    CheckDropPrivsMustBeDisabled(Fetcher);
    CheckDropPrivsMustBeDisabled(Fetcher);
@@ -1500,18 +1508,18 @@ static bool DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher,
    // try downloading it, if that fails, try third-party-changelogs location
    // try downloading it, if that fails, try third-party-changelogs location
    // FIXME: Fetcher.Run() is "Continue" even if I get a 404?!?
    // FIXME: Fetcher.Run() is "Continue" even if I get a 404?!?
    Fetcher.Run();
    Fetcher.Run();
-   if (!FileExists(targetfile))
+   if (itm->Status != pkgAcquire::Item::StatDone)
    {
    {
       string third_party_uri;
       string third_party_uri;
       if (GuessThirdPartyChangelogUri(CacheFile, Ver, third_party_uri))
       if (GuessThirdPartyChangelogUri(CacheFile, Ver, third_party_uri))
       {
       {
          strprintf(descr, _("Changelog for %s (%s)"), Pkg.Name(), third_party_uri.c_str());
          strprintf(descr, _("Changelog for %s (%s)"), Pkg.Name(), third_party_uri.c_str());
-         new pkgAcqFile(&Fetcher, third_party_uri, "", 0, descr, Pkg.Name(), "ignored", targetfile);
+         itm = new pkgAcqFile(&Fetcher, third_party_uri, "", 0, descr, Pkg.Name(), "ignored", targetfile);
          Fetcher.Run();
          Fetcher.Run();
       }
       }
    }
    }
 
 
-   if (FileExists(targetfile))
+   if (itm->Status == pkgAcquire::Item::StatDone)
       return true;
       return true;
 
 
    // error
    // error
@@ -1557,6 +1565,19 @@ static bool DoChangelog(CommandLine &CmdL)
       tmpdir = mkdtemp(tmpname);
       tmpdir = mkdtemp(tmpname);
       if (tmpdir == NULL)
       if (tmpdir == NULL)
 	 return _error->Errno("mkdtemp", "mkdtemp failed");
 	 return _error->Errno("mkdtemp", "mkdtemp failed");
+
+      std::string const SandboxUser = _config->Find("APT::Sandbox::User");
+      if (getuid() == 0 && SandboxUser.empty() == false) // if we aren't root, we can't chown, so don't try it
+      {
+	 struct passwd const * const pw = getpwnam(SandboxUser.c_str());
+	 struct group const * const gr = getgrnam("root");
+	 if (pw != NULL && gr != NULL)
+	 {
+	    // chown the tmp dir directory we use to the sandbox user
+	    if(chown(tmpdir, pw->pw_uid, gr->gr_gid) != 0)
+	       _error->WarningE("DoChangelog", "chown to %s:%s of directory %s failed", SandboxUser.c_str(), "root", tmpdir);
+	 }
+      }
    }
    }
 
 
    for (APT::VersionList::const_iterator Ver = verset.begin(); 
    for (APT::VersionList::const_iterator Ver = verset.begin(); 
@@ -1572,7 +1593,7 @@ static bool DoChangelog(CommandLine &CmdL)
       {
       {
          DisplayFileInPager(changelogfile);
          DisplayFileInPager(changelogfile);
          // cleanup temp file
          // cleanup temp file
-         unlink(changelogfile.c_str());
+	 unlink(changelogfile.c_str());
       }
       }
    }
    }
    // clenaup tmp dir
    // clenaup tmp dir

+ 16 - 14
test/integration/framework

@@ -32,7 +32,7 @@ msgprintf() {
 		printf "$START " "$1"
 		printf "$START " "$1"
 		shift
 		shift
 		while [ -n "$1" ]; do
 		while [ -n "$1" ]; do
-			printf "$MIDDLE " "$(echo "$1" | sed -e 's#^apt\([cgfs]\)#apt-\1#')"
+			printf "$MIDDLE " "$(echo "$1" | sed -e 's#^apt\([cfghs]\)#apt-\1#')"
 			shift
 			shift
 		done
 		done
 	fi
 	fi
@@ -57,9 +57,9 @@ msgskip() {
 msgfail() {
 msgfail() {
 	if [ $# -gt 0 ]; then printf "${CFAIL}FAIL: $*${CNORMAL}\n" >&2;
 	if [ $# -gt 0 ]; then printf "${CFAIL}FAIL: $*${CNORMAL}\n" >&2;
 	else printf "${CFAIL}FAIL${CNORMAL}\n" >&2; fi
 	else printf "${CFAIL}FAIL${CNORMAL}\n" >&2; fi
-        if [ -n "$APT_DEBUG_TESTS" ]; then
-            bash
-        fi
+	if [ -n "$APT_DEBUG_TESTS" ]; then
+		$SHELL
+	fi
 	EXIT_CODE=$((EXIT_CODE+1));
 	EXIT_CODE=$((EXIT_CODE+1));
 }
 }
 
 
@@ -184,10 +184,12 @@ setupenvironment() {
 	addtrap "cd /; rm -rf $TMPWORKINGDIRECTORY;"
 	addtrap "cd /; rm -rf $TMPWORKINGDIRECTORY;"
 	msgninfo "Preparing environment for ${CCMD}$(basename $0)${CINFO} in ${TMPWORKINGDIRECTORY}… "
 	msgninfo "Preparing environment for ${CCMD}$(basename $0)${CINFO} in ${TMPWORKINGDIRECTORY}… "
 
 
+	mkdir -m 700 "${TMPWORKINGDIRECTORY}/downloaded"
 	if [ "$(id -u)" = '0' ]; then
 	if [ "$(id -u)" = '0' ]; then
 		# relax permissions so that running as root with user switching works
 		# relax permissions so that running as root with user switching works
 		umask 022
 		umask 022
-		chmod o+rx "$TMPWORKINGDIRECTORY"
+		chmod 711 "$TMPWORKINGDIRECTORY"
+		chown _apt:root "${TMPWORKINGDIRECTORY}/downloaded"
 	fi
 	fi
 
 
 	TESTDIRECTORY=$(readlink -f $(dirname $0))
 	TESTDIRECTORY=$(readlink -f $(dirname $0))
@@ -1061,7 +1063,7 @@ downloadfile() {
 }
 }
 
 
 checkdiff() {
 checkdiff() {
-	local DIFFTEXT="$(command diff -u "$@" | sed -e '/^---/ d' -e '/^+++/ d' -e '/^@@/ d')"
+	local DIFFTEXT="$(command diff -u "$@" 2>&1 | sed -e '/^---/ d' -e '/^+++/ d' -e '/^@@/ d')"
 	if [ -n "$DIFFTEXT" ]; then
 	if [ -n "$DIFFTEXT" ]; then
 		echo >&2
 		echo >&2
 		echo >&2 "$DIFFTEXT"
 		echo >&2 "$DIFFTEXT"
@@ -1307,7 +1309,7 @@ testaccessrights() {
 
 
 testwebserverlaststatuscode() {
 testwebserverlaststatuscode() {
 	local DOWNLOG='rootdir/tmp/webserverstatus-testfile.log'
 	local DOWNLOG='rootdir/tmp/webserverstatus-testfile.log'
-	local STATUS='rootdir/tmp/webserverstatus-statusfile.log'
+	local STATUS='downloaded/webserverstatus-statusfile.log'
 	rm -f "$DOWNLOG" "$STATUS"
 	rm -f "$DOWNLOG" "$STATUS"
 	msgtest 'Test last status code from the webserver was' "$1"
 	msgtest 'Test last status code from the webserver was' "$1"
 	downloadfile "http://localhost:8080/_config/find/aptwebserver::last-status-code" "$STATUS" > "$DOWNLOG"
 	downloadfile "http://localhost:8080/_config/find/aptwebserver::last-status-code" "$STATUS" > "$DOWNLOG"
@@ -1353,20 +1355,20 @@ aptautotest() {
 		shift 3
 		shift 3
 		# save and restore the *.output files from other tests
 		# save and restore the *.output files from other tests
 		# as we might otherwise override them in these automatic tests
 		# as we might otherwise override them in these automatic tests
-		rm -rf rootdir/tmp-before
-		mv rootdir/tmp rootdir/tmp-before
-		mkdir rootdir/tmp
+		rm -rf ${TMPWORKINGDIRECTORY}/rootdir/tmp-before
+		mv ${TMPWORKINGDIRECTORY}/rootdir/tmp ${TMPWORKINGDIRECTORY}/rootdir/tmp-before
+		mkdir ${TMPWORKINGDIRECTORY}/rootdir/tmp
 		$AUTOTEST "$TESTCALL" "$@"
 		$AUTOTEST "$TESTCALL" "$@"
-		rm -rf rootdir/tmp-aptautotest
-		mv rootdir/tmp rootdir/tmp-aptautotest
-		mv rootdir/tmp-before rootdir/tmp
+		rm -rf ${TMPWORKINGDIRECTORY}/rootdir/tmp-aptautotest
+		mv ${TMPWORKINGDIRECTORY}/rootdir/tmp ${TMPWORKINGDIRECTORY}/rootdir/tmp-aptautotest
+		mv ${TMPWORKINGDIRECTORY}/rootdir/tmp-before ${TMPWORKINGDIRECTORY}/rootdir/tmp
 	fi
 	fi
 }
 }
 
 
 aptautotest_aptget_update() {
 aptautotest_aptget_update() {
 	if ! test -d "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists"; then return; fi
 	if ! test -d "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists"; then return; fi
 	# all copied files are properly chmodded
 	# all copied files are properly chmodded
-	find "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists" -maxdepth 1 -type f | while read file; do
+	for file in $(find "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists" -maxdepth 1 -type f); do
 		testfilestats "$file" '%U:%G:%a' '=' "${USER}:${USER}:644"
 		testfilestats "$file" '%U:%G:%a' '=' "${USER}:${USER}:644"
 	done
 	done
 }
 }

+ 2 - 0
test/integration/test-apt-cdrom

@@ -99,6 +99,7 @@ Conf testing:i386 (0.8.15 stable [i386])' aptget install testing:i386 -s
 	testsuccess aptget purge testing:i386 -y
 	testsuccess aptget purge testing:i386 -y
 	testdpkgnotinstalled testing:i386
 	testdpkgnotinstalled testing:i386
 
 
+	cd downloaded
 	rm -f testing_0.8.15_amd64.deb
 	rm -f testing_0.8.15_amd64.deb
 	testsuccess aptget download testing
 	testsuccess aptget download testing
 	testsuccess test -s testing_0.8.15_amd64.deb
 	testsuccess test -s testing_0.8.15_amd64.deb
@@ -108,6 +109,7 @@ Conf testing:i386 (0.8.15 stable [i386])' aptget install testing:i386 -s
 	testsuccess aptget source testing --dsc-only -d
 	testsuccess aptget source testing --dsc-only -d
 	testsuccess test -s testing_0.8.15.dsc
 	testsuccess test -s testing_0.8.15.dsc
 	rm -f testing_0.8.15.dsc
 	rm -f testing_0.8.15.dsc
+	cd - >/dev/null
 }
 }
 testcdromusage
 testcdromusage
 
 

+ 15 - 10
test/integration/test-apt-get-changelog

@@ -21,23 +21,28 @@ chmod -R -w rootdir/var/cache/apt/archives
 
 
 echo 'Apt::Changelogs::Server "http://localhost:8080/";' > rootdir/etc/apt/apt.conf.d/changelog.conf
 echo 'Apt::Changelogs::Server "http://localhost:8080/";' > rootdir/etc/apt/apt.conf.d/changelog.conf
 
 
-testequal "'http://localhost:8080//pool/apt_1.0/changelog'" aptget changelog apt --print-uris
+testequal "'http://localhost:8080/pool/apt_1.0/changelog'" aptget changelog apt --print-uris
 
 
-testequal "'http://localhost:8080//pool/apt_1.0/changelog'
-'http://localhost:8080//pool/apt_1.0/changelog'" aptget changelog apt apt --print-uris
+testequal "'http://localhost:8080/pool/apt_1.0/changelog'
+'http://localhost:8080/pool/apt_1.0/changelog'" aptget changelog apt apt --print-uris
+
+cd downloaded
 
 
 testsuccess aptget changelog apt -qq
 testsuccess aptget changelog apt -qq
-testfileequal 'rootdir/tmp/testsuccess.output' "$(cat aptarchive/pool/apt_1.0/changelog)"
+testfileequal '../rootdir/tmp/testsuccess.output' "$(cat ../aptarchive/pool/apt_1.0/changelog)"
 
 
 testsuccess aptget changelog apt -d
 testsuccess aptget changelog apt -d
-testfileequal 'apt.changelog' "$(cat  aptarchive/pool/apt_1.0/changelog)"
-rm -f apt.changelog aptarchive/pool/apt_1.0/changelog
+testfileequal 'apt.changelog' "$(cat  ../aptarchive/pool/apt_1.0/changelog)"
+testfilestats 'apt.changelog' '%U:%G:%a' '=' "${USER}:${USER}:644"
+rm -f apt.changelog ../aptarchive/pool/apt_1.0/changelog
 
 
-testequal "$(cat aptarchive/pool/apt_1.0.changelog)" aptget changelog apt \
+testequal "$(cat ../aptarchive/pool/apt_1.0.changelog)" aptget changelog apt \
 	-qq -o APT::Changelogs::Server='http://not-on-the-main-server:8080/'
 	-qq -o APT::Changelogs::Server='http://not-on-the-main-server:8080/'
 
 
 testsuccess aptget changelog apt -d
 testsuccess aptget changelog apt -d
-testfileequal 'apt.changelog' "$(cat aptarchive/pool/apt_1.0.changelog)"
-rm -f apt.changelog aptarchive/pool/apt_1.0.changelog
+testfileequal 'apt.changelog' "$(cat ../aptarchive/pool/apt_1.0.changelog)"
+testfilestats 'apt.changelog' '%U:%G:%a' '=' "${USER}:${USER}:644"
+rm -f apt.changelog ../aptarchive/pool/apt_1.0.changelog
 
 
-testequal 'E: changelog download failed' aptget changelog apt -qq -o APT::Changelogs::Server='http://not-on-the-main-server:8080/'
+testequal 'E: changelog download failed' aptget changelog apt -qq -d -o APT::Changelogs::Server='http://not-on-the-main-server:8080/'
+testfailure test -e apt.changelog

+ 11 - 1
test/integration/test-apt-get-download

@@ -44,7 +44,10 @@ testdownload() {
 }
 }
 
 
 # normal case as "root"
 # normal case as "root"
+OLDPWD="$(pwd)"
+cd downloaded
 testdownload apt_2.0_all.deb apt
 testdownload apt_2.0_all.deb apt
+cd "$OLDPWD"
 
 
 # simulate normal user with non-existent root-owned directories
 # simulate normal user with non-existent root-owned directories
 rm -rf rootdir/var/cache/apt/archives/
 rm -rf rootdir/var/cache/apt/archives/
@@ -52,14 +55,18 @@ mkdir rootdir/var/cache/apt/archives/
 addtrap 'prefix' "chmod -f -R +w $PWD/rootdir/var/cache/apt/archives || true;"
 addtrap 'prefix' "chmod -f -R +w $PWD/rootdir/var/cache/apt/archives || true;"
 chmod -R -w rootdir/var/cache/apt/archives
 chmod -R -w rootdir/var/cache/apt/archives
 
 
+OLDPWD="$(pwd)"
+cd downloaded
+
 # normal case(es)
 # normal case(es)
 testdownload apt_1.0_all.deb apt stable
 testdownload apt_1.0_all.deb apt stable
 testdownload apt_2.0_all.deb apt
 testdownload apt_2.0_all.deb apt
 
 
-DEBFILE="$(readlink -f aptarchive)/pool/apt_2.0_all.deb"
+DEBFILE="$(readlink -f ../aptarchive)/pool/apt_2.0_all.deb"
 testequal "'file://${DEBFILE}' apt_2.0_all.deb $(stat -c%s $DEBFILE) SHA512:$(sha512sum $DEBFILE | cut -d' ' -f 1)" aptget download apt --print-uris
 testequal "'file://${DEBFILE}' apt_2.0_all.deb $(stat -c%s $DEBFILE) SHA512:$(sha512sum $DEBFILE | cut -d' ' -f 1)" aptget download apt --print-uris
 
 
 # deb:677887
 # deb:677887
+testequal "E: Can't find a source to download version '1.0' of 'vrms:i386'" aptget download vrms --print-uris
 testequal "E: Can't find a source to download version '1.0' of 'vrms:i386'" aptget download vrms
 testequal "E: Can't find a source to download version '1.0' of 'vrms:i386'" aptget download vrms
 
 
 # deb:736962
 # deb:736962
@@ -74,6 +81,7 @@ testsuccess aptget download apt apt apt/unstable apt=2.0
 testsuccess test -s apt_2.0_all.deb
 testsuccess test -s apt_2.0_all.deb
 
 
 # restore "root" rights
 # restore "root" rights
+cd "$OLDPWD"
 chmod -f -R +w $PWD/rootdir/var/cache/apt/archives
 chmod -f -R +w $PWD/rootdir/var/cache/apt/archives
 rm -rf rootdir/var/cache/apt/archives/
 rm -rf rootdir/var/cache/apt/archives/
 
 
@@ -86,5 +94,7 @@ testsuccess aptget install -d apt
 testsuccess test -s rootdir/var/cache/apt/archives/apt_2.0_all.deb
 testsuccess test -s rootdir/var/cache/apt/archives/apt_2.0_all.deb
 testaccessrights 'aptarchive/pool/apt_2.0_all.deb' '644'
 testaccessrights 'aptarchive/pool/apt_2.0_all.deb' '644'
 mv aptarchive/pool/apt_2.0_all.deb aptarchive/pool/apt_2.0_all.deb.gone
 mv aptarchive/pool/apt_2.0_all.deb aptarchive/pool/apt_2.0_all.deb.gone
+cd downloaded
 testdownload apt_2.0_all.deb apt
 testdownload apt_2.0_all.deb apt
+cd "$OLDPWD"
 mv aptarchive/pool/apt_2.0_all.deb.gone aptarchive/pool/apt_2.0_all.deb
 mv aptarchive/pool/apt_2.0_all.deb.gone aptarchive/pool/apt_2.0_all.deb

+ 5 - 2
test/integration/test-apt-get-source-authenticated

@@ -25,7 +25,10 @@ testwarning aptget update --allow-insecure-repositories
 
 
 # this all should fail
 # this all should fail
 testfailure aptget install -y foo
 testfailure aptget install -y foo
+cd downloaded
 testfailure aptget source foo
 testfailure aptget source foo
-
+testfailure test -e foo_2.0.dsc
 # allow overriding the warning
 # allow overriding the warning
-testsuccess aptget source --allow-unauthenticated foo
+testsuccess aptget source --allow-unauthenticated foo -o Debug::pkgAcquire::Worker=1
+testsuccess test -s foo_2.0.dsc -a -L foo_2.0.dsc
+testaccessrights 'foo_2.0.dsc' '777'

+ 11 - 10
test/integration/test-apt-helper

@@ -13,23 +13,23 @@ test_apt_helper_download() {
     echo 'foo' > aptarchive/foo
     echo 'foo' > aptarchive/foo
 
 
     msgtest 'apt-file download-file' 'md5sum'
     msgtest 'apt-file download-file' 'md5sum'
-    apthelper -qq download-file http://localhost:8080/foo foo2 MD5Sum:d3b07384d113edec49eaa6238ad5ff00 && msgpass || msgfail
-    testfileequal foo2 'foo'
+    apthelper -qq download-file http://localhost:8080/foo downloaded/foo2 MD5Sum:d3b07384d113edec49eaa6238ad5ff00 && msgpass || msgfail
+    testfileequal downloaded/foo2 'foo'
 
 
     msgtest 'apt-file download-file' 'sha1'
     msgtest 'apt-file download-file' 'sha1'
-    apthelper -qq download-file http://localhost:8080/foo foo1 SHA1:f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 && msgpass || msgfail
-    testfileequal foo1 'foo'
+    apthelper -qq download-file http://localhost:8080/foo downloaded/foo1 SHA1:f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 && msgpass || msgfail
+    testfileequal downloaded/foo1 'foo'
 
 
     msgtest 'apt-file download-file' 'sha256'
     msgtest 'apt-file download-file' 'sha256'
-    apthelper -qq download-file http://localhost:8080/foo foo3 SHA256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c && msgpass || msgfail
-    testfileequal foo3 'foo'
+    apthelper -qq download-file http://localhost:8080/foo downloaded/foo3 SHA256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c && msgpass || msgfail
+    testfileequal downloaded/foo3 'foo'
 
 
     msgtest 'apt-file download-file' 'no-hash'
     msgtest 'apt-file download-file' 'no-hash'
-    apthelper -qq download-file http://localhost:8080/foo foo4 && msgpass || msgfail
-    testfileequal foo4 'foo'
+    apthelper -qq download-file http://localhost:8080/foo downloaded/foo4 && msgpass || msgfail
+    testfileequal downloaded/foo4 'foo'
     
     
     msgtest 'apt-file download-file' 'wrong hash'
     msgtest 'apt-file download-file' 'wrong hash'
-    if ! apthelper -qq download-file http://localhost:8080/foo foo5 MD5Sum:aabbcc 2>&1 2> download.stderr; then
+    if ! apthelper -qq download-file http://localhost:8080/foo downloaded/foo5 MD5Sum:aabbcc 2>&1 2> download.stderr; then
         msgpass
         msgpass
     else
     else
         msgfail
         msgfail
@@ -37,7 +37,8 @@ test_apt_helper_download() {
     testfileequal download.stderr 'E: Failed to fetch http://localhost:8080/foo  Hash Sum mismatch
     testfileequal download.stderr 'E: Failed to fetch http://localhost:8080/foo  Hash Sum mismatch
 
 
 E: Download Failed'
 E: Download Failed'
-    testfileequal foo5.FAILED 'foo'
+    testfileequal downloaded/foo5.FAILED 'foo'
+    testfailure test -e downloaded/foo5
 }
 }
 
 
 test_apt_helper_detect_proxy() {
 test_apt_helper_detect_proxy() {

+ 1 - 1
test/integration/test-apt-update-nofallback

@@ -195,7 +195,7 @@ test_release_gpg_to_invalid_release_release_gpg()
 
 
     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 Release.gpg: The following signatures were invalid: BADSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org>
     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 Release.gpg: The following signatures were invalid: BADSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org>
 
 
-W: Failed to fetch file:${APTARCHIVE}/dists/unstable/Release.gpg  
+W: Failed to fetch file:${APTARCHIVE}/dists/unstable/Release.gpg  The following signatures were invalid: BADSIG 5A90D141DBAC8DAE Joe Sixpack (APT Testcases Dummy) <joe@example.org>
 
 
 W: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq
 W: Some index files failed to download. They have been ignored, or old ones used instead." aptget update -qq
 
 

+ 6 - 4
test/integration/test-bug-617690-allow-unauthenticated-makes-all-untrusted

@@ -24,6 +24,7 @@ testfilemissing() {
 
 
 testrun() {
 testrun() {
 	rm -rf rootdir/var/lib/apt
 	rm -rf rootdir/var/lib/apt
+	cd downloaded
 
 
 	if [ "$1" = 'trusted' ]; then
 	if [ "$1" = 'trusted' ]; then
 	        testsuccess aptget update
 	        testsuccess aptget update
@@ -43,18 +44,19 @@ testrun() {
 		testfileexists 'cool_1.0_i386.deb'
 		testfileexists 'cool_1.0_i386.deb'
 	fi
 	fi
 
 
-	mv aptarchive/pool/cool_1.0_i386.deb aptarchive/pool/cool_1.0_i386.deb.bak
-	echo 'this is not a good package' > aptarchive/pool/cool_1.0_i386.deb
+	mv ../aptarchive/pool/cool_1.0_i386.deb ../aptarchive/pool/cool_1.0_i386.deb.bak
+	echo 'this is not a good package' > ../aptarchive/pool/cool_1.0_i386.deb
 	testfailure aptget download cool
 	testfailure aptget download cool
 	testfilemissing cool_1.0_i386.deb
 	testfilemissing cool_1.0_i386.deb
 
 
 	testfailure aptget download cool --allow-unauthenticated  # unauthenticated doesn't mean unchecked
 	testfailure aptget download cool --allow-unauthenticated  # unauthenticated doesn't mean unchecked
 	testfilemissing cool_1.0_i386.deb
 	testfilemissing cool_1.0_i386.deb
 
 
-	rm -f aptarchive/pool/cool_1.0_i386.deb
-	mv aptarchive/pool/cool_1.0_i386.deb.bak aptarchive/pool/cool_1.0_i386.deb
+	rm -f ../aptarchive/pool/cool_1.0_i386.deb
+	mv ../aptarchive/pool/cool_1.0_i386.deb.bak ../aptarchive/pool/cool_1.0_i386.deb
 	testsuccess aptget download cool --allow-unauthenticated
 	testsuccess aptget download cool --allow-unauthenticated
 	testfileexists 'cool_1.0_i386.deb'
 	testfileexists 'cool_1.0_i386.deb'
+	cd - >/dev/null
 }
 }
 
 
 testrun 'trusted'
 testrun 'trusted'

+ 5 - 7
test/integration/test-bug-738785-switch-protocol

@@ -25,6 +25,7 @@ downloadfile 'http://localhost:8080/pool/apt_1.0/changelog' changelog >/dev/null
 echo 'Apt::Changelogs::Server "http://localhost:8080/redirectme";' > rootdir/etc/apt/apt.conf.d/changelog.conf
 echo 'Apt::Changelogs::Server "http://localhost:8080/redirectme";' > rootdir/etc/apt/apt.conf.d/changelog.conf
 testequal "'http://localhost:8080/redirectme/pool/apt_1.0/changelog'" aptget changelog apt --print-uris
 testequal "'http://localhost:8080/redirectme/pool/apt_1.0/changelog'" aptget changelog apt --print-uris
 
 
+cd downloaded
 testsuccess aptget changelog apt -d
 testsuccess aptget changelog apt -d
 testsuccess test -s apt.changelog
 testsuccess test -s apt.changelog
 rm -f apt.changelog
 rm -f apt.changelog
@@ -32,6 +33,7 @@ rm -f apt.changelog
 testsuccess aptget download apt
 testsuccess aptget download apt
 testsuccess test -s apt_1.0_all.deb
 testsuccess test -s apt_1.0_all.deb
 rm apt_1.0_all.deb
 rm apt_1.0_all.deb
+cd - >/dev/null
 
 
 testsuccess aptget install apt -y
 testsuccess aptget install apt -y
 testdpkginstalled 'apt'
 testdpkginstalled 'apt'
@@ -49,15 +51,11 @@ rm https
 cd - >/dev/null
 cd - >/dev/null
 echo "Dir::Bin::Methods \"${COPYMETHODS}\";" >> aptconfig.conf
 echo "Dir::Bin::Methods \"${COPYMETHODS}\";" >> aptconfig.conf
 
 
-if [ "$(id -u)" = '0' ]; then
-	testequal "Can't drop privileges for downloading as file '$(pwd)/apt_1.0_all.deb' couldn't be accessed by user '_apt'.
-E: The method driver $(pwd)/rootdir/usr/lib/apt/methods/https could not be found.
-N: Is the package apt-transport-https installed?" aptget download apt -q=0
-else
-	testequal "E: The method driver $(pwd)/rootdir/usr/lib/apt/methods/https could not be found.
+cd downloaded
+testequal "E: The method driver $(readlink -f './../')/rootdir/usr/lib/apt/methods/https could not be found.
 N: Is the package apt-transport-https installed?" aptget download apt -q=0
 N: Is the package apt-transport-https installed?" aptget download apt -q=0
-fi
 testsuccess test ! -e apt_1.0_all.deb
 testsuccess test ! -e apt_1.0_all.deb
+cd - >/dev/null
 
 
 # revert to all methods
 # revert to all methods
 rm -rf rootdir/$COPYMETHODS
 rm -rf rootdir/$COPYMETHODS

+ 6 - 2
test/integration/test-compressed-indexes

@@ -58,9 +58,11 @@ testrun() {
 		msgpass
 		msgpass
 	fi
 	fi
 	msgtest 'Check if package is downloadable'
 	msgtest 'Check if package is downloadable'
+	cd downloaded
 	testsuccess --nomsg aptget download testpkg
 	testsuccess --nomsg aptget download testpkg
 	msgtest '\tdeb file is present'; testsuccess --nomsg test -f testpkg_1.0_i386.deb
 	msgtest '\tdeb file is present'; testsuccess --nomsg test -f testpkg_1.0_i386.deb
 	rm testpkg_1.0_i386.deb
 	rm testpkg_1.0_i386.deb
+	cd - >/dev/null
 	testequal 'Reading package lists...
 	testequal 'Reading package lists...
 Building dependency tree...
 Building dependency tree...
 The following NEW packages will be installed:
 The following NEW packages will be installed:
@@ -79,10 +81,12 @@ Conf testpkg (1.0 unstable [i386])' aptget install testpkg -s
 	testequal "$GOODSHOWSRC" aptcache showsrc testpkg
 	testequal "$GOODSHOWSRC" aptcache showsrc testpkg
 	aptget clean
 	aptget clean
 	msgtest 'Check if the source is aptgetable'
 	msgtest 'Check if the source is aptgetable'
+	cd downloaded
 	testsuccess --nomsg aptget source testpkg
 	testsuccess --nomsg aptget source testpkg
-	msgtest '\tdsc file is present'; testsuccess --nomsg test -f testpkg_1.0.dsc
-	msgtest '\tdirectory is present'; testsuccess --nomsg test -d testpkg-1.0
+	testsuccess test -s testpkg_1.0.dsc
+	testsuccess test -d testpkg-1.0
 	rm -rf testpkg-1.0*
 	rm -rf testpkg-1.0*
+	cd - >/dev/null
 	testequal "$(aptcache show testpkg -o Acquire::Languages=none)
 	testequal "$(aptcache show testpkg -o Acquire::Languages=none)
 " aptcache dumpavail
 " aptcache dumpavail
 }
 }

+ 16 - 15
test/integration/test-partial-file-support

@@ -52,36 +52,37 @@ cp -a ${TESTDIR}/framework $TESTFILE
 
 
 testrun() {
 testrun() {
 	webserverconfig 'aptwebserver::support::range' 'true'
 	webserverconfig 'aptwebserver::support::range' 'true'
+	local DOWN='./downloaded/testfile'
 
 
-	copysource $TESTFILE 0 ./testfile
-	testdownloadfile 'no data' "${1}/testfile" './testfile' '='
+	copysource $TESTFILE 0 $DOWN
+	testdownloadfile 'no data' "${1}/testfile" "$DOWN" '='
 	testwebserverlaststatuscode '200' "$DOWNLOADLOG"
 	testwebserverlaststatuscode '200' "$DOWNLOADLOG"
 
 
-	copysource $TESTFILE 20 ./testfile
-	testdownloadfile 'valid partial data' "${1}/testfile" './testfile' '='
+	copysource $TESTFILE 20 $DOWN
+	testdownloadfile 'valid partial data' "${1}/testfile" "$DOWN" '='
 	testwebserverlaststatuscode '206' "$DOWNLOADLOG"
 	testwebserverlaststatuscode '206' "$DOWNLOADLOG"
 
 
-	copysource /dev/zero 20 ./testfile
-	testdownloadfile 'invalid partial data' "${1}/testfile" './testfile' '!='
+	copysource /dev/zero 20 $DOWN
+	testdownloadfile 'invalid partial data' "${1}/testfile" "$DOWN" '!='
 	testwebserverlaststatuscode '206' "$DOWNLOADLOG"
 	testwebserverlaststatuscode '206' "$DOWNLOADLOG"
 
 
-	copysource $TESTFILE 1M ./testfile
-	testdownloadfile 'completely downloaded file' "${1}/testfile" './testfile' '='
+	copysource $TESTFILE 1M $DOWN
+	testdownloadfile 'completely downloaded file' "${1}/testfile" "$DOWN" '='
 	testwebserverlaststatuscode '416' "$DOWNLOADLOG"
 	testwebserverlaststatuscode '416' "$DOWNLOADLOG"
 
 
-	copysource /dev/zero 1M ./testfile
-	testdownloadfile 'too-big partial file' "${1}/testfile" './testfile' '='
+	copysource /dev/zero 1M $DOWN
+	testdownloadfile 'too-big partial file' "${1}/testfile" "$DOWN" '='
 	testwebserverlaststatuscode '200' "$DOWNLOADLOG"
 	testwebserverlaststatuscode '200' "$DOWNLOADLOG"
 
 
-	copysource /dev/zero 20 ./testfile
-	touch ./testfile
-	testdownloadfile 'old data' "${1}/testfile" './testfile' '='
+	copysource /dev/zero 20 $DOWN
+	touch $DOWN
+	testdownloadfile 'old data' "${1}/testfile" "$DOWN" '='
 	testwebserverlaststatuscode '200' "$DOWNLOADLOG"
 	testwebserverlaststatuscode '200' "$DOWNLOADLOG"
 
 
 	webserverconfig 'aptwebserver::support::range' 'false'
 	webserverconfig 'aptwebserver::support::range' 'false'
 
 
-	copysource $TESTFILE 20 ./testfile
-	testdownloadfile 'no server support' "${1}/testfile" './testfile' '='
+	copysource $TESTFILE 20 $DOWN
+	testdownloadfile 'no server support' "${1}/testfile" "$DOWN" '='
 	testwebserverlaststatuscode '200' "$DOWNLOADLOG"
 	testwebserverlaststatuscode '200' "$DOWNLOADLOG"
 }
 }
 
 

+ 8 - 2
test/integration/test-sourceslist-trusted-options

@@ -20,6 +20,7 @@ foo/testing 2 amd64
 foo/stable 1 amd64
 foo/stable 1 amd64
 ' apt list foo -a
 ' apt list foo -a
 
 
+	cd downloaded
 	rm -f foo_1_amd64.deb foo_2_amd64.deb
 	rm -f foo_1_amd64.deb foo_2_amd64.deb
 	testsuccess aptget download foo "$@"
 	testsuccess aptget download foo "$@"
 	testsuccess test -s foo_1_amd64.deb -o -s foo_2_amd64.deb
 	testsuccess test -s foo_1_amd64.deb -o -s foo_2_amd64.deb
@@ -27,6 +28,7 @@ foo/stable 1 amd64
 	rm -f foo_1.dsc foo_2.dsc
 	rm -f foo_1.dsc foo_2.dsc
 	testsuccess aptget source foo --dsc-only -d "$@"
 	testsuccess aptget source foo --dsc-only -d "$@"
 	testsuccess test -s foo_1.dsc -o -s foo_2.dsc
 	testsuccess test -s foo_1.dsc -o -s foo_2.dsc
+	cd - >/dev/null
 }
 }
 
 
 everythingfails() {
 everythingfails() {
@@ -39,18 +41,22 @@ foo/stable 1 amd64
   foo
   foo
 E: Some packages could not be authenticated'
 E: Some packages could not be authenticated'
 
 
+	cd downloaded
 	rm -f foo_1_amd64.deb foo_2_amd64.deb
 	rm -f foo_1_amd64.deb foo_2_amd64.deb
 	testfailure aptget download foo "$@"
 	testfailure aptget download foo "$@"
-	testequal "$WARNING" tail -n 3 rootdir/tmp/testfailure.output
+	testequal "$WARNING" tail -n 3 ../rootdir/tmp/testfailure.output
 	testfailure test -s foo_1_amd64.deb -o -s foo_2_amd64.deb
 	testfailure test -s foo_1_amd64.deb -o -s foo_2_amd64.deb
 
 
 	rm -f foo_1.dsc foo_2.dsc
 	rm -f foo_1.dsc foo_2.dsc
 	testfailure aptget source foo --dsc-only -d "$@"
 	testfailure aptget source foo --dsc-only -d "$@"
-	testequal "$WARNING" tail -n 3 rootdir/tmp/testfailure.output
+	testequal "$WARNING" tail -n 3 ../rootdir/tmp/testfailure.output
 	testfailure test -s foo_1.dsc -o -s foo_2.dsc
 	testfailure test -s foo_1.dsc -o -s foo_2.dsc
+	cd - >/dev/null
 }
 }
 
 
 cp -a rootdir/etc/apt/sources.list.d/ rootdir/etc/apt/sources.list.d.bak/
 cp -a rootdir/etc/apt/sources.list.d/ rootdir/etc/apt/sources.list.d.bak/
+echo 'Debug::Acquire::Transaction "true";
+Debug::pkgAcquire::Worker "true";' > rootdir/etc/apt/apt.conf.d/00debugging
 
 
 aptgetupdate() {
 aptgetupdate() {
 	rm -rf rootdir/var/lib/apt/lists
 	rm -rf rootdir/var/lib/apt/lists

+ 2 - 0
test/integration/test-ubuntu-bug-1098738-apt-get-source-md5sum

@@ -141,6 +141,8 @@ setupaptarchive
 changetowebserver
 changetowebserver
 testsuccess aptget update
 testsuccess aptget update
 
 
+cd downloaded
+
 testok() {
 testok() {
 	rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz
 	rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz
 	testequal "Reading package lists...
 	testequal "Reading package lists...