1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4# Copyright (c) 2022 Huawei Device Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16""" 17Description : Unpack updater package 18""" 19import os 20import struct 21import time 22from create_update_package import UPGRADE_FILE_HEADER_LEN 23from create_update_package import UPGRADE_COMPINFO_SIZE 24from create_update_package import UPGRADE_COMPINFO_SIZE_L2 25from create_update_package import COMPONENT_ADDR_SIZE 26from create_update_package import COMPONENT_ADDR_SIZE_L2 27from create_update_package import UPGRADE_RESERVE_LEN 28from create_update_package import UPGRADE_SIGNATURE_LEN 29 30from create_hashdata import HASH_TYPE_SIZE 31from create_hashdata import HASH_LENGTH_SIZE 32from create_hashdata import HASH_TLV_SIZE 33from create_hashdata import UPGRADE_HASHINFO_SIZE 34from create_hashdata import CreateHash 35from create_hashdata import HashType 36 37from log_exception import UPDATE_LOGGER 38from utils import OPTIONS_MANAGER 39 40COMPINFO_LEN_OFFSET = 178 41COMPINFO_LEN_SIZE = 2 42COMPONENT_ADDR_OFFSET = UPGRADE_FILE_HEADER_LEN 43COMPONENT_TYPE_OFFSET = COMPONENT_ADDR_OFFSET + COMPONENT_ADDR_SIZE + 4 44COMPONENT_TYPE_OFFSET_L2 = COMPONENT_ADDR_OFFSET + COMPONENT_ADDR_SIZE_L2 + 4 45COMPONENT_TYPE_SIZE = 1 46COMPONENT_SIZE_OFFSET = 11 47COMPONENT_SIZE_SIZE = 4 48 49""" 50Format 51H: unsigned short 52I: unsigned int 53B: unsigned char 54s: char[] 55""" 56COMPINFO_LEN_FMT = "H" 57COMPONENT_TYPE_FMT = "B" 58COMPONENT_SIZE_FMT = "I" 59 60 61class UnpackPackage(object): 62 """ 63 Unpack the update.bin file 64 """ 65 66 def __init__(self): 67 self.count = 0 68 self.component_offset = 0 69 self.save_path = None 70 71 if OPTIONS_MANAGER.not_l2: 72 self.compinfo_size = UPGRADE_COMPINFO_SIZE 73 self.type_offset = COMPONENT_TYPE_OFFSET 74 self.addr_size = COMPONENT_ADDR_SIZE 75 else: 76 self.compinfo_size = UPGRADE_COMPINFO_SIZE_L2 77 self.type_offset = COMPONENT_TYPE_OFFSET_L2 78 self.addr_size = COMPONENT_ADDR_SIZE_L2 79 80 self.addr_offset = COMPONENT_ADDR_OFFSET 81 self.size_offset = self.type_offset + COMPONENT_SIZE_OFFSET 82 83 def check_args(self): 84 if not os.access(OPTIONS_MANAGER.unpack_package_path, os.R_OK) and \ 85 not os.path.exists(self.save_path): 86 UPDATE_LOGGER.print_log( 87 "Access unpack_package_path fail! path: %s" % \ 88 OPTIONS_MANAGER.unpack_package_path, UPDATE_LOGGER.ERROR_LOG) 89 return False 90 return True 91 92 def parse_package_file(self, package_file): 93 try: 94 package_file.seek(COMPINFO_LEN_OFFSET) 95 compinfo_len_buffer = package_file.read(COMPINFO_LEN_SIZE) 96 compinfo_len = struct.unpack(COMPINFO_LEN_FMT, compinfo_len_buffer) 97 except (struct.error, IOError): 98 return False 99 100 self.count = compinfo_len[0] // self.compinfo_size 101 self.component_offset = UPGRADE_FILE_HEADER_LEN + compinfo_len[0] + UPGRADE_RESERVE_LEN 102 103 try: 104 package_file.seek(self.component_offset) 105 next_tlv_type_buffer = package_file.read(HASH_TYPE_SIZE) 106 next_tlv_type = struct.unpack(COMPINFO_LEN_FMT, next_tlv_type_buffer) 107 if next_tlv_type[0] == 0x06: 108 UPDATE_LOGGER.print_log("parse update.bin in SDcard package") 109 self.component_offset += HASH_TLV_SIZE + UPGRADE_HASHINFO_SIZE + HASH_TYPE_SIZE 110 package_file.seek(self.component_offset) 111 hashdata_len_buffer = package_file.read(HASH_LENGTH_SIZE) 112 hashdata_len = struct.unpack(COMPONENT_SIZE_FMT, hashdata_len_buffer) 113 self.component_offset += HASH_LENGTH_SIZE + hashdata_len[0] + HASH_TYPE_SIZE 114 elif next_tlv_type[0] == 0x08: 115 self.component_offset += HASH_TYPE_SIZE 116 117 package_file.seek(self.component_offset) 118 sign_len_buffer = package_file.read(HASH_LENGTH_SIZE) 119 sign_len = struct.unpack(COMPONENT_SIZE_FMT, sign_len_buffer) 120 UPDATE_LOGGER.print_log( 121 "signdata offset:%d length:%d" % (self.component_offset + HASH_LENGTH_SIZE, sign_len[0])) 122 self.component_offset += HASH_LENGTH_SIZE + sign_len[0] 123 except (struct.error, IOError): 124 return False 125 126 UPDATE_LOGGER.print_log("parse package file success! components: %d" % self.count) 127 return True 128 129 def parse_component(self, package_file): 130 try: 131 package_file.seek(self.addr_offset) 132 component_addr = package_file.read(self.addr_size) 133 component_addr = component_addr.split(b"\x00")[0].decode('utf-8') 134 135 package_file.seek(self.type_offset) 136 component_type_buffer = package_file.read(COMPONENT_TYPE_SIZE) 137 component_type = struct.unpack(COMPONENT_TYPE_FMT, component_type_buffer) 138 139 package_file.seek(self.size_offset) 140 component_size_buffer = package_file.read(COMPONENT_SIZE_SIZE) 141 component_size = struct.unpack(COMPONENT_SIZE_FMT, component_size_buffer) 142 except (struct.error, IOError): 143 UPDATE_LOGGER.print_log( 144 "parse component failed!", UPDATE_LOGGER.ERROR_LOG) 145 return False, False, False 146 147 return component_addr, component_type[0], component_size[0] 148 149 def create_image_file(self, package_file): 150 component_name, component_type, component_size = \ 151 self.parse_component(package_file) 152 if component_name is None or component_type is None or component_size is None: 153 UPDATE_LOGGER.print_log("get component_info failed!", UPDATE_LOGGER.ERROR_LOG) 154 return False 155 component_name = component_name.strip('/') 156 if component_name == "version_list": 157 component_name = "VERSION.mbn" 158 elif component_name == "board_list": 159 component_name = "BOARD.list" 160 elif component_type == 0: 161 component_name = ''.join([component_name, '.img']) 162 elif component_type == 1: 163 component_name = ''.join([component_name, '.zip']) 164 165 image_file_path = os.path.join(self.save_path, component_name) 166 167 package_file.seek(self.component_offset) 168 169 UPDATE_LOGGER.print_log("component name: %s" % component_name) 170 UPDATE_LOGGER.print_log("component offset: %s" % self.component_offset) 171 UPDATE_LOGGER.print_log("component size: %s" % component_size) 172 173 image_fd = os.open(image_file_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0o755) 174 with os.fdopen(image_fd, "wb") as image_file: 175 image_buffer = package_file.read(component_size) 176 image_file.write(image_buffer) 177 178 self.addr_offset += self.compinfo_size 179 self.type_offset += self.compinfo_size 180 self.size_offset += self.compinfo_size 181 self.component_offset += component_size 182 UPDATE_LOGGER.print_log("Create file: %s" % image_file_path) 183 return True 184 185 def unpack_package(self): 186 """ 187 Unpack the update.bin file 188 return: result 189 """ 190 UPDATE_LOGGER.print_log( 191 "Start unpack updater package: %s" % OPTIONS_MANAGER.unpack_package_path) 192 filename = ''.join(['unpack_result_', time.strftime("%H%M%S", time.localtime())]) 193 self.save_path = os.path.join(OPTIONS_MANAGER.target_package, filename) 194 os.makedirs(self.save_path) 195 196 if not self.check_args(): 197 UPDATE_LOGGER.print_log( 198 "check args failed!", UPDATE_LOGGER.ERROR_LOG) 199 return False 200 201 with open(OPTIONS_MANAGER.unpack_package_path, "rb") as package_file: 202 if not self.parse_package_file(package_file): 203 UPDATE_LOGGER.print_log( 204 "parse package file failed!", UPDATE_LOGGER.ERROR_LOG) 205 return False 206 207 for image_id in range(0, self.count): 208 UPDATE_LOGGER.print_log("Start to parse component_%d" % image_id) 209 if not self.create_image_file(package_file): 210 UPDATE_LOGGER.print_log( 211 "create image file failed!", UPDATE_LOGGER.ERROR_LOG) 212 return False 213 return True 214 215