Skip to content

Commit

Permalink
Fix move_ent with signed binpkg
Browse files Browse the repository at this point in the history
The gpkg file that cannot be updated will be removed.

[sam: We did discuss stripping the signatures to re-use them but that
feels messier and likely unexpected by the user. We also don't distinguish
between locally-made vs fetched binpkgs (where perhaps for local ones, we could
just re-sign if signed).

It was also raised that for fetched binpkgs, we could not worry once it's
been verified the first time, but that's again complicated.

Simply dropping these binpkgs seems like the best solution and to allow
the binhost to re-sign it on updating instead.]

Bug: https://bugs.gentoo.org/919419
Signed-off-by: Sheng Yu <syu.os@protonmail.com>
Closes: gentoo#1043
Signed-off-by: Sam James <sam@gentoo.org>
  • Loading branch information
syu-nya authored and thesamesam committed Dec 8, 2023
1 parent 1d85674 commit a7bbb4f
Show file tree
Hide file tree
Showing 7 changed files with 436 additions and 5 deletions.
21 changes: 20 additions & 1 deletion lib/portage/dbapi/bintree.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,24 @@ def aux_update(self, cpv, values):
elif binpkg_format == "gpkg":
mybinpkg = portage.gpkg.gpkg(self.settings, cpv_str, binpkg_path)
mydata = mybinpkg.get_metadata()
if mybinpkg.signature_exist:
writemsg(
colorize(
"WARN",
f"Binpkg update ignored for signed package: {binpkg_path}, "
"the file will be removed.",
)
)
try:
os.remove(binpkg_path)
except OSError as err:
writemsg(
colorize(
"WARN",
f"Failed to remove moved signed package: {binpkg_path} {str(err)}",
)
)
return
encoding_key = False
else:
raise InvalidBinaryPackageFormat(
Expand Down Expand Up @@ -687,7 +705,6 @@ def move_ent(self, mylist, repo_match=None):
)
continue

moves += 1
binpkg_format = get_binpkg_format(binpkg_path)
if binpkg_format == "xpak":
mytbz2 = portage.xpak.tbz2(binpkg_path)
Expand All @@ -708,6 +725,8 @@ def move_ent(self, mylist, repo_match=None):
else:
continue

moves += 1

updated_items = update_dbentries([mylist], mydata, parent=mycpv)
mydata.update(updated_items)
if decode_metadata_name:
Expand Down
4 changes: 4 additions & 0 deletions lib/portage/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ class CompressorOperationFailed(PortagePackageException):
"""An error occurred during external operation"""


class SignedPackage(PortagePackageException):
"""Unable to update a signed package"""


class InvalidAtom(PortagePackageException):
"""Malformed atom spec"""

Expand Down
6 changes: 5 additions & 1 deletion lib/portage/gpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
DigestException,
MissingSignature,
InvalidSignature,
SignedPackage,
)
from portage.output import colorize, EOutput
from portage.util._urlopen import urlopen
Expand Down Expand Up @@ -991,14 +992,17 @@ def decompress(self, decompress_dir):
finally:
image_tar.kill()

def update_metadata(self, metadata, new_basename=None):
def update_metadata(self, metadata, new_basename=None, force=False):
"""
Update metadata in the gpkg file.
"""
self._verify_binpkg()
self.checksums = []
old_basename = self.prefix

if self.signature_exist and not force:
raise SignedPackage("Cannot update a signed gpkg file")

if new_basename is None:
new_basename = old_basename
else:
Expand Down
2 changes: 1 addition & 1 deletion lib/portage/tests/gpkg/test_gpkg_metadata_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class test_gpkg_metadata_case(TestCase):
def test_gpkg_update_metadata(self):
playground = ResolverPlayground(
user_config={
"make.conf": ('BINPKG_COMPRESS="gzip"',),
"make.conf": ('BINPKG_COMPRESS="gzip"', 'FEATURES="-binpkg-signing"'),
}
)
tmpdir = tempfile.mkdtemp()
Expand Down
108 changes: 108 additions & 0 deletions lib/portage/tests/update/test_move_ent.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,111 @@ def testMoveEnt(self):

finally:
playground.cleanup()

def testMoveEntWithSignature(self):
ebuilds = {
"dev-libs/A-2::dont_apply_updates": {
"EAPI": "4",
"SLOT": "2",
},
}

installed = {
"dev-libs/A-1::test_repo": {
"EAPI": "4",
},
"dev-libs/A-2::dont_apply_updates": {
"EAPI": "4",
"SLOT": "2",
},
}

binpkgs = {
"dev-libs/A-1::test_repo": {
"EAPI": "4",
},
"dev-libs/A-2::dont_apply_updates": {
"EAPI": "4",
"SLOT": "2",
},
}

updates = textwrap.dedent(
"""
move dev-libs/A dev-libs/A-moved
"""
)

for binpkg_format in ("gpkg",):
with self.subTest(binpkg_format=binpkg_format):
print(colorize("HILITE", binpkg_format), end=" ... ")
sys.stdout.flush()
playground = ResolverPlayground(
binpkgs=binpkgs,
ebuilds=ebuilds,
installed=installed,
user_config={
"make.conf": (f'BINPKG_FORMAT="{binpkg_format}"',),
},
)

settings = playground.settings
trees = playground.trees
eroot = settings["EROOT"]
test_repo_location = settings.repositories["test_repo"].location
portdb = trees[eroot]["porttree"].dbapi
vardb = trees[eroot]["vartree"].dbapi
bindb = trees[eroot]["bintree"].dbapi

updates_dir = os.path.join(test_repo_location, "profiles", "updates")

try:
ensure_dirs(updates_dir)
with open(os.path.join(updates_dir, "1Q-2010"), "w") as f:
f.write(updates)

# Create an empty updates directory, so that this
# repo doesn't inherit updates from the main repo.
ensure_dirs(
os.path.join(
portdb.getRepositoryPath("dont_apply_updates"),
"profiles",
"updates",
)
)

global_noiselimit = portage.util.noiselimit
portage.util.noiselimit = -2
try:
_do_global_updates(trees, {})
finally:
portage.util.noiselimit = global_noiselimit

# Workaround for cache validation not working
# correctly when filesystem has timestamp precision
# of 1 second.
vardb._clear_cache()

# A -> A-moved
self.assertRaises(KeyError, vardb.aux_get, "dev-libs/A-1", ["EAPI"])
vardb.aux_get("dev-libs/A-moved-1", ["EAPI"])
# The original package should still exist because a binary
# package move is a copy on write operation.
bindb.aux_get("dev-libs/A-1", ["EAPI"])
print(bindb.aux_get("dev-libs/A-1", "PF"))
self.assertRaises(
KeyError, bindb.aux_get, "dev-libs/A-moved-1", ["EAPI"]
)

# dont_apply_updates
self.assertRaises(
KeyError, vardb.aux_get, "dev-libs/A-moved-2", ["EAPI"]
)
vardb.aux_get("dev-libs/A-2", ["EAPI"])
self.assertRaises(
KeyError, bindb.aux_get, "dev-libs/A-moved-2", ["EAPI"]
)
bindb.aux_get("dev-libs/A-2", ["EAPI"])

finally:
playground.cleanup()
148 changes: 147 additions & 1 deletion lib/portage/tests/update/test_move_slot_ent.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ def testMoveSlotEnt(self):
ebuilds=ebuilds,
installed=installed,
user_config={
"make.conf": (f'BINPKG_FORMAT="{binpkg_format}"',),
"make.conf": (
f'BINPKG_FORMAT="{binpkg_format}"',
'FEATURES="-binpkg-signing"',
),
},
)

Expand Down Expand Up @@ -154,3 +157,146 @@ def testMoveSlotEnt(self):

finally:
playground.cleanup()

def testMoveSlotEntWithSignature(self):
ebuilds = {
"dev-libs/A-2::dont_apply_updates": {
"EAPI": "5",
"SLOT": "0/2.30",
},
"dev-libs/B-2::dont_apply_updates": {
"SLOT": "0",
},
"dev-libs/C-2.1::dont_apply_updates": {
"EAPI": "5",
"SLOT": "0/2.1",
},
}

installed = {
"dev-libs/A-1::test_repo": {
"EAPI": "5",
"SLOT": "0/2.30",
},
"dev-libs/B-1::test_repo": {
"SLOT": "0",
},
"dev-libs/C-1::test_repo": {
"EAPI": "5",
"SLOT": "0/1",
},
}

binpkgs = {
"dev-libs/A-1::test_repo": {
"EAPI": "5",
"SLOT": "0/2.30",
},
"dev-libs/A-2::dont_apply_updates": {
"EAPI": "5",
"SLOT": "0/2.30",
},
"dev-libs/B-1::test_repo": {
"SLOT": "0",
},
"dev-libs/B-2::dont_apply_updates": {
"SLOT": "0",
},
"dev-libs/C-1::test_repo": {
"EAPI": "5",
"SLOT": "0/1",
},
"dev-libs/C-2.1::dont_apply_updates": {
"EAPI": "5",
"SLOT": "0/2.1",
},
}

updates = textwrap.dedent(
"""
slotmove dev-libs/A 0 2
slotmove dev-libs/B 0 1
slotmove dev-libs/C 0 1
"""
)

for binpkg_format in ("gpkg",):
with self.subTest(binpkg_format=binpkg_format):
print(colorize("HILITE", binpkg_format), end=" ... ")
sys.stdout.flush()
playground = ResolverPlayground(
binpkgs=binpkgs,
ebuilds=ebuilds,
installed=installed,
user_config={
"make.conf": (
f'BINPKG_FORMAT="{binpkg_format}"',
'FEATURES="binpkg-signing"',
),
},
)

settings = playground.settings
trees = playground.trees
eroot = settings["EROOT"]
test_repo_location = settings.repositories["test_repo"].location
portdb = trees[eroot]["porttree"].dbapi
vardb = trees[eroot]["vartree"].dbapi
bindb = trees[eroot]["bintree"].dbapi

updates_dir = os.path.join(test_repo_location, "profiles", "updates")

try:
ensure_dirs(updates_dir)
with open(os.path.join(updates_dir, "1Q-2010"), "w") as f:
f.write(updates)

# Create an empty updates directory, so that this
# repo doesn't inherit updates from the main repo.
ensure_dirs(
os.path.join(
portdb.getRepositoryPath("dont_apply_updates"),
"profiles",
"updates",
)
)

global_noiselimit = portage.util.noiselimit
portage.util.noiselimit = -2
try:
_do_global_updates(trees, {})
finally:
portage.util.noiselimit = global_noiselimit

# Workaround for cache validation not working
# correctly when filesystem has timestamp precision
# of 1 second.
vardb._clear_cache()

# 0/2.30 -> 2/2.30
self.assertEqual(
"2/2.30", vardb.aux_get("dev-libs/A-1", ["SLOT"])[0]
)
self.assertEqual(
"0/2.30", bindb.aux_get("dev-libs/A-1", ["SLOT"])[0]
)

# 0 -> 1
self.assertEqual("1", vardb.aux_get("dev-libs/B-1", ["SLOT"])[0])
self.assertEqual("0", bindb.aux_get("dev-libs/B-1", ["SLOT"])[0])

# 0/1 -> 1 (equivalent to 1/1)
self.assertEqual("1", vardb.aux_get("dev-libs/C-1", ["SLOT"])[0])
self.assertEqual("0/1", bindb.aux_get("dev-libs/C-1", ["SLOT"])[0])

# dont_apply_updates
self.assertEqual(
"0/2.30", bindb.aux_get("dev-libs/A-2", ["SLOT"])[0]
)
self.assertEqual("0", bindb.aux_get("dev-libs/B-2", ["SLOT"])[0])
self.assertEqual(
"0/2.1", bindb.aux_get("dev-libs/C-2.1", ["SLOT"])[0]
)

finally:
playground.cleanup()

0 comments on commit a7bbb4f

Please sign in to comment.