laforge has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/pysim/+/37926?usp=email )


Change subject: contrib: script to generate "update" commands from diff of 
fsdumps
......................................................................

contrib: script to generate "update" commands from diff of fsdumps

Change-Id: I08897cd353093575f98c68580afbc68b6f2f878f
---
A contrib/fsdump-diff-apply.py
1 file changed, 169 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/26/37926/1

diff --git a/contrib/fsdump-diff-apply.py b/contrib/fsdump-diff-apply.py
new file mode 100755
index 0000000..c4f907e
--- /dev/null
+++ b/contrib/fsdump-diff-apply.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python3
+
+# The purpose of this script is to
+# * load two SIM card 'fsdump' files
+# * determine which file contents in "B" differs from that of "A"
+# * create a pySim-shell script to update the contents of "A" to match that of 
"B"
+
+# (C) 2024 by Harald Welte <[email protected]>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import json
+import argparse
+
+# Files that we should not update
+FILES_TO_SKIP = [
+    "MF/EF.ICCID",
+    #"MF/DF.GSM/EF.IMSI",
+    #"MF/ADF.USIM/EF.IMSI",
+    ]
+
+# Files that need zero-padding at the end, not ff-padding
+FILES_PAD_ZERO = [
+    "DF.GSM/EF.SST",
+    "MF/ADF.USIM/EF.UST",
+    "MF/ADF.USIM/EF.EST",
+    "MF/ADF.ISIM/EF.IST",
+    ]
+
+def pad_file(path, instr, byte_len):
+    if path in FILES_PAD_ZERO:
+        pad = '0'
+    else:
+        pad = 'f'
+    return pad_hexstr(instr, byte_len, pad)
+
+def pad_hexstr(instr, byte_len:int, pad='f'):
+    """Pad given hex-string to the number of bytes given in byte_len, using ff 
as padding."""
+    if len(instr) == byte_len*2:
+        return instr
+    elif len(instr) > byte_len*2:
+        raise ValueError('Cannot pad string of length %u to smaller length %u' 
% (len(instr)/2, byte_len))
+    else:
+        return instr + pad * (byte_len*2 - len(instr))
+
+def is_all_ff(instr):
+    """Determine if the entire input hex-string consists of f-digits."""
+    if all([x == 'f' for x in instr.lower()]):
+        return True
+    else:
+        return False
+
+parser = argparse.ArgumentParser()
+parser.add_argument('file_a')
+parser.add_argument('file_b')
+
+
+if __name__ == '__main__':
+    opts = parser.parse_args()
+
+    with open(opts.file_a, 'r') as file_a:
+        json_a = json.loads(file_a.read())
+    with open(opts.file_b, 'r') as file_b:
+        json_b = json.loads(file_b.read())
+
+    for path in json_b.keys():
+        print()
+        print("# %s" % path)
+
+        if not path in json_a:
+            raise ValueError("%s doesn't exist in file_a!" % path)
+
+        if path in FILES_TO_SKIP:
+            print("# skipped explicitly as it is in FILES_TO_SKIP")
+            continue
+
+        if not 'body' in json_b[path]:
+            print("# file doesn't exist in B so we cannot possibly need to 
modify A")
+            continue
+
+        if not 'body' in json_a[path]:
+            # file was not readable in original (permissions? deactivated?)
+            print("# ERROR: %s not readable in A; please fix that" % path)
+            continue
+
+        body_a = json_a[path]['body']
+        body_b = json_b[path]['body']
+        if body_a == body_b:
+            print("# file body is identical")
+            continue
+
+        file_size_a = json_a[path]['fcp']['file_size']
+        file_size_b = json_b[path]['fcp']['file_size']
+
+        cmds = []
+        structure = 
json_b[path]['fcp']['file_descriptor']['file_descriptor_byte']['structure']
+        if structure == 'transparent':
+            val_a = body_a
+            val_b = body_b
+            if file_size_a < file_size_b:
+                if not is_all_ff(val_b[2*file_size_a:]):
+                    print("# ERROR: file_size_a (%u) < file_size_b (%u); 
pleaes fix!" % (file_size_a, file_size_b))
+                    continue
+                else:
+                    print("# WARN: file_size_a (%u) < file_size_b (%u); pleaes 
fix!" % (file_size_a, file_size_b))
+                    # truncate val_b to fit in A
+                    val_b = val_b[:file_size_a*2]
+
+            elif file_size_a != file_size_b:
+                print("# NOTE: file_size_a (%u) != file_size_b (%u)" % 
(file_size_a, file_size_b))
+
+            # Pad to file_size_a
+            val_b = pad_file(path, val_b, file_size_a)
+            if val_b != val_a:
+                cmds.append("update_binary %s" % val_b)
+            else:
+                print("# padded file body is identical")
+        elif structure in ['linear_fixed', 'cyclic']:
+            record_len_a = json_a[path]['fcp']['file_descriptor']['record_len']
+            record_len_b = json_b[path]['fcp']['file_descriptor']['record_len']
+            if record_len_a < record_len_b:
+                print("# ERROR: record_len_a (%u) < record_len_b (%u); pleaes 
fix!" % (file_size_a, file_size_b))
+                continue
+            elif record_len_a != record_len_b:
+                print("# NOTE: record_len_a (%u) != record_len_b (%u)" % 
(record_len_a, record_len_b))
+
+            num_rec_a = file_size_a // record_len_a
+            num_rec_b = file_size_b // record_len_b
+            if num_rec_a < num_rec_b:
+                if not all([is_all_ff(x) for x in body_b[num_rec_a:]]):
+                    print("# ERROR: num_rec_a (%u) < num_rec_b (%u); please 
fix!" % (num_rec_a, num_rec_b))
+                    continue
+                else:
+                    print("# WARN: num_rec_a (%u) < num_rec_b (%u); but 
they're empty" % (num_rec_a, num_rec_b))
+            elif num_rec_a != num_rec_b:
+                print("# NOTE: num_rec_a (%u) != num_rec_b (%u)" % (num_rec_a, 
num_rec_b))
+
+            i = 0
+            for r in body_b:
+                if i < len(body_a):
+                    break
+                val_a = body_a[i]
+                # Pad to record_len_a
+                val_b = pad_file(path, body_b[i], record_len_a)
+                if val_a != val_b:
+                    cmds.append("update_record %u %s" % (i+1, val_b))
+                i = i + 1
+            if len(cmds) == 0:
+                print("# padded file body is identical")
+        elif structure == 'ber_tlv':
+            print("# FIXME: Implement BER-TLV")
+        else:
+            raise ValueError('Unsupported structure %s' % structure)
+
+        if len(cmds):
+            print("select %s" % path)
+            for cmd in cmds:
+                print(cmd)

--
To view, visit https://gerrit.osmocom.org/c/pysim/+/37926?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I08897cd353093575f98c68580afbc68b6f2f878f
Gerrit-Change-Number: 37926
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <[email protected]>

Reply via email to