From 08fa1cab614ec50d2b1de807439195c71d1530cf Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Tue, 30 Nov 2010 18:54:27 +0100 Subject: apply a very simple speed-up in case we try to set the candidate version of a package to the version which is already the candidate (apt-get does that for all packages it installs for simplicity) --- apt-pkg/depcache.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 23abc76c1..58e7ebf07 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1510,11 +1510,15 @@ void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To) /* */ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer, bool const &Pseudo) { - ActionGroup group(*this); pkgCache::PkgIterator Pkg = TargetVer.ParentPkg(); StateCache &P = PkgState[Pkg->ID]; + if (P.CandidateVer == TargetVer) + return; + + ActionGroup group(*this); + RemoveSizes(Pkg); RemoveStates(Pkg); @@ -1549,7 +1553,10 @@ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer, bool const &Pseudo) } } } - + /*}}}*/ +// DepCache::MarkAuto - set the Auto flag for a package /*{{{*/ +// --------------------------------------------------------------------- +/* */ void pkgDepCache::MarkAuto(const PkgIterator &Pkg, bool Auto) { StateCache &state = PkgState[Pkg->ID]; -- cgit v1.2.3-70-g09d2 From 2f6557b96c08c1adebf3b1e292ae636a27f624d0 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Tue, 30 Nov 2010 18:57:42 +0100 Subject: add the possibility to disable only the progress reporting stuff as the quiet level 1 does this, but also disables other stuff we might want to test against in a testcase --- apt-pkg/contrib/progress.cc | 2 +- doc/examples/configure-index | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'apt-pkg') diff --git a/apt-pkg/contrib/progress.cc b/apt-pkg/contrib/progress.cc index cffdddc4f..45e81edcb 100644 --- a/apt-pkg/contrib/progress.cc +++ b/apt-pkg/contrib/progress.cc @@ -135,7 +135,7 @@ bool OpProgress::CheckChange(float Interval) OpTextProgress::OpTextProgress(Configuration &Config) : NoUpdate(false), NoDisplay(false), LastLen(0) { - if (Config.FindI("quiet",0) >= 1) + if (Config.FindI("quiet",0) >= 1 || Config.FindB("quiet::NoUpdate", false) == true) NoUpdate = true; if (Config.FindI("quiet",0) >= 2) NoDisplay = true; diff --git a/doc/examples/configure-index b/doc/examples/configure-index index c4c2acb64..6c078d75f 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -17,6 +17,7 @@ */ quiet "0"; +quiet::NoUpdate "true"; // never update progress information - included in -q=1 // Options for APT in general APT -- cgit v1.2.3-70-g09d2 From 54f2f0a3e46e4369285be8c25912fd45413114c0 Mon Sep 17 00:00:00 2001 From: Nobuhiro Hayashi Date: Fri, 3 Dec 2010 12:09:09 +0900 Subject: Permit base256 encoded value in the numeric field of tar header. --- apt-inst/contrib/extracttar.cc | 12 ++++++++---- apt-pkg/contrib/strutl.cc | 18 ++++++++++++++++++ apt-pkg/contrib/strutl.h | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) (limited to 'apt-pkg') diff --git a/apt-inst/contrib/extracttar.cc b/apt-inst/contrib/extracttar.cc index 3d2788aaf..1a358d57e 100644 --- a/apt-inst/contrib/extracttar.cc +++ b/apt-inst/contrib/extracttar.cc @@ -195,10 +195,14 @@ bool ExtractTar::Go(pkgDirStream &Stream) // Decode all of the fields pkgDirStream::Item Itm; if (StrToNum(Tar->Mode,Itm.Mode,sizeof(Tar->Mode),8) == false || - StrToNum(Tar->UserID,Itm.UID,sizeof(Tar->UserID),8) == false || - StrToNum(Tar->GroupID,Itm.GID,sizeof(Tar->GroupID),8) == false || - StrToNum(Tar->Size,Itm.Size,sizeof(Tar->Size),8) == false || - StrToNum(Tar->MTime,Itm.MTime,sizeof(Tar->MTime),8) == false || + (Base256ToNum(Tar->UserID,Itm.UID,8) == false && + StrToNum(Tar->UserID,Itm.UID,sizeof(Tar->UserID),8) == false) || + (Base256ToNum(Tar->GroupID,Itm.GID,8) == false && + StrToNum(Tar->GroupID,Itm.GID,sizeof(Tar->GroupID),8) == false) || + (Base256ToNum(Tar->Size,Itm.Size,12) == false && + StrToNum(Tar->Size,Itm.Size,sizeof(Tar->Size),8) == false) || + (Base256ToNum(Tar->MTime,Itm.MTime,12) == false && + StrToNum(Tar->MTime,Itm.MTime,sizeof(Tar->MTime),8) == false) || StrToNum(Tar->Major,Itm.Major,sizeof(Tar->Major),8) == false || StrToNum(Tar->Minor,Itm.Minor,sizeof(Tar->Minor),8) == false) return _error->Error(_("Corrupted archive")); diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc index 987f4c3a4..daf87c87f 100644 --- a/apt-pkg/contrib/strutl.cc +++ b/apt-pkg/contrib/strutl.cc @@ -968,6 +968,24 @@ bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base) return true; } /*}}}*/ +// Base256ToNum - Convert a fixed length binary to a number /*{{{*/ +// --------------------------------------------------------------------- +/* This is used in decoding the 256bit encoded fixed length fields in + tar files */ +bool Base256ToNum(const char *Str,unsigned long &Res,unsigned Len) +{ + int i; + if ((Str[0] & 0x80) == 0) + return false; + else + { + Res = Str[0] & 0x7F; + for(i=1; i &List); bool StrToNum(const char *Str,unsigned long &Res,unsigned Len,unsigned Base = 0); +bool Base256ToNum(const char *Str,unsigned long &Res,unsigned Len); bool Hex2Num(const string &Str,unsigned char *Num,unsigned int Length); bool TokSplitString(char Tok,char *Input,char **List, unsigned long ListMax); -- cgit v1.2.3-70-g09d2 From 2c085486d34c45e123c24b0e36294245fd5bf734 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Fri, 3 Dec 2010 17:06:34 +0100 Subject: * apt-pkg/depcache.cc: - add SetCandidateRelease() to set a candidate version and the candidates of dependencies if needed to a specified release (Closes: #572709) - change pkg/release behavior to use the new SetCandidateRelease so installing packages from experimental or backports is easier --- apt-pkg/depcache.cc | 161 ++++++++ apt-pkg/depcache.h | 19 + cmdline/apt-get.cc | 36 +- debian/NEWS | 9 + debian/changelog | 8 +- test/integration/framework | 10 +- test/integration/test-release-candidate-switching | 435 ++++++++++++++++++++++ 7 files changed, 670 insertions(+), 8 deletions(-) create mode 100755 test/integration/test-release-candidate-switching (limited to 'apt-pkg') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 58e7ebf07..4a8e53eb3 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -10,6 +10,7 @@ // Include Files /*{{{*/ #include #include +#include #include #include #include @@ -1554,6 +1555,166 @@ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer, bool const &Pseudo) } } /*}}}*/ +// DepCache::SetCandidateRelease - Change the candidate version /*{{{*/ +// --------------------------------------------------------------------- +/* changes the candidate of a package and walks over all its dependencies + to check if it needs to change the candidate of the dependency, too, + to reach a installable versionstate */ +bool pkgDepCache::SetCandidateRelease(pkgCache::VerIterator TargetVer, + std::string const &TargetRel) +{ + std::list > Changed; + return SetCandidateRelease(TargetVer, TargetRel, Changed); +} +bool pkgDepCache::SetCandidateRelease(pkgCache::VerIterator TargetVer, + std::string const &TargetRel, + std::list > &Changed) +{ + SetCandidateVersion(TargetVer); + + if (TargetRel == "installed" || TargetRel == "candidate") // both doesn't make sense in this context + return true; + + pkgVersionMatch Match(TargetRel, pkgVersionMatch::Release); + // save the position of the last element we will not undo - if we have to + std::list >::iterator newChanged = --(Changed.end()); + + for (pkgCache::DepIterator D = TargetVer.DependsList(); D.end() == false; ++D) + { + if (D->Type != pkgCache::Dep::PreDepends && D->Type != pkgCache::Dep::Depends && + ((D->Type != pkgCache::Dep::Recommends && D->Type != pkgCache::Dep::Suggests) || + IsImportantDep(D) == false)) + continue; + + // walk over an or-group and check if we need to do anything + // for simpilicity no or-group is handled as a or-group including one dependency + pkgCache::DepIterator Start = D; + bool itsFine = false; + for (bool stillOr = true; stillOr == true; ++Start) + { + stillOr = (Start->CompareOp & Dep::Or) == Dep::Or; + pkgCache::PkgIterator const P = Start.TargetPkg(); + // virtual packages can't be a solution + if (P.end() == true || (P->ProvidesList == 0 && P->VersionList == 0)) + continue; + pkgCache::VerIterator const Cand = PkgState[P->ID].CandidateVerIter(*this); + // no versioned dependency - but is it installable? + if (Start.TargetVer() == 0 || Start.TargetVer()[0] == '\0') + { + // Check if one of the providers is installable + if (P->ProvidesList != 0) + { + pkgCache::PrvIterator Prv = P.ProvidesList(); + for (; Prv.end() == false; ++Prv) + { + pkgCache::VerIterator const C = PkgState[Prv.OwnerPkg()->ID].CandidateVerIter(*this); + if (C.end() == true || C != Prv.OwnerVer() || + (VersionState(C.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) != DepCandMin) + continue; + break; + } + if (Prv.end() == true) + continue; + } + // no providers, so check if we have an installable candidate version + else if (Cand.end() == true || + (VersionState(Cand.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) != DepCandMin) + continue; + itsFine = true; + break; + } + if (Cand.end() == true) + continue; + // check if the current candidate is enough for the versioned dependency - and installable? + if (VS().CheckDep(P.CandVersion(), Start->CompareOp, Start.TargetVer()) == true && + (VersionState(Cand.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) == DepCandMin) + { + itsFine = true; + break; + } + } + + if (itsFine == true) { + // something in the or-group was fine, skip all other members + for (; (D->CompareOp & Dep::Or) == Dep::Or; ++D); + continue; + } + + // walk again over the or-group and check each if a candidate switch would help + itsFine = false; + for (bool stillOr = true; stillOr == true; ++D) + { + stillOr = (D->CompareOp & Dep::Or) == Dep::Or; + // changing candidate will not help if the dependency is not versioned + if (D.TargetVer() == 0 || D.TargetVer()[0] == '\0') + { + if (stillOr == true) + continue; + break; + } + + pkgCache::VerIterator V; + if (TargetRel == "newest") + V = D.TargetPkg().VersionList(); + else + V = Match.Find(D.TargetPkg()); + + // check if the version from this release could satisfy the dependency + if (V.end() == true || VS().CheckDep(V.VerStr(), D->CompareOp, D.TargetVer()) == false) + { + if (stillOr == true) + continue; + break; + } + + pkgCache::VerIterator oldCand = PkgState[D.TargetPkg()->ID].CandidateVerIter(*this); + if (V == oldCand) + { + // Do we already touched this Version? If so, their versioned dependencies are okay, no need to check again + for (std::list >::const_iterator c = Changed.begin(); + c != Changed.end(); ++c) + { + if (c->first->ParentPkg != V->ParentPkg) + continue; + itsFine = true; + break; + } + } + + if (itsFine == false) + { + // change the candidate + Changed.push_back(make_pair(oldCand, TargetVer)); + if (SetCandidateRelease(V, TargetRel, Changed) == false) + { + if (stillOr == false) + break; + // undo the candidate changing + SetCandidateVersion(oldCand); + Changed.pop_back(); + continue; + } + itsFine = true; + } + + // something in the or-group was fine, skip all other members + for (; (D->CompareOp & Dep::Or) == Dep::Or; ++D); + break; + } + + if (itsFine == false && (D->Type == pkgCache::Dep::PreDepends || D->Type == pkgCache::Dep::Depends)) + { + // undo all changes which aren't lead to a solution + for (std::list >::const_iterator c = ++newChanged; + c != Changed.end(); ++c) + SetCandidateVersion(c->first); + Changed.erase(newChanged, Changed.end()); + return false; + } + } + return true; +} + /*}}}*/ // DepCache::MarkAuto - set the Auto flag for a package /*{{{*/ // --------------------------------------------------------------------- /* */ diff --git a/apt-pkg/depcache.h b/apt-pkg/depcache.h index 2d3dbdf77..dba3e22dc 100644 --- a/apt-pkg/depcache.h +++ b/apt-pkg/depcache.h @@ -396,6 +396,25 @@ class pkgDepCache : protected pkgCache::Namespace void SetReInstall(PkgIterator const &Pkg,bool To); void SetCandidateVersion(VerIterator TargetVer, bool const &Pseudo = true); + bool SetCandidateRelease(pkgCache::VerIterator TargetVer, + std::string const &TargetRel); + /** Set the candidate version for dependencies too if needed. + * + * Sets not only the candidate version as SetCandidateVersion does, + * but walks also down the dependency tree and checks if it is required + * to set the candidate of the dependency to a version from the given + * release, too. + * + * \param TargetVer new candidate version of the package + * \param TargetRel try to switch to this release if needed + * \param[out] Changed a list of pairs consisting of the \b old + * version of the changed package and the version which + * required the switch of this dependency + * \return \b true if the switch was successful, \b false otherwise + */ + bool SetCandidateRelease(pkgCache::VerIterator TargetVer, + std::string const &TargetRel, + std::list > &Changed); /** Set the "is automatically installed" flag of Pkg. */ void MarkAuto(const PkgIterator &Pkg, bool Auto); diff --git a/cmdline/apt-get.cc b/cmdline/apt-get.cc index 57065c528..476896322 100644 --- a/cmdline/apt-get.cc +++ b/cmdline/apt-get.cc @@ -628,6 +628,8 @@ class CacheSetHelperAPTGet : public APT::CacheSetHelper { APT::PackageSet virtualPkgs; public: + std::list > selectedByRelease; + CacheSetHelperAPTGet(std::ostream &out) : APT::CacheSetHelper(true), out(out) { explicitlyNamed = true; } @@ -646,9 +648,9 @@ public: } virtual void showSelectedVersion(pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const Ver, string const &ver, bool const &verIsRel) { - if (ver != Ver.VerStr()) - ioprintf(out, _("Selected version '%s' (%s) for '%s'\n"), - Ver.VerStr(), Ver.RelStr().c_str(), Pkg.FullName(true).c_str()); + if (ver == Ver.VerStr()) + return; + selectedByRelease.push_back(make_pair(Ver, ver)); } bool showVirtualPackageErrors(pkgCacheFile &Cache) { @@ -829,6 +831,33 @@ struct TryToInstall { } } + bool propergateReleaseCandiateSwitching(std::list > start, std::ostream &out) + { + bool Success = true; + std::list > Changed; + for (std::list >::const_iterator s = start.begin(); + s != start.end(); ++s) + { + Changed.push_back(std::make_pair(s->first, pkgCache::VerIterator(*Cache))); + // We continue here even if it failed to enhance the ShowBroken output + Success &= Cache->GetDepCache()->SetCandidateRelease(s->first, s->second, Changed); + } + for (std::list >::const_iterator c = Changed.begin(); + c != Changed.end(); ++c) + { + if (c->second.end() == true) + ioprintf(out, _("Selected version '%s' (%s) for '%s'\n"), + c->first.VerStr(), c->first.RelStr().c_str(), c->first.ParentPkg().FullName(true).c_str()); + else if (c->first.ParentPkg()->Group != c->second.ParentPkg()->Group) + { + pkgCache::VerIterator V = (*Cache)[c->first.ParentPkg()].CandidateVerIter(*Cache); + ioprintf(out, _("Selected version '%s' (%s) for '%s' because of '%s'\n"), V.VerStr(), + V.RelStr().c_str(), V.ParentPkg().FullName(true).c_str(), c->second.ParentPkg().FullName(true).c_str()); + } + } + return Success; + } + void doAutoInstall() { for (APT::PackageSet::const_iterator P = doAutoInstallLater.begin(); P != doAutoInstallLater.end(); ++P) { @@ -1779,6 +1808,7 @@ bool DoInstall(CommandLine &CmdL) { if (order[i] == MOD_INSTALL) { InstallAction = std::for_each(verset[MOD_INSTALL].begin(), verset[MOD_INSTALL].end(), InstallAction); + InstallAction.propergateReleaseCandiateSwitching(helper.selectedByRelease, c0out); InstallAction.doAutoInstall(); } else if (order[i] == MOD_REMOVE) diff --git a/debian/NEWS b/debian/NEWS index 775dc9458..c90cff6b2 100644 --- a/debian/NEWS +++ b/debian/NEWS @@ -1,3 +1,12 @@ +apt (0.8.11+wheezy) UNRELEASED; urgency=low + + * apt-get install pkg/experimental will now not only switch the + candidate of package pkg to the version from the release experimental + but also of all dependencies of pkg if the current candidate can't + satisfy a versioned dependency. + + -- David Kalnischkies Fri, 03 Dez 2010 14:09:12 +0100 + apt (0.7.26~exp3) experimental; urgency=low * apt-ftparchive now reads the standard configuration files in diff --git a/debian/changelog b/debian/changelog index 65603587d..9d7564462 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,9 @@ apt (0.8.11+wheezy) unstable; urgency=low + * apt-pkg/depcache.cc: + - add SetCandidateRelease() to set a candidate version and + the candidates of dependencies if needed to a specified + release (Closes: #572709) * cmdline/apt-get.cc: - if --print-uris is used don't setup downloader as we don't need progress, lock nor the directories it would create otherwise @@ -7,10 +11,12 @@ apt (0.8.11+wheezy) unstable; urgency=low only if they cause the remove of this essential (Closes: #601961) - keep not installed garbage packages uninstalled instead of showing in the autoremove section and installing those (Closes: #604222) + - change pkg/release behavior to use the new SetCandidateRelease + so installing packages from experimental or backports is easier * debian/control: - add Vcs-Browser now that loggerhead works again (Closes: #511168) - -- David Kalnischkies Fri, 03 Dec 2010 14:00:18 +0100 + -- David Kalnischkies Fri, 03 Dec 2010 17:06:28 +0100 apt (0.8.10) unstable; urgency=low diff --git a/test/integration/framework b/test/integration/framework index aa8cdacc6..4bbe1b408 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -134,6 +134,7 @@ setupenvironment() { echo "DPKG::options:: \"--force-not-root\";" >> aptconfig.conf echo "DPKG::options:: \"--force-bad-path\";" >> aptconfig.conf echo "DPKG::options:: \"--log=${TMPWORKINGDIRECTORY}/rootdir/var/log/dpkg.log\";" >> aptconfig.conf + echo 'quiet::NoUpdate "true";' >> aptconfig.conf export LC_ALL=C msgdone "info" } @@ -337,10 +338,11 @@ insertpackage() { echo "Package: $NAME Priority: optional Section: other -Installed-Size: 23356 +Installed-Size: 42 Maintainer: Joe Sixpack Architecture: $ARCH -Version: $VERSION" >> $FILE +Version: $VERSION +Filename: pool/main/${NAME}/${NAME}_${VERSION}_${ARCH}.deb" >> $FILE test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" >> $FILE echo "Description: an autogenerated dummy ${NAME}=${VERSION}/${RELEASE} If you find such a package installed on your system, @@ -380,8 +382,8 @@ generatereleasefiles() { if [ -e aptarchive/dists ]; then for dir in $(find ./aptarchive/dists -mindepth 1 -maxdepth 1 -type d); do local CODENAME="$(echo "$dir" | cut -d'/' -f 4)" - aptftparchive -qq release $dir -o APT::FTPArchive::Release::Codename="${CODENAME}" | sed -e '/0 Release$/ d' > $dir/Release # remove the self reference - if [ "$CODENAME" = "experimental" ]; then + aptftparchive -qq release $dir -o APT::FTPArchive::Release::Suite="${CODENAME}" -o APT::FTPArchive::Release::Codename="${CODENAME}" | sed -e '/0 Release$/ d' > $dir/Release # remove the self reference + if [ "$CODENAME" = "experimental" -o "$CODENAME" = "experimental2" ]; then sed -i '/^Date: / a\ NotAutomatic: yes' $dir/Release fi diff --git a/test/integration/test-release-candidate-switching b/test/integration/test-release-candidate-switching new file mode 100755 index 000000000..ea601ae8b --- /dev/null +++ b/test/integration/test-release-candidate-switching @@ -0,0 +1,435 @@ +#!/bin/sh +set -e + +local TESTDIR=$(readlink -f $(dirname $0)) +. $TESTDIR/framework +setupenvironment +configarchitecture "i386" + +insertpackage 'unstable' 'libc6' 'i386' '2.11.2-7+sid' +insertpackage 'unstable' 'phonon-backend-xine' 'i386' '4:4.6.0really4.4.2-1+sid' 'Provides: phonon-backend' +insertpackage 'unstable' 'phonon-backend-xine2' 'i386' '4:4.6.0really4.4.2-1+sid' +insertpackage 'unstable' 'phonon-backend-xine3' 'i386' '4:4.6.0really4.4.2-1+sid' +insertpackage 'unstable' 'phonon-backend-xine4' 'i386' '4:4.6.0really4.4.2-1+sid' +insertpackage 'unstable' 'phonon-backend-null' 'i386' '4:4.20.0+sid' 'Provides: phonon-backend' +insertpackage 'unstable' 'intermediatepkg' 'all' '1.0' + +insertpackage 'unstable' 'amarok-common' 'all' '2.3.1-1+sid' +insertpackage 'unstable' 'amarok-utils' 'i386' '2.3.1-1+sid' +insertpackage 'unstable' 'libmtp8' 'i386' '0.3.1+sid' +insertpackage 'unstable' 'amarok' 'i386' '2.3.1-1+sid' 'Depends: amarok-common (= 2.3.1-1+sid), amarok-utils (= 2.3.1-1+sid), phonon-backend-xine | phonon-backend, libmtp8 (>= 0.3.1), libc6' + +insertpackage 'experimental' 'amarok-common' 'all' '2.3.2-2+exp' +insertpackage 'experimental' 'amarok-utils' 'i386' '2.3.2-2+exp' +insertpackage 'experimental' 'libmtp8' 'i386' '0.3.3+exp' +insertpackage 'experimental' 'phonon-backend-xine' 'i386' '5:4.6.0+exp' 'Provides: phonon-backend' +insertpackage 'experimental' 'phonon-backend-xine2' 'i386' '5:4.6.0+exp' 'Depends: uninstallablepkg +Provides: phonon-backend-broken' +insertpackage 'experimental' 'phonon-backend-xine3' 'i386' '5:4.6.0+exp' 'Depends: intermediatepkg (>= 1.5)' +insertpackage 'experimental' 'phonon-backend-xine4' 'i386' '5:4.6.0+exp' 'Depends: intermediateuninstallablepkg (= 2.0) +Provides: phonon-backend-broken' +insertpackage 'experimental' 'intermediatepkg' 'all' '2.0' 'Depends: libc6' +insertpackage 'experimental' 'intermediateuninstallablepkg' 'all' '2.0' 'Depends: uninstallablepkg' +insertpackage 'experimental' 'phonon-backend-null' 'i386' '5:4.20.0+exp' 'Provides: phonon-backend' +insertpackage 'experimental' 'amarok' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), amarok-utils (= 2.3.2-2+exp), phonon-backend-xine | phonon-backend, libmtp8 (>= 0.3.1), libc6' + +insertpackage 'experimental2' 'phonon-backend-xine' 'i386' '5:4.00.0+exp' 'Provides: phonon-backend' +insertpackage 'experimental2' 'amarok-less' 'i386' '2.3.2-2+exp' 'Depends: amarok-common, phonon-backend-xine (>= 5:4.00.0+exp), libmtp8, libc6, amarok-utils' +insertpackage 'experimental' 'amarok-higher' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine (>= 5:4.6.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)' + +insertpackage 'experimental' 'amarok-null' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine (= 1:1.0-1) | phonon-backend, libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)' +insertpackage 'experimental' 'amarok-null2' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-null (= 1:1.0-1) | phonon-backend, libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)' +insertpackage 'experimental' 'amarok-null2' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-null (= 1:1.0-1) | phonon-backend, libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)' +insertpackage 'experimental' 'amarok-xine' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine (= 5:4.6.0+exp) | phonon-backend-null (= 5:4.20.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)' +insertpackage 'experimental' 'amarok-xine2' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine2 (= 5:4.6.0+exp) | phonon-backend-null (= 5:4.20.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)' +insertpackage 'experimental' 'amarok-xine3' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine3 (= 5:4.6.0+exp) | phonon-backend-null (= 5:4.20.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)' +insertpackage 'experimental' 'amarok-xine4' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine4 (= 5:4.6.0+exp) | phonon-backend-null (= 5:4.20.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)' +insertpackage 'experimental' 'amarok-broken' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-broken | phonon-backend-null (= 5:4.20.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)' + +insertpackage 'experimental' 'amarok-recommends' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp) +Recommends: amarok-utils (= 2.3.2-2+exp), phonon-backend-xine | phonon-backend, libmtp8 (>= 0.3.1), libc6' +insertpackage 'experimental' 'amarok-recommends2' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp) +Recommends: amarok-utils (= 2.30.2-2+exp), phonon-backend-xine | phonon-backend, libmtp8 (>= 0.3.1), libc6' + +insertpackage 'experimental' 'uninstallablepkg' 'all' '1.0' 'Depends: libmtp8 (>= 10:0.20.1), amarok-utils (= 2.3.2-2+exp)' + +setupaptarchive + +testequal "Reading package lists... +Building dependency tree... +The following extra packages will be installed: + amarok-common (2.3.1-1+sid) + amarok-utils (2.3.1-1+sid) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) +The following NEW packages will be installed: + amarok (2.3.1-1+sid) + amarok-common (2.3.1-1+sid) + amarok-utils (2.3.1-1+sid) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) +0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded. +After this operation, 258 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok --trivial-only -V -q=0 + +testequal "Reading package lists... +Building dependency tree... +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.3+exp) + phonon-backend-xine (4.6.0+exp) +The following NEW packages will be installed: + amarok (2.3.2-2+exp) + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.3+exp) + phonon-backend-xine (4.6.0+exp) +0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded. +After this operation, 258 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok -t experimental --trivial-only -V -q=0 + +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok' +The following extra packages will be installed: + amarok (2.3.2-2+exp) + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) +The following NEW packages will be installed: + amarok (2.3.2-2+exp) + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) +0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded. +After this operation, 258 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok/experimental --trivial-only -V -q=0 + +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-null' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-null' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-null' +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-null (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-null (4.20.0+sid) +The following NEW packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-null (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-null (4.20.0+sid) +0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded. +After this operation, 258 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-null/experimental --trivial-only -V -q=0 + +# do not select the same version multiple times +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-null' +The following extra packages will be installed: + amarok (2.3.2-2+exp) + amarok-common (2.3.2-2+exp) + amarok-null (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) +The following NEW packages will be installed: + amarok (2.3.2-2+exp) + amarok-common (2.3.2-2+exp) + amarok-null (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) +0 upgraded, 7 newly installed, 0 to remove and 0 not upgraded. +After this operation, 301 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok/experimental amarok-null/experimental --trivial-only -V -q=0 + +# … but thighten the version if needed +# in theory, the second line is wrong, but printing the right version is too much of a hassle +# (we have to check if later in the Changed list is another change and if so use this version +# instead of the current candidate) - and it wouldn't be (really) useful anyway… +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental2 [i386]) for 'amarok-less' +Selected version '5:4.6.0+exp' (experimental [i386]) for 'phonon-backend-xine' because of 'amarok-less' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-higher' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-higher' +Selected version '5:4.6.0+exp' (experimental [i386]) for 'phonon-backend-xine' because of 'amarok-higher' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-higher' +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-higher (2.3.2-2+exp) + amarok-less (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0+exp) +The following NEW packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-higher (2.3.2-2+exp) + amarok-less (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0+exp) +0 upgraded, 7 newly installed, 0 to remove and 0 not upgraded. +After this operation, 301 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-less/experimental2 amarok-higher/experimental --trivial-only -V -q=0 + +# phonon-backend-null can't be used directly, but as it provides it is still fine… +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-null2' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-null2' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-null2' +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-null2 (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-null (4.20.0+sid) +The following NEW packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-null2 (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-null (4.20.0+sid) +0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded. +After this operation, 258 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-null2/experimental --trivial-only -V -q=0 + +# if an or-group satisfier is already found, do not set others +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-xine' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-xine' +Selected version '5:4.6.0+exp' (experimental [i386]) for 'phonon-backend-xine' because of 'amarok-xine' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-xine' +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + amarok-xine (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0+exp) +The following NEW packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + amarok-xine (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0+exp) +0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded. +After this operation, 258 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-xine/experimental --trivial-only -V -q=0 + +# … but proceed testing if the first doesn't work out +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-xine2' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-xine2' +Selected version '5:4.20.0+exp' (experimental [i386]) for 'phonon-backend-null' because of 'amarok-xine2' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-xine2' +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + amarok-xine2 (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-null (4.20.0+exp) +The following NEW packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + amarok-xine2 (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-null (4.20.0+exp) +0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded. +After this operation, 258 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-xine2/experimental --trivial-only -V -q=0 + +# sometimes, the second level need to be corrected, too +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-xine3' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-xine3' +Selected version '5:4.6.0+exp' (experimental [i386]) for 'phonon-backend-xine3' because of 'amarok-xine3' +Selected version '2.0' (experimental [all]) for 'intermediatepkg' because of 'phonon-backend-xine3' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-xine3' +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + amarok-xine3 (2.3.2-2+exp) + intermediatepkg (2.0) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine3 (4.6.0+exp) +The following NEW packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + amarok-xine3 (2.3.2-2+exp) + intermediatepkg (2.0) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine3 (4.6.0+exp) +0 upgraded, 7 newly installed, 0 to remove and 0 not upgraded. +After this operation, 301 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-xine3/experimental --trivial-only -V -q=0 + +# … but proceed testing if the first doesn't work out even in second deep +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-xine4' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-xine4' +Selected version '5:4.20.0+exp' (experimental [i386]) for 'phonon-backend-null' because of 'amarok-xine4' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-xine4' +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + amarok-xine4 (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-null (4.20.0+exp) +The following NEW packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + amarok-xine4 (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-null (4.20.0+exp) +0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded. +After this operation, 258 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-xine4/experimental --trivial-only -V -q=0 + +# providers can be broken, too +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-broken' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-broken' +Selected version '5:4.20.0+exp' (experimental [i386]) for 'phonon-backend-null' because of 'amarok-broken' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-broken' +The following extra packages will be installed: + amarok-broken (2.3.2-2+exp) + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-null (4.20.0+exp) +The following NEW packages will be installed: + amarok-broken (2.3.2-2+exp) + amarok-common (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-null (4.20.0+exp) +0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded. +After this operation, 258 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-broken/experimental --trivial-only -V -q=0 + +# switch the candidate for recommends too if they should be installed +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-recommends' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-recommends' +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-recommends' +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-recommends (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) +The following NEW packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-recommends (2.3.2-2+exp) + amarok-utils (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) +0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded. +After this operation, 258 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-recommends/experimental --trivial-only -V -q=0 -o APT::Install-Recommends=1 + +# … or not if not +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-recommends' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-recommends' +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-recommends (2.3.2-2+exp) +Recommended packages: + amarok-utils (2.3.1-1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) + phonon-backend () + libmtp8 (0.3.1+sid) + libc6 (2.11.2-7+sid) +The following NEW packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-recommends (2.3.2-2+exp) +0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded. +After this operation, 86.0 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-recommends/experimental --trivial-only -V -q=0 -o APT::Install-Recommends=0 + +# but broken recommends are not the end of the world +# FIXME: the version output for recommend packages is a bit strange… but what would be better? +testequal "Reading package lists... +Building dependency tree... +Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-recommends2' +Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-recommends2' +The following extra packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-recommends2 (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) +Recommended packages: + amarok-utils (2.3.1-1+sid) +The following NEW packages will be installed: + amarok-common (2.3.2-2+exp) + amarok-recommends2 (2.3.2-2+exp) + libc6 (2.11.2-7+sid) + libmtp8 (0.3.1+sid) + phonon-backend-xine (4.6.0really4.4.2-1+sid) +0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded. +After this operation, 215 kB of additional disk space will be used. +E: Trivial Only specified but this is not a trivial operation." aptget install amarok-recommends2/experimental --trivial-only -V -q=0 -o APT::Install-Recommends=1 + +# if one depends doesn't work, we don't need to look deeper… +testequal "Reading package lists... +Building dependency tree... +Selected version '1.0' (experimental [all]) for 'uninstallablepkg' +Some packages could not be installed. This may mean that you have +requested an impossible situation or if you are using the unstable +distribution that some required packages have not yet been created +or been moved out of Incoming. +The following information may help to resolve the situation: + +The following packages have unmet dependencies: + uninstallablepkg : Depends: libmtp8 (>= 10:0.20.1) but it is not going to be installed + Depends: amarok-utils (= 2.3.2-2+exp) but 2.3.1-1+sid is to be installed +E: Broken packages" aptget install uninstallablepkg/experimental --trivial-only -V -q=0 -- cgit v1.2.3-70-g09d2 From 758729c8084a19859d3c9ccf948bb2ec507b0d0c Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 5 Dec 2010 12:59:07 +0100 Subject: check in the DepCache::SetCandidateVersion that the package is marked to be installed before setting the InstallVer as otherwise the Sizes states will be confused in some cases - this can happen now as SetCandidateRelease works also on packages which are not installed now (or will never in the final solution) --- apt-pkg/depcache.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'apt-pkg') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 4a8e53eb3..594c085a5 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1523,7 +1523,7 @@ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer, bool const &Pseudo) RemoveSizes(Pkg); RemoveStates(Pkg); - if (P.CandidateVer == P.InstallVer) + if (P.CandidateVer == P.InstallVer && P.Install() == true) P.InstallVer = (Version *)TargetVer; P.CandidateVer = (Version *)TargetVer; P.Update(Pkg,*this); @@ -1570,6 +1570,7 @@ bool pkgDepCache::SetCandidateRelease(pkgCache::VerIterator TargetVer, std::string const &TargetRel, std::list > &Changed) { + ActionGroup group(*this); SetCandidateVersion(TargetVer); if (TargetRel == "installed" || TargetRel == "candidate") // both doesn't make sense in this context -- cgit v1.2.3-70-g09d2 From 36f1098aed548651a32a2c15cc9ad80c4330b4d9 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Wed, 12 Jan 2011 17:09:04 +0100 Subject: * apt-pkg/contrib/fileutl.cc: - add a RealFileExists method and check that your configuration files are real files to avoid endless loops if not (Closes: #604401) --- apt-pkg/contrib/fileutl.cc | 20 ++++++- apt-pkg/contrib/fileutl.h | 1 + apt-pkg/depcache.cc | 4 +- apt-pkg/init.cc | 6 +- apt-pkg/policy.cc | 2 +- apt-pkg/sourcelist.cc | 6 +- apt-pkg/vendorlist.cc | 4 +- debian/changelog | 5 +- .../test-bug-604401-files-are-directories | 66 ++++++++++++++++++++++ 9 files changed, 101 insertions(+), 13 deletions(-) create mode 100755 test/integration/test-bug-604401-files-are-directories (limited to 'apt-pkg') diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc index f4ab066d7..db6057ea3 100644 --- a/apt-pkg/contrib/fileutl.cc +++ b/apt-pkg/contrib/fileutl.cc @@ -191,7 +191,7 @@ int GetLock(string File,bool Errors) /*}}}*/ // FileExists - Check if a file exists /*{{{*/ // --------------------------------------------------------------------- -/* */ +/* Beware: Directories are also files! */ bool FileExists(string File) { struct stat Buf; @@ -200,6 +200,17 @@ bool FileExists(string File) return true; } /*}}}*/ +// RealFileExists - Check if a file exists and if it is really a file /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool RealFileExists(string File) +{ + struct stat Buf; + if (stat(File.c_str(),&Buf) != 0) + return false; + return ((Buf.st_mode & S_IFREG) != 0); +} + /*}}}*/ // DirectoryExists - Check if a directory exists and is really one /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -304,6 +315,13 @@ std::vector GetListOfFilesInDir(string const &Dir, std::vector c } std::vector List; + + if (DirectoryExists(Dir.c_str()) == false) + { + _error->Error(_("List of files can't be created as '%s' is not a directory"), Dir.c_str()); + return List; + } + Configuration::MatchAgainstConfig SilentIgnore("Dir::Ignore-Files-Silently"); DIR *D = opendir(Dir.c_str()); if (D == 0) diff --git a/apt-pkg/contrib/fileutl.h b/apt-pkg/contrib/fileutl.h index 1380f06b4..146d917d8 100644 --- a/apt-pkg/contrib/fileutl.h +++ b/apt-pkg/contrib/fileutl.h @@ -93,6 +93,7 @@ bool RunScripts(const char *Cnf); bool CopyFile(FileFd &From,FileFd &To); int GetLock(string File,bool Errors = true); bool FileExists(string File); +bool RealFileExists(string File); bool DirectoryExists(string const &Path) __attrib_const; bool CreateDirectory(string const &Parent, string const &Path); diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 594c085a5..d2557386d 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -167,7 +167,7 @@ bool pkgDepCache::readStateFile(OpProgress *Prog) /*{{{*/ { FileFd state_file; string const state = _config->FindFile("Dir::State::extended_states"); - if(FileExists(state)) { + if(RealFileExists(state)) { state_file.Open(state, FileFd::ReadOnly); int const file_size = state_file.Size(); if(Prog != NULL) @@ -226,7 +226,7 @@ bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly) /*{{{*/ string const state = _config->FindFile("Dir::State::extended_states"); // if it does not exist, create a empty one - if(!FileExists(state)) + if(!RealFileExists(state)) { StateFile.Open(state, FileFd::WriteAtomic); StateFile.Close(); diff --git a/apt-pkg/init.cc b/apt-pkg/init.cc index f0bad78df..734f5b2c4 100644 --- a/apt-pkg/init.cc +++ b/apt-pkg/init.cc @@ -94,10 +94,10 @@ bool pkgInitConfig(Configuration &Cnf) const char *Cfg = getenv("APT_CONFIG"); if (Cfg != 0) { - if (FileExists(Cfg) == true) + if (RealFileExists(Cfg) == true) Res &= ReadConfigFile(Cnf,Cfg); else - _error->WarningE("FileExists",_("Unable to read %s"),Cfg); + _error->WarningE("RealFileExists",_("Unable to read %s"),Cfg); } // Read the configuration parts dir @@ -109,7 +109,7 @@ bool pkgInitConfig(Configuration &Cnf) // Read the main config file string FName = Cnf.FindFile("Dir::Etc::main"); - if (FileExists(FName) == true) + if (RealFileExists(FName) == true) Res &= ReadConfigFile(Cnf,FName); if (Res == false) diff --git a/apt-pkg/policy.cc b/apt-pkg/policy.cc index 4f9d56775..f05b6ca49 100644 --- a/apt-pkg/policy.cc +++ b/apt-pkg/policy.cc @@ -328,7 +328,7 @@ bool ReadPinFile(pkgPolicy &Plcy,string File) if (File.empty() == true) File = _config->FindFile("Dir::Etc::Preferences"); - if (FileExists(File) == false) + if (RealFileExists(File) == false) return true; FileFd Fd(File,FileFd::ReadOnly); diff --git a/apt-pkg/sourcelist.cc b/apt-pkg/sourcelist.cc index c3ec9865a..851eefdfe 100644 --- a/apt-pkg/sourcelist.cc +++ b/apt-pkg/sourcelist.cc @@ -197,7 +197,7 @@ bool pkgSourceList::ReadMainList() string Main = _config->FindFile("Dir::Etc::sourcelist"); string Parts = _config->FindDir("Dir::Etc::sourceparts"); - if (FileExists(Main) == true) + if (RealFileExists(Main) == true) Res &= ReadAppend(Main); else if (DirectoryExists(Parts) == false) // Only warn if there are no sources.list.d. @@ -205,9 +205,9 @@ bool pkgSourceList::ReadMainList() if (DirectoryExists(Parts) == true) Res &= ReadSourceDir(Parts); - else if (FileExists(Main) == false) + else if (RealFileExists(Main) == false) // Only warn if there is no sources.list file. - _error->WarningE("FileExists", _("Unable to read %s"), Main.c_str()); + _error->WarningE("RealFileExists", _("Unable to read %s"), Main.c_str()); return Res; } diff --git a/apt-pkg/vendorlist.cc b/apt-pkg/vendorlist.cc index 589997081..92ff38894 100644 --- a/apt-pkg/vendorlist.cc +++ b/apt-pkg/vendorlist.cc @@ -21,11 +21,11 @@ bool pkgVendorList::ReadMainList() Configuration Cnf; string CnfFile = _config->FindDir("Dir::Etc::vendorparts"); - if (FileExists(CnfFile) == true) + if (DirectoryExists(CnfFile) == true) if (ReadConfigDir(Cnf,CnfFile,true) == false) return false; CnfFile = _config->FindFile("Dir::Etc::vendorlist"); - if (FileExists(CnfFile) == true) + if (RealFileExists(CnfFile) == true) if (ReadConfigFile(Cnf,CnfFile,true) == false) return false; diff --git a/debian/changelog b/debian/changelog index 2a0b5a8cf..123b88430 100644 --- a/debian/changelog +++ b/debian/changelog @@ -17,8 +17,11 @@ apt (0.8.11+wheezy) unstable; urgency=low requested on the commandline, e.g. with a modifier * debian/control: - add Vcs-Browser now that loggerhead works again (Closes: #511168) + * apt-pkg/contrib/fileutl.cc: + - add a RealFileExists method and check that your configuration files + are real files to avoid endless loops if not (Closes: #604401) - -- David Kalnischkies Fri, 03 Dec 2010 17:30:52 +0100 + -- David Kalnischkies Wed, 12 Jan 2011 16:53:41 +0100 apt (0.8.10) unstable; urgency=low diff --git a/test/integration/test-bug-604401-files-are-directories b/test/integration/test-bug-604401-files-are-directories new file mode 100755 index 000000000..917fb106f --- /dev/null +++ b/test/integration/test-bug-604401-files-are-directories @@ -0,0 +1,66 @@ +#!/bin/sh +set -e + +local TESTDIR=$(readlink -f $(dirname $0)) +. $TESTDIR/framework +setupenvironment +configarchitecture "i386" +setupaptarchive + +test ! -e rootdir/etc/apt/apt.conf || mv rootdir/etc/apt/apt.conf rootdir/etc/apt/apt.conf.d/000move-away-apt.conf + +msgtest "Directory instead of a file as apt.conf ignored" +mkdir -p rootdir/etc/apt/apt.conf +aptconfig dump > /dev/null && msgpass || msgfail +rmdir rootdir/etc/apt/apt.conf + +msgtest "Good link instead of a file as apt.conf ignored" +echo 'Test::APT::Link "good";' > rootdir/etc/apt/good-link.conf +ln -s rootdir/etc/apt/good-link.conf rootdir/etc/apt/apt.conf +test -n "$(aptconfig shell TestLink 'Test::APT::Link')" && msgfail || msgpass +rm rootdir/etc/apt/apt.conf + +msgtest "Broken link instead of a file as apt.conf ignored" +ln -s /tmp/doesnt-exist rootdir/etc/apt/apt.conf +aptconfig dump > /dev/null && msgpass || msgfail +rm rootdir/etc/apt/apt.conf + + +test ! -e rootdir/etc/apt/sources.list || mv rootdir/etc/apt/sources.list rootdir/etc/apt/sources.list.d/000move-away-sources.list + +msgtest "Directory instead of a file as sources.list ignored" +mkdir -p rootdir/etc/apt/sources.list +aptget update --print-uris 2> /dev/null && msgpass || msgfail +rmdir rootdir/etc/apt/sources.list + +msgtest "Good link instead of a file as sources.list ignored" +echo 'deb file:///tmp/debian sid main' > rootdir/etc/apt/good-link.list +ln -s rootdir/etc/apt/good-link.list rootdir/etc/apt/sources.list +test -n "$(aptget update --print-uris)" && msgfail || msgpass +rm rootdir/etc/apt/sources.list + +msgtest "Broken link instead of a file as sources.list ignored" +ln -s /tmp/doesnt-exist rootdir/etc/apt/sources.list +test -n "$(aptget update --print-uris)" && msgfail || msgpass +rm rootdir/etc/apt/sources.list + + +test ! -e rootdir/etc/apt/preferences || mv rootdir/etc/apt/preferences rootdir/etc/apt/preferences.d/000move-away-preferences + +msgtest "Directory instead of a file as preferences ignored" +mkdir -p rootdir/etc/apt/preferences +aptcache policy > /dev/null 2> /dev/null && msgpass || msgfail +rmdir rootdir/etc/apt/preferences + +msgtest "Good link instead of a file as preferences ignored" +echo 'Package: apt +Pin: release a=now +Pin-Value: 1000' > rootdir/etc/apt/good-link.pref +ln -s rootdir/etc/apt/good-link.pref rootdir/etc/apt/preferences +test -n "$(aptcache policy | grep 1000)" && msgfail || msgpass +rm rootdir/etc/apt/preferences + +msgtest "Broken link instead of a file as preferences ignored" +ln -s /tmp/doesnt-exist rootdir/etc/apt/preferences +aptcache policy > /dev/null 2> /dev/null && msgpass || msgfail +rm rootdir/etc/apt/preferences -- cgit v1.2.3-70-g09d2 From c65607c5ecc2c3a16d3432583dd859d6fb2dc3aa Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Wed, 12 Jan 2011 17:15:26 +0100 Subject: * apt-pkg/contrib/weakptr.h: - include stddefs.h to fix compile error (undefined NULL) with gcc-4.6 --- apt-pkg/contrib/weakptr.h | 2 ++ debian/changelog | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'apt-pkg') diff --git a/apt-pkg/contrib/weakptr.h b/apt-pkg/contrib/weakptr.h index 5158e393c..8de727d89 100644 --- a/apt-pkg/contrib/weakptr.h +++ b/apt-pkg/contrib/weakptr.h @@ -22,6 +22,8 @@ #define WEAK_POINTER_H #include +#include + /** * Class for objects providing support for weak pointers. * diff --git a/debian/changelog b/debian/changelog index 123b88430..61b551cc9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -20,8 +20,10 @@ apt (0.8.11+wheezy) unstable; urgency=low * apt-pkg/contrib/fileutl.cc: - add a RealFileExists method and check that your configuration files are real files to avoid endless loops if not (Closes: #604401) + * apt-pkg/contrib/weakptr.h: + - include stddefs.h to fix compile error (undefined NULL) with gcc-4.6 - -- David Kalnischkies Wed, 12 Jan 2011 16:53:41 +0100 + -- David Kalnischkies Wed, 12 Jan 2011 17:14:14 +0100 apt (0.8.10) unstable; urgency=low -- cgit v1.2.3-70-g09d2 From cda456d793c0f1e16a9572645caaa11ca636aec3 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Wed, 12 Jan 2011 23:23:36 +0100 Subject: remove two unused variables gcc-4.6 reports from depcache.cc --- apt-pkg/depcache.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index d2557386d..5f59b6d49 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1330,8 +1330,6 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, for (DepIterator D = instVer.DependsList(); D.end() != true; D++) { //FIXME: deal better with or-groups(?) - DepIterator LocalStart = D; - if(IsImportantDep(D) && !D.IsCritical() && Start.TargetPkg() == D.TargetPkg()) { @@ -1921,10 +1919,11 @@ void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &pkg, return; VerIterator const currver = pkg.CurrentVer(); - VerIterator const candver = state.CandidateVerIter(*this); VerIterator const instver = state.InstVerIter(*this); #if 0 + VerIterator const candver = state.CandidateVerIter(*this); + // If a package was garbage-collected but is now being marked, we // should re-select it // For cases when a pkg is set to upgrade and this trigger the -- cgit v1.2.3-70-g09d2 From ed0fe654305469c1f9869f308e2f595ac8bc4c13 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Wed, 12 Jan 2011 23:31:37 +0100 Subject: remove the unused Die boolean - error reporting is done by ExecWait --- apt-pkg/deb/dpkgpm.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 9f0da3be6..95a3f173b 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -322,7 +322,6 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) return _error->Errno("fdopen","Faild to open new FD"); // Feed it the filenames. - bool Die = false; if (Version <= 1) { for (vector::iterator I = List.begin(); I != List.end(); I++) @@ -339,14 +338,11 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) into the pipe. */ fprintf(F,"%s\n",I->File.c_str()); if (ferror(F) != 0) - { - Die = true; break; - } } } else - Die = !SendV2Pkgs(F); + SendV2Pkgs(F); fclose(F); -- cgit v1.2.3-70-g09d2 From 23f3cfd036630c1c8f84159c60986f67167066e8 Mon Sep 17 00:00:00 2001 From: Petter Reinholdtsen Date: Wed, 12 Jan 2011 23:57:03 +0100 Subject: * deb/dpkgpm.cc: - fix popen/fclose mismatch reported by cppcheck. Thanks to Petter Reinholdtsen for report and patch! (Closes: #607803) --- apt-pkg/deb/dpkgpm.cc | 4 ++-- debian/changelog | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 95a3f173b..3b10e1a23 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1411,7 +1411,7 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) { while( fgets(buf, sizeof(buf), log) != NULL) fprintf(report, " %s", buf); - fclose(log); + pclose(log); } } @@ -1427,7 +1427,7 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) { while( fgets(buf, sizeof(buf), log) != NULL) fprintf(report, " %s", buf); - fclose(log); + pclose(log); } } diff --git a/debian/changelog b/debian/changelog index 81f741d03..dde28c635 100644 --- a/debian/changelog +++ b/debian/changelog @@ -24,8 +24,11 @@ apt (0.8.11+wheezy) unstable; urgency=low - include stddefs.h to fix compile error (undefined NULL) with gcc-4.6 * methods/https.cc: - fix CURLOPT_SSL_VERIFYHOST by really passing 2 to it if enabled + * deb/dpkgpm.cc: + - fix popen/fclose mismatch reported by cppcheck. Thanks to Petter + Reinholdtsen for report and patch! (Closes: #607803) - -- David Kalnischkies Wed, 12 Jan 2011 23:46:08 +0100 + -- David Kalnischkies Wed, 12 Jan 2011 23:53:32 +0100 apt (0.8.10) unstable; urgency=low -- cgit v1.2.3-70-g09d2 From c55b8a54780c4db6a5fa270ddd2d05ab837f6ffb Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Thu, 13 Jan 2011 17:16:11 +0100 Subject: * apt-pkg/cacheiterator.h: - do not segfault if cache is not build (Closes: #254770) --- apt-pkg/cacheiterators.h | 18 +++---- apt-pkg/policy.cc | 2 + debian/changelog | 4 +- ...test-bug-254770-segfault-if-cache-not-buildable | 57 ++++++++++++++++++++++ 4 files changed, 71 insertions(+), 10 deletions(-) create mode 100755 test/integration/test-bug-254770-segfault-if-cache-not-buildable (limited to 'apt-pkg') diff --git a/apt-pkg/cacheiterators.h b/apt-pkg/cacheiterators.h index 26070636e..449d4b441 100644 --- a/apt-pkg/cacheiterators.h +++ b/apt-pkg/cacheiterators.h @@ -96,7 +96,7 @@ class pkgCache::GrpIterator: public Iterator { protected: inline Group* OwnerPointer() const { - return Owner->GrpP; + return (Owner != 0) ? Owner->GrpP : 0; }; public: @@ -137,7 +137,7 @@ class pkgCache::PkgIterator: public Iterator { protected: inline Package* OwnerPointer() const { - return Owner->PkgP; + return (Owner != 0) ? Owner->PkgP : 0; }; public: @@ -184,7 +184,7 @@ class pkgCache::PkgIterator: public Iterator { class pkgCache::VerIterator : public Iterator { protected: inline Version* OwnerPointer() const { - return Owner->VerP; + return (Owner != 0) ? Owner->VerP : 0; }; public: @@ -241,7 +241,7 @@ class pkgCache::VerIterator : public Iterator { class pkgCache::DescIterator : public Iterator { protected: inline Description* OwnerPointer() const { - return Owner->DescP; + return (Owner != 0) ? Owner->DescP : 0; }; public: @@ -270,7 +270,7 @@ class pkgCache::DepIterator : public Iterator { protected: inline Dependency* OwnerPointer() const { - return Owner->DepP; + return (Owner != 0) ? Owner->DepP : 0; }; public: @@ -315,7 +315,7 @@ class pkgCache::PrvIterator : public Iterator { protected: inline Provides* OwnerPointer() const { - return Owner->ProvideP; + return (Owner != 0) ? Owner->ProvideP : 0; }; public: @@ -349,7 +349,7 @@ class pkgCache::PrvIterator : public Iterator { class pkgCache::PkgFileIterator : public Iterator { protected: inline PackageFile* OwnerPointer() const { - return Owner->PkgFileP; + return (Owner != 0) ? Owner->PkgFileP : 0; }; public: @@ -382,7 +382,7 @@ class pkgCache::PkgFileIterator : public Iterator class pkgCache::VerFileIterator : public pkgCache::Iterator { protected: inline VerFile* OwnerPointer() const { - return Owner->VerFileP; + return (Owner != 0) ? Owner->VerFileP : 0; }; public: @@ -401,7 +401,7 @@ class pkgCache::VerFileIterator : public pkgCache::Iterator { protected: inline DescFile* OwnerPointer() const { - return Owner->DescFileP; + return (Owner != 0) ? Owner->DescFileP : 0; }; public: diff --git a/apt-pkg/policy.cc b/apt-pkg/policy.cc index f05b6ca49..5427271b6 100644 --- a/apt-pkg/policy.cc +++ b/apt-pkg/policy.cc @@ -45,6 +45,8 @@ using namespace std; file matches the V0 policy engine. */ pkgPolicy::pkgPolicy(pkgCache *Owner) : Pins(0), PFPriority(0), Cache(Owner) { + if (Owner == 0 || &(Owner->Head()) == 0) + return; PFPriority = new signed short[Owner->Head().PackageFileCount]; Pins = new Pin[Owner->Head().PackageCount]; diff --git a/debian/changelog b/debian/changelog index 3a59ac171..febd259c0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -32,8 +32,10 @@ apt (0.8.11+wheezy) unstable; urgency=low * apt-inst/contrib/extracttar.cc: - let apt-utils work with encoded tar headers if uid/gid are large. Thanks to Nobuhiro Hayashi for the patch! (Closes: #330162) + * apt-pkg/cacheiterator.h: + - do not segfault if cache is not build (Closes: #254770) - -- David Kalnischkies Thu, 13 Jan 2011 00:25:32 +0100 + -- David Kalnischkies Thu, 13 Jan 2011 17:10:36 +0100 apt (0.8.10) unstable; urgency=low diff --git a/test/integration/test-bug-254770-segfault-if-cache-not-buildable b/test/integration/test-bug-254770-segfault-if-cache-not-buildable new file mode 100755 index 000000000..25c564daa --- /dev/null +++ b/test/integration/test-bug-254770-segfault-if-cache-not-buildable @@ -0,0 +1,57 @@ +#!/bin/sh +set -e + +local TESTDIR=$(readlink -f $(dirname $0)) +. $TESTDIR/framework +setupenvironment +configarchitecture "i386" +setupaptarchive + +CURRENTTRAP="chmod a+x rootdir/var/lib/dpkg; $CURRENTTRAP" +trap "$CURRENTTRAP" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM +chmod a-x rootdir/var/lib/dpkg + +testsegfault() { + rm -f rootdir/var/cache/apt/*.bin + msgtest "No segfault in" "$*" + local TEST="$($* 2>&1 | grep -v 'E:')" + if [ -z "$TEST" ]; then + msgpass + else + echo + echo $TEST + msgfail + fi +} + +echo 'quiet 2;' > rootdir/etc/apt/apt.conf.d/00be-quiet + +testsegfault aptcache gencaches +testsegfault aptcache showpkg +testsegfault aptcache showsrc +testsegfault aptcache stats +testsegfault aptcache dump +testsegfault aptcache dumpavail +testsegfault aptcache unmet +testsegfault aptcache search +testsegfault aptcache show apt +testsegfault aptcache depends apt +testsegfault aptcache rdepends apt +testsegfault aptcache pkgnames apt +testsegfault aptcache dotty apt +testsegfault aptcache xvcg apt +testsegfault aptcache policy apt + +testsegfault aptget update +testsegfault aptget upgrade +testsegfault aptget dselect-upgrade +testsegfault aptget dist-upgrade +testsegfault aptget install apt +testsegfault aptget remove apt +testsegfault aptget purge apt +testsegfault aptget source apt +testsegfault aptget build-dep apt +testsegfault aptget check +testsegfault aptget clean +testsegfault aptget autoclean +testsegfault aptget autoremove -- cgit v1.2.3-70-g09d2 From 491058e3570ec66769c4e7e9797f549c6d724848 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Thu, 13 Jan 2011 23:22:17 +0100 Subject: ignore non-regular files in GetListOfFilesInDir (Closes: #594694) --- apt-pkg/contrib/fileutl.cc | 28 ++++++++++++++++------------ debian/changelog | 3 ++- test/libapt/run-tests | 5 +++++ 3 files changed, 23 insertions(+), 13 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/contrib/fileutl.cc b/apt-pkg/contrib/fileutl.cc index db6057ea3..52f517ee0 100644 --- a/apt-pkg/contrib/fileutl.cc +++ b/apt-pkg/contrib/fileutl.cc @@ -336,6 +336,20 @@ std::vector GetListOfFilesInDir(string const &Dir, std::vector c if (Ent->d_name[0] == '.') continue; + // Make sure it is a file and not something else + string const File = flCombine(Dir,Ent->d_name); +#ifdef _DIRENT_HAVE_D_TYPE + if (Ent->d_type != DT_REG) +#endif + { + if (RealFileExists(File.c_str()) == false) + { + if (SilentIgnore.Match(Ent->d_name) == false) + _error->Notice(_("Ignoring '%s' in directory '%s' as it is not a regular file"), Ent->d_name, Dir.c_str()); + continue; + } + } + // check for accepted extension: // no extension given -> periods are bad as hell! // extensions given -> "" extension allows no extension @@ -349,7 +363,7 @@ std::vector GetListOfFilesInDir(string const &Dir, std::vector c if (Debug == true) std::clog << "Bad file: " << Ent->d_name << " → no extension" << std::endl; if (SilentIgnore.Match(Ent->d_name) == false) - _error->Notice("Ignoring file '%s' in directory '%s' as it has no filename extension", Ent->d_name, Dir.c_str()); + _error->Notice(_("Ignoring file '%s' in directory '%s' as it has no filename extension"), Ent->d_name, Dir.c_str()); continue; } } @@ -358,7 +372,7 @@ std::vector GetListOfFilesInDir(string const &Dir, std::vector c if (Debug == true) std::clog << "Bad file: " << Ent->d_name << " → bad extension »" << flExtension(Ent->d_name) << "«" << std::endl; if (SilentIgnore.Match(Ent->d_name) == false) - _error->Notice("Ignoring file '%s' in directory '%s' as it has an invalid filename extension", Ent->d_name, Dir.c_str()); + _error->Notice(_("Ignoring file '%s' in directory '%s' as it has an invalid filename extension"), Ent->d_name, Dir.c_str()); continue; } } @@ -391,16 +405,6 @@ std::vector GetListOfFilesInDir(string const &Dir, std::vector c continue; } - // Make sure it is a file and not something else - string const File = flCombine(Dir,Ent->d_name); - struct stat St; - if (stat(File.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0) - { - if (Debug == true) - std::clog << "Bad file: " << Ent->d_name << " → stat says not a good file" << std::endl; - continue; - } - if (Debug == true) std::clog << "Accept file: " << Ent->d_name << " in " << Dir << std::endl; List.push_back(File); diff --git a/debian/changelog b/debian/changelog index febd259c0..ca5d4f235 100644 --- a/debian/changelog +++ b/debian/changelog @@ -20,6 +20,7 @@ apt (0.8.11+wheezy) unstable; urgency=low * apt-pkg/contrib/fileutl.cc: - add a RealFileExists method and check that your configuration files are real files to avoid endless loops if not (Closes: #604401) + - ignore non-regular files in GetListOfFilesInDir (Closes: #594694) * apt-pkg/contrib/weakptr.h: - include stddefs.h to fix compile error (undefined NULL) with gcc-4.6 * methods/https.cc: @@ -35,7 +36,7 @@ apt (0.8.11+wheezy) unstable; urgency=low * apt-pkg/cacheiterator.h: - do not segfault if cache is not build (Closes: #254770) - -- David Kalnischkies Thu, 13 Jan 2011 17:10:36 +0100 + -- David Kalnischkies Thu, 13 Jan 2011 23:19:03 +0100 apt (0.8.10) unstable; urgency=low diff --git a/test/libapt/run-tests b/test/libapt/run-tests index f5fc03446..a66de386d 100755 --- a/test/libapt/run-tests +++ b/test/libapt/run-tests @@ -39,6 +39,11 @@ do "${tmppath}/invälid.conf" \ "${tmppath}/invalíd" \ "${tmppath}/01invalíd" + mkdir "${tmppath}/invaliddir" \ + "${tmppath}/directory.conf" \ + "${tmppath}/directory.list" \ + "${tmppath}/directory.wron" \ + "${tmppath}/directory.list.disabled" ln -s "${tmppath}/anormalfile" "${tmppath}/linkedfile.list" ln -s "${tmppath}/non-existing-file" "${tmppath}/brokenlink.list" elif [ $name = "getLanguages${EXT}" ]; then -- cgit v1.2.3-70-g09d2 From caffd4807f612e931ecca8f7d9b0c0c10de27ce6 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sat, 15 Jan 2011 20:28:03 +0100 Subject: * methods/rred.cc: - operate optional on gzip compressed pdiffs * apt-pkg/acquire-item.cc: - don't uncompress downloaded pdiff files before feeding it to rred --- apt-pkg/acquire-item.cc | 19 +-------------- apt-pkg/acquire-item.h | 2 +- apt-pkg/contrib/fileutl.h | 1 + debian/changelog | 6 ++++- methods/rred.cc | 49 ++++++++++++++++++++++++++++++++------- test/integration/test-pdiff-usage | 1 + 6 files changed, 49 insertions(+), 29 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index 2c4ce91a0..d4e90b552 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -570,24 +570,7 @@ void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash, /* FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI); // sucess in downloading a diff, enter ApplyDiff state - if(State == StateFetchDiff) - { - - if(Debug) - std::clog << "Sending to gzip method: " << FinalFile << std::endl; - - string FileName = LookupTag(Message,"Filename"); - State = StateUnzipDiff; - Local = true; - Desc.URI = "gzip:" + FileName; - DestFile += ".decomp"; - QueueURI(Desc); - Mode = "gzip"; - return; - } - - // sucess in downloading a diff, enter ApplyDiff state - if(State == StateUnzipDiff) + if(State == StateFetchDiff) { // rred excepts the patch as $FinalFile.ed diff --git a/apt-pkg/acquire-item.h b/apt-pkg/acquire-item.h index 943c61876..d97a96a0f 100644 --- a/apt-pkg/acquire-item.h +++ b/apt-pkg/acquire-item.h @@ -449,7 +449,7 @@ class pkgAcqIndexDiffs : public pkgAcquire::Item StateFetchDiff, /** \brief The diff is currently being uncompressed. */ - StateUnzipDiff, + StateUnzipDiff, // FIXME: No longer used /** \brief The diff is currently being applied. */ StateApplyDiff diff --git a/apt-pkg/contrib/fileutl.h b/apt-pkg/contrib/fileutl.h index 146d917d8..cde288ad2 100644 --- a/apt-pkg/contrib/fileutl.h +++ b/apt-pkg/contrib/fileutl.h @@ -72,6 +72,7 @@ class FileFd // Simple manipulators inline int Fd() {return iFd;}; inline void Fd(int fd) {iFd = fd;}; + inline gzFile gzFd() {return gz;}; inline bool IsOpen() {return iFd >= 0;}; inline bool Failed() {return (Flags & Fail) == Fail;}; inline void EraseOnFailure() {Flags |= DelOnFail;}; diff --git a/debian/changelog b/debian/changelog index 11768794d..5c8adb434 100644 --- a/debian/changelog +++ b/debian/changelog @@ -49,8 +49,12 @@ apt (0.8.11+wheezy) unstable; urgency=low - remove as it is not needed for a working 'bzr bd' * debian/{apt,apt-utils}.symbols: - ship experimental unmangled c++ symbol files + * methods/rred.cc: + - operate optional on gzip compressed pdiffs + * apt-pkg/acquire-item.cc: + - don't uncompress downloaded pdiff files before feeding it to rred - -- David Kalnischkies Sat, 15 Jan 2011 02:42:18 +0100 + -- David Kalnischkies Sat, 15 Jan 2011 20:24:03 +0100 apt (0.8.10) unstable; urgency=low diff --git a/methods/rred.cc b/methods/rred.cc index d51c45c85..1a18a381c 100644 --- a/methods/rred.cc +++ b/methods/rred.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include /*}}}*/ /** \brief RredMethod - ed-style incremential patch method {{{ @@ -33,11 +34,14 @@ class RredMethod : public pkgAcqMethod { // return values enum State {ED_OK, ED_ORDERING, ED_PARSER, ED_FAILURE, MMAP_FAILED}; - State applyFile(FILE *ed_cmds, FILE *in_file, FILE *out_file, + State applyFile(gzFile &ed_cmds, FILE *in_file, FILE *out_file, unsigned long &line, char *buffer, Hashes *hash) const; void ignoreLineInFile(FILE *fin, char *buffer) const; + void ignoreLineInFile(gzFile &fin, char *buffer) const; void copyLinesFromFileToFile(FILE *fin, FILE *fout, unsigned int lines, Hashes *hash, char *buffer) const; + void copyLinesFromFileToFile(gzFile &fin, FILE *fout, unsigned int lines, + Hashes *hash, char *buffer) const; State patchFile(FileFd &Patch, FileFd &From, FileFd &out_file, Hashes *hash) const; State patchMMap(FileFd &Patch, FileFd &From, FileFd &out_file, Hashes *hash) const; @@ -65,10 +69,10 @@ public: * \param hash the created file for correctness * \return the success State of the ed command executor */ -RredMethod::State RredMethod::applyFile(FILE *ed_cmds, FILE *in_file, FILE *out_file, +RredMethod::State RredMethod::applyFile(gzFile &ed_cmds, FILE *in_file, FILE *out_file, unsigned long &line, char *buffer, Hashes *hash) const { // get the current command and parse it - if (fgets(buffer, BUF_SIZE, ed_cmds) == NULL) { + if (gzgets(ed_cmds, buffer, BUF_SIZE) == NULL) { if (Debug == true) std::clog << "rred: encounter end of file - we can start patching now." << std::endl; line = 0; @@ -123,7 +127,7 @@ RredMethod::State RredMethod::applyFile(FILE *ed_cmds, FILE *in_file, FILE *out_ unsigned char mode = *idx; // save the current position - unsigned const long pos = ftell(ed_cmds); + unsigned const long pos = gztell(ed_cmds); // if this is add or change then go to the next full stop unsigned int data_length = 0; @@ -157,7 +161,7 @@ RredMethod::State RredMethod::applyFile(FILE *ed_cmds, FILE *in_file, FILE *out_ // include data from ed script if (mode == MODE_CHANGED || mode == MODE_ADDED) { - fseek(ed_cmds, pos, SEEK_SET); + gzseek(ed_cmds, pos, SEEK_SET); copyLinesFromFileToFile(ed_cmds, out_file, data_length, hash, buffer); } @@ -183,6 +187,18 @@ void RredMethod::copyLinesFromFileToFile(FILE *fin, FILE *fout, unsigned int lin } } /*}}}*/ +void RredMethod::copyLinesFromFileToFile(gzFile &fin, FILE *fout, unsigned int lines,/*{{{*/ + Hashes *hash, char *buffer) const { + while (0 < lines--) { + do { + gzgets(fin, buffer, BUF_SIZE); + size_t const written = fwrite(buffer, 1, strlen(buffer), fout); + hash->Add((unsigned char*)buffer, written); + } while (strlen(buffer) == (BUF_SIZE - 1) && + buffer[BUF_SIZE - 2] != '\n'); + } +} + /*}}}*/ void RredMethod::ignoreLineInFile(FILE *fin, char *buffer) const { /*{{{*/ fgets(buffer, BUF_SIZE, fin); while (strlen(buffer) == (BUF_SIZE - 1) && @@ -192,11 +208,20 @@ void RredMethod::ignoreLineInFile(FILE *fin, char *buffer) const { /*{{{*/ } } /*}}}*/ +void RredMethod::ignoreLineInFile(gzFile &fin, char *buffer) const { /*{{{*/ + gzgets(fin, buffer, BUF_SIZE); + while (strlen(buffer) == (BUF_SIZE - 1) && + buffer[BUF_SIZE - 2] != '\n') { + gzgets(fin, buffer, BUF_SIZE); + buffer[0] = ' '; + } +} + /*}}}*/ RredMethod::State RredMethod::patchFile(FileFd &Patch, FileFd &From, /*{{{*/ FileFd &out_file, Hashes *hash) const { char buffer[BUF_SIZE]; FILE* fFrom = fdopen(From.Fd(), "r"); - FILE* fPatch = fdopen(Patch.Fd(), "r"); + gzFile fPatch = Patch.gzFd(); FILE* fTo = fdopen(out_file.Fd(), "w"); /* we do a tail recursion to read the commands in the right order */ @@ -228,6 +253,12 @@ RredMethod::State RredMethod::patchMMap(FileFd &Patch, FileFd &From, /*{{{*/ FileFd &out_file, Hashes *hash) const { #ifdef _POSIX_MAPPED_FILES MMap ed_cmds(Patch, MMap::ReadOnly); + if (Patch.gzFd() != NULL) { + unsigned long mapSize = Patch.Size(); + DynamicMMap dyn(0, mapSize, 0); + gzread(Patch.gzFd(), dyn.Data(), mapSize); + ed_cmds = dyn; + } MMap in_file(From, MMap::ReadOnly); if (ed_cmds.Size() == 0 || in_file.Size() == 0) @@ -445,7 +476,7 @@ bool RredMethod::Fetch(FetchItem *Itm) /*{{{*/ // Open the source and destination files (the d'tor of FileFd will do // the cleanup/closing of the fds) FileFd From(Path,FileFd::ReadOnly); - FileFd Patch(Path+".ed",FileFd::ReadOnly); + FileFd Patch(Path+".ed",FileFd::ReadOnlyGzip); FileFd To(Itm->DestFile,FileFd::WriteAtomic); To.EraseOnFailure(); if (_error->PendingError() == true) @@ -456,8 +487,8 @@ bool RredMethod::Fetch(FetchItem *Itm) /*{{{*/ State const result = patchMMap(Patch, From, To, &Hash); if (result == MMAP_FAILED) { // retry with patchFile - lseek(Patch.Fd(), 0, SEEK_SET); - lseek(From.Fd(), 0, SEEK_SET); + Patch.Seek(0); + From.Seek(0); To.Open(Itm->DestFile,FileFd::WriteAtomic); if (_error->PendingError() == true) return false; diff --git a/test/integration/test-pdiff-usage b/test/integration/test-pdiff-usage index a70b6122c..eb1818bcd 100755 --- a/test/integration/test-pdiff-usage +++ b/test/integration/test-pdiff-usage @@ -44,6 +44,7 @@ sed -i aptarchive/Release \ -e "/^SHA256:/ a\ \ $(sha256sum $PATCHINDEX | cut -d' ' -f 1) $(stat -c%s $PATCHINDEX) Packages.diff/Index" signreleasefiles +rm -f aptarchive/Packages aptarchive/Packages.gz aptarchive/Packages.bz2 aptarchive/Packages.lzma aptget update -qq testnopackage oldstuff -- cgit v1.2.3-70-g09d2 From fe0f7911b650918e1d511b3453664a07f6d966d0 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Thu, 20 Jan 2011 14:53:52 +0100 Subject: - try downloading clearsigned InRelease before trying Release.gpg * apt-pkg/deb/deblistparser.cc: - rewrite LoadReleaseInfo to cope with clearsigned Releasefiles --- apt-pkg/acquire-item.cc | 54 ++++++- apt-pkg/acquire-item.h | 33 +++++ apt-pkg/deb/debindexfile.cc | 8 +- apt-pkg/deb/deblistparser.cc | 114 ++++++++++----- apt-pkg/deb/debmetaindex.cc | 34 +++-- apt-pkg/indexcopy.cc | 3 +- apt-pkg/indexrecords.cc | 17 ++- apt-pkg/tagfile.cc | 10 +- apt-pkg/tagfile.h | 2 + debian/changelog | 5 +- doc/apt-secure.8.xml | 5 +- test/integration/Packages-releasefile-verification | 18 +++ .../Packages-releasefile-verification-new | 21 +++ test/integration/framework | 7 + test/integration/marvinparanoid.pub | Bin 0 -> 629 bytes test/integration/marvinparanoid.sec | Bin 0 -> 1280 bytes .../test-bug-595691-empty-and-broken-archive-files | 32 ++--- test/integration/test-releasefile-verification | 160 +++++++++++++++++++++ 18 files changed, 430 insertions(+), 93 deletions(-) create mode 100644 test/integration/Packages-releasefile-verification create mode 100644 test/integration/Packages-releasefile-verification-new create mode 100644 test/integration/marvinparanoid.pub create mode 100644 test/integration/marvinparanoid.sec create mode 100755 test/integration/test-releasefile-verification (limited to 'apt-pkg') diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index d4e90b552..2cd6ab359 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -1077,6 +1077,8 @@ void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string Hash, /*{{{* { string FinalFile = _config->FindDir("Dir::State::lists"); FinalFile += URItoFileName(RealURI); + if (SigFile == DestFile) + SigFile = FinalFile; Rename(DestFile,FinalFile); chmod(FinalFile.c_str(),0644); DestFile = FinalFile; @@ -1110,6 +1112,8 @@ void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/ { string FinalFile = _config->FindDir("Dir::State::lists"); FinalFile += URItoFileName(RealURI); + if (SigFile == DestFile) + SigFile = FinalFile; DestFile = FinalFile; } Complete = true; @@ -1141,6 +1145,10 @@ void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/ // Download further indexes with verification QueueIndexes(true); + // is it a clearsigned MetaIndex file? + if (DestFile == SigFile) + return; + // Done, move signature file into position string VerifiedSigFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI) + ".gpg"; @@ -1300,13 +1308,20 @@ void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) if (AuthPass == true) { // gpgv method failed, if we have a good signature - string LastGoodSigFile = _config->FindDir("Dir::State::lists") + - "partial/" + URItoFileName(RealURI) + ".gpg.reverify"; + string LastGoodSigFile = _config->FindDir("Dir::State::lists"); + if (DestFile == SigFile) + LastGoodSigFile.append(URItoFileName(RealURI)); + else + LastGoodSigFile.append("partial/").append(URItoFileName(RealURI)).append(".gpg.reverify"); + if(FileExists(LastGoodSigFile)) { - string VerifiedSigFile = _config->FindDir("Dir::State::lists") + - URItoFileName(RealURI) + ".gpg"; - Rename(LastGoodSigFile,VerifiedSigFile); + if (DestFile != SigFile) + { + string VerifiedSigFile = _config->FindDir("Dir::State::lists") + + URItoFileName(RealURI) + ".gpg"; + Rename(LastGoodSigFile,VerifiedSigFile); + } Status = StatTransientNetworkError; _error->Warning(_("A error occurred during the signature " "verification. The repository is not updated " @@ -1330,6 +1345,35 @@ void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) QueueIndexes(false); } /*}}}*/ +pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/ + string const &URI, string const &URIDesc, string const &ShortDesc, + string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc, + string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc, + const vector* IndexTargets, + indexRecords* MetaIndexParser) : + pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser), + MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc), + MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc) +{ + SigFile = DestFile; +} + /*}}}*/ +void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/ +{ + if (AuthPass == false) + { + new pkgAcqMetaSig(Owner, + MetaSigURI, MetaSigURIDesc, MetaSigShortDesc, + MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc, + IndexTargets, MetaIndexParser); + if (Cnf->LocalOnly == true || + StringToBool(LookupTag(Message, "Transient-Failure"), false) == false) + Dequeue(); + } + else + pkgAcqMetaIndex::Failed(Message, Cnf); +} + /*}}}*/ // AcqArchive::AcqArchive - Constructor /*{{{*/ // --------------------------------------------------------------------- /* This just sets up the initial fetch environment and queues the first diff --git a/apt-pkg/acquire-item.h b/apt-pkg/acquire-item.h index d97a96a0f..581761e32 100644 --- a/apt-pkg/acquire-item.h +++ b/apt-pkg/acquire-item.h @@ -772,6 +772,39 @@ class pkgAcqMetaIndex : public pkgAcquire::Item indexRecords* MetaIndexParser); }; /*}}}*/ +/** \brief An item repsonsible for downloading clearsigned metaindexes {{{*/ +class pkgAcqMetaClearSig : public pkgAcqMetaIndex +{ + /** \brief The URI of the meta-index file for the detached signature */ + string MetaIndexURI; + + /** \brief A "URI-style" description of the meta-index file */ + string MetaIndexURIDesc; + + /** \brief A brief description of the meta-index file */ + string MetaIndexShortDesc; + + /** \brief The URI of the detached meta-signature file if the clearsigned one failed. */ + string MetaSigURI; + + /** \brief A "URI-style" description of the meta-signature file */ + string MetaSigURIDesc; + + /** \brief A brief description of the meta-signature file */ + string MetaSigShortDesc; + +public: + void Failed(string Message,pkgAcquire::MethodConfig *Cnf); + + /** \brief Create a new pkgAcqMetaClearSig. */ + pkgAcqMetaClearSig(pkgAcquire *Owner, + string const &URI, string const &URIDesc, string const &ShortDesc, + string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc, + string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc, + const vector* IndexTargets, + indexRecords* MetaIndexParser); +}; + /*}}}*/ /** \brief An item that is responsible for fetching a package file. {{{ * * If the package file already exists in the cache, nothing will be diff --git a/apt-pkg/deb/debindexfile.cc b/apt-pkg/deb/debindexfile.cc index af1209ccb..9961b5ae4 100644 --- a/apt-pkg/deb/debindexfile.cc +++ b/apt-pkg/deb/debindexfile.cc @@ -324,8 +324,14 @@ bool debPackagesIndex::Merge(pkgCacheGenerator &Gen,OpProgress *Prog) const return _error->Error("Problem with MergeList %s",PackageFile.c_str()); // Check the release file - string ReleaseFile = debReleaseIndex(URI,Dist).MetaIndexFile("Release"); + string ReleaseFile = debReleaseIndex(URI,Dist).MetaIndexFile("InRelease"); + bool releaseExists = false; if (FileExists(ReleaseFile) == true) + releaseExists = true; + else + ReleaseFile = debReleaseIndex(URI,Dist).MetaIndexFile("Release"); + + if (releaseExists == true || FileExists(ReleaseFile) == true) { FileFd Rel(ReleaseFile,FileFd::ReadOnly); if (_error->PendingError() == true) diff --git a/apt-pkg/deb/deblistparser.cc b/apt-pkg/deb/deblistparser.cc index 1b3bfd6ae..9201e6a54 100644 --- a/apt-pkg/deb/deblistparser.cc +++ b/apt-pkg/deb/deblistparser.cc @@ -783,45 +783,89 @@ bool debListParser::Step() bool debListParser::LoadReleaseInfo(pkgCache::PkgFileIterator &FileI, FileFd &File, string component) { - pkgTagFile Tags(&File, File.Size() + 256); // XXX - pkgTagSection Section; - if (Tags.Step(Section) == false) - return false; - - // FIXME: Do we need it now for multi-arch? - // mvo: I don't think we need to fill that in (it's unused since apt-0.6) -// FileI->Architecture = WriteUniqString(Arch); - // apt-secure does no longer download individual (per-section) Release // file. to provide Component pinning we use the section name now FileI->Component = WriteUniqString(component); - const char *Start; - const char *Stop; - if (Section.Find("Suite",Start,Stop) == true) - FileI->Archive = WriteUniqString(Start,Stop - Start); - if (Section.Find("Component",Start,Stop) == true) - FileI->Component = WriteUniqString(Start,Stop - Start); - if (Section.Find("Version",Start,Stop) == true) - FileI->Version = WriteUniqString(Start,Stop - Start); - if (Section.Find("Origin",Start,Stop) == true) - FileI->Origin = WriteUniqString(Start,Stop - Start); - if (Section.Find("Codename",Start,Stop) == true) - FileI->Codename = WriteUniqString(Start,Stop - Start); - if (Section.Find("Label",Start,Stop) == true) - FileI->Label = WriteUniqString(Start,Stop - Start); - if (Section.Find("Architecture",Start,Stop) == true) - FileI->Architecture = WriteUniqString(Start,Stop - Start); - - if (Section.FindFlag("NotAutomatic",FileI->Flags, - pkgCache::Flag::NotAutomatic) == false) - _error->Warning("Bad NotAutomatic flag"); - if (Section.FindFlag("ButAutomaticUpgrades",FileI->Flags, - pkgCache::Flag::ButAutomaticUpgrades) == false) - _error->Warning("Bad ButAutomaticUpgrades flag"); - // overrule the NotAutomatic setting if needed as they are both present for compatibility - else if ((FileI->Flags & pkgCache::Flag::ButAutomaticUpgrades) == pkgCache::Flag::ButAutomaticUpgrades) - FileI->Flags &= ~pkgCache::Flag::NotAutomatic; + FILE* release = fdopen(dup(File.Fd()), "r"); + if (release == NULL) + return false; + + char buffer[101]; + bool gpgClose = false; + while (fgets(buffer, sizeof(buffer), release) != NULL) + { + size_t len = 0; + + // Skip empty lines + for (; buffer[len] == '\r' && buffer[len] == '\n'; ++len); + if (buffer[len] == '\0') + continue; + + // only evalute the first GPG section + if (strncmp("-----", buffer, 5) == 0) + { + if (gpgClose == true) + break; + gpgClose = true; + continue; + } + + // seperate the tag from the data + for (; buffer[len] != ':' && buffer[len] != '\0'; ++len); + if (buffer[len] == '\0') + continue; + char* dataStart = buffer + len; + for (++dataStart; *dataStart == ' '; ++dataStart); + char* dataEnd = dataStart; + for (++dataEnd; *dataEnd != '\0'; ++dataEnd); + + // which datastorage need to be updated + map_ptrloc* writeTo = NULL; + if (buffer[0] == ' ') + ; + #define APT_PARSER_WRITETO(X, Y) else if (strncmp(Y, buffer, len) == 0) writeTo = &X; + APT_PARSER_WRITETO(FileI->Archive, "Suite") + APT_PARSER_WRITETO(FileI->Component, "Component") + APT_PARSER_WRITETO(FileI->Version, "Version") + APT_PARSER_WRITETO(FileI->Origin, "Origin") + APT_PARSER_WRITETO(FileI->Codename, "Codename") + APT_PARSER_WRITETO(FileI->Label, "Label") + #undef APT_PARSER_WRITETO + #define APT_PARSER_FLAGIT(X) else if (strncmp(#X, buffer, len) == 0) \ + pkgTagSection::FindFlag(FileI->Flags, pkgCache::Flag:: X, dataStart, dataEnd-1); + APT_PARSER_FLAGIT(NotAutomatic) + APT_PARSER_FLAGIT(ButAutomaticUpgrades) + #undef APT_PARSER_FLAGIT + + // load all data from the line and save it + string data; + if (writeTo != NULL) + data.append(dataStart, dataEnd); + if (sizeof(buffer) - 1 == (dataEnd - buffer)) + { + while (fgets(buffer, sizeof(buffer), release) != NULL) + { + if (writeTo != NULL) + data.append(buffer); + if (strlen(buffer) != sizeof(buffer) - 1) + break; + } + } + if (writeTo != NULL) + { + // remove spaces and stuff from the end of the data line + for (std::string::reverse_iterator s = data.rbegin(); + s != data.rend(); ++s) + { + if (*s != '\r' && *s != '\n' && *s != ' ') + break; + *s = '\0'; + } + *writeTo = WriteUniqString(data); + } + } + fclose(release); return !_error->PendingError(); } diff --git a/apt-pkg/deb/debmetaindex.cc b/apt-pkg/deb/debmetaindex.cc index 717d0bcde..e2c680b14 100644 --- a/apt-pkg/deb/debmetaindex.cc +++ b/apt-pkg/deb/debmetaindex.cc @@ -182,21 +182,15 @@ bool debReleaseIndex::GetIndexes(pkgAcquire *Owner, bool const &GetAll) const new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description, (*Target)->ShortDesc, HashString()); } - // this is normally created in pkgAcqMetaSig, but if we run - // in --print-uris mode, we add it here - new pkgAcqMetaIndex(Owner, MetaIndexURI("Release"), - MetaIndexInfo("Release"), "Release", - MetaIndexURI("Release.gpg"), - ComputeIndexTargets(), - new indexRecords (Dist)); - } - new pkgAcqMetaSig(Owner, MetaIndexURI("Release.gpg"), - MetaIndexInfo("Release.gpg"), "Release.gpg", - MetaIndexURI("Release"), MetaIndexInfo("Release"), "Release", - ComputeIndexTargets(), - new indexRecords (Dist)); + new pkgAcqMetaClearSig(Owner, MetaIndexURI("InRelease"), + MetaIndexInfo("InRelease"), "InRelease", + MetaIndexURI("Release"), MetaIndexInfo("Release"), "Release", + MetaIndexURI("Release.gpg"), MetaIndexInfo("Release.gpg"), "Release.gpg", + ComputeIndexTargets(), + new indexRecords (Dist)); + // Queue the translations std::vector const lang = APT::Configuration::getLanguages(true); @@ -224,16 +218,20 @@ bool debReleaseIndex::GetIndexes(pkgAcquire *Owner, bool const &GetAll) const bool debReleaseIndex::IsTrusted() const { - string VerifiedSigFile = _config->FindDir("Dir::State::lists") + - URItoFileName(MetaIndexURI("Release")) + ".gpg"; - if(_config->FindB("APT::Authentication::TrustCDROM", false)) if(URI.substr(0,strlen("cdrom:")) == "cdrom:") return true; - + + string VerifiedSigFile = _config->FindDir("Dir::State::lists") + + URItoFileName(MetaIndexURI("Release")) + ".gpg"; + if (FileExists(VerifiedSigFile)) return true; - return false; + + VerifiedSigFile = _config->FindDir("Dir::State::lists") + + URItoFileName(MetaIndexURI("InRelease")); + + return FileExists(VerifiedSigFile); } vector *debReleaseIndex::GetIndexFiles() { diff --git a/apt-pkg/indexcopy.cc b/apt-pkg/indexcopy.cc index f88d51fc5..c2ee1c347 100644 --- a/apt-pkg/indexcopy.cc +++ b/apt-pkg/indexcopy.cc @@ -722,7 +722,8 @@ bool SigVerify::RunGPGV(std::string const &File, std::string const &FileGPG, } Args.push_back(FileGPG.c_str()); - Args.push_back(File.c_str()); + if (FileGPG != File) + Args.push_back(File.c_str()); Args.push_back(NULL); if (Debug == true) diff --git a/apt-pkg/indexrecords.cc b/apt-pkg/indexrecords.cc index eb9a36866..10e154ad2 100644 --- a/apt-pkg/indexrecords.cc +++ b/apt-pkg/indexrecords.cc @@ -55,14 +55,17 @@ bool indexRecords::Load(const string Filename) /*{{{*/ } pkgTagSection Section; - if (TagFile.Step(Section) == false) - { - strprintf(ErrorText, _("No sections in Release file %s"), Filename.c_str()); - return false; - } - const char *Start, *End; - Section.Get (Start, End, 0); + // Skip over sections beginning with ----- as this is an idicator for clearsigns + do { + if (TagFile.Step(Section) == false) + { + strprintf(ErrorText, _("No sections in Release file %s"), Filename.c_str()); + return false; + } + + Section.Get (Start, End, 0); + } while (End - Start > 5 && strncmp(Start, "-----", 5) == 0); Suite = Section.FindS("Suite"); Dist = Section.FindS("Codename"); diff --git a/apt-pkg/tagfile.cc b/apt-pkg/tagfile.cc index 96a681bec..4a2f3f7e6 100644 --- a/apt-pkg/tagfile.cc +++ b/apt-pkg/tagfile.cc @@ -399,9 +399,13 @@ bool pkgTagSection::FindFlag(const char *Tag,unsigned long &Flags, const char *Stop; if (Find(Tag,Start,Stop) == false) return true; - - switch (StringToBool(string(Start,Stop))) - { + return FindFlag(Flags, Flag, Start, Stop); +} +bool const pkgTagSection::FindFlag(unsigned long &Flags, unsigned long Flag, + char const* Start, char const* Stop) +{ + switch (StringToBool(string(Start, Stop))) + { case 0: Flags &= ~Flag; return true; diff --git a/apt-pkg/tagfile.h b/apt-pkg/tagfile.h index 6891c1d81..61491aa04 100644 --- a/apt-pkg/tagfile.h +++ b/apt-pkg/tagfile.h @@ -60,6 +60,8 @@ class pkgTagSection unsigned long long FindULL(const char *Tag, unsigned long long const &Default = 0) const; bool FindFlag(const char *Tag,unsigned long &Flags, unsigned long Flag) const; + bool static const FindFlag(unsigned long &Flags, unsigned long Flag, + const char* Start, const char* Stop); bool Scan(const char *Start,unsigned long MaxLength); inline unsigned long size() const {return Stop - Section;}; void Trim(); diff --git a/debian/changelog b/debian/changelog index b74508804..59f516751 100644 --- a/debian/changelog +++ b/debian/changelog @@ -53,11 +53,14 @@ apt (0.8.11+wheezy) unstable; urgency=low - operate optional on gzip compressed pdiffs * apt-pkg/acquire-item.cc: - don't uncompress downloaded pdiff files before feeding it to rred + - try downloading clearsigned InRelease before trying Release.gpg * cmdline/apt-key: - don't set trustdb-name as non-root so 'list' and 'finger' can be used without being root (Closes: #393005, #592107) + * apt-pkg/deb/deblistparser.cc: + - rewrite LoadReleaseInfo to cope with clearsigned Releasefiles - -- David Kalnischkies Sun, 16 Jan 2011 17:23:28 +0100 + -- David Kalnischkies Thu, 20 Jan 2011 14:52:32 +0100 apt (0.8.10) unstable; urgency=low diff --git a/doc/apt-secure.8.xml b/doc/apt-secure.8.xml index f345c3f89..f8ff678b9 100644 --- a/doc/apt-secure.8.xml +++ b/doc/apt-secure.8.xml @@ -148,8 +148,8 @@ (you should make sure you are using a trusted communication channel when retrieving it), add it with apt-key and then run apt-get update so that apt can download - and verify the Release.gpg files from the archives you - have configured. + and verify the InRelease or Release.gpg + files from the archives you have configured. @@ -166,6 +166,7 @@ (provided in apt-utils). Sign it. You can do this by running + gpg --clearsign -o InRelease Release and gpg -abs -o Release.gpg Release. Publish the key fingerprint, diff --git a/test/integration/Packages-releasefile-verification b/test/integration/Packages-releasefile-verification new file mode 100644 index 000000000..29a385f4f --- /dev/null +++ b/test/integration/Packages-releasefile-verification @@ -0,0 +1,18 @@ +Package: apt +Version: 0.7.25.3 +Architecture: i386 +Maintainer: APT Development Team +Installed-Size: 5244 +Replaces: libapt-pkg-dev (<< 0.3.7), libapt-pkg-doc (<< 0.3.7) +Provides: libapt-pkg-libc6.9-6-4.8 +Suggests: aptitude | synaptic | wajig, dpkg-dev, apt-doc, bzip2, lzma, python-apt +Filename: apt.deb +Size: 0 +MD5sum: d41d8cd98f00b204e9800998ecf8427e +Description: Advanced front-end for dpkg + This is Debian's next generation front-end for the dpkg package manager. + It provides the apt-get utility and APT dselect method that provides a + simpler, safer way to install and upgrade packages. + . + APT features complete installation ordering, multiple source capability + and several other unique features, see the Users Guide in apt-doc. diff --git a/test/integration/Packages-releasefile-verification-new b/test/integration/Packages-releasefile-verification-new new file mode 100644 index 000000000..e3b2edf1f --- /dev/null +++ b/test/integration/Packages-releasefile-verification-new @@ -0,0 +1,21 @@ +Package: apt +Priority: important +Section: admin +Installed-Size: 5672 +Maintainer: APT Development Team +Architecture: i386 +Version: 0.8.0~pre1 +Replaces: manpages-pl (<< 20060617-3~) +Provides: libapt-pkg4.10 +Suggests: aptitude | synaptic | wajig, dpkg-dev, apt-doc, bzip2, lzma, python-apt +Conflicts: python-apt (<< 0.7.93.2~) +Filename: apt.deb +Size: 0 +MD5sum: d41d8cd98f00b204e9800998ecf8427e +Description: Advanced front-end for dpkg + This is Debian's next generation front-end for the dpkg package manager. + It provides the apt-get utility and APT dselect method that provides a + simpler, safer way to install and upgrade packages. + . + APT features complete installation ordering, multiple source capability + and several other unique features, see the Users Guide in apt-doc. diff --git a/test/integration/framework b/test/integration/framework index 5d54e49b6..cb3fca35d 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -383,6 +383,7 @@ buildaptarchivefromfiles() { generatereleasefiles() { msgninfo "\tGenerate Release files… " + local DATE="${1:-now}" if [ -e aptarchive/dists ]; then for dir in $(find ./aptarchive/dists -mindepth 1 -maxdepth 1 -type d); do local CODENAME="$(echo "$dir" | cut -d'/' -f 4)" @@ -395,6 +396,11 @@ NotAutomatic: yes' $dir/Release else aptftparchive -qq release ./aptarchive | sed -e '/0 Release$/ d' > aptarchive/Release # remove the self reference fi + if [ "$DATE" != "now" ]; then + for release in $(find ./aptarchive -name 'Release'); do + touch -d "$1" $release + done + fi msgdone "info" } @@ -455,6 +461,7 @@ signreleasefiles() { done for RELEASE in $(find aptarchive/ -name Release); do gpg --yes --no-default-keyring $SECKEYS $PUBKEYS --default-key "$SIGNER" -abs -o ${RELEASE}.gpg ${RELEASE} + gpg --yes --no-default-keyring $SECKEYS $PUBKEYS --default-key "$SIGNER" --clearsign -o "$(echo "${RELEASE}" | sed 's#/Release$#/InRelease#')" $RELEASE done msgdone "info" } diff --git a/test/integration/marvinparanoid.pub b/test/integration/marvinparanoid.pub new file mode 100644 index 000000000..9a59c2e40 Binary files /dev/null and b/test/integration/marvinparanoid.pub differ diff --git a/test/integration/marvinparanoid.sec b/test/integration/marvinparanoid.sec new file mode 100644 index 000000000..ff54e8680 Binary files /dev/null and b/test/integration/marvinparanoid.sec differ diff --git a/test/integration/test-bug-595691-empty-and-broken-archive-files b/test/integration/test-bug-595691-empty-and-broken-archive-files index 66792899a..c5379dca0 100755 --- a/test/integration/test-bug-595691-empty-and-broken-archive-files +++ b/test/integration/test-bug-595691-empty-and-broken-archive-files @@ -74,28 +74,24 @@ testoverfile() { setupcompressor "$1" createemptyfile 'en' - testaptgetupdate "Get:1 file: Release.gpg [] -Get:2 file: Release [] + testaptgetupdate "Get:1 file: InRelease [] Ign file:$(readlink -f aptarchive)/ Translation-en Reading package lists..." "empty file en.$COMPRESS over file" createemptyarchive 'en' - testaptgetupdate "Get:1 file: Release.gpg [] -Get:2 file: Release [] + testaptgetupdate "Get:1 file: InRelease [] Reading package lists..." "empty archive en.$COMPRESS over file" createemptyarchive 'Packages' # FIXME: Why omits the file transport the Packages Get line? #Get:3 file: Packages [] testaptgetupdate "Ign file:$(readlink -f aptarchive)/ Translation-en -Get:1 file: Release.gpg [] -Get:2 file: Release [] +Get:1 file: InRelease [] Reading package lists..." "empty archive Packages.$COMPRESS over file" createemptyfile 'Packages' testaptgetupdate "Ign file:$(readlink -f aptarchive)/ Translation-en -Get:1 file: Release.gpg [] -Get:2 file: Release [] +Get:1 file: InRelease [] Err file: Packages Undetermined Error W: Failed to fetch file:$(readlink -f aptarchive/Packages.$COMPRESS) Undetermined Error @@ -107,33 +103,29 @@ testoverhttp() { setupcompressor "$1" createemptyfile 'en' - testaptgetupdate "Get:1 http://localhost Release.gpg [] + testaptgetupdate "Get:1 http://localhost InRelease [] Get:2 http://localhost/ Translation-en -Get:3 http://localhost Release [] +Get:3 http://localhost Packages [] Ign http://localhost/ Translation-en -Get:4 http://localhost Packages [] Reading package lists..." "empty file en.$COMPRESS over http" createemptyarchive 'en' - testaptgetupdate "Get:1 http://localhost Release.gpg [] + testaptgetupdate "Get:1 http://localhost InRelease [] Get:2 http://localhost/ Translation-en [] -Get:3 http://localhost Release [] -Get:4 http://localhost Packages [] +Get:3 http://localhost Packages [] Reading package lists..." "empty archive en.$COMPRESS over http" createemptyarchive 'Packages' - testaptgetupdate "Get:1 http://localhost Release.gpg [] + testaptgetupdate "Get:1 http://localhost InRelease [] Ign http://localhost/ Translation-en -Get:2 http://localhost Release [] -Get:3 http://localhost Packages [] +Get:2 http://localhost Packages [] Reading package lists..." "empty archive Packages.$COMPRESS over http" createemptyfile 'Packages' #FIXME: we should response with a good error message instead - testaptgetupdate "Get:1 http://localhost Release.gpg [] + testaptgetupdate "Get:1 http://localhost InRelease [] Ign http://localhost/ Translation-en -Get:2 http://localhost Release [] -Get:3 http://localhost Packages +Get:2 http://localhost Packages Err http://localhost Packages Undetermined Error W: Failed to fetch http://localhost:8080/Packages.$COMPRESS Undetermined Error diff --git a/test/integration/test-releasefile-verification b/test/integration/test-releasefile-verification new file mode 100755 index 000000000..961c49895 --- /dev/null +++ b/test/integration/test-releasefile-verification @@ -0,0 +1,160 @@ +#!/bin/sh +set -e + +TESTDIR=$(readlink -f $(dirname $0)) +. $TESTDIR/framework + +setupenvironment +configarchitecture "i386" + +buildaptarchive +setupflataptarchive +changetowebserver + +prepare() { + local DATE="${2:-now}" + if [ "$DATE" = 'now' -a "$1" = "${PKGFILE}-new" ]; then + DATE='now + 6 days' + fi + for release in $(find rootdir/var/lib/apt/lists 2> /dev/null); do + touch -d 'now - 6 hours' $release + done + rm -rf rootdir/var/cache/apt/archives + rm -f rootdir/var/cache/apt/*.bin + cp $1 aptarchive/Packages + find aptarchive -name 'Release' -delete + cat aptarchive/Packages | gzip > aptarchive/Packages.gz + cat aptarchive/Packages | bzip2 > aptarchive/Packages.bz2 + cat aptarchive/Packages | lzma > aptarchive/Packages.lzma + generatereleasefiles "$DATE" +} + +installaptold() { + testequal 'Reading package lists... +Building dependency tree... +Suggested packages: + aptitude synaptic wajig dpkg-dev apt-doc bzip2 lzma python-apt +The following NEW packages will be installed: + apt +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +After this operation, 5370 kB of additional disk space will be used. +Get:1 http://localhost/ apt 0.7.25.3 +Download complete and in download only mode' aptget install apt -dy +} + +installaptnew() { + testequal 'Reading package lists... +Building dependency tree... +Suggested packages: + aptitude synaptic wajig dpkg-dev apt-doc bzip2 lzma python-apt +The following NEW packages will be installed: + apt +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +After this operation, 5808 kB of additional disk space will be used. +Get:1 http://localhost/ apt 0.8.0~pre1 +Download complete and in download only mode' aptget install apt -dy +} + +failaptold() { + testequal 'Reading package lists... +Building dependency tree... +Suggested packages: + aptitude synaptic wajig dpkg-dev apt-doc bzip2 lzma python-apt +The following NEW packages will be installed: + apt +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +After this operation, 5370 kB of additional disk space will be used. +WARNING: The following packages cannot be authenticated! + apt +E: There are problems and -y was used without --force-yes' aptget install apt -dy +} + +failaptnew() { + testequal 'Reading package lists... +Building dependency tree... +Suggested packages: + aptitude synaptic wajig dpkg-dev apt-doc bzip2 lzma python-apt +The following NEW packages will be installed: + apt +0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. +After this operation, 5808 kB of additional disk space will be used. +WARNING: The following packages cannot be authenticated! + apt +E: There are problems and -y was used without --force-yes' aptget install apt -dy +} + +# fake our downloadable file +touch aptarchive/apt.deb + +PKGFILE="${TESTDIR}/$(echo "$(basename $0)" | sed 's#^test-#Packages-#')" + +runtest() { + prepare ${PKGFILE} + rm -rf rootdir/var/lib/apt/lists + signreleasefiles 'Joe Sixpack' + find aptarchive/ -name "$DELETEFILE" -delete + msgtest 'Cold archive signed by' 'Joe Sixpack' + aptget update 2>&1 | grep -E '^(W|E): ' > /dev/null && msgfail || msgpass + testequal "$(cat ${PKGFILE}) +" aptcache show apt + installaptold + + prepare ${PKGFILE}-new + signreleasefiles 'Joe Sixpack' + find aptarchive/ -name "$DELETEFILE" -delete + msgtest 'Good warm archive signed by' 'Joe Sixpack' + aptget update 2>&1 | grep -E '^(W|E): ' > /dev/null && msgfail || msgpass + testequal "$(cat ${PKGFILE}-new) +" aptcache show apt + installaptnew + + + prepare ${PKGFILE} + rm -rf rootdir/var/lib/apt/lists + signreleasefiles 'Marvin Paranoid' + find aptarchive/ -name "$DELETEFILE" -delete + msgtest 'Cold archive signed by' 'Marvin Paranoid' + aptget update 2>&1 | grep -E '^(W|E): ' > /dev/null && msgpass || msgfail + testequal "$(cat ${PKGFILE}) +" aptcache show apt + failaptold + + prepare ${PKGFILE}-new + # weborf doesn't support If-Range + for release in $(find rootdir/var/lib/apt/lists/partial/ -name '*Release'); do + rm $release + touch $release + done + signreleasefiles 'Joe Sixpack' + find aptarchive/ -name "$DELETEFILE" -delete + msgtest 'Bad warm archive signed by' 'Joe Sixpack' + aptget update 2>&1 | grep -E '^(W|E): ' > /dev/null && msgfail || msgpass + testequal "$(cat ${PKGFILE}-new) +" aptcache show apt + installaptnew + + + prepare ${PKGFILE} + rm -rf rootdir/var/lib/apt/lists + signreleasefiles 'Joe Sixpack' + find aptarchive/ -name "$DELETEFILE" -delete + msgtest 'Cold archive signed by' 'Joe Sixpack' + aptget update 2>&1 | grep -E '^(W|E): ' > /dev/null && msgfail || msgpass + testequal "$(cat ${PKGFILE}) +" aptcache show apt + installaptold + + prepare ${PKGFILE}-new + signreleasefiles 'Marvin Paranoid' + find aptarchive/ -name "$DELETEFILE" -delete + msgtest 'Good warm archive signed by' 'Marvin Paranoid' + aptget update 2>&1 | grep -E '^(W|E): ' > /dev/null && msgpass || msgfail + testequal "$(cat ${PKGFILE}) +" aptcache show apt + installaptold +} + +DELETEFILE="InRelease" +runtest +DELETEFILE="Release.gpg" +runtest -- cgit v1.2.3-70-g09d2 From 5d88572318ed7e271101b1ae8f2cc139a1a3f705 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Wed, 26 Jan 2011 16:19:30 +0100 Subject: - change the internal handling of Extensions in pkgAcqIndex - add a special uncompressed compression type to prefer those files * methods/{gzip,bzip}.cc: - print a good error message if FileSize() is zero --- apt-pkg/acquire-item.cc | 114 ++++++++++++--------- apt-pkg/acquire-item.h | 9 +- apt-pkg/aptconfiguration.cc | 8 ++ debian/changelog | 6 +- doc/apt.conf.5.xml | 9 +- doc/examples/configure-index | 2 +- methods/bzip2.cc | 3 +- methods/gzip.cc | 3 +- .../test-bug-595691-empty-and-broken-archive-files | 9 +- 9 files changed, 96 insertions(+), 67 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index 2cd6ab359..a603a3d70 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -622,29 +622,61 @@ pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, HashString ExpectedHash, string comprExt) : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash) { + if(comprExt.empty() == true) + { + // autoselect the compression method + std::vector types = APT::Configuration::getCompressionTypes(); + for (std::vector::const_iterator t = types.begin(); t != types.end(); ++t) + comprExt.append(*t).append(" "); + if (comprExt.empty() == false) + comprExt.erase(comprExt.end()-1); + } + CompressionExtension = comprExt; + + Init(URI, URIDesc, ShortDesc); +} +pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const *Target, + HashString const &ExpectedHash, indexRecords const *MetaIndexParser) + : Item(Owner), RealURI(Target->URI), ExpectedHash(ExpectedHash) +{ + // autoselect the compression method + std::vector types = APT::Configuration::getCompressionTypes(); + CompressionExtension = ""; + if (ExpectedHash.empty() == false) + { + for (std::vector::const_iterator t = types.begin(); t != types.end(); ++t) + if (*t == "uncompressed" || MetaIndexParser->Exists(string(Target->MetaKey).append(".").append(*t)) == true) + CompressionExtension.append(*t).append(" "); + } + else + { + for (std::vector::const_iterator t = types.begin(); t != types.end(); ++t) + CompressionExtension.append(*t).append(" "); + } + if (CompressionExtension.empty() == false) + CompressionExtension.erase(CompressionExtension.end()-1); + + Init(Target->URI, Target->Description, Target->ShortDesc); +} + /*}}}*/ +// AcqIndex::Init - defered Constructor /*{{{*/ +void pkgAcqIndex::Init(string const &URI, string const &URIDesc, string const &ShortDesc) { Decompression = false; Erase = false; DestFile = _config->FindDir("Dir::State::lists") + "partial/"; DestFile += URItoFileName(URI); - if(comprExt.empty()) - { - // autoselect the compression method - std::vector types = APT::Configuration::getCompressionTypes(); - if (types.empty() == true) - comprExt = "plain"; - else - comprExt = "." + types[0]; - } - CompressionExtension = ((comprExt == "plain" || comprExt == ".") ? "" : comprExt); - - Desc.URI = URI + CompressionExtension; + std::string const comprExt = CompressionExtension.substr(0, CompressionExtension.find(' ')); + if (comprExt == "uncompressed") + Desc.URI = URI; + else + Desc.URI = URI + '.' + comprExt; Desc.Description = URIDesc; Desc.Owner = this; Desc.ShortDesc = ShortDesc; - + QueueURI(Desc); } /*}}}*/ @@ -666,37 +698,18 @@ string pkgAcqIndex::Custom600Headers() /*}}}*/ void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/ { - std::vector types = APT::Configuration::getCompressionTypes(); - - for (std::vector::const_iterator t = types.begin(); - t != types.end(); t++) + size_t const nextExt = CompressionExtension.find(' '); + if (nextExt != std::string::npos) { - // jump over all already tried compression types - const unsigned int nameLen = Desc.URI.size() - (*t).size(); - if(Desc.URI.substr(nameLen) != *t) - continue; - - // we want to try it with the next extension (and make sure to - // not skip over the end) - t++; - if (t == types.end()) - break; - - // queue new download - Desc.URI = Desc.URI.substr(0, nameLen) + *t; - new pkgAcqIndex(Owner, RealURI, Desc.Description, Desc.ShortDesc, - ExpectedHash, string(".").append(*t)); - - Status = StatDone; - Complete = false; - Dequeue(); + CompressionExtension = CompressionExtension.substr(nextExt+1); + Init(RealURI, Desc.Description, Desc.ShortDesc); return; } // on decompression failure, remove bad versions in partial/ - if(Decompression && Erase) { + if (Decompression && Erase) { string s = _config->FindDir("Dir::State::lists") + "partial/"; - s += URItoFileName(RealURI); + s.append(URItoFileName(RealURI)); unlink(s.c_str()); } @@ -773,8 +786,8 @@ void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash, Status = StatError; ErrorText = "Method gave a blank filename"; } - - string compExt = flExtension(flNotDir(URI(Desc.URI).Path)); + + std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' ')); // The files timestamp matches if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) { @@ -807,12 +820,7 @@ void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash, // get the binary name for your used compression type decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),""); if(decompProg.empty() == false); - // flExtensions returns the full name if no extension is found - // this is why we have this complicated compare operation here - // FIMXE: add a new flJustExtension() that return "" if no - // extension is found and use that above so that it can - // be tested against "" - else if(compExt == flNotDir(URI(Desc.URI).Path)) + else if(compExt == "uncompressed") decompProg = "copy"; else { _error->Error("Unsupported extension: %s", compExt.c_str()); @@ -853,6 +861,15 @@ string pkgAcqIndexTrans::Custom600Headers() /* */ void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf) { + size_t const nextExt = CompressionExtension.find(' '); + if (nextExt != std::string::npos) + { + CompressionExtension = CompressionExtension.substr(nextExt+1); + Init(RealURI, Desc.Description, Desc.ShortDesc); + Status = StatIdle; + return; + } + if (Cnf->LocalOnly == true || StringToBool(LookupTag(Message,"Transient-Failure"),false) == false) { @@ -862,7 +879,7 @@ void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf) Dequeue(); return; } - + Item::Failed(Message,Cnf); } /*}}}*/ @@ -1197,8 +1214,7 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/ new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description, (*Target)->ShortDesc, ExpectedIndexHash); else - new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description, - (*Target)->ShortDesc, ExpectedIndexHash); + new pkgAcqIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser); } } /*}}}*/ diff --git a/apt-pkg/acquire-item.h b/apt-pkg/acquire-item.h index 581761e32..92098e3d4 100644 --- a/apt-pkg/acquire-item.h +++ b/apt-pkg/acquire-item.h @@ -528,8 +528,8 @@ class pkgAcqIndex : public pkgAcquire::Item /** \brief The expected hashsum of the decompressed index file. */ HashString ExpectedHash; - /** \brief The compression-related file extension that is being - * added to the downloaded file (e.g., ".gz" or ".bz2"). + /** \brief The compression-related file extensions that are being + * added to the downloaded file one by one if first fails (e.g., "gz bz2"). */ string CompressionExtension; @@ -540,7 +540,7 @@ class pkgAcqIndex : public pkgAcquire::Item virtual void Done(string Message,unsigned long Size,string Md5Hash, pkgAcquire::MethodConfig *Cnf); virtual string Custom600Headers(); - virtual string DescURI() {return RealURI + CompressionExtension;}; + virtual string DescURI() {return Desc.URI;}; virtual string HashSum() {return ExpectedHash.toStr(); }; /** \brief Create a pkgAcqIndex. @@ -565,6 +565,9 @@ class pkgAcqIndex : public pkgAcquire::Item pkgAcqIndex(pkgAcquire *Owner,string URI,string URIDesc, string ShortDesc, HashString ExpectedHash, string compressExt=""); + pkgAcqIndex(pkgAcquire *Owner, struct IndexTarget const * const Target, + HashString const &ExpectedHash, indexRecords const *MetaIndexParser); + void Init(string const &URI, string const &URIDesc, string const &ShortDesc); }; /*}}}*/ /** \brief An acquire item that is responsible for fetching a {{{ diff --git a/apt-pkg/aptconfiguration.cc b/apt-pkg/aptconfiguration.cc index 52f54073c..e97ebfed7 100644 --- a/apt-pkg/aptconfiguration.cc +++ b/apt-pkg/aptconfiguration.cc @@ -90,6 +90,14 @@ const Configuration::getCompressionTypes(bool const &Cached) { types.push_back(Types->Tag); } + // add the special "uncompressed" type + if (std::find(types.begin(), types.end(), "uncompressed") == types.end()) + { + string const uncompr = _config->FindFile("Dir::Bin::uncompressed", ""); + if (uncompr.empty() == true || FileExists(uncompr) == true) + types.push_back("uncompressed"); + } + return types; } /*}}}*/ diff --git a/debian/changelog b/debian/changelog index 2413b9c5b..7b125fc43 100644 --- a/debian/changelog +++ b/debian/changelog @@ -54,6 +54,8 @@ apt (0.8.11+wheezy) unstable; urgency=low * apt-pkg/acquire-item.cc: - don't uncompress downloaded pdiff files before feeding it to rred - try downloading clearsigned InRelease before trying Release.gpg + - change the internal handling of Extensions in pkgAcqIndex + - add a special uncompressed compression type to prefer those files * cmdline/apt-key: - don't set trustdb-name as non-root so 'list' and 'finger' can be used without being root (Closes: #393005, #592107) @@ -62,8 +64,10 @@ apt (0.8.11+wheezy) unstable; urgency=low * ftparchive/writer.cc: - add config option to search for more patterns in release command - include Index files by default in the Release file + * methods/{gzip,bzip}.cc: + - print a good error message if FileSize() is zero - -- David Kalnischkies Mon, 24 Jan 2011 15:36:50 +0100 + -- David Kalnischkies Wed, 26 Jan 2011 16:06:10 +0100 apt (0.8.10.3) unstable; urgency=low diff --git a/doc/apt.conf.5.xml b/doc/apt.conf.5.xml index a19d85dbc..a423dac24 100644 --- a/doc/apt.conf.5.xml +++ b/doc/apt.conf.5.xml @@ -442,12 +442,11 @@ DPkg::Pre-Install-Pkgs {"/usr/sbin/dpkg-preconfigure --apt";}; the bzip2 method (the inbuilt) setting is Dir::Bin::bzip2 "/bin/bzip2"; Note also that list entries specified on the command line will be added at the end of the list specified in the configuration files, but before the default entries. To prefer a type in this case - over the ones specified in in the configuration files you can set the option direct - not in list style. + over the ones specified in the configuration files you can set the option direct - not in list style. This will not override the defined list, it will only prefix the list with this type. - While it is possible to add an empty compression type to the order list, but APT in its current - version doesn't understand it correctly and will display many warnings about not downloaded files - - these warnings are most of the time false negatives. Future versions will maybe include a way to - really prefer uncompressed files to support the usage of local mirrors. + The special type uncompressed can be used to give uncompressed files a + preference, but note that most archives doesn't provide uncompressed files so this is mostly only + useable for local mirrors. GzipIndexes diff --git a/doc/examples/configure-index b/doc/examples/configure-index index 6c078d75f..fd14d4dd7 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -281,7 +281,7 @@ Acquire lzma "lzma"; gz "gzip"; - Order { "gz"; "lzma"; "bz2"; }; + Order { "uncompressed"; "gz"; "lzma"; "bz2"; }; }; Languages diff --git a/methods/bzip2.cc b/methods/bzip2.cc index ccc3669a2..42932dded 100644 --- a/methods/bzip2.cc +++ b/methods/bzip2.cc @@ -56,9 +56,8 @@ bool Bzip2Method::Fetch(FetchItem *Itm) // Open the source and destination files FileFd From(Path,FileFd::ReadOnly); - // FIXME add an error message saying that empty files can't be valid archives if(From.FileSize() == 0) - return false; + return _error->Error(_("Empty files can't be valid archives")); int GzOut[2]; if (pipe(GzOut) < 0) diff --git a/methods/gzip.cc b/methods/gzip.cc index f1c76066e..fc4e1ecfd 100644 --- a/methods/gzip.cc +++ b/methods/gzip.cc @@ -48,9 +48,8 @@ bool GzipMethod::Fetch(FetchItem *Itm) // Open the source and destination files FileFd From(Path,FileFd::ReadOnlyGzip); - // FIXME add an error message saying that empty files can't be valid archives if(From.FileSize() == 0) - return false; + return _error->Error(_("Empty files can't be valid archives")); FileFd To(Itm->DestFile,FileFd::WriteAtomic); To.EraseOnFailure(); diff --git a/test/integration/test-bug-595691-empty-and-broken-archive-files b/test/integration/test-bug-595691-empty-and-broken-archive-files index c5379dca0..398d0cd1b 100755 --- a/test/integration/test-bug-595691-empty-and-broken-archive-files +++ b/test/integration/test-bug-595691-empty-and-broken-archive-files @@ -56,6 +56,7 @@ setupcompressor() { lzma) COMPRESS="lzma";; esac echo "Acquire::CompressionTypes::Order { \"${COMPRESS}\"; }; +Dir::Bin::uncompressed \"/does/not/exist\"; Dir::Bin::gzip \"/does/not/exist\"; Dir::Bin::bzip2 \"/does/not/exist\"; Dir::Bin::lzma \"/does/not/exist\";" > rootdir/etc/apt/apt.conf.d/00compressor @@ -93,8 +94,8 @@ Reading package lists..." "empty archive Packages.$COMPRESS over file" testaptgetupdate "Ign file:$(readlink -f aptarchive)/ Translation-en Get:1 file: InRelease [] Err file: Packages - Undetermined Error -W: Failed to fetch file:$(readlink -f aptarchive/Packages.$COMPRESS) Undetermined Error + Empty files can't be valid archives +W: Failed to fetch ${COMPRESSOR}:$(readlink -f aptarchive/Packages.$COMPRESS) Empty files can't be valid archives E: Some index files failed to download, they have been ignored, or old ones used instead." "empty file Packages.$COMPRESS over file" } @@ -127,8 +128,8 @@ Reading package lists..." "empty archive Packages.$COMPRESS over http" Ign http://localhost/ Translation-en Get:2 http://localhost Packages Err http://localhost Packages - Undetermined Error -W: Failed to fetch http://localhost:8080/Packages.$COMPRESS Undetermined Error + Empty files can't be valid archives +W: Failed to fetch ${COMPRESSOR}:$(readlink -f rootdir/var/lib/apt/lists/partial/localhost:8080_Packages) Empty files can't be valid archives E: Some index files failed to download, they have been ignored, or old ones used instead." "empty file Packages.$COMPRESS over http" } -- cgit v1.2.3-70-g09d2 From ab53c018fbc7aa01c0d89586c0aa98bc944dd9e4 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Fri, 28 Jan 2011 12:22:37 +0100 Subject: - download and use i18n/Index to choose which Translations to download * apt-pkg/aptconfiguration.cc: - remove the inbuilt Translation files whitelist --- apt-pkg/acquire-item.cc | 218 +++++++++++++++++++-- apt-pkg/acquire-item.h | 56 ++++++ apt-pkg/aptconfiguration.cc | 51 ++--- apt-pkg/deb/debmetaindex.cc | 77 +++++--- apt-pkg/deb/debmetaindex.h | 2 + debian/changelog | 5 +- .../test-bug-595691-empty-and-broken-archive-files | 28 +-- .../test-bug-601016-description-translation | 1 + test/libapt/getlanguages_test.cc | 71 ++++--- 9 files changed, 381 insertions(+), 128 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index a603a3d70..776009493 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -184,6 +184,153 @@ void pkgAcquire::Item::ReportMirrorFailure(string FailCode) } } /*}}}*/ +// AcqSubIndex::AcqSubIndex - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* Get the DiffIndex file first and see if there are patches availabe + * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the + * patches. If anything goes wrong in that process, it will fall back to + * the original packages file + */ +pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, string const &URI, + string const &URIDesc, string const &ShortDesc, + HashString const &ExpectedHash) + : Item(Owner), ExpectedHash(ExpectedHash) +{ + Debug = _config->FindB("Debug::pkgAcquire::SubIndex",false); + + DestFile = _config->FindDir("Dir::State::lists") + "partial/"; + DestFile += URItoFileName(URI); + + Desc.URI = URI; + Desc.Description = URIDesc; + Desc.Owner = this; + Desc.ShortDesc = ShortDesc; + + QueueURI(Desc); + + if(Debug) + std::clog << "pkgAcqSubIndex: " << Desc.URI << std::endl; +} + /*}}}*/ +// AcqSubIndex::Custom600Headers - Insert custom request headers /*{{{*/ +// --------------------------------------------------------------------- +/* The only header we use is the last-modified header. */ +string pkgAcqSubIndex::Custom600Headers() +{ + string Final = _config->FindDir("Dir::State::lists"); + Final += URItoFileName(Desc.URI); + + struct stat Buf; + if (stat(Final.c_str(),&Buf) != 0) + return "\nIndex-File: true"; + return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime); +} + /*}}}*/ +void pkgAcqSubIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/ +{ + if(Debug) + std::clog << "pkgAcqSubIndex failed: " << Desc.URI << std::endl; + + Complete = false; + Status = StatDone; + Dequeue(); + + // No good Index is provided, so try guessing + std::vector langs = APT::Configuration::getLanguages(true); + for (std::vector::const_iterator l = langs.begin(); + l != langs.end(); ++l) + { + if (*l == "none") continue; + string const file = "Translation-" + *l; + new pkgAcqIndexTrans(Owner, Desc.URI.substr(0, Desc.URI.rfind('/')+1).append(file), + Desc.Description.erase(Desc.Description.rfind(' ')+1).append(file), + file); + } +} + /*}}}*/ +void pkgAcqSubIndex::Done(string Message,unsigned long Size,string Md5Hash, /*{{{*/ + pkgAcquire::MethodConfig *Cnf) +{ + if(Debug) + std::clog << "pkgAcqSubIndex::Done(): " << Desc.URI << std::endl; + + string FileName = LookupTag(Message,"Filename"); + if (FileName.empty() == true) + { + Status = StatError; + ErrorText = "Method gave a blank filename"; + return; + } + + if (FileName != DestFile) + { + Local = true; + Desc.URI = "copy:" + FileName; + QueueURI(Desc); + return; + } + + Item::Done(Message,Size,Md5Hash,Cnf); + + string FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(Desc.URI); + + // sucess in downloading the index + // rename the index + if(Debug) + std::clog << "Renaming: " << DestFile << " -> " << FinalFile << std::endl; + Rename(DestFile,FinalFile); + chmod(FinalFile.c_str(),0644); + DestFile = FinalFile; + + if(ParseIndex(DestFile) == false) + return Failed("", NULL); + + Complete = true; + Status = StatDone; + Dequeue(); + return; +} + /*}}}*/ +bool pkgAcqSubIndex::ParseIndex(string const &IndexFile) /*{{{*/ +{ + indexRecords SubIndexParser; + if (FileExists(IndexFile) == false || SubIndexParser.Load(IndexFile) == false) + return false; + + std::vector lang = APT::Configuration::getLanguages(true); + for (std::vector::const_iterator l = lang.begin(); + l != lang.end(); ++l) + { + if (*l == "none") + continue; + + string file = "Translation-" + *l; + indexRecords::checkSum const *Record = SubIndexParser.Lookup(file); + HashString expected; + if (Record == NULL) + { + // FIXME: the Index file provided by debian currently only includes bz2 records + Record = SubIndexParser.Lookup(file + ".bz2"); + if (Record == NULL) + continue; + } + else + { + expected = Record->Hash; + if (expected.empty() == true) + continue; + } + + IndexTarget target; + target.Description = Desc.Description.erase(Desc.Description.rfind(' ')+1).append(file); + target.MetaKey = file; + target.ShortDesc = file; + target.URI = Desc.URI.substr(0, Desc.URI.rfind('/')+1).append(file); + new pkgAcqIndexTrans(Owner, &target, expected, &SubIndexParser); + } + return true; +} + /*}}}*/ // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/ // --------------------------------------------------------------------- /* Get the DiffIndex file first and see if there are patches availabe @@ -841,6 +988,11 @@ pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, string URI,string URIDesc,string ShortDesc) : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashString(), "") { +} +pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const *Target, + HashString const &ExpectedHash, indexRecords const *MetaIndexParser) + : pkgAcqIndex(Owner, Target, ExpectedHash, MetaIndexParser) +{ } /*}}}*/ // AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/ @@ -1182,27 +1334,41 @@ void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/ HashString ExpectedIndexHash; if (verify) { - const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey); - if (!Record) - { - Status = StatAuthError; - ErrorText = "Unable to find expected entry " - + (*Target)->MetaKey + " in Meta-index file (malformed Release file?)"; - return; - } - ExpectedIndexHash = Record->Hash; - if (_config->FindB("Debug::pkgAcquire::Auth", false)) - { - std::cerr << "Queueing: " << (*Target)->URI << std::endl; - std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl; - } - if (ExpectedIndexHash.empty()) - { - Status = StatAuthError; - ErrorText = "Unable to find hash sum for " - + (*Target)->MetaKey + " in Meta-index file"; - return; - } + const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey); + if (Record == NULL) + { + if ((*Target)->IsOptional() == false) + { + Status = StatAuthError; + strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str()); + return; + } + } + else + { + ExpectedIndexHash = Record->Hash; + if (_config->FindB("Debug::pkgAcquire::Auth", false)) + { + std::cerr << "Queueing: " << (*Target)->URI << std::endl; + std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl; + } + if (ExpectedIndexHash.empty() == true && (*Target)->IsOptional() == false) + { + Status = StatAuthError; + strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str()); + return; + } + } + } + + if ((*Target)->IsOptional() == true) + { + if ((*Target)->IsSubIndex() == true) + new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description, + (*Target)->ShortDesc, ExpectedIndexHash); + else + new pkgAcqIndexTrans(Owner, *Target, ExpectedIndexHash, MetaIndexParser); + continue; } /* Queue Packages file (either diff or full packages files, depending @@ -1836,3 +2002,13 @@ string pkgAcqFile::Custom600Headers() return ""; } /*}}}*/ +bool IndexTarget::IsOptional() const { + if (strncmp(ShortDesc.c_str(), "Translation", 11) != 0) + return false; + return true; +} +bool IndexTarget::IsSubIndex() const { + if (ShortDesc != "TranslationIndex") + return false; + return true; +} diff --git a/apt-pkg/acquire-item.h b/apt-pkg/acquire-item.h index 92098e3d4..9bcc32f21 100644 --- a/apt-pkg/acquire-item.h +++ b/apt-pkg/acquire-item.h @@ -287,6 +287,50 @@ struct DiffInfo { unsigned long size; }; /*}}}*/ +/** \brief An item that is responsible for fetching a SubIndex {{{ + * + * The MetaIndex file includes only records for important indexes + * and records for these SubIndex files so these can carry records + * for addition files like PDiffs and Translations + */ +class pkgAcqSubIndex : public pkgAcquire::Item +{ + protected: + /** \brief If \b true, debugging information will be written to std::clog. */ + bool Debug; + + /** \brief The item that is currently being downloaded. */ + pkgAcquire::ItemDesc Desc; + + /** \brief The Hash that this file should have after download + */ + HashString ExpectedHash; + + public: + // Specialized action members + virtual void Failed(string Message,pkgAcquire::MethodConfig *Cnf); + virtual void Done(string Message,unsigned long Size,string Md5Hash, + pkgAcquire::MethodConfig *Cnf); + virtual string DescURI() {return Desc.URI;}; + virtual string Custom600Headers(); + virtual bool ParseIndex(string const &IndexFile); + + /** \brief Create a new pkgAcqDiffIndex. + * + * \param Owner The Acquire object that owns this item. + * + * \param URI The URI of the list file to download. + * + * \param URIDesc A long description of the list file to download. + * + * \param ShortDesc A short description of the list file to download. + * + * \param ExpectedHash The list file's MD5 signature. + */ + pkgAcqSubIndex(pkgAcquire *Owner, string const &URI,string const &URIDesc, + string const &ShortDesc, HashString const &ExpectedHash); +}; + /*}}}*/ /** \brief An item that is responsible for fetching an index file of {{{ * package list diffs and starting the package list's download. * @@ -597,6 +641,8 @@ class pkgAcqIndexTrans : public pkgAcqIndex */ pkgAcqIndexTrans(pkgAcquire *Owner,string URI,string URIDesc, string ShortDesc); + pkgAcqIndexTrans(pkgAcquire *Owner, struct IndexTarget const * const Target, + HashString const &ExpectedHash, indexRecords const *MetaIndexParser); }; /*}}}*/ /** \brief Information about an index file. */ /*{{{*/ @@ -615,8 +661,18 @@ struct IndexTarget * looked up within the meta signature file. */ string MetaKey; + + //FIXME: We should use virtual methods here instead… + bool IsOptional() const; + bool IsSubIndex() const; }; /*}}}*/ +/** \brief Information about an optional index file. */ /*{{{*/ +struct OptionalIndexTarget : public IndexTarget +{ +}; + /*}}}*/ + /** \brief An acquire item that downloads the detached signature {{{ * of a meta-index (Release) file, then queues up the release * file itself. diff --git a/apt-pkg/aptconfiguration.cc b/apt-pkg/aptconfiguration.cc index e97ebfed7..3cf4d2429 100644 --- a/apt-pkg/aptconfiguration.cc +++ b/apt-pkg/aptconfiguration.cc @@ -163,33 +163,6 @@ std::vector const Configuration::getLanguages(bool const &All, } closedir(D); - // get the environment language codes: LC_MESSAGES (and later LANGUAGE) - // we extract both, a long and a short code and then we will - // check if we actually need both (rare) or if the short is enough - string const envMsg = string(Locale == 0 ? std::setlocale(LC_MESSAGES, NULL) : *Locale); - size_t const lenShort = (envMsg.find('_') != string::npos) ? envMsg.find('_') : 2; - size_t const lenLong = (envMsg.find_first_of(".@") != string::npos) ? envMsg.find_first_of(".@") : (lenShort + 3); - - string envLong = envMsg.substr(0,lenLong); - string const envShort = envLong.substr(0,lenShort); - bool envLongIncluded = true; - - // to save the servers from unneeded queries, we only try also long codes - // for languages it is realistic to have a long code translation file… - // TODO: Improve translation acquire system to drop them dynamic - char const *needLong[] = { "cs", "en", "pt", "sv", "zh", NULL }; - if (envLong != envShort) { - for (char const **l = needLong; *l != NULL; l++) - if (envShort.compare(*l) == 0) { - envLongIncluded = false; - break; - } - } - - // we don't add the long code, but we allow the user to do so - if (envLongIncluded == true) - envLong.clear(); - // FIXME: Remove support for the old APT::Acquire::Translation // it was undocumented and so it should be not very widthly used string const oldAcquire = _config->Find("APT::Acquire::Translation",""); @@ -211,12 +184,22 @@ std::vector const Configuration::getLanguages(bool const &All, return codes; } - // It is very likely we will need to environment codes later, + // get the environment language codes: LC_MESSAGES (and later LANGUAGE) + // we extract both, a long and a short code and then we will + // check if we actually need both (rare) or if the short is enough + string const envMsg = string(Locale == 0 ? std::setlocale(LC_MESSAGES, NULL) : *Locale); + size_t const lenShort = (envMsg.find('_') != string::npos) ? envMsg.find('_') : 2; + size_t const lenLong = (envMsg.find_first_of(".@") != string::npos) ? envMsg.find_first_of(".@") : (lenShort + 3); + + string const envLong = envMsg.substr(0,lenLong); + string const envShort = envLong.substr(0,lenShort); + + // It is very likely we will need the environment codes later, // so let us generate them now from LC_MESSAGES and LANGUAGE std::vector environment; if (envShort != "C") { // take care of LC_MESSAGES - if (envLongIncluded == false) + if (envLong != envShort) environment.push_back(envLong); environment.push_back(envShort); // take care of LANGUAGE @@ -233,16 +216,6 @@ std::vector const Configuration::getLanguages(bool const &All, continue; if (std::find(environment.begin(), environment.end(), *e) != environment.end()) continue; - if (e->find('_') != string::npos) { - // Drop LongCodes here - ShortCodes are also included - string const shorty = e->substr(0, e->find('_')); - char const **n = needLong; - for (; *n != NULL; ++n) - if (shorty == *n) - break; - if (*n == NULL) - continue; - } ++addedLangs; environment.push_back(*e); } diff --git a/apt-pkg/deb/debmetaindex.cc b/apt-pkg/deb/debmetaindex.cc index e2c680b14..a6edab6b9 100644 --- a/apt-pkg/deb/debmetaindex.cc +++ b/apt-pkg/deb/debmetaindex.cc @@ -119,6 +119,29 @@ string debReleaseIndex::SourceIndexURI(const char *Type, const string &Section) return URI + "dists/" + Dist + "/" + SourceIndexURISuffix(Type, Section); } +string debReleaseIndex::TranslationIndexURISuffix(const char *Type, const string &Section) const +{ + string Res =""; + if (Dist[Dist.size() - 1] != '/') + Res += Section + "/i18n/"; + return Res + Type; +} + +string debReleaseIndex::TranslationIndexURI(const char *Type, const string &Section) const +{ + string Res; + if (Dist[Dist.size() - 1] == '/') + { + if (Dist != "/") + Res = URI + Dist; + else + Res = URI; + return Res + Type; + } + else + return URI + "dists/" + Dist + "/" + TranslationIndexURISuffix(Type, Section); +} + debReleaseIndex::debReleaseIndex(string const &URI, string const &Dist) { this->URI = URI; this->Dist = Dist; @@ -155,6 +178,7 @@ vector * debReleaseIndex::ComputeIndexTargets() const { if (IndexTargets->empty() == false && ArchEntries.size() == 1) return IndexTargets; + std::set sections; for (map >::const_iterator a = ArchEntries.begin(); a != ArchEntries.end(); ++a) { if (a->first == "source") @@ -167,6 +191,37 @@ vector * debReleaseIndex::ComputeIndexTargets() const { Target->URI = IndexURI(Target->ShortDesc.c_str(), (*I)->Section, a->first); Target->Description = Info (Target->ShortDesc.c_str(), (*I)->Section, a->first); IndexTargets->push_back (Target); + sections.insert((*I)->Section); + } + } + + // get the Translations: + // - if its a dists-style repository get the i18n/Index first + // - if its flat try to acquire files by guessing + if (Dist[Dist.size() - 1] == '/') { + std::vector const lang = APT::Configuration::getLanguages(true); + for (std::set::const_iterator s = sections.begin(); + s != sections.end(); ++s) { + for (std::vector::const_iterator l = lang.begin(); + l != lang.end(); l++) { + if (*l == "none") continue; + IndexTarget * Target = new OptionalIndexTarget(); + Target->ShortDesc = "Translation-" + *l; + Target->MetaKey = TranslationIndexURISuffix(l->c_str(), *s); + Target->URI = TranslationIndexURI(l->c_str(), *s); + Target->Description = Info (Target->ShortDesc.c_str(), *s); + IndexTargets->push_back(Target); + } + } + } else { + for (std::set::const_iterator s = sections.begin(); + s != sections.end(); ++s) { + IndexTarget * Target = new OptionalIndexTarget(); + Target->ShortDesc = "TranslationIndex"; + Target->MetaKey = TranslationIndexURISuffix("Index", *s); + Target->URI = TranslationIndexURI("Index", *s); + Target->Description = Info (Target->ShortDesc.c_str(), *s); + IndexTargets->push_back (Target); } } @@ -191,28 +246,6 @@ bool debReleaseIndex::GetIndexes(pkgAcquire *Owner, bool const &GetAll) const ComputeIndexTargets(), new indexRecords (Dist)); - - // Queue the translations - std::vector const lang = APT::Configuration::getLanguages(true); - map > sections; - for (map >::const_iterator a = ArchEntries.begin(); - a != ArchEntries.end(); ++a) { - if (a->first == "source") - continue; - for (vector::const_iterator I = a->second.begin(); - I != a->second.end(); I++) - sections[(*I)->Section].insert(lang.begin(), lang.end()); - } - - for (map >::const_iterator s = sections.begin(); - s != sections.end(); ++s) - for (set::const_iterator l = s->second.begin(); - l != s->second.end(); l++) { - if (*l == "none") continue; - debTranslationsIndex i = debTranslationsIndex(URI,Dist,s->first,(*l).c_str()); - i.GetIndexes(Owner); - } - return true; } diff --git a/apt-pkg/deb/debmetaindex.h b/apt-pkg/deb/debmetaindex.h index 360fa5419..1561c6e00 100644 --- a/apt-pkg/deb/debmetaindex.h +++ b/apt-pkg/deb/debmetaindex.h @@ -37,6 +37,8 @@ class debReleaseIndex : public metaIndex { string IndexURISuffix(const char *Type, string const &Section, string const &Arch="native") const; string SourceIndexURI(const char *Type, const string &Section) const; string SourceIndexURISuffix(const char *Type, const string &Section) const; + string TranslationIndexURI(const char *Type, const string &Section) const; + string TranslationIndexURISuffix(const char *Type, const string &Section) const; virtual vector *GetIndexFiles(); virtual bool IsTrusted() const; diff --git a/debian/changelog b/debian/changelog index 7b125fc43..b3da32247 100644 --- a/debian/changelog +++ b/debian/changelog @@ -56,6 +56,7 @@ apt (0.8.11+wheezy) unstable; urgency=low - try downloading clearsigned InRelease before trying Release.gpg - change the internal handling of Extensions in pkgAcqIndex - add a special uncompressed compression type to prefer those files + - download and use i18n/Index to choose which Translations to download * cmdline/apt-key: - don't set trustdb-name as non-root so 'list' and 'finger' can be used without being root (Closes: #393005, #592107) @@ -66,8 +67,10 @@ apt (0.8.11+wheezy) unstable; urgency=low - include Index files by default in the Release file * methods/{gzip,bzip}.cc: - print a good error message if FileSize() is zero + * apt-pkg/aptconfiguration.cc: + - remove the inbuilt Translation files whitelist - -- David Kalnischkies Wed, 26 Jan 2011 16:06:10 +0100 + -- David Kalnischkies Fri, 28 Jan 2011 12:22:25 +0100 apt (0.8.10.3) unstable; urgency=low diff --git a/test/integration/test-bug-595691-empty-and-broken-archive-files b/test/integration/test-bug-595691-empty-and-broken-archive-files index 398d0cd1b..684559aa7 100755 --- a/test/integration/test-bug-595691-empty-and-broken-archive-files +++ b/test/integration/test-bug-595691-empty-and-broken-archive-files @@ -32,7 +32,7 @@ createemptyarchive() { fi touch aptarchive/Packages echo -n "" | $COMPRESSOR > aptarchive/${1}.$COMPRESS - aptftparchive release aptarchive/ > aptarchive/Release + generatereleasefiles signreleasefiles rm -f aptarchive/Packages } @@ -43,7 +43,7 @@ createemptyfile() { echo -n "" | $COMPRESSOR > aptarchive/Packages.$COMPRESS fi touch aptarchive/Packages aptarchive/${1}.$COMPRESS - aptftparchive release aptarchive/ > aptarchive/Release + generatereleasefiles signreleasefiles rm -f aptarchive/Packages } @@ -76,7 +76,7 @@ testoverfile() { createemptyfile 'en' testaptgetupdate "Get:1 file: InRelease [] -Ign file:$(readlink -f aptarchive)/ Translation-en +Ign file: Translation-en Reading package lists..." "empty file en.$COMPRESS over file" createemptyarchive 'en' @@ -86,13 +86,13 @@ Reading package lists..." "empty archive en.$COMPRESS over file" createemptyarchive 'Packages' # FIXME: Why omits the file transport the Packages Get line? #Get:3 file: Packages [] - testaptgetupdate "Ign file:$(readlink -f aptarchive)/ Translation-en -Get:1 file: InRelease [] + testaptgetupdate "Get:1 file: InRelease [] +Ign file: Translation-en Reading package lists..." "empty archive Packages.$COMPRESS over file" createemptyfile 'Packages' - testaptgetupdate "Ign file:$(readlink -f aptarchive)/ Translation-en -Get:1 file: InRelease [] + testaptgetupdate "Get:1 file: InRelease [] +Ign file: Translation-en Err file: Packages Empty files can't be valid archives W: Failed to fetch ${COMPRESSOR}:$(readlink -f aptarchive/Packages.$COMPRESS) Empty files can't be valid archives @@ -105,28 +105,28 @@ testoverhttp() { createemptyfile 'en' testaptgetupdate "Get:1 http://localhost InRelease [] -Get:2 http://localhost/ Translation-en -Get:3 http://localhost Packages [] -Ign http://localhost/ Translation-en +Get:2 http://localhost Packages [] +Get:3 http://localhost Translation-en +Ign http://localhost Translation-en Reading package lists..." "empty file en.$COMPRESS over http" createemptyarchive 'en' testaptgetupdate "Get:1 http://localhost InRelease [] -Get:2 http://localhost/ Translation-en [] -Get:3 http://localhost Packages [] +Get:2 http://localhost Packages [] +Get:3 http://localhost Translation-en [] Reading package lists..." "empty archive en.$COMPRESS over http" createemptyarchive 'Packages' testaptgetupdate "Get:1 http://localhost InRelease [] -Ign http://localhost/ Translation-en Get:2 http://localhost Packages [] +Ign http://localhost Translation-en Reading package lists..." "empty archive Packages.$COMPRESS over http" createemptyfile 'Packages' #FIXME: we should response with a good error message instead testaptgetupdate "Get:1 http://localhost InRelease [] -Ign http://localhost/ Translation-en Get:2 http://localhost Packages +Ign http://localhost Translation-en Err http://localhost Packages Empty files can't be valid archives W: Failed to fetch ${COMPRESSOR}:$(readlink -f rootdir/var/lib/apt/lists/partial/localhost:8080_Packages) Empty files can't be valid archives diff --git a/test/integration/test-bug-601016-description-translation b/test/integration/test-bug-601016-description-translation index a31e42ee1..2a323a201 100755 --- a/test/integration/test-bug-601016-description-translation +++ b/test/integration/test-bug-601016-description-translation @@ -57,6 +57,7 @@ Description-${LOCALE}: Mächtige Oberfläche für dpkg testrun() { echo "Acquire::Languages { \"${LOCALE}\"; \"en\"; };" > rootdir/etc/apt/apt.conf.d/00languages export LC_ALL="" + rm -rf rootdir/var/lib/apt/lists rootdir/var/cache/apt/ setupaptarchive testequal "$LOCALESTANZA" aptcache show apt -o Test=File-${LOCALE} testequal "$NOLONGSTANZA" aptcache show apt -o Acquire::Languages="ww" -o Test=File-${LOCALE} diff --git a/test/libapt/getlanguages_test.cc b/test/libapt/getlanguages_test.cc index 3559aae0c..707142aef 100644 --- a/test/libapt/getlanguages_test.cc +++ b/test/libapt/getlanguages_test.cc @@ -26,16 +26,18 @@ int main(int argc,char *argv[]) env[1] = ""; std::vector vec = APT::Configuration::getLanguages(false, false, env); - equals(vec.size(), 2); - equals(vec[0], "de"); - equals(vec[1], "en"); + equals(vec.size(), 3); + equals(vec[0], "de_DE"); + equals(vec[1], "de"); + equals(vec[2], "en"); // Special: Check if the cache is actually in use env[0] = "en_GB.UTF-8"; vec = APT::Configuration::getLanguages(false, true, env); - equals(vec.size(), 2); - equals(vec[0], "de"); - equals(vec[1], "en"); + equals(vec.size(), 3); + equals(vec[0], "de_DE"); + equals(vec[1], "de"); + equals(vec[2], "en"); env[0] = "en_GB.UTF-8"; vec = APT::Configuration::getLanguages(false, false, env); @@ -52,19 +54,21 @@ int main(int argc,char *argv[]) env[0] = "tr_DE@euro"; vec = APT::Configuration::getLanguages(false, false, env); - equals(vec.size(), 2); - equals(vec[0], "tr"); - equals(vec[1], "en"); + equals(vec.size(), 3); + equals(vec[0], "tr_DE"); + equals(vec[1], "tr"); + equals(vec[2], "en"); env[0] = "de_NO"; - env[1] = "se_NO:en_GB:nb_NO:nb:no_NO:no:nn_NO:nn:da:sv:en"; + env[1] = "de_NO:en_GB:nb_NO:nb:no_NO:no:nn_NO:nn:da:sv:en"; vec = APT::Configuration::getLanguages(false, false, env); - equals(vec.size(), 5); - equals(vec[0], "de"); - equals(vec[1], "en_GB"); - equals(vec[2], "nb"); - equals(vec[3], "no"); - equals(vec[4], "en"); + equals(vec.size(), 6); + equals(vec[0], "de_NO"); + equals(vec[1], "de"); + equals(vec[2], "en_GB"); + equals(vec[3], "nb_NO"); + equals(vec[4], "nb"); + equals(vec[5], "en"); env[0] = "pt_PR.UTF-8"; env[1] = ""; @@ -76,9 +80,10 @@ int main(int argc,char *argv[]) env[0] = "ast_DE.UTF-8"; vec = APT::Configuration::getLanguages(false, false, env); // bogus, but syntactical correct - equals(vec.size(), 2); - equals(vec[0], "ast"); - equals(vec[1], "en"); + equals(vec.size(), 3); + equals(vec[0], "ast_DE"); + equals(vec[1], "ast"); + equals(vec[2], "en"); env[0] = "C"; vec = APT::Configuration::getLanguages(false, false, env); @@ -113,25 +118,28 @@ int main(int argc,char *argv[]) _config->Set("Acquire::Languages::2", "en"); env[0] = "de_DE.UTF-8"; vec = APT::Configuration::getLanguages(false, false, env); - equals(vec.size(), 2); - equals(vec[0], "de"); - equals(vec[1], "en"); + equals(vec.size(), 3); + equals(vec[0], "de_DE"); + equals(vec[1], "de"); + equals(vec[2], "en"); _config->Set("Acquire::Languages::3", "de"); env[0] = "de_DE.UTF-8"; vec = APT::Configuration::getLanguages(false, false, env); - equals(vec.size(), 2); - equals(vec[0], "de"); - equals(vec[1], "en"); + equals(vec.size(), 3); + equals(vec[0], "de_DE"); + equals(vec[1], "de"); + equals(vec[2], "en"); _config->Set("Dir::State::lists", argv[1]); vec = APT::Configuration::getLanguages(true, false, env); - equals(vec.size(), 5); - equals(vec[0], "de"); - equals(vec[1], "en"); - equals(vec[2], "none"); - equals(vec[3], "pt"); - equals(vec[4], "tr"); + equals(vec.size(), 6); + equals(vec[0], "de_DE"); + equals(vec[1], "de"); + equals(vec[2], "en"); + equals(vec[3], "none"); + equals(vec[4], "pt"); + equals(vec[5], "tr"); _config->Set("Dir::State::lists", "/non-existing-dir"); _config->Set("Acquire::Languages::1", "none"); @@ -140,6 +148,7 @@ int main(int argc,char *argv[]) equals(vec.size(), 0); env[0] = "de_DE.UTF-8"; vec = APT::Configuration::getLanguages(true, false, env); + equals(vec.size(), 2); equals(vec[0], "en"); equals(vec[1], "de"); -- cgit v1.2.3-70-g09d2