diff mbox series

[meta-oe] bmap-writer: honour ImageSize for the last block

Message ID 20260929173728.6952-1-f_l_k@t-online.de
State New
Headers show
Series [meta-oe] bmap-writer: honour ImageSize for the last block | expand

Commit Message

Markus Volk Sept. 29, 2026, 5:37 p.m. UTC
Writing an image whose size is not a multiple of the bmap block size
failed at its last block with "Image ended before the block map did",
because every range was taken to end at (endBlock + 1) * BlockSize.
Add a patch that reads ImageSize from the bmap and clamps the ranges to
it.

Tested by writing a wic image whose size ends 2048 bytes into its
last 4 KiB block to an NVMe disk.

AI-Generated: Uses Claude Code (Claude Fable 5.1)
Signed-off-by: Markus Volk <f_l_k@t-online.de>
---
 ...-Honour-ImageSize-for-the-last-block.patch | 85 +++++++++++++++++++
 .../bmap-writer/bmap-writer_1.0.5.bb          |  4 +-
 2 files changed, 88 insertions(+), 1 deletion(-)
 create mode 100644 meta-oe/recipes-support/bmap-writer/bmap-writer/0001-Honour-ImageSize-for-the-last-block.patch

Comments

Khem Raj Sept. 29, 2026, 10:48 p.m. UTC | #1
/quiltrc push', 1, 'stdout: Applying patch
0001-Honour-ImageSize-for-the-last-block.patch
patch: **** Only garbage was found in the patch input.
Patch 0001-Honour-ImageSize-for-the-last-block.patch does not apply
(enforce with -f)

stderr: ')

Also rebase it on master-next when you send fixed v2

On Tue, Sep 29, 2026 at 10:37 AM Markus Volk via lists.openembedded.org
<f_l_k=t-online.de@lists.openembedded.org> wrote:

> Writing an image whose size is not a multiple of the bmap block size
> failed at its last block with "Image ended before the block map did",
> because every range was taken to end at (endBlock + 1) * BlockSize.
> Add a patch that reads ImageSize from the bmap and clamps the ranges to
> it.
>
> Tested by writing a wic image whose size ends 2048 bytes into its
> last 4 KiB block to an NVMe disk.
>
> AI-Generated: Uses Claude Code (Claude Fable 5.1)
> Signed-off-by: Markus Volk <f_l_k@t-online.de>
> ---
>  ...-Honour-ImageSize-for-the-last-block.patch | 85 +++++++++++++++++++
>  .../bmap-writer/bmap-writer_1.0.5.bb          |  4 +-
>  2 files changed, 88 insertions(+), 1 deletion(-)
>  create mode 100644
> meta-oe/recipes-support/bmap-writer/bmap-writer/0001-Honour-ImageSize-for-the-last-block.patch
>
> diff --git
> a/meta-oe/recipes-support/bmap-writer/bmap-writer/0001-Honour-ImageSize-for-the-last-block.patch
> b/meta-oe/recipes-support/bmap-writer/bmap-writer/0001-Honour-ImageSize-for-the-last-block.patch
> new file mode 100644
> index 0000000000..1943256d71
> --- /dev/null
> +++
> b/meta-oe/recipes-support/bmap-writer/bmap-writer/0001-Honour-ImageSize-for-the-last-block.patch
> @@ -0,0 +1,85 @@
> +From e63a11dedcdece8b9b285485a86405fd258b37d8 Mon Sep 17 00:00:00 2001
> +From: Markus Volk <f_l_k@t-online.de>
> +Date: Tue, 29 Sep 2026 17:29:52 +0200
> +Subject: [PATCH] Honour ImageSize for the last block
> +
> +bmaptool records the exact image size in ImageSize, and the last block
> +of an image whose size is not a multiple of BlockSize is only partly
> +filled. Writing and verifying used (endBlock + 1) * BlockSize as the end
> +of every range, so the range containing that block asked for more bytes
> +than the image has and the write failed with "Image ended before the
> +block map did". Read ImageSize and clamp the end of each range to it.
> +
> +AI-Generated: Uses Claude Code (Claude Fable 5.1)
> +Upstream-Status: Pending
> +---
> + src/bmap.cpp         | 5 +++++
> + src/bmap.h           | 1 +
> + src/image_writer.cpp | 2 +-
> + src/main.cpp         | 2 +-
> + 4 files changed, 8 insertions(+), 2 deletions(-)
> +
> +diff --git a/src/bmap.cpp b/src/bmap.cpp
> +index fabf2c2..0162ac0 100644
> +--- a/src/bmap.cpp
> ++++ b/src/bmap.cpp
> +@@ -135,6 +135,7 @@ int parseBMap(const std::string &filename, bmap_t&
> bmapData) {
> +         bmapData.bmapVersion = (version != nullptr) ?
> trimWhitespace(version) : "unknown";
> +
> +         // Parse image information
> ++        bmapData.imageSize = requireChildUnsigned(p_root, "ImageSize");
> +         bmapData.blocksTotal = requireChildUnsigned(p_root,
> "BlocksCount");
> +         bmapData.blocksMapped = requireChildUnsigned(p_root,
> "MappedBlocksCount");
> +         bmapData.blockSize = requireChildUnsigned(p_root, "BlockSize");
> +@@ -200,6 +201,10 @@ int validateBmap(const bmap_t& bmap, uint64_t
> deviceSize) {
> +         if (bmap.blocksTotal == 0) {
> +             throw BmapError("BMAP: BlocksCount must not be zero");
> +         }
> ++        if (bmap.imageSize > bmap.blocksTotal * bmap.blockSize ||
> ++            bmap.imageSize <= (bmap.blocksTotal - 1) * bmap.blockSize) {
> ++            throw BmapError("BMAP: ImageSize does not match BlocksCount
> and BlockSize");
> ++        }
> +         if (bmap.blocksMapped > bmap.blocksTotal) {
> +             throw BmapError("BMAP: MappedBlocksCount exceeds
> BlocksCount");
> +         }
> +diff --git a/src/bmap.h b/src/bmap.h
> +index cf6ff9d..98cb969 100644
> +--- a/src/bmap.h
> ++++ b/src/bmap.h
> +@@ -38,6 +38,7 @@ struct bmap_t {
> +     std::string checksumType;
> +     size_t blockSize = 0;
> +     size_t blocksTotal = 0;
> ++    size_t imageSize = 0;
> +     size_t blocksMapped = 0;
> +     std::string bmapVersion;
> +     std::string bmapChecksum;
> +diff --git a/src/image_writer.cpp b/src/image_writer.cpp
> +index bb802ea..f864151 100644
> +--- a/src/image_writer.cpp
> ++++ b/src/image_writer.cpp
> +@@ -177,7 +177,7 @@ int BmapWriteImage(int fd, const bmap_t &bmap, int
> dev_fd, const std::string &de
> +         for (const auto &range : bmap.ranges) {
> +             // validateBmap() has already proven that none of this can
> overflow.
> +             const size_t outStart = range.startBlock * bmap.blockSize;
> +-            const size_t outEnd = (range.endBlock + 1) * bmap.blockSize;
> ++            const size_t outEnd = std::min((range.endBlock + 1) *
> bmap.blockSize, bmap.imageSize);
> +             const size_t rangeSize = outEnd - outStart;
> +             const off_t writeOffset = static_cast<off_t>(outStart);
> +             size_t writtenSize = 0;
> +diff --git a/src/main.cpp b/src/main.cpp
> +index ca42c12..b8cbbc5 100644
> +--- a/src/main.cpp
> ++++ b/src/main.cpp
> +@@ -166,7 +166,7 @@ int main(int argc, char *argv[]) {
> +     FdGuard imageGuard(image_fd);
> +
> +     std::cout << "BMAP format version: " << bmap.bmapVersion <<
> std::endl;
> +-    std::cout << "Image size: " << (bmap.blocksTotal * bmap.blockSize)
> << " bytes" << std::endl;
> ++    std::cout << "Image size: " << bmap.imageSize << " bytes" <<
> std::endl;
> +     std::cout << "Block size: " << bmap.blockSize << " bytes" <<
> std::endl;
> +     std::cout << "Mapped blocks: " << bmap.blocksMapped << " out of " <<
> bmap.blocksTotal
> +               << " (" << std::fixed << std::setprecision(1)
> +--
> +2.55.0
> +
> diff --git a/meta-oe/recipes-support/bmap-writer/bmap-writer_1.0.5.bb
> b/meta-oe/recipes-support/bmap-writer/bmap-writer_1.0.5.bb
> index 160d1b7112..5d493f3122 100644
> --- a/meta-oe/recipes-support/bmap-writer/bmap-writer_1.0.5.bb
> +++ b/meta-oe/recipes-support/bmap-writer/bmap-writer_1.0.5.bb
> @@ -8,7 +8,9 @@ SECTION = "console/utils"
>  LICENSE = "GPL-3.0-only"
>  LIC_FILES_CHKSUM = "file://LICENSE;md5=e49f4652534af377a713df3d9dec60cb"
>
> -SRC_URI = "git://
> github.com/embetrix/${BPN};branch=master;protocol=https;tag=${PV}
> <http://github.com/embetrix/$%7BBPN%7D;branch=master;protocol=https;tag=$%7BPV%7D>
> "
> +SRC_URI = "git://
> github.com/embetrix/${BPN};branch=master;protocol=https;tag=${PV}
> <http://github.com/embetrix/$%7BBPN%7D;branch=master;protocol=https;tag=$%7BPV%7D>
> \
> +           file://0001-Honour-ImageSize-for-the-last-block.patch \
> +           "
>  SRCREV = "c7b1bec0f40217d17e315226552e461a4a816abc"
>
>  DEPENDS = "libtinyxml2 libarchive"
> --
> 2.55.0
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#130486):
> https://lists.openembedded.org/g/openembedded-devel/message/130486
> Mute This Topic: https://lists.openembedded.org/mt/121494426/1997914
> Group Owner: openembedded-devel+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [
> raj.khem@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
>
diff mbox series

Patch

diff --git a/meta-oe/recipes-support/bmap-writer/bmap-writer/0001-Honour-ImageSize-for-the-last-block.patch b/meta-oe/recipes-support/bmap-writer/bmap-writer/0001-Honour-ImageSize-for-the-last-block.patch
new file mode 100644
index 0000000000..1943256d71
--- /dev/null
+++ b/meta-oe/recipes-support/bmap-writer/bmap-writer/0001-Honour-ImageSize-for-the-last-block.patch
@@ -0,0 +1,85 @@ 
+From e63a11dedcdece8b9b285485a86405fd258b37d8 Mon Sep 17 00:00:00 2001
+From: Markus Volk <f_l_k@t-online.de>
+Date: Tue, 29 Sep 2026 17:29:52 +0200
+Subject: [PATCH] Honour ImageSize for the last block
+
+bmaptool records the exact image size in ImageSize, and the last block
+of an image whose size is not a multiple of BlockSize is only partly
+filled. Writing and verifying used (endBlock + 1) * BlockSize as the end
+of every range, so the range containing that block asked for more bytes
+than the image has and the write failed with "Image ended before the
+block map did". Read ImageSize and clamp the end of each range to it.
+
+AI-Generated: Uses Claude Code (Claude Fable 5.1)
+Upstream-Status: Pending
+---
+ src/bmap.cpp         | 5 +++++
+ src/bmap.h           | 1 +
+ src/image_writer.cpp | 2 +-
+ src/main.cpp         | 2 +-
+ 4 files changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/src/bmap.cpp b/src/bmap.cpp
+index fabf2c2..0162ac0 100644
+--- a/src/bmap.cpp
++++ b/src/bmap.cpp
+@@ -135,6 +135,7 @@ int parseBMap(const std::string &filename, bmap_t& bmapData) {
+         bmapData.bmapVersion = (version != nullptr) ? trimWhitespace(version) : "unknown";
+ 
+         // Parse image information
++        bmapData.imageSize = requireChildUnsigned(p_root, "ImageSize");
+         bmapData.blocksTotal = requireChildUnsigned(p_root, "BlocksCount");
+         bmapData.blocksMapped = requireChildUnsigned(p_root, "MappedBlocksCount");
+         bmapData.blockSize = requireChildUnsigned(p_root, "BlockSize");
+@@ -200,6 +201,10 @@ int validateBmap(const bmap_t& bmap, uint64_t deviceSize) {
+         if (bmap.blocksTotal == 0) {
+             throw BmapError("BMAP: BlocksCount must not be zero");
+         }
++        if (bmap.imageSize > bmap.blocksTotal * bmap.blockSize ||
++            bmap.imageSize <= (bmap.blocksTotal - 1) * bmap.blockSize) {
++            throw BmapError("BMAP: ImageSize does not match BlocksCount and BlockSize");
++        }
+         if (bmap.blocksMapped > bmap.blocksTotal) {
+             throw BmapError("BMAP: MappedBlocksCount exceeds BlocksCount");
+         }
+diff --git a/src/bmap.h b/src/bmap.h
+index cf6ff9d..98cb969 100644
+--- a/src/bmap.h
++++ b/src/bmap.h
+@@ -38,6 +38,7 @@ struct bmap_t {
+     std::string checksumType;
+     size_t blockSize = 0;
+     size_t blocksTotal = 0;
++    size_t imageSize = 0;
+     size_t blocksMapped = 0;
+     std::string bmapVersion;
+     std::string bmapChecksum;
+diff --git a/src/image_writer.cpp b/src/image_writer.cpp
+index bb802ea..f864151 100644
+--- a/src/image_writer.cpp
++++ b/src/image_writer.cpp
+@@ -177,7 +177,7 @@ int BmapWriteImage(int fd, const bmap_t &bmap, int dev_fd, const std::string &de
+         for (const auto &range : bmap.ranges) {
+             // validateBmap() has already proven that none of this can overflow.
+             const size_t outStart = range.startBlock * bmap.blockSize;
+-            const size_t outEnd = (range.endBlock + 1) * bmap.blockSize;
++            const size_t outEnd = std::min((range.endBlock + 1) * bmap.blockSize, bmap.imageSize);
+             const size_t rangeSize = outEnd - outStart;
+             const off_t writeOffset = static_cast<off_t>(outStart);
+             size_t writtenSize = 0;
+diff --git a/src/main.cpp b/src/main.cpp
+index ca42c12..b8cbbc5 100644
+--- a/src/main.cpp
++++ b/src/main.cpp
+@@ -166,7 +166,7 @@ int main(int argc, char *argv[]) {
+     FdGuard imageGuard(image_fd);
+ 
+     std::cout << "BMAP format version: " << bmap.bmapVersion << std::endl;
+-    std::cout << "Image size: " << (bmap.blocksTotal * bmap.blockSize) << " bytes" << std::endl;
++    std::cout << "Image size: " << bmap.imageSize << " bytes" << std::endl;
+     std::cout << "Block size: " << bmap.blockSize << " bytes" << std::endl;
+     std::cout << "Mapped blocks: " << bmap.blocksMapped << " out of " << bmap.blocksTotal
+               << " (" << std::fixed << std::setprecision(1)
+-- 
+2.55.0
+
diff --git a/meta-oe/recipes-support/bmap-writer/bmap-writer_1.0.5.bb b/meta-oe/recipes-support/bmap-writer/bmap-writer_1.0.5.bb
index 160d1b7112..5d493f3122 100644
--- a/meta-oe/recipes-support/bmap-writer/bmap-writer_1.0.5.bb
+++ b/meta-oe/recipes-support/bmap-writer/bmap-writer_1.0.5.bb
@@ -8,7 +8,9 @@  SECTION = "console/utils"
 LICENSE = "GPL-3.0-only"
 LIC_FILES_CHKSUM = "file://LICENSE;md5=e49f4652534af377a713df3d9dec60cb"
 
-SRC_URI = "git://github.com/embetrix/${BPN};branch=master;protocol=https;tag=${PV}"
+SRC_URI = "git://github.com/embetrix/${BPN};branch=master;protocol=https;tag=${PV} \
+           file://0001-Honour-ImageSize-for-the-last-block.patch \
+           "
 SRCREV = "c7b1bec0f40217d17e315226552e461a4a816abc"
 
 DEPENDS = "libtinyxml2 libarchive"