diff mbox series

[meta-swupdate] swupdate classes: fix hash mismatch on master

Message ID 20241201211304.2199299-1-danwalkes@trellis-logic.com
State New
Delegated to: Stefano Babic
Headers show
Series [meta-swupdate] swupdate classes: fix hash mismatch on master | expand

Commit Message

Dan Walkes Dec. 1, 2024, 9:13 p.m. UTC
The fix discussed in [1] resolves build issues on styhead but
introduces an issue which causes hash mismatches.

The first build succeeds to generate a working .swu, however
future builds fail with hash mismatch and a message like this:

```
[ERROR] : SWUPDATE failed [0] ERROR cpio_utils.c : __swupdate_copy : 670 : HASH mismatch : 5bb39965bfcedd87fc02bd51fac6636b924aed606d0984dcebae6a3250b01af6 <--> ff70a8dbc50a9ce2bad724554f5f14b8c933ad87fc544d054142755e8ef07f1e
[ERROR] : SWUPDATE failed [0] ERROR archive_handler.c : install_archive_image : 344 : Error copying extracted file
```

This happens because the modifications made to files in the
S directory only happen once.  This means there's a single
conversion of `$swupdate_get_sha256(` to the file sha, and future
builds use the hash previously placed in the file in the S directory
which is no longer correct when the rootfs file hash changes.

It would probably be possible to make a simpler change here which
splits the UNPACKDIR and S and continues to make runtime
edits in S.  However, it seems the intent of S and/or UNPACKDIR
is to contain unmodified source as discussed in [1].

This change instead:
1. Moves the sw-description file to WORKDIR for editing.
2. Continues to use S as source location for sw-description
and other source files which are not edited.
3. Makes all edits in WORKDIR.
4. When preparing the cpio, prepares from WORKDIR instead of
S. using any existing files in WORKDIR if they exist.  If they
do not exist, symlinks the relevant files in S instead.
6. Adds the -L option to cpio to prevent adding symlinks to the
cpio file, and avoid the need to copy from S to WORKDIR when
preparing the cpio.

1: https://groups.google.com/g/swupdate/c/8K-9H7C9o5E/m/a9fqhIOjAAAJ

Signed-off-by: Dan Walkes <danwalkes@trellis-logic.com>
---
 classes-recipe/swupdate-common.bbclass | 37 +++++++++++++++-----------
 classes-recipe/swupdate-image.bbclass  |  2 +-
 2 files changed, 23 insertions(+), 16 deletions(-)

Comments

Dan Walkes Dec. 1, 2024, 9:17 p.m. UTC | #1
Sending this for review and comment.

Khem I think as discussed at 
https://groups.google.com/g/swupdate/c/8K-9H7C9o5E/m/a9fqhIOjAAAJ the 
correct fix here is more complicated.

These changes work for me on 
https://github.com/OE4T/tegra-demo-distro/tree/master/layers/meta-tegrademo/dynamic-layers/meta-swupdate 
but I could use some help testing the encryption/signing setup since I 
don't have a demo setup for this.

Also if you prefer attempting to rollback and split S and UNPACKDIR for a 
simpler version which still modifies S let me know and I can try that 
instead.

On Sunday, December 1, 2024 at 2:13:11 PM UTC-7 Dan Walkes wrote:

> The fix discussed in [1] resolves build issues on styhead but
> introduces an issue which causes hash mismatches.
>
> The first build succeeds to generate a working .swu, however
> future builds fail with hash mismatch and a message like this:
>
> ```
> [ERROR] : SWUPDATE failed [0] ERROR cpio_utils.c : __swupdate_copy : 670 : 
> HASH mismatch : 
> 5bb39965bfcedd87fc02bd51fac6636b924aed606d0984dcebae6a3250b01af6 <--> 
> ff70a8dbc50a9ce2bad724554f5f14b8c933ad87fc544d054142755e8ef07f1e
> [ERROR] : SWUPDATE failed [0] ERROR archive_handler.c : 
> install_archive_image : 344 : Error copying extracted file
> ```
>
> This happens because the modifications made to files in the
> S directory only happen once. This means there's a single
> conversion of `$swupdate_get_sha256(` to the file sha, and future
> builds use the hash previously placed in the file in the S directory
> which is no longer correct when the rootfs file hash changes.
>
> It would probably be possible to make a simpler change here which
> splits the UNPACKDIR and S and continues to make runtime
> edits in S. However, it seems the intent of S and/or UNPACKDIR
> is to contain unmodified source as discussed in [1].
>
> This change instead:
> 1. Moves the sw-description file to WORKDIR for editing.
> 2. Continues to use S as source location for sw-description
> and other source files which are not edited.
> 3. Makes all edits in WORKDIR.
> 4. When preparing the cpio, prepares from WORKDIR instead of
> S. using any existing files in WORKDIR if they exist. If they
> do not exist, symlinks the relevant files in S instead.
> 6. Adds the -L option to cpio to prevent adding symlinks to the
> cpio file, and avoid the need to copy from S to WORKDIR when
> preparing the cpio.
>
> 1: https://groups.google.com/g/swupdate/c/8K-9H7C9o5E/m/a9fqhIOjAAAJ
>
> Signed-off-by: Dan Walkes <danw...@trellis-logic.com>
> ---
> classes-recipe/swupdate-common.bbclass | 37 +++++++++++++++-----------
> classes-recipe/swupdate-image.bbclass | 2 +-
> 2 files changed, 23 insertions(+), 16 deletions(-)
>
> diff --git a/classes-recipe/swupdate-common.bbclass 
> b/classes-recipe/swupdate-common.bbclass
> index 7b49561..085a7b5 100644
> --- a/classes-recipe/swupdate-common.bbclass
> +++ b/classes-recipe/swupdate-common.bbclass
> @@ -66,22 +66,22 @@ def swupdate_getdepends(d):
>
> return depstr
>
> -def swupdate_write_sha256(s):
> +def swupdate_write_sha256(workdir):
> import re
> write_lines = []
> - with open(os.path.join(s, "sw-description"), 'r') as f:
> + with open(os.path.join(workdir, "sw-description"), 'r') as f:
> for line in f:
> shastr = r"sha256.+=.+@(.+\")"
> m = 
> re.match(r"^(?P<before_placeholder>.+)(sha256|version).+[=:].*(?P<quote>[\'\"])@(?P<filename>.*)(?P=quote)", 
> line)
> if m:
> filename = m.group('filename')
> bb.warn("Syntax for sha256 changed, please use $swupdate_get_sha256(%s)" % 
> filename)
> - hash = swupdate_get_sha256(None, s, filename)
> + hash = swupdate_get_sha256(None, workdir, filename)
> write_lines.append(line.replace("@%s" % (filename), hash))
> else:
> write_lines.append(line)
>
> - with open(os.path.join(s, "sw-description"), 'w+') as f:
> + with open(os.path.join(workdir, "sw-description"), 'w+') as f:
> for line in write_lines:
> f.write(line)
>
> @@ -97,7 +97,7 @@ def swupdate_exec_functions(d, s, write_lines):
> write_lines[index] = line
>
>
> -def swupdate_expand_bitbake_variables(d, s):
> +def swupdate_expand_bitbake_variables(d, s, workdir):
> write_lines = []
>
> with open(os.path.join(s, "sw-description"), 'r') as f:
> @@ -131,7 +131,7 @@ def swupdate_expand_bitbake_variables(d, s):
>
> swupdate_exec_functions(d, s, write_lines)
>
> - with open(os.path.join(s, "sw-description"), 'w+') as f:
> + with open(os.path.join(workdir, "sw-description"), 'w+') as f:
> for line in write_lines:
> f.write(line)
>
> @@ -173,17 +173,18 @@ def prepare_sw_description(d):
> import subprocess
>
> s = d.getVar('S')
> - swupdate_expand_bitbake_variables(d, s)
> + workdir = d.getVar('WORKDIR')
> + swupdate_expand_bitbake_variables(d, s, workdir)
>
> - swupdate_write_sha256(s)
> + swupdate_write_sha256(workdir)
>
> encrypt = d.getVar('SWUPDATE_ENCRYPT_SWDESC')
> if encrypt:
> bb.note("Encryption of sw-description")
> - shutil.copyfile(os.path.join(s, 'sw-description'), os.path.join(s, 
> 'sw-description.plain'))
> + shutil.copyfile(os.path.join(workdir, 'sw-description'), 
> os.path.join(workdir, 'sw-description.plain'))
> key,iv = swupdate_extract_keys(d.getVar('SWUPDATE_AES_FILE'))
> - iv = swupdate_get_IV(d, s, 'sw-description')
> - swupdate_encrypt_file(os.path.join(s, 'sw-description.plain'), 
> os.path.join(s, 'sw-description'), key, iv)
> + iv = swupdate_get_IV(d, workdir, 'sw-description')
> + swupdate_encrypt_file(os.path.join(workdir, 'sw-description.plain'), 
> os.path.join(workdir, 'sw-description'), key, iv)
>
> signing = d.getVar('SWUPDATE_SIGNING')
> if signing == "1":
> @@ -191,8 +192,8 @@ def prepare_sw_description(d):
> signing = "RSA"
> if signing:
>
> - sw_desc_sig = os.path.join(s, 'sw-description.sig')
> - sw_desc = os.path.join(s, 'sw-description.plain' if encrypt else 
> 'sw-description')
> + sw_desc_sig = os.path.join(workdir, 'sw-description.sig')
> + sw_desc = os.path.join(workdir, 'sw-description.plain' if encrypt else 
> 'sw-description')
>
> if signing == "CUSTOM":
> signcmd = []
> @@ -233,6 +234,7 @@ def swupdate_add_src_uri(d, list_for_cpio):
> import shutil
>
> s = d.getVar('S')
> + workdir = d.getVar('WORKDIR')
> exclude = (d.getVar("SWUPDATE_SRC_URI_EXCLUDE") or "").split()
>
> fetch = bb.fetch2.Fetch([], d)
> @@ -310,9 +312,14 @@ def swupdate_add_artifacts(d, list_for_cpio):
>
> def swupdate_create_cpio(d, swudeploydir, list_for_cpio):
> s = d.getVar('S')
> - os.chdir(s)
> + workdir = d.getVar('WORKDIR')
> + os.chdir(workdir)
> + for file in list_for_cpio:
> + if not os.path.exists(file):
> + os.symlink(os.path.join(s, file), file)
> +
> updateimage = d.getVar('IMAGE_NAME') + '.swu'
> - line = 'for i in ' + ' '.join(list_for_cpio) + '; do echo $i;done | cpio 
> -ov -H crc --reproducible > ' + os.path.join(swudeploydir, updateimage)
> + line = 'for i in ' + ' '.join(list_for_cpio) + '; do echo $i;done | cpio 
> -ov -H crc --reproducible -L > ' + os.path.join(swudeploydir, updateimage)
> os.system(line)
> os.chdir(swudeploydir)
> updateimage_link = d.getVar('IMAGE_LINK_NAME')
> diff --git a/classes-recipe/swupdate-image.bbclass 
> b/classes-recipe/swupdate-image.bbclass
> index 1cd3eeb..bf8c5cc 100644
> --- a/classes-recipe/swupdate-image.bbclass
> +++ b/classes-recipe/swupdate-image.bbclass
> @@ -40,7 +40,7 @@ python do_swupdate_copy_swdescription() {
>
> import shutil
>
> - workdir = d.getVar('S')
> + workdir = d.getVar('WORKDIR')
> filespath = d.getVar('FILESPATH')
> sw_desc_path = bb.utils.which(filespath, "sw-description")
> shutil.copyfile(sw_desc_path, os.path.join(workdir, "sw-description"))
> -- 
> 2.34.1
>
>
diff mbox series

Patch

diff --git a/classes-recipe/swupdate-common.bbclass b/classes-recipe/swupdate-common.bbclass
index 7b49561..085a7b5 100644
--- a/classes-recipe/swupdate-common.bbclass
+++ b/classes-recipe/swupdate-common.bbclass
@@ -66,22 +66,22 @@  def swupdate_getdepends(d):
 
     return depstr
 
-def swupdate_write_sha256(s):
+def swupdate_write_sha256(workdir):
     import re
     write_lines = []
-    with open(os.path.join(s, "sw-description"), 'r') as f:
+    with open(os.path.join(workdir, "sw-description"), 'r') as f:
        for line in f:
           shastr = r"sha256.+=.+@(.+\")"
           m = re.match(r"^(?P<before_placeholder>.+)(sha256|version).+[=:].*(?P<quote>[\'\"])@(?P<filename>.*)(?P=quote)", line)
           if m:
               filename = m.group('filename')
               bb.warn("Syntax for sha256 changed, please use $swupdate_get_sha256(%s)" % filename)
-              hash = swupdate_get_sha256(None, s, filename)
+              hash = swupdate_get_sha256(None, workdir, filename)
               write_lines.append(line.replace("@%s" % (filename), hash))
           else:
               write_lines.append(line)
 
-    with open(os.path.join(s, "sw-description"), 'w+') as f:
+    with open(os.path.join(workdir, "sw-description"), 'w+') as f:
         for line in write_lines:
             f.write(line)
 
@@ -97,7 +97,7 @@  def swupdate_exec_functions(d, s, write_lines):
             write_lines[index] = line
 
 
-def swupdate_expand_bitbake_variables(d, s):
+def swupdate_expand_bitbake_variables(d, s, workdir):
     write_lines = []
 
     with open(os.path.join(s, "sw-description"), 'r') as f:
@@ -131,7 +131,7 @@  def swupdate_expand_bitbake_variables(d, s):
 
     swupdate_exec_functions(d, s, write_lines)
 
-    with open(os.path.join(s, "sw-description"), 'w+') as f:
+    with open(os.path.join(workdir, "sw-description"), 'w+') as f:
         for line in write_lines:
             f.write(line)
 
@@ -173,17 +173,18 @@  def prepare_sw_description(d):
     import subprocess
 
     s = d.getVar('S')
-    swupdate_expand_bitbake_variables(d, s)
+    workdir = d.getVar('WORKDIR')
+    swupdate_expand_bitbake_variables(d, s, workdir)
 
-    swupdate_write_sha256(s)
+    swupdate_write_sha256(workdir)
 
     encrypt = d.getVar('SWUPDATE_ENCRYPT_SWDESC')
     if encrypt:
         bb.note("Encryption of sw-description")
-        shutil.copyfile(os.path.join(s, 'sw-description'), os.path.join(s, 'sw-description.plain'))
+        shutil.copyfile(os.path.join(workdir, 'sw-description'), os.path.join(workdir, 'sw-description.plain'))
         key,iv = swupdate_extract_keys(d.getVar('SWUPDATE_AES_FILE'))
-        iv = swupdate_get_IV(d, s, 'sw-description')
-        swupdate_encrypt_file(os.path.join(s, 'sw-description.plain'), os.path.join(s, 'sw-description'), key, iv)
+        iv = swupdate_get_IV(d, workdir, 'sw-description')
+        swupdate_encrypt_file(os.path.join(workdir, 'sw-description.plain'), os.path.join(workdir, 'sw-description'), key, iv)
 
     signing = d.getVar('SWUPDATE_SIGNING')
     if signing == "1":
@@ -191,8 +192,8 @@  def prepare_sw_description(d):
         signing = "RSA"
     if signing:
 
-        sw_desc_sig = os.path.join(s, 'sw-description.sig')
-        sw_desc =  os.path.join(s, 'sw-description.plain' if encrypt else 'sw-description')
+        sw_desc_sig = os.path.join(workdir, 'sw-description.sig')
+        sw_desc =  os.path.join(workdir, 'sw-description.plain' if encrypt else 'sw-description')
 
         if signing == "CUSTOM":
             signcmd = []
@@ -233,6 +234,7 @@  def swupdate_add_src_uri(d, list_for_cpio):
     import shutil
 
     s = d.getVar('S')
+    workdir = d.getVar('WORKDIR')
     exclude = (d.getVar("SWUPDATE_SRC_URI_EXCLUDE") or "").split()
 
     fetch = bb.fetch2.Fetch([], d)
@@ -310,9 +312,14 @@  def swupdate_add_artifacts(d, list_for_cpio):
 
 def swupdate_create_cpio(d, swudeploydir, list_for_cpio):
     s = d.getVar('S')
-    os.chdir(s)
+    workdir = d.getVar('WORKDIR')
+    os.chdir(workdir)
+    for file in list_for_cpio:
+        if not os.path.exists(file):
+            os.symlink(os.path.join(s, file), file)
+
     updateimage = d.getVar('IMAGE_NAME') + '.swu'
-    line = 'for i in ' + ' '.join(list_for_cpio) + '; do echo $i;done | cpio -ov -H crc --reproducible > ' + os.path.join(swudeploydir, updateimage)
+    line = 'for i in ' + ' '.join(list_for_cpio) + '; do echo $i;done | cpio -ov -H crc --reproducible -L > ' + os.path.join(swudeploydir, updateimage)
     os.system(line)
     os.chdir(swudeploydir)
     updateimage_link = d.getVar('IMAGE_LINK_NAME')
diff --git a/classes-recipe/swupdate-image.bbclass b/classes-recipe/swupdate-image.bbclass
index 1cd3eeb..bf8c5cc 100644
--- a/classes-recipe/swupdate-image.bbclass
+++ b/classes-recipe/swupdate-image.bbclass
@@ -40,7 +40,7 @@  python do_swupdate_copy_swdescription() {
 
     import shutil
 
-    workdir = d.getVar('S')
+    workdir = d.getVar('WORKDIR')
     filespath = d.getVar('FILESPATH')
     sw_desc_path = bb.utils.which(filespath, "sw-description")
     shutil.copyfile(sw_desc_path, os.path.join(workdir, "sw-description"))