1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4""" 5Libxl Migration v2 streams 6 7Record structures as per docs/specs/libxl-migration-stream.pandoc, and 8verification routines. 9""" 10 11import sys 12 13from struct import calcsize, unpack, unpack_from 14from xen.migration.verify import StreamError, RecordError, VerifyBase 15from xen.migration.libxc import VerifyLibxc 16 17# Header 18HDR_FORMAT = "!QII" 19 20HDR_IDENT = 0x4c6962786c466d74 # "LibxlFmt" in ASCII 21HDR_VERSION = 2 22 23HDR_OPT_BIT_ENDIAN = 0 24HDR_OPT_BIT_LEGACY = 1 25 26HDR_OPT_LE = (0 << HDR_OPT_BIT_ENDIAN) 27HDR_OPT_BE = (1 << HDR_OPT_BIT_ENDIAN) 28HDR_OPT_LEGACY = (1 << HDR_OPT_BIT_LEGACY) 29 30HDR_OPT_RESZ_MASK = 0xfffc 31 32# Records 33RH_FORMAT = "II" 34 35REC_TYPE_end = 0x00000000 36REC_TYPE_libxc_context = 0x00000001 37REC_TYPE_emulator_xenstore_data = 0x00000002 38REC_TYPE_emulator_context = 0x00000003 39REC_TYPE_checkpoint_end = 0x00000004 40REC_TYPE_checkpoint_state = 0x00000005 41 42rec_type_to_str = { 43 REC_TYPE_end : "End", 44 REC_TYPE_libxc_context : "Libxc context", 45 REC_TYPE_emulator_xenstore_data : "Emulator xenstore data", 46 REC_TYPE_emulator_context : "Emulator context", 47 REC_TYPE_checkpoint_end : "Checkpoint end", 48 REC_TYPE_checkpoint_state : "Checkpoint state", 49} 50 51# emulator_* header 52EMULATOR_HEADER_FORMAT = "II" 53 54EMULATOR_ID_unknown = 0x00000000 55EMULATOR_ID_qemu_trad = 0x00000001 56EMULATOR_ID_qemu_upstream = 0x00000002 57 58emulator_id_to_str = { 59 EMULATOR_ID_unknown : "Unknown", 60 EMULATOR_ID_qemu_trad : "Qemu Traditional", 61 EMULATOR_ID_qemu_upstream : "Qemu Upstream", 62} 63 64 65# 66# libxl format 67# 68 69LIBXL_QEMU_SIGNATURE = "DeviceModelRecord0002" 70LIBXL_QEMU_RECORD_HDR = "=%dsI" % (len(LIBXL_QEMU_SIGNATURE), ) 71 72class VerifyLibxl(VerifyBase): 73 """ Verify a Libxl v2 stream """ 74 75 def __init__(self, info, read): 76 VerifyBase.__init__(self, info, read) 77 78 79 def verify(self): 80 """ Verity a libxl stream """ 81 82 self.verify_hdr() 83 84 while self.verify_record() != REC_TYPE_end: 85 pass 86 87 88 def verify_hdr(self): 89 """ Verify a Header """ 90 ident, version, options = self.unpack_exact(HDR_FORMAT) 91 92 if ident != HDR_IDENT: 93 raise StreamError("Bad image id: Expected 0x%x, got 0x%x" % 94 (HDR_IDENT, ident)) 95 96 if version != HDR_VERSION: 97 raise StreamError("Unknown image version: Expected %d, got %d" % 98 (HDR_VERSION, version)) 99 100 if options & HDR_OPT_RESZ_MASK: 101 raise StreamError("Reserved bits set in image options field: 0x%x" % 102 (options & HDR_OPT_RESZ_MASK)) 103 104 if ( (sys.byteorder == "little") and 105 ((options & HDR_OPT_BIT_ENDIAN) != HDR_OPT_LE) ): 106 raise StreamError( 107 "Stream is not native endianess - unable to validate") 108 109 endian = ["little", "big"][options & HDR_OPT_LE] 110 111 if options & HDR_OPT_LEGACY: 112 self.info("Libxl Header: %s endian, legacy converted" % (endian, )) 113 else: 114 self.info("Libxl Header: %s endian" % (endian, )) 115 116 117 def verify_record(self): 118 """ Verify an individual record """ 119 rtype, length = self.unpack_exact(RH_FORMAT) 120 121 if rtype not in rec_type_to_str: 122 raise StreamError("Unrecognised record type %x" % (rtype, )) 123 124 self.info("Libxl Record: %s, length %d" % 125 (rec_type_to_str[rtype], length)) 126 127 contentsz = (length + 7) & ~7 128 content = self.rdexact(contentsz) 129 130 padding = content[length:] 131 if padding != b"\x00" * len(padding): 132 raise StreamError("Padding containing non0 bytes found") 133 134 if rtype not in record_verifiers: 135 raise RuntimeError( 136 "No verification function for libxl record '%s'" % 137 rec_type_to_str[rtype]) 138 else: 139 record_verifiers[rtype](self, content[:length]) 140 141 return rtype 142 143 144 def verify_record_end(self, content): 145 """ End record """ 146 147 if len(content) != 0: 148 raise RecordError("End record with non-zero length") 149 150 151 def verify_record_libxc_context(self, content): 152 """ Libxc context record """ 153 154 if len(content) != 0: 155 raise RecordError("Libxc context record with non-zero length") 156 157 # Verify the libxc stream, as we can't seek forwards through it 158 VerifyLibxc(self.info, self.read).verify() 159 160 161 def verify_record_emulator_xenstore_data(self, content): 162 """ Emulator Xenstore Data record """ 163 minsz = calcsize(EMULATOR_HEADER_FORMAT) 164 165 if len(content) < minsz: 166 raise RecordError("Length must be at least %d bytes, got %d" % 167 (minsz, len(content))) 168 169 emu_id, emu_idx = unpack(EMULATOR_HEADER_FORMAT, content[:minsz]) 170 171 if emu_id not in emulator_id_to_str: 172 raise RecordError("Unrecognised emulator id 0x%x" % (emu_id, )) 173 174 self.info("Emulator Xenstore Data (%s, idx %d)" % 175 (emulator_id_to_str[emu_id], emu_idx)) 176 177 # Chop off the emulator header 178 content = content[minsz:] 179 180 if len(content): 181 182 if content[-1] != '\x00': 183 raise RecordError("Data not NUL terminated") 184 185 # Split without the final NUL, to get an even number of parts 186 parts = content[:-1].split("\x00") 187 188 if (len(parts) % 2) != 0: 189 raise RecordError("Expected an even number of strings, got %d" % 190 (len(parts), )) 191 192 for key, val in zip(parts[0::2], parts[1::2]): 193 self.info(" '%s' = '%s'" % (key, val)) 194 195 196 def verify_record_emulator_context(self, content): 197 """ Emulator Context record """ 198 minsz = calcsize(EMULATOR_HEADER_FORMAT) 199 200 if len(content) < minsz: 201 raise RecordError("Length must be at least %d bytes, got %d" % 202 (minsz, len(content))) 203 204 emu_id, emu_idx = unpack(EMULATOR_HEADER_FORMAT, content[:minsz]) 205 206 if emu_id not in emulator_id_to_str: 207 raise RecordError("Unrecognised emulator id 0x%x" % (emu_id, )) 208 209 self.info(" Index %d, type %s" % (emu_idx, emulator_id_to_str[emu_id])) 210 211 212 def verify_record_checkpoint_end(self, content): 213 """ Checkpoint end record """ 214 215 if len(content) != 0: 216 raise RecordError("Checkpoint end record with non-zero length") 217 218 def verify_record_checkpoint_state(self, content): 219 """ Checkpoint state """ 220 if len(content) == 0: 221 raise RecordError("Checkpoint state record with zero length") 222 223 224record_verifiers = { 225 REC_TYPE_end: 226 VerifyLibxl.verify_record_end, 227 REC_TYPE_libxc_context: 228 VerifyLibxl.verify_record_libxc_context, 229 REC_TYPE_emulator_xenstore_data: 230 VerifyLibxl.verify_record_emulator_xenstore_data, 231 REC_TYPE_emulator_context: 232 VerifyLibxl.verify_record_emulator_context, 233 REC_TYPE_checkpoint_end: 234 VerifyLibxl.verify_record_checkpoint_end, 235 REC_TYPE_checkpoint_state: 236 VerifyLibxl.verify_record_checkpoint_state, 237} 238