• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2021 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
17"""
18The tool for making updater package.
19
20positional arguments:
21  target_package        Target package file path.
22  update_package        Update package file path.
23
24optional arguments:
25  -h, --help            show this help message and exit
26  -s SOURCE_PACKAGE, --source_package SOURCE_PACKAGE
27                        Source package file path.
28  -nz, --no_zip         No zip mode,
29                        which means to output the update package without zip.
30  -pf PARTITION_FILE, --partition_file PARTITION_FILE
31                        Variable partition mode, Partition list file path.
32  -sa {ECC,RSA}, --signing_algorithm {ECC,RSA}
33                        The signing algorithms
34                        supported by the tool include ['ECC', 'RSA'].
35  -ha {sha256,sha384}, --hash_algorithm {sha256,sha384}
36                        The hash algorithms
37                        supported by the tool include ['sha256', 'sha384'].
38  -pk PRIVATE_KEY, --private_key PRIVATE_KEY
39                        Private key file path.
40  -nl2, --not_l2        Not L2 mode, Distinguish between L1 and L2.
41  -sl {256,384}, --signing_length {256,384}
42                        The signing content length
43                        supported by the tool include ['256', '384'].
44  -xp, --xml_path       XML file path.
45  -sc, --sd_card        SD Card mode, Create update package for SD Card.
46"""
47import filecmp
48import os
49import sys
50import argparse
51import subprocess
52import tempfile
53import hashlib
54import xmltodict
55import patch_package_process
56
57from gigraph_process import GigraphProcess
58from image_class import FullUpdateImage
59from image_class import IncUpdateImage
60from transfers_manager import TransfersManager
61from log_exception import UPDATE_LOGGER
62from script_generator import PreludeScript
63from script_generator import VerseScript
64from script_generator import RefrainScript
65from script_generator import EndingScript
66from update_package import build_update_package
67from unpack_updater_package import UnpackPackage
68from utils import OPTIONS_MANAGER
69from utils import UPDATER_CONFIG
70from utils import parse_partition_file_xml
71from utils import unzip_package
72from utils import clear_resource
73from utils import PRODUCT
74from utils import XML_FILE_PATH
75from utils import get_update_info
76from utils import SCRIPT_KEY_LIST
77from utils import PER_BLOCK_SIZE
78from utils import E2FSDROID_PATH
79from utils import MAXIMUM_RECURSION_DEPTH
80from utils import VERSE_SCRIPT_EVENT
81from utils import INC_IMAGE_EVENT
82from utils import DIFF_EXE_PATH
83from utils import get_update_config_softversion
84from vendor_script import create_vendor_script_class
85
86sys.setrecursionlimit(MAXIMUM_RECURSION_DEPTH)
87
88
89def type_check(arg):
90    """
91    Argument check, which is used to check whether the specified arg is a file.
92    :param arg: the arg to check
93    :return:  Check result, which is False if the arg is invalid.
94    """
95    if arg is not None and not os.path.exists(arg):
96        UPDATE_LOGGER.print_log(
97            "FileNotFoundError, path: %s" % arg, UPDATE_LOGGER.ERROR_LOG)
98        return False
99    return arg
100
101
102def private_key_check(arg):
103    """
104    Argument check, which is used to check whether
105    the specified arg is a private_key.
106    :param arg:  The arg to check.
107    :return: Check result, which is False if the arg is invalid.
108    """
109    if arg != "ON_SERVER" and not os.path.isfile(arg):
110        UPDATE_LOGGER.print_log(
111            "FileNotFoundError, path: %s" % arg, UPDATE_LOGGER.ERROR_LOG)
112        return False
113    return arg
114
115
116def check_update_package(arg):
117    """
118    Argument check, which is used to check whether
119    the update package path exists.
120    :param arg: The arg to check.
121    :return: Check result
122    """
123    make_dir_path = None
124    if os.path.exists(arg):
125        if os.path.isfile(arg):
126            UPDATE_LOGGER.print_log(
127                "Update package must be a dir path, not a file path. "
128                "path: %s" % arg, UPDATE_LOGGER.ERROR_LOG)
129            return False
130    else:
131        try:
132            UPDATE_LOGGER.print_log(
133                "Update package path does  not exist. The dir will be created!"
134                "path: %s" % arg, UPDATE_LOGGER.WARNING_LOG)
135            os.makedirs(arg)
136            make_dir_path = arg
137        except OSError:
138            UPDATE_LOGGER.print_log(
139                "Make update package path dir failed! "
140                "path: %s" % arg, UPDATE_LOGGER.ERROR_LOG)
141            return False
142    if make_dir_path is not None:
143        OPTIONS_MANAGER.make_dir_path = make_dir_path
144    OPTIONS_MANAGER.update_package = arg
145    return arg
146
147
148def unpack_check(arg):
149    """
150    Argument check, which is used to check whether
151    the update package path exists.
152    :param arg: The arg to check.
153    :return: Check result
154    """
155    unpack_package = os.path.join(OPTIONS_MANAGER.update_package, arg)
156    if not os.path.isfile(unpack_package):
157        UPDATE_LOGGER.print_log(
158            "FileNotFoundError, path: %s" % unpack_package, UPDATE_LOGGER.ERROR_LOG)
159        OPTIONS_MANAGER.unpack_package_path = None
160        return False
161    OPTIONS_MANAGER.unpack_package_path = unpack_package
162    return arg
163
164
165def create_entrance_args():
166    """
167    Arguments for the tool to create an update package
168    :return source_package : source version package
169            target_package : target version package
170            update_package : update package output path
171            no_zip : whether to enable the update package zip function.
172            partition_file : partition table XML file
173            signing_algorithm : signature algorithm (ECC and RSA (default))
174            private_key : path of the private key file
175    """
176    parser = OPTIONS_MANAGER.parser
177    parser.description = "Tool for creating update package."
178    parser.add_argument("-unpack", "--unpack_package", type=unpack_check,
179                        default=None, help="Unpack updater package.")
180    parser.add_argument("-s", "--source_package", type=type_check,
181                        default=None, help="Source package file path.")
182    parser.add_argument("target_package", type=type_check,
183                        help="Target package file path.")
184    parser.add_argument("update_package", type=check_update_package,
185                        help="Update package file path.")
186    parser.add_argument("-nz", "--no_zip", action='store_true',
187                        help="No zip mode, Output update package without zip.")
188    parser.add_argument("-pf", "--partition_file", default=None,
189                        help="Variable partition mode, "
190                             "Partition list file path.")
191    parser.add_argument("-sa", "--signing_algorithm", default='RSA',
192                        choices=['ECC', 'RSA'],
193                        help="The signing algorithm "
194                             "supported by the tool include ['ECC', 'RSA'].")
195    parser.add_argument("-ha", "--hash_algorithm", default='sha256',
196                        choices=['sha256', 'sha384'],
197                        help="The hash algorithm "
198                             "supported by the tool include "
199                             "['sha256', 'sha384'].")
200    parser.add_argument("-pk", "--private_key", type=private_key_check,
201                        default=None, help="Private key file path.")
202    parser.add_argument("-nl2", "--not_l2", action='store_true',
203                        help="Not L2 mode, Distinguish between L1 and L2.")
204    parser.add_argument("-sl", "--signing_length", default='256',
205                        choices=['256', '384'],
206                        help="The signing content length "
207                             "supported by the tool include "
208                             "['256', '384'].")
209    parser.add_argument("-xp", "--xml_path", type=private_key_check,
210                        default=None, help="XML file path.")
211    parser.add_argument("-sc", "--sd_card", action='store_true',
212                        help="SD Card mode, "
213                             "Create update package for SD Card.")
214
215
216def parse_args():
217    args = OPTIONS_MANAGER.parser.parse_args()
218    OPTIONS_MANAGER.source_package = args.source_package
219    OPTIONS_MANAGER.target_package = args.target_package
220    OPTIONS_MANAGER.update_package = args.update_package
221    OPTIONS_MANAGER.no_zip = args.no_zip
222    OPTIONS_MANAGER.partition_file = args.partition_file
223    OPTIONS_MANAGER.signing_algorithm = args.signing_algorithm
224    OPTIONS_MANAGER.hash_algorithm = args.hash_algorithm
225    OPTIONS_MANAGER.private_key = args.private_key
226    OPTIONS_MANAGER.not_l2 = args.not_l2
227    OPTIONS_MANAGER.signing_length = int(args.signing_length)
228    OPTIONS_MANAGER.xml_path = args.xml_path
229    OPTIONS_MANAGER.sd_card = args.sd_card
230
231
232def get_args():
233    ret_args = \
234        [OPTIONS_MANAGER.source_package,
235        OPTIONS_MANAGER.target_package,
236        OPTIONS_MANAGER.update_package,
237        OPTIONS_MANAGER.no_zip,
238        OPTIONS_MANAGER.not_l2,
239        OPTIONS_MANAGER.partition_file,
240        OPTIONS_MANAGER.signing_algorithm,
241        OPTIONS_MANAGER.hash_algorithm,
242        OPTIONS_MANAGER.private_key]
243    return ret_args
244
245
246def get_script_obj():
247    """
248    Obtain Opera script object
249    :return:
250    """
251    script_obj_list = create_vendor_script_class()
252    if script_obj_list == [None] * len(SCRIPT_KEY_LIST):
253        prelude_script = PreludeScript()
254        verse_script = VerseScript()
255        refrain_script = RefrainScript()
256        ending_script = EndingScript()
257
258        generate_verse_script = \
259            OPTIONS_MANAGER.init.invoke_event(VERSE_SCRIPT_EVENT)
260        if generate_verse_script:
261            verse_script = generate_verse_script()
262    else:
263        UPDATE_LOGGER.print_log(
264            "Get vendor extension object completed!"
265            "The vendor extension script will be generated.")
266        prelude_script = script_obj_list[0]
267        verse_script = script_obj_list[1]
268        refrain_script = script_obj_list[2]
269        ending_script = script_obj_list[3]
270    return prelude_script, verse_script, refrain_script, ending_script
271
272
273def get_source_package_path(source_package):
274    """
275    get_source_package_path.
276    :param source_package: source package path
277    :return:
278    """
279    if os.path.isdir(source_package):
280        OPTIONS_MANAGER.source_package_dir = source_package
281    elif source_package.endswith('.zip'):
282        # Decompress the source package.
283        tmp_dir_obj, unzip_dir = unzip_package(source_package)
284        if tmp_dir_obj is False or unzip_dir is False:
285            clear_resource(err_clear=True)
286            return False
287        OPTIONS_MANAGER.source_package_dir = unzip_dir
288        OPTIONS_MANAGER.source_package_temp_obj = tmp_dir_obj
289    else:
290        UPDATE_LOGGER.print_log("Input Update Package type exception!"
291            "path: %s" % source_package, UPDATE_LOGGER.ERROR_LOG)
292        clear_resource(err_clear=True)
293        return False
294    return True
295
296
297def check_incremental_args(no_zip, partition_file, source_package,
298                           incremental_img_list):
299    """
300    When the incremental list is not empty, incremental processing is required.
301    In this case, check related arguments.
302    :param no_zip: no zip mode
303    :param partition_file:
304    :param source_package:
305    :param incremental_img_list:
306    :return:
307    """
308    if "boot" in incremental_img_list:
309        UPDATE_LOGGER.print_log(
310            "boot cannot be incrementally processed!",
311            UPDATE_LOGGER.ERROR_LOG)
312        clear_resource(err_clear=True)
313        return False
314    if source_package is None:
315        UPDATE_LOGGER.print_log(
316            "The source package is missing, "
317            "cannot be incrementally processed!",
318            UPDATE_LOGGER.ERROR_LOG)
319        clear_resource(err_clear=True)
320        return False
321    if no_zip:
322        UPDATE_LOGGER.print_log(
323            "No ZIP mode, cannot be incrementally processed!",
324            UPDATE_LOGGER.ERROR_LOG)
325        clear_resource(err_clear=True)
326        return False
327    if partition_file is not None:
328        UPDATE_LOGGER.print_log(
329            "Partition file is not None, "
330            "cannot be incrementally processed!",
331            UPDATE_LOGGER.ERROR_LOG)
332        clear_resource(err_clear=True)
333        return False
334
335    if not get_source_package_path(source_package):
336        return False
337    xml_path = ''
338    if OPTIONS_MANAGER.source_package_dir is not False:
339        xml_path = os.path.join(OPTIONS_MANAGER.source_package_dir,
340                                UPDATER_CONFIG, XML_FILE_PATH)
341    if OPTIONS_MANAGER.source_package_dir is False:
342        OPTIONS_MANAGER.source_package_temp_obj = None
343        OPTIONS_MANAGER.source_package_dir = None
344    if os.path.exists(xml_path):
345        with open(xml_path, 'r') as xml_file:
346            xml_str = xml_file.read()
347    else:
348        UPDATE_LOGGER.print_log("XML file does not exist! xml path: %s" %
349                                xml_path, UPDATE_LOGGER.ERROR_LOG)
350        return False
351    xml_content_dict = xmltodict.parse(xml_str, encoding='utf-8')
352    package_dict = xml_content_dict.get('package', {})
353    get_update_config_softversion(OPTIONS_MANAGER.source_package_dir, package_dict.get('head', {}))
354    head_dict = package_dict.get('head', {}).get('info')
355    OPTIONS_MANAGER.source_package_version = head_dict.get("@softVersion")
356    if check_package_version(OPTIONS_MANAGER.target_package_version,
357                             OPTIONS_MANAGER.source_package_version) is False:
358        clear_resource(err_clear=True)
359        return False
360    return True
361
362
363def check_userdata_image():
364    """
365    Check the userdata image. Updating this image is prohibited.
366    :return:
367    """
368    if 'userdata' in OPTIONS_MANAGER.full_img_list or \
369            'userdata' in OPTIONS_MANAGER.incremental_img_list:
370        UPDATE_LOGGER.print_log(
371            "userdata image does not participate in update!"
372            "Please check xml config, path: %s!" %
373            os.path.join(OPTIONS_MANAGER.target_package_config_dir,
374                         XML_FILE_PATH),
375            UPDATE_LOGGER.ERROR_LOG)
376        clear_resource(err_clear=True)
377        return False
378    return True
379
380
381def check_images_list():
382    """
383    Check full_img_list and incremental_img_list.
384    If their lengths are 0, an error will be logged.
385    :return:
386    """
387    if len(OPTIONS_MANAGER.full_img_list) == 0 and \
388            len(OPTIONS_MANAGER.incremental_img_list) == 0:
389        UPDATE_LOGGER.print_log(
390            "The image list is empty!"
391            "Please check xml config, path: %s!" %
392            os.path.join(OPTIONS_MANAGER.target_package_config_dir,
393                         XML_FILE_PATH),
394            UPDATE_LOGGER.ERROR_LOG)
395        clear_resource(err_clear=True)
396        return False
397    return True
398
399
400def check_target_package_path(target_package):
401    """
402    Check the target_package path.
403    :param target_package: target package path
404    :return:
405    """
406    if os.path.isdir(target_package):
407        OPTIONS_MANAGER.target_package_dir = target_package
408        temp_dir_list = os.listdir(target_package)
409        if UPDATER_CONFIG in temp_dir_list:
410            OPTIONS_MANAGER.target_package_config_dir = \
411                os.path.join(target_package, UPDATER_CONFIG)
412        else:
413            UPDATE_LOGGER.print_log(
414                "Exception's target package path! path: %s" %
415                target_package, UPDATE_LOGGER.ERROR_LOG)
416            return False
417    elif target_package.endswith('.zip'):
418        # Decompress the target package.
419        tmp_dir_obj, unzip_dir = unzip_package(target_package)
420        if tmp_dir_obj is False or unzip_dir is False:
421            clear_resource(err_clear=True)
422            return False
423        OPTIONS_MANAGER.target_package_dir = unzip_dir
424        OPTIONS_MANAGER.target_package_temp_obj = tmp_dir_obj
425        OPTIONS_MANAGER.target_package_config_dir = \
426            os.path.join(unzip_dir, UPDATER_CONFIG)
427    else:
428        UPDATE_LOGGER.print_log(
429            "Input Update Package type exception! path: %s" %
430            target_package, UPDATE_LOGGER.ERROR_LOG)
431        clear_resource(err_clear=True)
432        return False
433    return True
434
435
436def check_miss_private_key(private_key):
437    """
438    Check private key.
439    :param private_key:
440    :return:
441    """
442    if private_key is None:
443        UPDATE_LOGGER.print_log(
444            "Private key is None, update package cannot be signed! "
445            "Please specify the signature private key by -pk.",
446            UPDATE_LOGGER.ERROR_LOG)
447        clear_resource(err_clear=True)
448        return False
449    return True
450
451
452def check_package_version(target_ver, source_ver):
453    """
454    target_ver: target version
455    source_ver: source version
456    return:
457    """
458    try:
459        target_num = ''.join(target_ver.split(' ')[-1].replace('.', ''))
460        source_num = ''.join(source_ver.split(' ')[-1].replace('.', ''))
461        if int(target_num) <= int(source_num):
462            UPDATE_LOGGER.print_log(
463                'Target package version %s <= Source package version!'
464                'Unable to make updater package!',
465                UPDATE_LOGGER.ERROR_LOG)
466            return False
467    except ValueError:
468        UPDATE_LOGGER.print_log('your package version number is not compliant.'
469                                'Please check your package version number!',
470                                UPDATE_LOGGER.ERROR_LOG)
471        return False
472    return True
473
474
475def generate_image_map_file(image_path, map_path, image_name):
476    """
477    :param image_path: image path
478    :param map_path: image map file path
479    :param image_name: image name
480    :return:
481    """
482    if not os.path.exists(image_path):
483        UPDATE_LOGGER.print_log("The source %s.img file is missing from the"
484            "source package, cannot be incrementally processed. ",
485            image_name, UPDATE_LOGGER.ERROR_LOG)
486        return False
487
488    cmd = \
489        [E2FSDROID_PATH, "-B", map_path, "-a", "/%s" % image_name, image_path, "-e"]
490
491    sub_p = subprocess.Popen(
492            cmd, shell=False, stdout=subprocess.PIPE,
493            stderr=subprocess.STDOUT)
494    sub_p.wait()
495
496    if not os.path.exists(map_path):
497        UPDATE_LOGGER.print_log("%s generate image map file failed."
498                                % image_path)
499        return False
500    return True
501
502
503def get_file_sha256(update_package):
504    sha256obj = hashlib.sha256()
505    maxbuf = 8192
506    with open(update_package, 'rb') as package_file:
507        while True:
508            buf = package_file.read(maxbuf)
509            if not buf:
510                break
511            sha256obj.update(buf)
512    hash_value = sha256obj.hexdigest()
513    return str(hash_value).upper()
514
515
516def write_image_patch_script(partition, src_image_path, tgt_image_path,
517                             script_check_cmd_list, script_write_cmd_list, verse_script):
518    """
519    Add command content to the script.
520    :param partition: image name
521    :param script_check_cmd_list: incremental check command list
522    :param script_write_cmd_list: incremental write command list
523    :param verse_script: verse script object
524    :return:
525    """
526    src_sha = get_file_sha256(src_image_path)
527    src_size = os.path.getsize(src_image_path)
528    tgt_sha = get_file_sha256(tgt_image_path)
529    tgt_size = os.path.getsize(tgt_image_path)
530
531    sha_check_cmd = verse_script.image_sha_check(partition,
532        src_size, src_sha, tgt_size, tgt_sha)
533
534    first_block_check_cmd = verse_script.first_block_check(partition)
535
536    abort_cmd = verse_script.abort(partition)
537
538    cmd = 'if ({sha_check_cmd} != 0)' \
539            '{{\n    {abort_cmd}}}\n'.format(
540            sha_check_cmd=sha_check_cmd,
541            abort_cmd=abort_cmd)
542
543    script_check_cmd_list.append(cmd)
544
545    image_patch_cmd = verse_script.image_patch(partition, os.path.getsize(src_image_path),
546        get_file_sha256(src_image_path), os.path.getsize(tgt_image_path),
547        get_file_sha256(tgt_image_path))
548
549    cmd = '%s_WRITE_FLAG%s' % (partition, image_patch_cmd)
550    script_write_cmd_list.append(cmd)
551    return True
552
553
554def increment_image_diff_processing(
555        partition, src_image_path, tgt_image_path,
556        script_check_cmd_list, script_write_cmd_list, verse_script):
557    """
558    Incremental image processing
559    :param verse_script: verse script
560    :param incremental_img_list: incremental image list
561    :param source_package_dir: source package path
562    :param target_package_dir: target package path
563    :return:
564    """
565    patch_file_obj = tempfile.NamedTemporaryFile(
566            prefix="%s_patch.dat-" % partition, mode='wb')
567    OPTIONS_MANAGER.incremental_image_file_obj_list.append(
568            patch_file_obj)
569    cmd = [DIFF_EXE_PATH]
570
571    cmd.extend(['-s', src_image_path, '-d', tgt_image_path,
572                '-p', patch_file_obj.name, '-l', '4096'])
573    sub_p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
574                                stderr=subprocess.STDOUT)
575    try:
576        output, _ = sub_p.communicate(timeout=5)
577    except subprocess.TimeoutExpired:
578        sub_p.kill()
579
580    sub_p.wait()
581    if sub_p.returncode != 0:
582        raise ValueError(output)
583    return write_image_patch_script(partition, src_image_path, tgt_image_path,
584        script_check_cmd_list, script_write_cmd_list, verse_script)
585
586
587def increment_image_processing(
588        verse_script, incremental_img_list, source_package_dir,
589        target_package_dir):
590    """
591    Incremental image processing
592    :param verse_script: verse script
593    :param incremental_img_list: incremental image list
594    :param source_package_dir: source package path
595    :param target_package_dir: target package path
596    :return:
597    """
598    script_check_cmd_list = []
599    script_write_cmd_list = []
600    patch_process = None
601    block_diff = 0
602    for each_img_name in OPTIONS_MANAGER.incremental_img_name_list:
603        each_img = each_img_name[:-4]
604        each_src_image_path = \
605            os.path.join(source_package_dir,
606                         '%s.img' % each_img)
607        each_src_map_path = \
608            os.path.join(source_package_dir,
609                         '%s.map' % each_img)
610        each_tgt_image_path = \
611            os.path.join(target_package_dir,
612                         '%s.img' % each_img)
613        each_tgt_map_path = \
614            os.path.join(target_package_dir,
615                         '%s.map' % each_img)
616
617        check_make_map_path(each_img)
618
619        if filecmp.cmp(each_src_image_path, each_tgt_image_path):
620            UPDATE_LOGGER.print_log(
621                "Source Image is the same as Target Image!"
622                "src image path: %s, tgt image path: %s" %
623                (each_src_image_path, each_tgt_image_path),
624                UPDATE_LOGGER.INFO_LOG)
625
626        src_generate_map = True
627        tgt_generate_map = True
628        if not os.path.exists(each_src_map_path):
629            src_generate_map = generate_image_map_file(each_src_image_path,
630                                    each_src_map_path, each_img)
631            if not src_generate_map:
632                UPDATE_LOGGER.print_log("The source %s.img file"
633                        "generate map file failed. " % each_img)
634
635        if not os.path.exists(each_tgt_map_path):
636            tgt_generate_map = generate_image_map_file(each_tgt_image_path,
637                                    each_tgt_map_path, each_img)
638            if not tgt_generate_map:
639                UPDATE_LOGGER.print_log("The target %s.img file"
640                        "generate map file failed. " % each_img)
641
642        if not src_generate_map or not tgt_generate_map:
643            if increment_image_diff_processing(each_img, each_src_image_path, each_tgt_image_path,
644                script_check_cmd_list, script_write_cmd_list, verse_script) is True:
645                continue
646            UPDATE_LOGGER.print_log("increment_image_diff_processing %s failed" % each_img)
647            clear_resource(err_clear=True)
648            return False
649
650        block_diff += 1
651        src_image_class = \
652            IncUpdateImage(each_src_image_path, each_src_map_path)
653        tgt_image_class = \
654            IncUpdateImage(each_tgt_image_path, each_tgt_map_path)
655        OPTIONS_MANAGER.src_image = src_image_class
656        OPTIONS_MANAGER.tgt_image = tgt_image_class
657
658        inc_image = OPTIONS_MANAGER.init.invoke_event(INC_IMAGE_EVENT)
659        if inc_image:
660            src_image_class, tgt_image_class = inc_image()
661
662        transfers_manager = TransfersManager(
663            each_img, tgt_image_class, src_image_class)
664        transfers_manager.find_process_needs()
665        actions_list = transfers_manager.get_action_list()
666
667        graph_process = GigraphProcess(actions_list, src_image_class,
668                                       tgt_image_class)
669        actions_list = graph_process.actions_list
670        patch_process = \
671            patch_package_process.PatchProcess(
672                each_img, tgt_image_class, src_image_class, actions_list)
673        patch_process.patch_process()
674        patch_process.package_patch_zip.package_patch_zip()
675        patch_process.write_script(each_img, script_check_cmd_list,
676                                   script_write_cmd_list, verse_script)
677    if block_diff > 0:
678        if not check_patch_file(patch_process):
679            UPDATE_LOGGER.print_log(
680                'Verify the incremental result failed!',
681                UPDATE_LOGGER.ERROR_LOG)
682            raise RuntimeError
683    UPDATE_LOGGER.print_log(
684            'Verify the incremental result successfully!',
685            UPDATE_LOGGER.INFO_LOG)
686
687    verse_script.add_command(
688        "\n# ---- start incremental check here ----\n")
689    for each_check_cmd in script_check_cmd_list:
690        verse_script.add_command(each_check_cmd)
691    verse_script.add_command(
692        "\n# ---- start incremental write here ----\n")
693    for each_write_cmd in script_write_cmd_list:
694        verse_script.add_command(each_write_cmd)
695    return True
696
697
698def check_patch_file(patch_process):
699    new_dat_file_obj, patch_dat_file_obj, transfer_list_file_obj = \
700        patch_process.package_patch_zip.get_file_obj()
701    with open(transfer_list_file_obj.name) as f_t:
702        num = 0
703        diff_str = None
704        diff_num = 0
705        for line in f_t:
706            if line.startswith('new '):
707                each_line_list = \
708                    line.strip().replace("new ", "").split(",")[1:]
709                for idx in range(0, len(each_line_list), 2):
710                    num += \
711                        int(each_line_list[idx + 1]) - int(each_line_list[idx])
712                continue
713            if line.startswith('bsdiff ') or line.startswith('pkgdiff '):
714                diff_str = line
715        if diff_str:
716            diff_list = diff_str.split('\n')[0].split(' ')
717            diff_num = int(diff_list[1]) + int(diff_list[2])
718    check_flag = \
719        (os.path.getsize(new_dat_file_obj.name) == num * PER_BLOCK_SIZE) and \
720        (os.path.getsize(patch_dat_file_obj.name) == diff_num)
721    return check_flag
722
723
724def check_make_map_path(each_img):
725    """
726    If env does not exist, the command for map generation does not exist
727    in the environment variable, and False will be returned.
728    """
729    try:
730        cmd = [E2FSDROID_PATH, " -h"]
731        subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE,
732                         stderr=subprocess.STDOUT)
733    except FileNotFoundError:
734        UPDATE_LOGGER.print_log(
735            "Command not found, need check the env! "
736            "Make %s.map failed!" % each_img,
737            UPDATE_LOGGER.ERROR_LOG)
738        clear_resource(err_clear=True)
739        raise RuntimeError
740    return True
741
742
743def incremental_processing(no_zip, partition_file, source_package,
744                           verse_script):
745    """
746    Incremental processing.
747    :param no_zip: no zip mode
748    :param partition_file: partition xml file path
749    :param source_package: source package path
750    :param verse_script: verse script obj
751    :return : processing result
752    """
753    if len(OPTIONS_MANAGER.incremental_img_list) != 0:
754        if check_incremental_args(no_zip, partition_file, source_package,
755                                  OPTIONS_MANAGER.incremental_img_list) \
756                is False:
757            return False
758        if increment_image_processing(
759                verse_script, OPTIONS_MANAGER.incremental_img_list,
760                OPTIONS_MANAGER.source_package_dir,
761                OPTIONS_MANAGER.target_package_dir) is False:
762            return False
763    else:
764        if source_package is not None:
765            UPDATE_LOGGER.print_log(
766                "There is no incremental image, "
767                "the - S parameter is not required!",
768                UPDATE_LOGGER.ERROR_LOG)
769            raise RuntimeError
770
771
772def check_args(private_key, source_package, target_package, update_package):
773    """
774    Input args check.
775    :param private_key: private key path
776    :param source_package: source package path
777    :param target_package: target package path
778    :param update_package: output package path
779    :return : Judgment result
780    """
781    if source_package is False or private_key is False or \
782            target_package is False or update_package is False:
783        return False
784    if check_miss_private_key(private_key) is False:
785        return False
786    if check_target_package_path(target_package) is False:
787        return False
788    if get_update_info() is False:
789        return False
790    if check_images_list() is False:
791        return False
792    return True
793
794
795create_entrance_args()
796
797
798def main():
799    """
800    Entry function.
801    """
802    parse_args()
803
804    OPTIONS_MANAGER.product = PRODUCT
805
806    source_package, target_package, update_package, no_zip, not_l2, \
807        partition_file, signing_algorithm, hash_algorithm, private_key = \
808        get_args()
809    if not_l2:
810        no_zip = True
811
812    # Unpack updater package
813    if OPTIONS_MANAGER.unpack_package_path:
814        package = UnpackPackage()
815        if not package.unpack_package():
816            UPDATE_LOGGER.print_log(
817                "Unpack update package .bin failed!", UPDATE_LOGGER.ERROR_LOG)
818            clear_resource(err_clear=True)
819            return
820        UPDATE_LOGGER.print_log("Unpack update package .bin success!")
821        clear_resource(err_clear=True)
822        return
823
824    if OPTIONS_MANAGER.sd_card:
825        if source_package is not None or \
826                OPTIONS_MANAGER.xml_path is not None or \
827                partition_file is not None:
828            UPDATE_LOGGER.print_log(
829                "SD Card updater, "
830                "the -S/-xp/-pf parameter is not required!",
831                UPDATE_LOGGER.ERROR_LOG)
832            raise RuntimeError
833    if check_args(private_key, source_package,
834                  target_package, update_package) is False:
835        clear_resource(err_clear=True)
836        return
837
838    if not OPTIONS_MANAGER.sd_card:
839        if check_userdata_image() is False:
840            clear_resource(err_clear=True)
841            return
842
843    # Create a Script object.
844    prelude_script, verse_script, refrain_script, ending_script = \
845        get_script_obj()
846
847    # Create partition.
848    if partition_file is not None:
849        verse_script.add_command("\n# ---- do updater partitions ----\n")
850        updater_partitions_cmd = verse_script.updater_partitions()
851        verse_script.add_command(updater_partitions_cmd)
852
853        partition_file_obj, partitions_list, partitions_file_path_list = \
854            parse_partition_file_xml(partition_file)
855        if partition_file_obj is False:
856            clear_resource(err_clear=True)
857            return False
858        OPTIONS_MANAGER.partition_file_obj = partition_file_obj
859        OPTIONS_MANAGER.full_img_list = partitions_list
860        OPTIONS_MANAGER.full_image_path_list = partitions_file_path_list
861        OPTIONS_MANAGER.two_step = False
862
863    # Upgrade the updater image.
864    if OPTIONS_MANAGER.two_step:
865        get_status_cmd = verse_script.get_status()
866        set_status_0_cmd = verse_script.set_status('0')
867        set_status_1_cmd = verse_script.set_status('1')
868        reboot_now_cmd = verse_script.reboot_now()
869        create_updater_script_command = \
870            '\n# ---- do updater partitions ----\n\n' \
871            'if ({get_status_cmd} == 0){{\nUPDATER_WRITE_FLAG\n' \
872            '    {set_status_1_cmd}    {reboot_now_cmd}}}\n' \
873            'else{{    \nALL_WRITE_FLAG\n    {set_status_0_cmd}}}'.format(
874                get_status_cmd=get_status_cmd,
875                set_status_1_cmd=set_status_1_cmd,
876                set_status_0_cmd=set_status_0_cmd,
877                reboot_now_cmd=reboot_now_cmd)
878        verse_script.add_command(create_updater_script_command)
879
880    if incremental_processing(
881            no_zip, partition_file, source_package, verse_script) is False:
882        clear_resource(err_clear=True)
883        return
884
885    # Full processing
886    if len(OPTIONS_MANAGER.full_img_list) != 0:
887        verse_script.add_command("\n# ---- full image ----\n")
888        full_update_image = \
889            FullUpdateImage(OPTIONS_MANAGER.target_package_dir,
890                            OPTIONS_MANAGER.full_img_list,
891                            OPTIONS_MANAGER.full_img_name_list,
892                            verse_script, OPTIONS_MANAGER.full_image_path_list,
893                            no_zip=OPTIONS_MANAGER.no_zip)
894        full_image_content_len_list, full_image_file_obj_list = \
895            full_update_image.update_full_image()
896        if full_image_content_len_list is False or \
897                full_image_file_obj_list is False:
898            clear_resource(err_clear=True)
899            return
900        OPTIONS_MANAGER.full_image_content_len_list, \
901            OPTIONS_MANAGER.full_image_file_obj_list = \
902            full_image_content_len_list, full_image_file_obj_list
903
904    # Generate the update package.
905    build_re = build_update_package(no_zip, update_package,
906                                    prelude_script, verse_script,
907                                    refrain_script, ending_script)
908    if build_re is False:
909        clear_resource(err_clear=True)
910        return
911    # Clear resources.
912    clear_resource()
913
914
915if __name__ == '__main__':
916    main()
917