Browse Source

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)
debian/1.8.y
David Kalnischkies 10 years ago
parent
commit
c919ad6e4d
  1. 5
      apt-pkg/algorithms.cc
  2. 22
      apt-pkg/deb/deblistparser.cc
  3. 8
      apt-pkg/deb/dpkgpm.cc
  4. 6
      apt-pkg/pkgcache.cc
  5. 53
      apt-pkg/pkgcachegen.cc
  6. 4
      debian/changelog
  7. 12
      test/integration/framework
  8. 87
      test/integration/test-bug-686346-package-missing-architecture

5
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);

22
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
{

8
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());

6
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);

53
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))

4
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

12
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]'

87
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
Loading…
Cancel
Save