From: Savvas Etairidis <setairidis@gmail.com>
To: bitbake-devel@lists.openembedded.org
Cc: jhonata.poma@gmail.com, paul@pbarker.dev,
Savvas Etairidis <setairidis@gmail.com>
Subject: [PATCH v3] fetch2/local: verify checksums for file:// urls
Date: Wed, 23 Sep 2026 21:44:35 +0200 [thread overview]
Message-ID: <20260923194435.58965-1-setairidis@gmail.com> (raw)
In-Reply-To: <20260511-b4-yocto-9993-v2-1-9d5a1c1451ba@gmail.com>
Local.urldata_init unconditionally cleared ud.needdonestamp, which
skipped the donestamp + verify_checksum cycle for every file:// url.
A checksum supplied via a url parameter, such as UNINATIVE_CHECKSUM
expanded into the uninative tarball's file:// url, was therefore
never compared against the file's actual contents.
Keep needdonestamp at its default when the url carries a checksum
parameter, and move rename_bad_checksum onto FetchMethod so Local can
override it as a no-op, avoiding a rename of the user's source file
on mismatch.
Based on a patch by Jhonata Poma-Hansen, adapted to this tree's
Local.urldata_init(), which now uses ud.decodedurl in place of the
ud.path referenced by the original patch.
[YOCTO #9993]
Signed-off-by: Savvas Etairidis <setairidis@gmail.com>
---
bitbake/lib/bb/fetch2/__init__.py | 36 +++++++++++++++----------------
bitbake/lib/bb/fetch2/local.py | 18 ++++++++++++++--
bitbake/lib/bb/tests/fetch.py | 30 ++++++++++++++++++++++++++
3 files changed, 64 insertions(+), 20 deletions(-)
diff --git a/bitbake/lib/bb/fetch2/__init__.py b/bitbake/lib/bb/fetch2/__init__.py
index 8f0ed2b9e2..da96a4c399 100644
--- a/bitbake/lib/bb/fetch2/__init__.py
+++ b/bitbake/lib/bb/fetch2/__init__.py
@@ -701,7 +701,7 @@ def verify_donestamp(ud, d, origud=None):
logger.warning("Checksum mismatch for local file %s\n"
"Cleaning and trying again." % ud.localpath)
if os.path.exists(ud.localpath):
- rename_bad_checksum(ud, e.checksum)
+ ud.method.rename_bad_checksum(ud, e.checksum)
bb.utils.remove(ud.donestamp)
return False
@@ -734,7 +734,7 @@ def update_stamp(ud, d):
logger.warning("Checksum mismatch for local file %s\n"
"Cleaning and trying again." % ud.localpath)
if os.path.exists(ud.localpath):
- rename_bad_checksum(ud, e.checksum)
+ ud.method.rename_bad_checksum(ud, e.checksum)
bb.utils.remove(ud.donestamp)
raise
@@ -1029,20 +1029,6 @@ def build_mirroruris(origud, mirrors, ld):
return uris, uds
-def rename_bad_checksum(ud, suffix):
- """
- Renames files to have suffix from parameter
- """
-
- if ud.localpath is None:
- return
-
- new_localpath = "%s_bad-checksum_%s" % (ud.localpath, suffix)
- bb.warn("Renaming %s to %s" % (ud.localpath, new_localpath))
- if not bb.utils.movefile(ud.localpath, new_localpath):
- bb.warn("Renaming %s to %s failed, grep movefile in log.do_fetch to see why" % (ud.localpath, new_localpath))
-
-
def try_mirror_url(fetch, origud, ud, ld, check = False):
# Return of None or a value means we're finished
# False means try another url
@@ -1111,7 +1097,7 @@ def try_mirror_url(fetch, origud, ud, ld, check = False):
logger.warning("Mirror checksum failure for url %s (original url: %s)\nCleaning and trying again." % (ud.url, origud.url))
logger.warning(str(e))
if os.path.exists(ud.localpath):
- rename_bad_checksum(ud, e.checksum)
+ ud.method.rename_bad_checksum(ud, e.checksum)
elif isinstance(e, NoChecksumError):
raise
else:
@@ -1450,6 +1436,20 @@ class FetchMethod(object):
"""
return True
+ def rename_bad_checksum(self, ud, suffix):
+ """
+ Rename ud.localpath with the given suffix on checksum mismatch.
+ Local overrides this to a no-op since its localpath points at the
+ user's source tree.
+ """
+ if ud.localpath is None:
+ return
+
+ new_localpath = "%s_bad-checksum_%s" % (ud.localpath, suffix)
+ bb.warn("Renaming %s to %s" % (ud.localpath, new_localpath))
+ if not bb.utils.movefile(ud.localpath, new_localpath):
+ bb.warn("Renaming %s to %s failed, grep movefile in log.do_fetch to see why" % (ud.localpath, new_localpath))
+
def verify_donestamp(self, ud, d):
"""
Verify the donestamp file
@@ -1887,7 +1887,7 @@ class Fetch(object):
logger.warning("Checksum failure encountered with download of %s - will attempt other sources if available" % u)
logger.debug(str(e))
if os.path.exists(ud.localpath):
- rename_bad_checksum(ud, e.checksum)
+ ud.method.rename_bad_checksum(ud, e.checksum)
elif isinstance(e, NoChecksumError):
raise
else:
diff --git a/bitbake/lib/bb/fetch2/local.py b/bitbake/lib/bb/fetch2/local.py
index 7d7668110e..43b8dd25ab 100644
--- a/bitbake/lib/bb/fetch2/local.py
+++ b/bitbake/lib/bb/fetch2/local.py
@@ -17,7 +17,7 @@ import os
import urllib.request, urllib.parse, urllib.error
import bb
import bb.utils
-from bb.fetch2 import FetchMethod, FetchError, ParameterError
+from bb.fetch2 import CHECKSUM_LIST, FetchMethod, FetchError, ParameterError
from bb.fetch2 import logger
class Local(FetchMethod):
@@ -32,11 +32,25 @@ class Local(FetchMethod):
ud.decodedurl = urllib.parse.unquote(ud.url.split("://")[1].split(";")[0])
ud.basename = os.path.basename(ud.decodedurl)
ud.basepath = ud.decodedurl
- ud.needdonestamp = False
+ # Honor explicit checksum params (UNINATIVE_CHECKSUM, recipe
+ # ;sha256sum=) by leaving needdonestamp at FetchData's default so
+ # verify_checksum runs.
+ name = ud.parm.get("name")
+ ud.needdonestamp = any(
+ (name and "%s.%ssum" % (name, a) in ud.parm)
+ or "%ssum" % a in ud.parm
+ for a in CHECKSUM_LIST
+ )
if "*" in ud.decodedurl:
raise bb.fetch2.ParameterError("file:// urls using globbing are no longer supported. Please place the files in a directory and reference that instead.", ud.url)
return
+ def rename_bad_checksum(self, ud, suffix):
+ # file:// urls point at the user's source tree (recipe-shipped files
+ # under FILESPATH or an absolute path the user passed in); skip the
+ # rename so a ChecksumError surfaces without mutating their files.
+ return
+
def localpath(self, urldata, d):
"""
Return the local filename of a given url assuming a successful fetch.
diff --git a/bitbake/lib/bb/tests/fetch.py b/bitbake/lib/bb/tests/fetch.py
index 68b2dbba96..4bc82af422 100644
--- a/bitbake/lib/bb/tests/fetch.py
+++ b/bitbake/lib/bb/tests/fetch.py
@@ -808,6 +808,36 @@ class FetcherLocalTest(FetcherTest):
tree = self.fetchUnpack(['file://archive.tar.bz2;subdir=bar;striplevel=1'])
self.assertEqual(tree, ['bar/c', 'bar/d', 'bar/subdir/e'])
+ def test_local_checksum_match(self):
+ # Correct sha256 must run verify_checksum to completion; donestamp
+ # lands in DL_DIR.
+ import hashlib
+ content = b"file:// checksum match test\n"
+ with open(os.path.join(self.localsrcdir, 'sumfile'), 'wb') as f:
+ f.write(content)
+ good = hashlib.sha256(content).hexdigest()
+ fetcher = bb.fetch.Fetch(['file://sumfile;sha256sum=' + good], self.d)
+ ud = fetcher.ud[fetcher.urls[0]]
+ self.assertTrue(ud.needdonestamp)
+ fetcher.download()
+ self.assertTrue(os.path.exists(ud.donestamp))
+
+ def test_local_checksum_mismatch(self):
+ # Bad sha256 raises ChecksumError without renaming the user's
+ # source file to the <localpath>_bad-checksum_<sha> sibling.
+ content = b"file:// checksum mismatch test\n"
+ srcpath = os.path.join(self.localsrcdir, 'sumfile')
+ with open(srcpath, 'wb') as f:
+ f.write(content)
+ bad = "0" * 64
+ fetcher = bb.fetch.Fetch(['file://sumfile;sha256sum=' + bad], self.d)
+ with self.assertRaises(bb.fetch2.FetchError):
+ fetcher.download()
+ self.assertTrue(os.path.exists(srcpath))
+ with open(srcpath, 'rb') as f:
+ self.assertEqual(f.read(), content)
+ self.assertFalse(os.path.exists(srcpath + '_bad-checksum_' + bad))
+
def dummyGitTest(self, suffix):
# Create dummy local Git repo
src_dir = tempfile.mkdtemp(dir=self.tempdir,
--
2.34.1
next prev parent reply other threads:[~2026-09-23 19:45 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-30 14:25 [PATCH] fetch2/local: verify checksums for file:// urls Jhonata Poma-Hansen
2026-05-08 14:13 ` [bitbake-devel] " Paul Barker
2026-05-11 14:58 ` [PATCH v2] " Jhonata Poma-Hansen
2026-05-11 15:01 ` Jhonata Poma-Hansen
2026-09-23 19:44 ` Savvas Etairidis [this message]
2026-09-24 6:36 ` [bitbake-devel] [PATCH v3] " Antonin Godard
2026-09-24 20:45 ` [PATCH v4] fetch/local: " Savvas Etairidis
2026-09-28 9:30 ` Paul Barker
2026-09-28 20:59 ` [PATCH v5] " Savvas Etairidis
2026-09-28 21:02 ` Sawas Etairidis
2026-09-29 8:22 ` Jhonata Poma-Hansen
2026-09-30 10:51 ` Paul Barker
2026-09-30 21:21 ` [PATCH v6] " Savvas Etairidis
2026-10-01 8:32 ` Paul Barker
2026-10-01 20:53 ` [PATCH v7] " Savvas Etairidis
2026-10-02 8:50 ` Paul Barker
2026-10-02 20:48 ` Sawas Etairidis
2026-10-02 20:50 ` [PATCH v8] " Savvas Etairidis
2026-10-05 14:36 ` Paul Barker
2026-10-06 16:40 ` Sawas Etairidis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260923194435.58965-1-setairidis@gmail.com \
--to=setairidis@gmail.com \
--cc=bitbake-devel@lists.openembedded.org \
--cc=jhonata.poma@gmail.com \
--cc=paul@pbarker.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.