Skip to content

Commit 192a6fe

Browse files
authored
Merge pull request #68 from learmj/idp
Improve UUID integration
2 parents ec1b0d8 + 530d012 commit 192a6fe

File tree

16 files changed

+320
-250
lines changed

16 files changed

+320
-250
lines changed

bin/pmap

Lines changed: 114 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,94 @@
55
import argparse
66
import json
77
import sys
8+
import uuid
9+
import re
810

911

10-
# Print slot mapping
12+
VALID_ROLES = ["boot", "system"]
13+
14+
15+
def pmap_version(data):
16+
value = get_key(data, "attributes.PMAPversion")
17+
if value is None:
18+
# Try parsing as fully assembled image.json
19+
value = get_key(data, "layout.provisionmap.attributes.PMAPversion")
20+
if value is None:
21+
sys.stderr.write("Error: No version\n")
22+
sys.exit(1)
23+
24+
if isinstance(value, str):
25+
pass
26+
else:
27+
sys.stderr.write("Error: Version is not a string\n")
28+
sys.exit(1)
29+
30+
parts = value.split('.')
31+
parts.extend(['0'] * (3 - len(parts)))
32+
33+
try:
34+
# Return a tuple of the version components as integers
35+
major = int(parts[0])
36+
minor = int(parts[1])
37+
patch = int(parts[2])
38+
return major, minor, patch
39+
except ValueError:
40+
sys.stderr.write(f"Error: Invalid version format in '{value}'\n")
41+
sys.exit(1)
42+
43+
44+
# Top level PMAP validator
45+
def validate(data):
46+
major, minor, patch = pmap_version(data)
47+
# TODO
48+
return major, minor, patch
49+
50+
51+
# Validates a static object and returns mandatory keys
52+
def chk_static(data):
53+
role = data.get("role")
54+
55+
# role: (mandatory, string)
56+
if not role:
57+
sys.stderr.write("Error: role is mandatory in a static object.\n")
58+
sys.exit(1)
59+
60+
if role not in VALID_ROLES:
61+
sys.stderr.write(f"Error: Invalid 'role': '{role}'. Must be one of {VALID_ROLES}.\n")
62+
sys.exit(1)
63+
64+
# id: (optional, string)
65+
if "id" in data:
66+
id_val = data.get("id")
67+
if not isinstance(id_val, str):
68+
sys.stderr.write("Error: id is not a string.\n")
69+
sys.exit(1)
70+
71+
# uuid: (optional, valid UUID string)
72+
if "uuid" in data:
73+
uuid_val = data.get("uuid")
74+
if not isinstance(uuid_val, str):
75+
sys.stderr.write("Error: uuid is not a string.\n")
76+
sys.exit(1)
77+
try:
78+
uuid.UUID(uuid_val)
79+
except ValueError:
80+
if (re.match(r'^[0-9a-f]{8}$', uuid_val, re.IGNORECASE) or
81+
re.match(r'^[0-9a-f]{4}-[0-9a-f]{4}$', uuid_val, re.IGNORECASE)):
82+
pass # Accept as valid VFAT UUID (label)
83+
else:
84+
sys.stderr.write(f"Error: uuid is invalid: '{uuid_val}'.\n")
85+
sys.exit(1)
86+
87+
# Return mandatory
88+
return role
89+
90+
91+
# Print slot mapping. This is retrieved from a partitions static object.
1192
def slotvars(data):
1293
# Check for slotted system_type
1394
if not any(e.get("attributes", {}).get("system_type") == "slotted" for e in data):
14-
sys.stderr.write("Not slotted\n")
95+
sys.stderr.write("Error: Not slotted\n")
1596
sys.exit(1)
1697

1798
for entry in data:
@@ -21,10 +102,14 @@ def slotvars(data):
21102
slots = entry["encrypted"]["slots"]
22103
for slot, slotval in slots.items():
23104
for part in slotval.get("partitions", []):
24-
role = part.get("role", "")
25-
id_ = part.get("id", "")
26-
print(f"{role.upper()}{slot.upper()}={mname}{id_}")
27-
print(f"{role.upper()}{slot.upper()}_ENCRYPTED=y")
105+
static = part.get("static")
106+
if static is not None:
107+
role = chk_static(static)
108+
if "id" in static:
109+
print(f"{role.upper()}{slot.upper()}_ID={mname}{static['id']}")
110+
if "uuid" in static:
111+
print(f"{role.upper()}{slot.upper()}_UUID={static['uuid']}")
112+
print(f"{role.upper()}{slot.upper()}_ENCRYPTED=y")
28113

29114
# slots at the top level with encapsulated unencrypted/encrypted
30115
elif "slots" in entry:
@@ -34,19 +119,27 @@ def slotvars(data):
34119
if "encrypted" in slotval:
35120
mname = slotval["encrypted"]["luks2"]["mname"]
36121
for part in slotval["encrypted"].get("partitions", []):
37-
role = part.get("role", "")
38-
id_ = part.get("id", "")
39-
print(f"{role.upper()}{slot.upper()}={mname}{id_}")
40-
print(f"{role.upper()}{slot.upper()}_ENCRYPTED=y")
122+
static = part.get("static")
123+
if static is not None:
124+
role = chk_static(static)
125+
if "id" in static:
126+
print(f"{role.upper()}{slot.upper()}_ID={mname}{static['id']}")
127+
if "uuid" in static:
128+
print(f"{role.upper()}{slot.upper()}_UUID={static['uuid']}")
129+
print(f"{role.upper()}{slot.upper()}_ENCRYPTED=y")
41130

42131
# Unencrypted
43132
for part in slotval.get("partitions", []):
44133
# Only print unencrypted if not also encrypted
45134
if "encrypted" not in slotval:
46-
role = part.get("role", "")
47-
id_ = part.get("id", "")
48-
print(f"{role.upper()}{slot.upper()}={id_}")
49-
print(f"{role.upper()}{slot.upper()}_ENCRYPTED=n")
135+
static = part.get("static")
136+
if static is not None:
137+
role = chk_static(static)
138+
if "id" in static:
139+
print(f"{role.upper()}{slot.upper()}_ID={static['id']}")
140+
if "uuid" in static:
141+
print(f"{role.upper()}{slot.upper()}_UUID={static['uuid']}")
142+
print(f"{role.upper()}{slot.upper()}_ENCRYPTED=n")
50143

51144

52145

@@ -99,9 +192,7 @@ if __name__ == '__main__':
99192
with open(args.file) as f:
100193
data = json.load(f)
101194

102-
if args.slotvars:
103-
slotvars(data)
104-
sys.exit(0);
195+
major, minor, patch = validate(data)
105196

106197
if args.get_key:
107198
value = get_key(data, args.get_key)
@@ -110,3 +201,9 @@ if __name__ == '__main__':
110201
else:
111202
print(value)
112203
sys.exit(0)
204+
205+
major, minor, patch = validate(data)
206+
207+
if args.slotvars:
208+
slotvars(data)
209+
sys.exit(0);

image/gpt/ab_userdata/bdebstrap/customize05-rootfs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,44 @@ set -eu
55
# Install slot rules
66
install -m 0644 -D ../device/slot.rules $1/etc/udev/rules.d/90-rpi-slot.rules
77

8+
89
# Install provision map
910
if igconf isset image_pmap ; then
1011
cp ../device/provisionmap-${IGconf_image_pmap}.json ${IGconf_sys_outputdir}/provisionmap.json
1112
else
1213
die "No pmap. Unable to generate slot mapping."
1314
fi
1415

16+
17+
# Generate pre-defined UUIDs
18+
BOOTA_LABEL=$(uuidgen | sed 's/-.*//' | tr 'a-f' 'A-F')
19+
BOOTA_UUID=$(echo "$BOOTA_LABEL" | sed 's/^\(....\)\(....\)$/\1-\2/')
20+
BOOTB_LABEL=$(uuidgen | sed 's/-.*//' | tr 'a-f' 'A-F')
21+
BOOTB_UUID=$(echo "$BOOTB_LABEL" | sed 's/^\(....\)\(....\)$/\1-\2/')
22+
SYSTEMA_UUID=$(uuidgen)
23+
SYSTEMB_UUID=$(uuidgen)
24+
CRYPT_UUID=$(uuidgen)
25+
26+
rm -f ${IGconf_sys_outputdir}/img_uuids
27+
for v in BOOTA_LABEL BOOTA_UUID BOOTB_LABEL BOOTB_UUID SYSTEMA_UUID SYSTEMB_UUID CRYPT_UUID; do
28+
eval "val=\$$v"
29+
echo "$v=$val" >> "${IGconf_sys_outputdir}/img_uuids"
30+
done
31+
32+
33+
# Populate PMAP UUIDs
34+
sed -i \
35+
-e "s|<BOOTA_UUID>|$BOOTA_UUID|g" \
36+
-e "s|<BOOTB_UUID>|$BOOTB_UUID|g" \
37+
-e "s|<SYSTEMA_UUID>|$SYSTEMA_UUID|g" \
38+
-e "s|<SYSTEMB_UUID>|$SYSTEMB_UUID|g" \
39+
-e "s|<CRYPT_UUID>|$CRYPT_UUID|g" ${IGconf_sys_outputdir}/provisionmap.json
40+
41+
1542
# Generate slot helper
1643
mkslot-helper ${IGconf_sys_outputdir}/provisionmap.json > $1/usr/bin/rpi-slot
1744
chmod +x $1/usr/bin/rpi-slot
1845

46+
1947
# Hint to initramfs-tools we have an ext4 rootfs
2048
sed -i "s|FSTYPE=\([^ ]*\)|FSTYPE=ext4|" $1/etc/initramfs-tools/initramfs.conf

image/gpt/ab_userdata/device/provisionmap-clear.json

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
33
"attributes": {
4-
"PMAPversion": "1.0.0",
4+
"PMAPversion": "1.1.0",
55
"system_type": "slotted"
66
}
77
},
@@ -18,8 +18,10 @@
1818
"partitions": [
1919
{
2020
"image": "bootA",
21-
"id": "2",
22-
"role": "boot"
21+
"static": {
22+
"uuid": "<BOOTA_UUID>",
23+
"role": "boot"
24+
}
2325
}
2426
]
2527
}
@@ -31,8 +33,10 @@
3133
"partitions": [
3234
{
3335
"image": "bootB",
34-
"id": "3",
35-
"role": "boot"
36+
"static": {
37+
"uuid": "<BOOTB_UUID>",
38+
"role": "boot"
39+
}
3640
}
3741
]
3842
}
@@ -44,8 +48,10 @@
4448
"partitions": [
4549
{
4650
"image": "systemA",
47-
"id": "4",
48-
"role": "system"
51+
"static": {
52+
"uuid": "<SYSTEMA_UUID>",
53+
"role": "system"
54+
}
4955
}
5056
]
5157
}
@@ -57,8 +63,10 @@
5763
"partitions": [
5864
{
5965
"image": "systemB",
60-
"id": "5",
61-
"role": "system"
66+
"static": {
67+
"uuid": "<SYSTEMB_UUID>",
68+
"role": "system"
69+
}
6270
}
6371
]
6472
}

image/gpt/ab_userdata/device/provisionmap-crypt.json

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
33
"attributes": {
4-
"PMAPversion": "1.0.0",
4+
"PMAPversion": "1.2.0",
55
"system_type": "slotted"
66
}
77
},
@@ -18,8 +18,10 @@
1818
"partitions": [
1919
{
2020
"image": "bootA",
21-
"id": "2",
22-
"role": "boot"
21+
"static": {
22+
"uuid": "<BOOTA_UUID>",
23+
"role": "boot"
24+
}
2325
}
2426
]
2527
}
@@ -31,8 +33,10 @@
3133
"partitions": [
3234
{
3335
"image": "bootB",
34-
"id": "3",
35-
"role": "boot"
36+
"static": {
37+
"uuid": "<BOOTB_UUID>",
38+
"role": "boot"
39+
}
3640
}
3741
]
3842
}
@@ -45,25 +49,30 @@
4549
"cipher": "aes-xts-plain64",
4650
"hash": "sha256",
4751
"label": "root",
52+
"uuid": "<CRYPT_UUID>",
4853
"mname": "cryptroot",
4954
"etype": "partitioned"
5055
},
5156
"slots": {
5257
"A": {
5358
"partitions": [
5459
{
55-
"id": "1",
5660
"image": "systemA",
57-
"role": "system"
61+
"static": {
62+
"uuid": "<SYSTEMA_UUID>",
63+
"role": "system"
64+
}
5865
}
5966
]
6067
},
6168
"B": {
6269
"partitions": [
6370
{
64-
"id": "2",
6571
"image": "systemB",
66-
"role": "system"
72+
"static": {
73+
"uuid": "<SYSTEMB_UUID>",
74+
"role": "system"
75+
}
6776
}
6877
]
6978
}

0 commit comments

Comments
 (0)