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 COMPONEBT_ADDR_SIZE 26from create_update_package import COMPONEBT_ADDR_SIZE_L2 27from create_update_package import UPGRADE_RESERVE_LEN 28from create_update_package import UPGRADE_SIGNATURE_LEN 29from log_exception import UPDATE_LOGGER 30from utils import OPTIONS_MANAGER 31 32COMPINFO_LEN_OFFSET = 178 33COMPINFO_LEN_SIZE = 2 34COMPONEBT_ADDR_OFFSET = UPGRADE_FILE_HEADER_LEN 35COMPONENT_TYPE_OFFSET = COMPONEBT_ADDR_OFFSET + COMPONEBT_ADDR_SIZE + 4 36COMPONENT_TYPE_OFFSET_L2 = COMPONEBT_ADDR_OFFSET + COMPONEBT_ADDR_SIZE_L2 + 4 37COMPONENT_TYPE_SIZE = 1 38COMPONENT_SIZE_OFFSET = 11 39COMPONENT_SIZE_SIZE = 4 40 41""" 42Format 43H: unsigned short 44I: unsigned int 45B: unsigned char 46s: char[] 47""" 48COMPINFO_LEN_FMT = "H" 49COMPONEBT_TYPE_FMT = "B" 50COMPONENT_SIZE_FMT = "I" 51 52 53class UnpackPackage(object): 54 """ 55 Unpack the update.bin file 56 """ 57 58 def __init__(self): 59 self.count = 0 60 self.component_offset = 0 61 self.save_path = None 62 63 if OPTIONS_MANAGER.not_l2: 64 self.compinfo_size = UPGRADE_COMPINFO_SIZE 65 self.type_offset = COMPONENT_TYPE_OFFSET 66 self.addr_size = COMPONEBT_ADDR_SIZE 67 else: 68 self.compinfo_size = UPGRADE_COMPINFO_SIZE_L2 69 self.type_offset = COMPONENT_TYPE_OFFSET_L2 70 self.addr_size = COMPONEBT_ADDR_SIZE_L2 71 72 self.addr_offset = COMPONEBT_ADDR_OFFSET 73 self.size_offset = self.type_offset + COMPONENT_SIZE_OFFSET 74 75 def check_args(self): 76 if not os.access(OPTIONS_MANAGER.unpack_package_path, os.R_OK) and \ 77 notos.path.exists(self.save_path): 78 UPDATE_LOGGER.print_log( 79 "Access unpack_package_path fail! path: %s" % \ 80 OPTIONS_MANAGER.unpack_package_path, UPDATE_LOGGER.ERROR_LOG) 81 return False 82 return True 83 84 def parse_package_file(self, package_file): 85 try: 86 package_file.seek(COMPINFO_LEN_OFFSET) 87 compinfo_len_buffer = package_file.read(COMPINFO_LEN_SIZE) 88 compinfo_len = struct.unpack(COMPINFO_LEN_FMT, compinfo_len_buffer) 89 except (struct.error, IOError): 90 return False 91 92 self.count = compinfo_len[0] // self.compinfo_size 93 self.component_offset = \ 94 UPGRADE_FILE_HEADER_LEN + compinfo_len[0] + \ 95 UPGRADE_RESERVE_LEN + UPGRADE_SIGNATURE_LEN 96 UPDATE_LOGGER.print_log( 97 "parse package file success! components: %d" % self.count) 98 return True 99 100 def parse_component(self, package_file): 101 try: 102 package_file.seek(self.addr_offset) 103 component_addr = package_file.read(self.addr_size) 104 component_addr = component_addr.split(b"\x00")[0].decode('utf-8') 105 106 package_file.seek(self.type_offset) 107 component_type_buffer = package_file.read(COMPONENT_TYPE_SIZE) 108 component_type = struct.unpack(COMPONEBT_TYPE_FMT, component_type_buffer) 109 110 package_file.seek(self.size_offset) 111 component_size_buffer = package_file.read(COMPONENT_SIZE_SIZE) 112 component_size = struct.unpack(COMPONENT_SIZE_FMT, component_size_buffer) 113 except (struct.error, IOError): 114 UPDATE_LOGGER.print_log( 115 "parse component failed!", UPDATE_LOGGER.ERROR_LOG) 116 return False, False, False 117 118 return component_addr, component_type[0], component_size[0] 119 120 def create_image_file(self, package_file): 121 component_name, component_type, component_size = \ 122 self.parse_component(package_file) 123 if not component_name or not component_type or not component_size: 124 UPDATE_LOGGER.print_log( 125 "get component_info failed!", UPDATE_LOGGER.ERROR_LOG) 126 return False 127 component_name = component_name.strip('/') 128 if component_name == "version_list": 129 component_name = "VERSION.mbn" 130 elif component_name == "board_list": 131 component_name = "BOARD.list" 132 elif component_type == 0: 133 component_name = ''.join([component_name, '.img']) 134 elif component_type == 1: 135 component_name = ''.join([component_name, '.zip']) 136 137 image_file_path = os.path.join(self.save_path, component_name) 138 139 package_file.seek(self.component_offset) 140 141 UPDATE_LOGGER.print_log("component name: %s" % component_name) 142 UPDATE_LOGGER.print_log("component offset: %s" % self.component_offset) 143 UPDATE_LOGGER.print_log("component size: %s" % component_size) 144 145 image_fd = os.open(image_file_path, os.O_RDWR | os.O_CREAT, 0o755) 146 with os.fdopen(image_fd, "wb") as image_file: 147 image_buffer = package_file.read(component_size) 148 image_file.write(image_buffer) 149 150 self.addr_offset += self.compinfo_size 151 self.type_offset += self.compinfo_size 152 self.size_offset += self.compinfo_size 153 self.component_offset += component_size 154 UPDATE_LOGGER.print_log("Create file: %s" % image_file_path) 155 return True 156 157 def unpack_package(self): 158 """ 159 Unpack the update.bin file 160 return: result 161 """ 162 UPDATE_LOGGER.print_log( 163 "Start unpack updater package: %s" % OPTIONS_MANAGER.unpack_package_path) 164 filename = ''.join(['unpack_result_', time.strftime("%H%M%S", time.localtime())]) 165 self.save_path = os.path.join(OPTIONS_MANAGER.target_package, filename) 166 os.makedirs(self.save_path) 167 168 if not self.check_args(): 169 UPDATE_LOGGER.print_log( 170 "check args failed!", UPDATE_LOGGER.ERROR_LOG) 171 return False 172 173 with open(OPTIONS_MANAGER.unpack_package_path, "rb") as package_file: 174 if not self.parse_package_file(package_file): 175 UPDATE_LOGGER.print_log( 176 "parse package file failed!", UPDATE_LOGGER.ERROR_LOG) 177 return False 178 for image_id in range(0, self.count): 179 UPDATE_LOGGER.print_log("Start to parse component_%d" % image_id) 180 if not self.create_image_file(package_file): 181 UPDATE_LOGGER.print_log( 182 "create image file failed!", UPDATE_LOGGER.ERROR_LOG) 183 return False 184 return True 185 186