Browse Source

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.
tags/debian/1.1.exp8
David Kalnischkies 7 years ago
parent
commit
03aa08472d
19 changed files with 229 additions and 141 deletions
  1. +61
    -50
      apt-pkg/acquire-item.cc
  2. +1
    -0
      apt-pkg/acquire-item.h
  3. +25
    -15
      apt-pkg/acquire.cc
  4. +2
    -0
      apt-pkg/acquire.h
  5. +9
    -2
      apt-private/private-download.cc
  6. +27
    -6
      cmdline/apt-get.cc
  7. +16
    -14
      test/integration/framework
  8. +2
    -0
      test/integration/test-apt-cdrom
  9. +15
    -10
      test/integration/test-apt-get-changelog
  10. +11
    -1
      test/integration/test-apt-get-download
  11. +5
    -2
      test/integration/test-apt-get-source-authenticated
  12. +11
    -10
      test/integration/test-apt-helper
  13. +1
    -1
      test/integration/test-apt-update-nofallback
  14. +6
    -4
      test/integration/test-bug-617690-allow-unauthenticated-makes-all-untrusted
  15. +5
    -7
      test/integration/test-bug-738785-switch-protocol
  16. +6
    -2
      test/integration/test-compressed-indexes
  17. +16
    -15
      test/integration/test-partial-file-support
  18. +8
    -2
      test/integration/test-sourceslist-trusted-options
  19. +2
    -0
      test/integration/test-ubuntu-bug-1098738-apt-get-source-md5sum

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

@@ -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) /*{{{*/
{
// 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)
_error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
}
@@ -155,7 +155,10 @@ pkgAcquire::Item::~Item()
fetch this object */
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");
UsedMirror = LookupTag(Message,"UsedMirror");
if (QueueCounter <= 1)
@@ -179,9 +182,9 @@ void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
Status = StatIdle;

// check fail reason
string FailReason = LookupTag(Message, "FailReason");
string const FailReason = LookupTag(Message, "FailReason");
if(FailReason == "MaximumSizeExceeded")
Rename(DestFile, DestFile+".FAILED");
RenameOnError(MaximumSizeExceeded);

// report mirror failure back to LP if we actually use a mirror
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)
{
Status = StatFetching;
ErrorText.clear();
if (FileSize == 0 && Complete == false)
FileSize = Size;
}
@@ -215,6 +219,8 @@ void pkgAcquire::Item::Done(string Message,unsigned long long Size,HashStringLis
if (Owner->Log != 0)
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)
FileSize= Size;
@@ -231,6 +237,7 @@ bool pkgAcquire::Item::Rename(string From,string To)
{
if (rename(From.c_str(),To.c_str()) == 0)
return true;
ChangeOwnerAndPermissionOfFile("Item::Failed", To.c_str(), "root", "root", 0644);

std::string S;
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)/*{{{*/
{
if(FileExists(DestFile))
if (RealFileExists(DestFile))
Rename(DestFile, DestFile + ".FAILED");

switch (error)
@@ -283,7 +290,11 @@ bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const
Status = StatError;
break;
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;
break;
}
@@ -702,14 +713,14 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/
/*}}}*/
void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * Cnf)/*{{{*/
{
Item::Failed(Message,Cnf);
Status = StatDone;

if(Debug)
std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
<< "Falling back to normal index file acquire" << std::endl;

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, /*{{{*/
@@ -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)
std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << 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 /*{{{*/
void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
{
Item::Failed(Message,Cnf);

size_t const nextExt = CompressionExtensions.find(' ');
if (nextExt != std::string::npos)
{
CompressionExtensions = CompressionExtensions.substr(nextExt+1);
Init(RealURI, Desc.Description, Desc.ShortDesc);
Status = StatIdle;
return;
}

@@ -1295,8 +1312,6 @@ void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
unlink(EraseFileName.c_str());
}

Item::Failed(Message,Cnf);

/// cancel the entire transaction
TransactionManager->AbortTransaction();
}
@@ -1521,6 +1536,8 @@ string pkgAcqIndexTrans::Custom600Headers() const
// AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
{
Item::Failed(Message,Cnf);

size_t const nextExt = CompressionExtensions.find(' ');
if (nextExt != std::string::npos)
{
@@ -1530,8 +1547,6 @@ void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
return;
}

Item::Failed(Message,Cnf);

// FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
if (Cnf->LocalOnly == true ||
StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
@@ -1563,16 +1578,9 @@ void pkgAcqMetaBase::AbortTransaction()
if ((*I)->Status == pkgAcquire::Item::StatIdle)
(*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);
}
Transaction.clear();
@@ -1608,8 +1616,6 @@ void pkgAcqMetaBase::CommitTransaction()
<< (*I)->DescURI() << std::endl;

Rename((*I)->PartialFile, (*I)->DestFile);
ChangeOwnerAndPermissionOfFile("CommitTransaction", (*I)->DestFile.c_str(), "root", "root", 0644);

} else {
if(_config->FindB("Debug::Acquire::Transaction", false) == true)
std::clog << "rm "
@@ -1758,16 +1764,17 @@ void pkgAcqMetaSig::Done(string Message,unsigned long long Size,
/*}}}*/
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
if (AuthPass == true && CheckStopAuthentication(RealURI, Message))
return;

// 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");
string FinalInRelease = _config->FindDir("Dir::State::lists") + URItoFileName(InReleaseURI);
string const FinalInRelease = _config->FindDir("Dir::State::lists") + URItoFileName(InReleaseURI);

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 "
"Acquire::AllowDowngradeToInsecureRepositories was "
"given to override it."));
Status = StatDone;
} else {
_error->Error("%s", downgrade_msg.c_str());
Rename(MetaIndexFile, MetaIndexFile+".FAILED");
@@ -1810,8 +1817,6 @@ void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
QueueIndexes(true);
}

Item::Failed(Message,Cnf);

// FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
if (Cnf->LocalOnly == true ||
StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
@@ -2226,10 +2231,12 @@ string pkgAcqMetaClearSig::Custom600Headers() const
/*}}}*/
// 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)
{
Item::Done(Message, Size, Hashes, 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
@@ -2567,7 +2574,6 @@ void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList
string FinalFile = _config->FindDir("Dir::Cache::Archives");
FinalFile += flNotDir(StoreFilename);
Rename(DestFile,FinalFile);
ChangeOwnerAndPermissionOfFile("pkgAcqArchive::Done", FinalFile.c_str(), "root", "root", 0644);
StoreFilename = DestFile = FinalFile;
Complete = true;
}
@@ -2577,8 +2583,8 @@ void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList
/* Here we try other sources */
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
that. An interesting observation is that permanent failures are not
recorded. */
@@ -2588,10 +2594,10 @@ void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
// Vf = Version.FileList();
while (Vf.end() == false) ++Vf;
StoreFilename = string();
Item::Failed(Message,Cnf);
return;
}

Status = StatIdle;
if (QueueNext() == false)
{
// This is the retry counter
@@ -2604,9 +2610,9 @@ void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
if (QueueNext() == true)
return;
}
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
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;
Complete = false;
}
@@ -2738,19 +2749,19 @@ void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList cons
/* Here we try other sources */
void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
{
ErrorText = LookupTag(Message,"Message");
Item::Failed(Message,Cnf);
// This is the retry counter
if (Retries != 0 &&
Cnf->LocalOnly == false &&
StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
{
Retries--;
--Retries;
QueueURI(Desc);
Status = StatIdle;
return;
}
Item::Failed(Message,Cnf);

}
/*}}}*/
// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/


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

@@ -323,6 +323,7 @@ class pkgAcquire::Item : public WeakPointable
InvalidFormat,
SignatureError,
NotClearsigned,
MaximumSizeExceeded
};

/** \brief Rename failed file and set error


+ 25
- 15
apt-pkg/acquire.cc View File

@@ -54,23 +54,38 @@ pkgAcquire::pkgAcquire() : LockFD(-1), Queues(0), Workers(0), Configs(0), Log(NU
Debug(_config->FindB("Debug::pkgAcquire",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),
Configs(0), Log(NULL), ToFetch(0),
Debug(_config->FindB("Debug::pkgAcquire",false)),
Running(false)
{
Initialize();
SetLog(Progress);
}
void pkgAcquire::Initialize()
{
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;
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 /*{{{*/
@@ -81,21 +96,16 @@ static bool SetupAPTPartialDirectory(std::string const &grand, std::string const
CreateAPTDirectoryIfNeeded(parent, partial) == 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)
{
// chown the partial dir
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());
// 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)


+ 2
- 0
apt-pkg/acquire.h View File

@@ -378,6 +378,8 @@ class pkgAcquire
*/
virtual ~pkgAcquire();

private:
APT_HIDDEN void Initialize();
};

/** \brief Represents a single download source from which an item


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

@@ -49,16 +49,23 @@ bool CheckDropPrivsMustBeDisabled(pkgAcquire &Fetcher) /*{{{*/
for (pkgAcquire::ItemIterator I = Fetcher.ItemsBegin();
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)
{
res = false;
std::string msg;
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());
c0out << msg << std::endl;
std::cerr << "W: " << msg << std::endl;
_config->Set("APT::Sandbox::User", "");
break;
}
unlink((*I)->DestFile.c_str());
close(fd);
}



+ 27
- 6
cmdline/apt-get.cc View File

@@ -80,6 +80,9 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>

#include <algorithm>
#include <fstream>
#include <iostream>
@@ -659,6 +662,8 @@ static bool DoDownload(CommandLine &CmdL)
Ver != verset.end(); ++Ver, ++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]);
storefile[i].assign(filename);
I->DestFile.assign(filename);
@@ -1481,7 +1486,10 @@ static bool DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher,
"http://packages.debian.org/changelogs");
string const path = GetChangelogPath(CacheFile, Ver);
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)
{
std::cout << '\'' << changelog_uri << '\'' << std::endl;
@@ -1492,7 +1500,7 @@ static bool DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher,
string descr;
strprintf(descr, _("Changelog for %s (%s)"), Pkg.Name(), changelog_uri.c_str());
// 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
CheckDropPrivsMustBeDisabled(Fetcher);
@@ -1500,18 +1508,18 @@ static bool DownloadChangelog(CacheFile &CacheFile, pkgAcquire &Fetcher,
// try downloading it, if that fails, try third-party-changelogs location
// FIXME: Fetcher.Run() is "Continue" even if I get a 404?!?
Fetcher.Run();
if (!FileExists(targetfile))
if (itm->Status != pkgAcquire::Item::StatDone)
{
string third_party_uri;
if (GuessThirdPartyChangelogUri(CacheFile, Ver, third_party_uri))
{
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();
}
}

if (FileExists(targetfile))
if (itm->Status == pkgAcquire::Item::StatDone)
return true;

// error
@@ -1557,6 +1565,19 @@ static bool DoChangelog(CommandLine &CmdL)
tmpdir = mkdtemp(tmpname);
if (tmpdir == NULL)
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();
@@ -1572,7 +1593,7 @@ static bool DoChangelog(CommandLine &CmdL)
{
DisplayFileInPager(changelogfile);
// cleanup temp file
unlink(changelogfile.c_str());
unlink(changelogfile.c_str());
}
}
// clenaup tmp dir


+ 16
- 14
test/integration/framework View File

@@ -32,7 +32,7 @@ msgprintf() {
printf "$START " "$1"
shift
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
done
fi
@@ -57,9 +57,9 @@ msgskip() {
msgfail() {
if [ $# -gt 0 ]; then printf "${CFAIL}FAIL: $*${CNORMAL}\n" >&2;
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));
}

@@ -184,10 +184,12 @@ setupenvironment() {
addtrap "cd /; rm -rf $TMPWORKINGDIRECTORY;"
msgninfo "Preparing environment for ${CCMD}$(basename $0)${CINFO} in ${TMPWORKINGDIRECTORY}… "

mkdir -m 700 "${TMPWORKINGDIRECTORY}/downloaded"
if [ "$(id -u)" = '0' ]; then
# relax permissions so that running as root with user switching works
umask 022
chmod o+rx "$TMPWORKINGDIRECTORY"
chmod 711 "$TMPWORKINGDIRECTORY"
chown _apt:root "${TMPWORKINGDIRECTORY}/downloaded"
fi

TESTDIRECTORY=$(readlink -f $(dirname $0))
@@ -1061,7 +1063,7 @@ downloadfile() {
}

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
echo >&2
echo >&2 "$DIFFTEXT"
@@ -1307,7 +1309,7 @@ testaccessrights() {

testwebserverlaststatuscode() {
local DOWNLOG='rootdir/tmp/webserverstatus-testfile.log'
local STATUS='rootdir/tmp/webserverstatus-statusfile.log'
local STATUS='downloaded/webserverstatus-statusfile.log'
rm -f "$DOWNLOG" "$STATUS"
msgtest 'Test last status code from the webserver was' "$1"
downloadfile "http://localhost:8080/_config/find/aptwebserver::last-status-code" "$STATUS" > "$DOWNLOG"
@@ -1353,20 +1355,20 @@ aptautotest() {
shift 3
# save and restore the *.output files from other 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" "$@"
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
}

aptautotest_aptget_update() {
if ! test -d "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists"; then return; fi
# 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"
done
}


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

@@ -99,6 +99,7 @@ Conf testing:i386 (0.8.15 stable [i386])' aptget install testing:i386 -s
testsuccess aptget purge testing:i386 -y
testdpkgnotinstalled testing:i386

cd downloaded
rm -f testing_0.8.15_amd64.deb
testsuccess aptget download testing
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 test -s testing_0.8.15.dsc
rm -f testing_0.8.15.dsc
cd - >/dev/null
}
testcdromusage



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

@@ -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

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
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
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/'

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 View File

@@ -44,7 +44,10 @@ testdownload() {
}

# normal case as "root"
OLDPWD="$(pwd)"
cd downloaded
testdownload apt_2.0_all.deb apt
cd "$OLDPWD"

# simulate normal user with non-existent root-owned directories
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;"
chmod -R -w rootdir/var/cache/apt/archives

OLDPWD="$(pwd)"
cd downloaded

# normal case(es)
testdownload apt_1.0_all.deb apt stable
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

# 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

# deb:736962
@@ -74,6 +81,7 @@ testsuccess aptget download apt apt apt/unstable apt=2.0
testsuccess test -s apt_2.0_all.deb

# restore "root" rights
cd "$OLDPWD"
chmod -f -R +w $PWD/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
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
cd downloaded
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

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

@@ -25,7 +25,10 @@ testwarning aptget update --allow-insecure-repositories

# this all should fail
testfailure aptget install -y foo
cd downloaded
testfailure aptget source foo
testfailure test -e foo_2.0.dsc
# 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 View File

@@ -13,23 +13,23 @@ test_apt_helper_download() {
echo 'foo' > aptarchive/foo

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'
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'
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'
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'
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
else
msgfail
@@ -37,7 +37,8 @@ test_apt_helper_download() {
testfileequal download.stderr 'E: Failed to fetch http://localhost:8080/foo Hash Sum mismatch

E: Download Failed'
testfileequal foo5.FAILED 'foo'
testfileequal downloaded/foo5.FAILED 'foo'
testfailure test -e downloaded/foo5
}

test_apt_helper_detect_proxy() {


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

@@ -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>

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



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

@@ -24,6 +24,7 @@ testfilemissing() {

testrun() {
rm -rf rootdir/var/lib/apt
cd downloaded

if [ "$1" = 'trusted' ]; then
testsuccess aptget update
@@ -43,18 +44,19 @@ testrun() {
testfileexists 'cool_1.0_i386.deb'
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
testfilemissing cool_1.0_i386.deb

testfailure aptget download cool --allow-unauthenticated # unauthenticated doesn't mean unchecked
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
testfileexists 'cool_1.0_i386.deb'
cd - >/dev/null
}

testrun 'trusted'


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

@@ -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
testequal "'http://localhost:8080/redirectme/pool/apt_1.0/changelog'" aptget changelog apt --print-uris

cd downloaded
testsuccess aptget changelog apt -d
testsuccess test -s apt.changelog
rm -f apt.changelog
@@ -32,6 +33,7 @@ rm -f apt.changelog
testsuccess aptget download apt
testsuccess test -s apt_1.0_all.deb
rm apt_1.0_all.deb
cd - >/dev/null

testsuccess aptget install apt -y
testdpkginstalled 'apt'
@@ -49,15 +51,11 @@ rm https
cd - >/dev/null
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
fi
testsuccess test ! -e apt_1.0_all.deb
cd - >/dev/null

# revert to all methods
rm -rf rootdir/$COPYMETHODS


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

@@ -58,9 +58,11 @@ testrun() {
msgpass
fi
msgtest 'Check if package is downloadable'
cd downloaded
testsuccess --nomsg aptget download testpkg
msgtest '\tdeb file is present'; testsuccess --nomsg test -f testpkg_1.0_i386.deb
rm testpkg_1.0_i386.deb
cd - >/dev/null
testequal 'Reading package lists...
Building dependency tree...
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
aptget clean
msgtest 'Check if the source is aptgetable'
cd downloaded
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*
cd - >/dev/null
testequal "$(aptcache show testpkg -o Acquire::Languages=none)
" aptcache dumpavail
}


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

@@ -52,36 +52,37 @@ cp -a ${TESTDIR}/framework $TESTFILE

testrun() {
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"

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"

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"

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"

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"

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"

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"
}



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

@@ -20,6 +20,7 @@ foo/testing 2 amd64
foo/stable 1 amd64
' apt list foo -a

cd downloaded
rm -f foo_1_amd64.deb foo_2_amd64.deb
testsuccess aptget download foo "$@"
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
testsuccess aptget source foo --dsc-only -d "$@"
testsuccess test -s foo_1.dsc -o -s foo_2.dsc
cd - >/dev/null
}

everythingfails() {
@@ -39,18 +41,22 @@ foo/stable 1 amd64
foo
E: Some packages could not be authenticated'

cd downloaded
rm -f foo_1_amd64.deb foo_2_amd64.deb
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

rm -f foo_1.dsc foo_2.dsc
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
cd - >/dev/null
}

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() {
rm -rf rootdir/var/lib/apt/lists


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

@@ -141,6 +141,8 @@ setupaptarchive
changetowebserver
testsuccess aptget update

cd downloaded

testok() {
rm -f ${1}_1.0.dsc ${1}_1.0.tar.gz
testequal "Reading package lists...


Loading…
Cancel
Save