55import argparse
66import json
77import 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.
1192def 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 );
0 commit comments