summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Kalnischkies <kalnischkies@gmail.com>2012-09-09 16:03:52 +0200
committerDavid Kalnischkies <kalnischkies@gmail.com>2012-09-09 16:03:52 +0200
commitc919ad6e4d0de48acb60f2a1371ade9bfb0451f8 (patch)
treef814cdfc029498db53928c06e619c91683b3f115
parent601c2c8ec3d33a1e787af75f20b51d1131cb6d74 (diff)
handle packages without a mandatory architecture (debian-policy §5.3)
by introducing a pseudo-architecture 'none' so that the small group of users with these packages can get right of them without introducing too much hassle for other users (Closes: #686346)
-rw-r--r--apt-pkg/algorithms.cc5
-rw-r--r--apt-pkg/deb/deblistparser.cc22
-rw-r--r--apt-pkg/deb/dpkgpm.cc8
-rw-r--r--apt-pkg/pkgcache.cc6
-rw-r--r--apt-pkg/pkgcachegen.cc53
-rw-r--r--debian/changelog4
-rw-r--r--test/integration/framework12
-rwxr-xr-xtest/integration/test-bug-686346-package-missing-architecture87
8 files changed, 183 insertions, 14 deletions
diff --git a/apt-pkg/algorithms.cc b/apt-pkg/algorithms.cc
index 2f5fcc7ab..1b0161ffd 100644
--- a/apt-pkg/algorithms.cc
+++ b/apt-pkg/algorithms.cc
@@ -194,6 +194,11 @@ bool pkgSimulate::Remove(PkgIterator iPkg,bool Purge)
{
// Adapt the iterator
PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
+ if (Pkg.end() == true)
+ {
+ std::cerr << (Purge ? "Purg" : "Remv") << " invalid package " << iPkg.FullName() << std::endl;
+ return false;
+ }
Flags[Pkg->ID] = 3;
Sim.MarkDelete(Pkg);
diff --git a/apt-pkg/deb/deblistparser.cc b/apt-pkg/deb/deblistparser.cc
index 12c6ab4c9..b84bd6fdd 100644
--- a/apt-pkg/deb/deblistparser.cc
+++ b/apt-pkg/deb/deblistparser.cc
@@ -645,6 +645,8 @@ bool debListParser::ParseDepends(pkgCache::VerIterator &Ver,
a != Architectures.end(); ++a)
if (NewDepends(Ver,Package,*a,Version,Op,Type) == false)
return false;
+ if (NewDepends(Ver,Package,"none",Version,Op,Type) == false)
+ return false;
}
else if (MultiArchEnabled == true && found != string::npos &&
strcmp(Package.c_str() + found, ":any") != 0)
@@ -658,8 +660,18 @@ bool debListParser::ParseDepends(pkgCache::VerIterator &Ver,
if (NewDepends(Ver,Package,Arch,Version,Op,Type) == false)
return false;
}
- else if (NewDepends(Ver,Package,pkgArch,Version,Op,Type) == false)
- return false;
+ else
+ {
+ if (NewDepends(Ver,Package,pkgArch,Version,Op,Type) == false)
+ return false;
+ if ((Type == pkgCache::Dep::Conflicts ||
+ Type == pkgCache::Dep::DpkgBreaks ||
+ Type == pkgCache::Dep::Replaces) &&
+ NewDepends(Ver, Package,
+ (pkgArch != "none") ? "none" : _config->Find("APT::Architecture"),
+ Version,Op,Type) == false)
+ return false;
+ }
if (Start == Stop)
break;
}
@@ -753,13 +765,15 @@ bool debListParser::Step()
drop the whole section. A missing arch tag only happens (in theory)
inside the Status file, so that is a positive return */
string const Architecture = Section.FindS("Architecture");
- if (Architecture.empty() == true)
- return true;
if (Arch.empty() == true || Arch == "any" || MultiArchEnabled == false)
{
if (APT::Configuration::checkArchitecture(Architecture) == true)
return true;
+ /* parse version stanzas without an architecture only in the status file
+ (and as misfortune bycatch flat-archives) */
+ if ((Arch.empty() == true || Arch == "any") && Architecture.empty() == true)
+ return true;
}
else
{
diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc
index ae9143e0d..c9df41d3a 100644
--- a/apt-pkg/deb/dpkgpm.cc
+++ b/apt-pkg/deb/dpkgpm.cc
@@ -1131,7 +1131,9 @@ bool pkgDPkgPM::Go(int OutStatusFd)
if (I->Op == Item::Configure && disappearedPkgs.find(I->Pkg.Name()) != disappearedPkgs.end())
continue;
// We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
- if (dpkgMultiArch == false && (I->Pkg.Arch() == nativeArch || !strcmp(I->Pkg.Arch(), "all")))
+ if (dpkgMultiArch == false && (I->Pkg.Arch() == nativeArch ||
+ strcmp(I->Pkg.Arch(), "all") == 0 ||
+ strcmp(I->Pkg.Arch(), "none") == 0))
{
char const * const name = I->Pkg.Name();
ADDARG(name);
@@ -1148,7 +1150,9 @@ bool pkgDPkgPM::Go(int OutStatusFd)
}
else
PkgVer = Cache[I->Pkg].InstVerIter(Cache);
- if (PkgVer.end() == false)
+ if (strcmp(I->Pkg.Arch(), "none") == 0)
+ ; // never arch-qualify a package without an arch
+ else if (PkgVer.end() == false)
name.append(":").append(PkgVer.Arch());
else
_error->Warning("Can not find PkgVer for '%s'", name.c_str());
diff --git a/apt-pkg/pkgcache.cc b/apt-pkg/pkgcache.cc
index 9acb7da72..353172d8a 100644
--- a/apt-pkg/pkgcache.cc
+++ b/apt-pkg/pkgcache.cc
@@ -238,7 +238,7 @@ pkgCache::PkgIterator pkgCache::FindPkg(const string &Name) {
// ---------------------------------------------------------------------
/* Returns 0 on error, pointer to the package otherwise */
pkgCache::PkgIterator pkgCache::FindPkg(const string &Name, string const &Arch) {
- if (MultiArchCache() == false) {
+ if (MultiArchCache() == false && Arch != "none") {
if (Arch == "native" || Arch == "all" || Arch == "any" ||
Arch == NativeArch())
return SingleArchFindPkg(Name);
@@ -376,6 +376,10 @@ pkgCache::PkgIterator pkgCache::GrpIterator::FindPreferredPkg(bool const &Prefer
if (Pkg.end() == false && (PreferNonVirtual == false || Pkg->VersionList != 0))
return Pkg;
}
+ // packages without an architecture
+ Pkg = FindPkg("none");
+ if (Pkg.end() == false && (PreferNonVirtual == false || Pkg->VersionList != 0))
+ return Pkg;
if (PreferNonVirtual == true)
return FindPreferredPkg(false);
diff --git a/apt-pkg/pkgcachegen.cc b/apt-pkg/pkgcachegen.cc
index f70cbd02a..490c2ecbb 100644
--- a/apt-pkg/pkgcachegen.cc
+++ b/apt-pkg/pkgcachegen.cc
@@ -200,7 +200,19 @@ bool pkgCacheGenerator::MergeList(ListParser &List,
}
if (Arch.empty() == true)
- Arch = _config->Find("APT::Architecture");
+ {
+ // use the pseudo arch 'none' for arch-less packages
+ Arch = "none";
+ /* We might built a SingleArchCache here, which we don't want to blow up
+ just for these :none packages to a proper MultiArchCache, so just ensure
+ that we have always a native package structure first for SingleArch */
+ pkgCache::PkgIterator NP;
+ if (NewPackage(NP, PackageName, _config->Find("APT::Architecture")) == false)
+ // TRANSLATOR: The first placeholder is a package name,
+ // the other two should be copied verbatim as they include debug info
+ return _error->Error(_("Error occurred while processing %s (%s%d)"),
+ PackageName.c_str(), "NewPackage", 0);
+ }
// Get a pointer to the package structure
pkgCache::PkgIterator Pkg;
@@ -418,6 +430,42 @@ bool pkgCacheGenerator::MergeListVersion(ListParser &List, pkgCache::PkgIterator
return _error->Error(_("Error occurred while processing %s (%s%d)"),
Pkg.Name(), "AddImplicitDepends", 1);
}
+ /* :none packages are packages without an architecture. They are forbidden by
+ debian-policy, so usually they will only be in (old) dpkg status files -
+ and dpkg will complain about them - and are pretty rare. We therefore do
+ usually not create conflicts while the parent is created, but only if a :none
+ package (= the target) appears. This creates incorrect dependencies on :none
+ for architecture-specific dependencies on the package we copy from, but we
+ will ignore this bug as architecture-specific dependencies are only allowed
+ in jessie and until then the :none packages should be extinct (hopefully).
+ In other words: This should work long enough to allow graceful removal of
+ these packages, it is not supposed to allow users to keep using them … */
+ if (strcmp(Pkg.Arch(), "none") == 0)
+ {
+ pkgCache::PkgIterator M = Grp.FindPreferredPkg();
+ if (M.end() == false && Pkg != M)
+ {
+ pkgCache::DepIterator D = M.RevDependsList();
+ Dynamic<pkgCache::DepIterator> DynD(D);
+ for (; D.end() == false; ++D)
+ {
+ if ((D->Type != pkgCache::Dep::Conflicts &&
+ D->Type != pkgCache::Dep::DpkgBreaks &&
+ D->Type != pkgCache::Dep::Replaces) ||
+ D.ParentPkg().Group() == Grp)
+ continue;
+
+ map_ptrloc *OldDepLast = NULL;
+ pkgCache::VerIterator ConVersion = D.ParentVer();
+ // duplicate the Conflicts/Breaks/Replaces for :none arch
+ if (D->Version == 0)
+ NewDepends(Pkg, ConVersion, "", 0, D->Type, OldDepLast);
+ else
+ NewDepends(Pkg, ConVersion, D.TargetVer(),
+ D->CompareOp, D->Type, OldDepLast);
+ }
+ }
+ }
}
if (unlikely(AddImplicitDepends(Grp, Pkg, Ver) == false))
return _error->Error(_("Error occurred while processing %s (%s%d)"),
@@ -871,6 +919,9 @@ bool pkgCacheGenerator::ListParser::NewDepends(pkgCache::VerIterator &Ver,
// Locate the target package
pkgCache::PkgIterator Pkg = Grp.FindPkg(Arch);
+ // we don't create 'none' packages and their dependencies if we can avoid it …
+ if (Pkg.end() == true && Arch == "none")
+ return true;
Dynamic<pkgCache::PkgIterator> DynPkg(Pkg);
if (Pkg.end() == true) {
if (unlikely(Owner->NewPackage(Pkg, PackageName, Arch) == false))
diff --git a/debian/changelog b/debian/changelog
index e62f0b681..90f3199b3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -4,6 +4,10 @@ apt (0.9.7.5) UNRELEASED; urgency=low
* Japanese (KURASAWA Nozomu) (Closes: #684435)
[ David Kalnischkies ]
+ * handle packages without a mandatory architecture (debian-policy §5.3)
+ by introducing a pseudo-architecture 'none' so that the small group of
+ users with these packages can get right of them without introducing too
+ much hassle for other users (Closes: #686346)
* apt-pkg/cdrom.cc:
- copy only configured translation files from a CD-ROM and not all
available translation files preventing new installs with d-i from
diff --git a/test/integration/framework b/test/integration/framework
index 57bf555af..1c4872c8e 100644
--- a/test/integration/framework
+++ b/test/integration/framework
@@ -468,7 +468,7 @@ insertpackage() {
local PRIORITY="${6:-optional}"
local ARCHS=""
for arch in $(echo "$ARCH" | sed -e 's#,#\n#g' | sed -e "s#^native\$#$(getarchitecture 'native')#"); do
- if [ "$arch" = "all" ]; then
+ if [ "$arch" = 'all' -o "$arch" = 'none' ]; then
ARCHS="$(getarchitectures)"
else
ARCHS="$arch"
@@ -482,9 +482,9 @@ insertpackage() {
Priority: $PRIORITY
Section: other
Installed-Size: 42
-Maintainer: Joe Sixpack <joe@example.org>
-Architecture: $arch
-Version: $VERSION
+Maintainer: Joe Sixpack <joe@example.org>" >> $FILE
+ test "$arch" = 'none' || echo "Architecture: $arch" >> $FILE
+ echo "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}
@@ -534,8 +534,8 @@ Priority: $PRIORITY
Section: other
Installed-Size: 42
Maintainer: Joe Sixpack <joe@example.org>
-Architecture: $arch
Version: $VERSION" >> $FILE
+ test "$arch" = 'none' || echo "Architecture: $arch" >> $FILE
test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" >> $FILE
echo "Description: an autogenerated dummy ${NAME}=${VERSION}/installed
If you find such a package installed on your system,
@@ -818,7 +818,7 @@ testnopackage() {
testdpkginstalled() {
msgtest "Test for correctly installed package(s) with" "dpkg -l $*"
- local PKGS="$(dpkg -l $* | grep '^i' | wc -l)"
+ local PKGS="$(dpkg -l $* 2>/dev/null | grep '^i' | wc -l)"
if [ "$PKGS" != $# ]; then
echo $PKGS
dpkg -l $* | grep '^[a-z]'
diff --git a/test/integration/test-bug-686346-package-missing-architecture b/test/integration/test-bug-686346-package-missing-architecture
new file mode 100755
index 000000000..b0e0aa3c4
--- /dev/null
+++ b/test/integration/test-bug-686346-package-missing-architecture
@@ -0,0 +1,87 @@
+#!/bin/sh
+set -e
+
+TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture 'amd64'
+
+insertinstalledpackage 'pkgb' 'none' '1'
+insertinstalledpackage 'pkgd' 'none' '1'
+insertpackage 'unstable' 'pkga' 'amd64' '2' 'Depends: pkgb'
+insertpackage 'unstable' 'pkgb' 'amd64' '2'
+insertpackage 'unstable' 'pkgc' 'amd64' '1' 'Conflicts: pkgb'
+insertpackage 'unstable' 'pkge' 'none' '1'
+
+setupaptarchive
+
+testequal 'Reading package lists...
+Building dependency tree...
+The following packages will be REMOVED:
+ pkgb:none
+The following NEW packages will be installed:
+ pkgc
+0 upgraded, 1 newly installed, 1 to remove and 0 not upgraded.
+Remv pkgb:none [1]
+Inst pkgc (1 unstable [amd64])
+Conf pkgc (1 unstable [amd64])' aptget install pkgc -s
+
+testequal 'Reading package lists...
+Building dependency tree...
+The following extra packages will be installed:
+ pkgb
+The following packages will be REMOVED:
+ pkgb:none
+The following NEW packages will be installed:
+ pkga pkgb
+0 upgraded, 2 newly installed, 1 to remove and 0 not upgraded.
+Remv pkgb:none [1]
+Inst pkgb (2 unstable [amd64])
+Inst pkga (2 unstable [amd64])
+Conf pkgb (2 unstable [amd64])
+Conf pkga (2 unstable [amd64])' aptget install pkga -s
+
+# ensure that arch-less stanzas from Packages files are ignored
+msgtest 'Package is distributed in the Packages files' 'pkge'
+grep -q 'Package: pkge' $(find aptarchive -name 'Packages') && msgpass || msgfail
+testnopackage pkge
+testnopackage pkge:none
+testnopackage pkge:*
+
+# do not automatically change from none-arch to whatever-arch as
+# this breaks other none packages and dpkg has this ruleset as
+# this difference seems so important that it has to be maintained …
+testequal 'Reading package lists...
+Building dependency tree...
+0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.' aptget dist-upgrade -s
+
+# pkgd has no update with an architecture
+testdpkginstalled pkgd
+msgtest 'Test apt-get purge' 'pkgd'
+aptget purge pkgd -y >/dev/null 2>&1 && msgpass || msgfail
+testdpkgnotinstalled pkgd
+
+# there is a pkgb with an architecture
+testdpkginstalled pkgb
+msgtest 'Test apt-get purge' 'pkgb:none'
+aptget purge pkgb:none -y >/dev/null 2>&1 && msgpass || msgfail
+testdpkgnotinstalled pkgb
+
+# check that dependencies are created after the none package exists in the cache
+rm rootdir/var/cache/apt/*.bin
+insertinstalledpackage 'pkgb' 'none' '1'
+insertinstalledpackage 'pkgf' 'none' '1' 'Conflicts: pkgb'
+insertinstalledpackage 'pkgg' 'amd64' '1' 'Conflicts: pkgb'
+insertinstalledpackage 'pkgb' 'amd64' '2'
+testequal "Reading package lists...
+Building dependency tree...
+Reading state information...
+You might want to run 'apt-get -f install' to correct these.
+The following packages have unmet dependencies:
+ pkgb : Conflicts: pkgb:none but 1 is installed
+ pkgb:none : Conflicts: pkgb but 2 is installed
+ pkgf:none : Conflicts: pkgb:none but 1 is installed
+ Conflicts: pkgb but 2 is installed
+ pkgg : Conflicts: pkgb but 2 is installed
+ Conflicts: pkgb:none but 1 is installed
+E: Unmet dependencies. Try using -f." aptget check