• 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_dict[partition] = patch_file_obj
568    cmd = [DIFF_EXE_PATH]
569
570    cmd.extend(['-s', src_image_path, '-d', tgt_image_path,
571                '-p', patch_file_obj.name, '-l', '4096'])
572    sub_p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
573                                stderr=subprocess.STDOUT)
574    output, _ = sub_p.communicate()
575    sub_p.wait()
576    if sub_p.returncode != 0:
577        raise ValueError(output)
578    return write_image_patch_script(partition, src_image_path, tgt_image_path,
579        script_check_cmd_list, script_write_cmd_list, verse_script)
580
581
582def increment_image_processing(
583        verse_script, incremental_img_list, source_package_dir,
584        target_package_dir):
585    """
586    Incremental image processing
587    :param verse_script: verse script
588    :param incremental_img_list: incremental image list
589    :param source_package_dir: source package path
590    :param target_package_dir: target package path
591    :return:
592    """
593    script_check_cmd_list = []
594    script_write_cmd_list = []
595    patch_process = None
596    block_diff = 0
597    for each_img_name in OPTIONS_MANAGER.incremental_img_name_list:
598        each_img = each_img_name[:-4]
599        each_src_image_path = \
600            os.path.join(source_package_dir,
601                         '%s.img' % each_img)
602        each_src_map_path = \
603            os.path.join(source_package_dir,
604                         '%s.map' % each_img)
605        each_tgt_image_path = \
606            os.path.join(target_package_dir,
607                         '%s.img' % each_img)
608        each_tgt_map_path = \
609            os.path.join(target_package_dir,
610                         '%s.map' % each_img)
611
612        check_make_map_path(each_img)
613
614        if filecmp.cmp(each_src_image_path, each_tgt_image_path):
615            UPDATE_LOGGER.print_log(
616                "Source Image is the same as Target Image!"
617                "src image path: %s, tgt image path: %s" %
618                (each_src_image_path, each_tgt_image_path),
619                UPDATE_LOGGER.INFO_LOG)
620            OPTIONS_MANAGER.incremental_img_list.remove(each_img)
621            first_block_check_cmd = verse_script.first_block_check(each_img)
622            abort_cmd = verse_script.abort(each_img)
623            cmd = 'if ({first_block_check_cmd} != 0)' '{{\n    {abort_cmd}}}\n'.format(
624            first_block_check_cmd=first_block_check_cmd, abort_cmd=abort_cmd)
625            script_check_cmd_list.append(cmd)
626            continue
627
628        src_generate_map = True
629        tgt_generate_map = True
630        if not os.path.exists(each_src_map_path):
631            src_generate_map = generate_image_map_file(each_src_image_path,
632                                    each_src_map_path, each_img)
633            if not src_generate_map:
634                UPDATE_LOGGER.print_log("The source %s.img file"
635                        "generate map file failed. " % each_img)
636
637        if not os.path.exists(each_tgt_map_path):
638            tgt_generate_map = generate_image_map_file(each_tgt_image_path,
639                                    each_tgt_map_path, each_img)
640            if not tgt_generate_map:
641                UPDATE_LOGGER.print_log("The target %s.img file"
642                        "generate map file failed. " % each_img)
643
644        if not src_generate_map or not tgt_generate_map:
645            if increment_image_diff_processing(each_img, each_src_image_path, each_tgt_image_path,
646                script_check_cmd_list, script_write_cmd_list, verse_script) is True:
647                continue
648            UPDATE_LOGGER.print_log("increment_image_diff_processing %s failed" % each_img)
649            clear_resource(err_clear=True)
650            return False
651
652        block_diff += 1
653        src_image_class = \
654            IncUpdateImage(each_src_image_path, each_src_map_path)
655        tgt_image_class = \
656            IncUpdateImage(each_tgt_image_path, each_tgt_map_path)
657        OPTIONS_MANAGER.src_image = src_image_class
658        OPTIONS_MANAGER.tgt_image = tgt_image_class
659
660        inc_image = OPTIONS_MANAGER.init.invoke_event(INC_IMAGE_EVENT)
661        if inc_image:
662            src_image_class, tgt_image_class = inc_image()
663
664        transfers_manager = TransfersManager(
665            each_img, tgt_image_class, src_image_class)
666        transfers_manager.find_process_needs()
667        actions_list = transfers_manager.get_action_list()
668
669        graph_process = GigraphProcess(actions_list, src_image_class,
670                                       tgt_image_class)
671        actions_list = graph_process.actions_list
672        patch_process = \
673            patch_package_process.PatchProcess(
674                each_img, tgt_image_class, src_image_class, actions_list)
675        patch_process.patch_process()
676        patch_process.write_script(each_img, script_check_cmd_list,
677                                   script_write_cmd_list, verse_script)
678        OPTIONS_MANAGER.incremental_block_file_obj_dict[each_img] = patch_process.package_patch_zip
679
680        if not check_patch_file(patch_process):
681            UPDATE_LOGGER.print_log(
682                'Verify the incremental result failed!',
683                UPDATE_LOGGER.ERROR_LOG)
684            raise RuntimeError
685
686    verse_script.add_command(
687        "\n# ---- start incremental check here ----\n")
688    for each_check_cmd in script_check_cmd_list:
689        verse_script.add_command(each_check_cmd)
690    verse_script.add_command(
691        "\n# ---- start incremental write here ----\n")
692    for each_write_cmd in script_write_cmd_list:
693        verse_script.add_command(each_write_cmd)
694    return True
695
696
697def check_patch_file(patch_process):
698    new_dat_file_obj, patch_dat_file_obj, transfer_list_file_obj = \
699        patch_process.package_patch_zip.get_file_obj()
700    with open(transfer_list_file_obj.name) as f_t:
701        num = 0
702        diff_str = None
703        diff_num = 0
704        for line in f_t:
705            if line.startswith('new '):
706                each_line_list = \
707                    line.strip().replace("new ", "").split(",")[1:]
708                for idx in range(0, len(each_line_list), 2):
709                    num += \
710                        int(each_line_list[idx + 1]) - int(each_line_list[idx])
711                continue
712            if line.startswith('bsdiff ') or line.startswith('pkgdiff '):
713                diff_str = line
714        if diff_str:
715            diff_list = diff_str.split('\n')[0].split(' ')
716            diff_num = int(diff_list[1]) + int(diff_list[2])
717    check_flag = \
718        (os.path.getsize(new_dat_file_obj.name) == num * PER_BLOCK_SIZE) and \
719        (os.path.getsize(patch_dat_file_obj.name) == diff_num)
720    return check_flag
721
722
723def check_make_map_path(each_img):
724    """
725    If env does not exist, the command for map generation does not exist
726    in the environment variable, and False will be returned.
727    """
728    try:
729        cmd = [E2FSDROID_PATH, " -h"]
730        subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE,
731                         stderr=subprocess.STDOUT)
732    except FileNotFoundError:
733        UPDATE_LOGGER.print_log(
734            "Command not found, need check the env! "
735            "Make %s.map failed!" % each_img,
736            UPDATE_LOGGER.ERROR_LOG)
737        clear_resource(err_clear=True)
738        raise RuntimeError
739    return True
740
741
742def incremental_processing(no_zip, partition_file, source_package,
743                           verse_script):
744    """
745    Incremental processing.
746    :param no_zip: no zip mode
747    :param partition_file: partition xml file path
748    :param source_package: source package path
749    :param verse_script: verse script obj
750    :return : processing result
751    """
752    if len(OPTIONS_MANAGER.incremental_img_list) != 0:
753        if check_incremental_args(no_zip, partition_file, source_package,
754                                  OPTIONS_MANAGER.incremental_img_list) \
755                is False:
756            return False
757        if increment_image_processing(
758                verse_script, OPTIONS_MANAGER.incremental_img_list,
759                OPTIONS_MANAGER.source_package_dir,
760                OPTIONS_MANAGER.target_package_dir) is False:
761            return False
762    else:
763        if source_package is not None:
764            UPDATE_LOGGER.print_log(
765                "There is no incremental image, "
766                "the - S parameter is not required!",
767                UPDATE_LOGGER.ERROR_LOG)
768            raise RuntimeError
769
770
771def check_args(private_key, source_package, target_package, update_package):
772    """
773    Input args check.
774    :param private_key: private key path
775    :param source_package: source package path
776    :param target_package: target package path
777    :param update_package: output package path
778    :return : Judgment result
779    """
780    if source_package is False or private_key is False or \
781            target_package is False or update_package is False:
782        return False
783    if check_miss_private_key(private_key) is False:
784        return False
785    if check_target_package_path(target_package) is False:
786        return False
787    if get_update_info() is False:
788        return False
789    if check_images_list() is False:
790        return False
791    return True
792
793
794create_entrance_args()
795
796
797def main():
798    """
799    Entry function.
800    """
801    parse_args()
802
803    OPTIONS_MANAGER.product = PRODUCT
804
805    source_package, target_package, update_package, no_zip, not_l2, \
806        partition_file, signing_algorithm, hash_algorithm, private_key = \
807        get_args()
808    if not_l2:
809        no_zip = True
810
811    # Unpack updater package
812    if OPTIONS_MANAGER.unpack_package_path:
813        package = UnpackPackage()
814        if not package.unpack_package():
815            UPDATE_LOGGER.print_log(
816                "Unpack update package .bin failed!", UPDATE_LOGGER.ERROR_LOG)
817            clear_resource(err_clear=True)
818            return
819        UPDATE_LOGGER.print_log("Unpack update package .bin success!")
820        clear_resource()
821        return
822
823    if OPTIONS_MANAGER.sd_card:
824        if source_package is not None or \
825                OPTIONS_MANAGER.xml_path is not None or \
826                partition_file is not None:
827            UPDATE_LOGGER.print_log(
828                "SD Card updater, "
829                "the -S/-xp/-pf parameter is not required!",
830                UPDATE_LOGGER.ERROR_LOG)
831            raise RuntimeError
832    if check_args(private_key, source_package,
833                  target_package, update_package) is False:
834        clear_resource(err_clear=True)
835        return
836
837    if not OPTIONS_MANAGER.sd_card:
838        if check_userdata_image() is False:
839            clear_resource(err_clear=True)
840            return
841
842    # Create a Script object.
843    prelude_script, verse_script, refrain_script, ending_script = \
844        get_script_obj()
845
846    # Create partition.
847    if partition_file is not None:
848        verse_script.add_command("\n# ---- do updater partitions ----\n")
849        updater_partitions_cmd = verse_script.updater_partitions()
850        verse_script.add_command(updater_partitions_cmd)
851
852        partition_file_obj, partitions_list, partitions_file_path_list = \
853            parse_partition_file_xml(partition_file)
854        if partition_file_obj is False:
855            clear_resource(err_clear=True)
856            return False
857        OPTIONS_MANAGER.partition_file_obj = partition_file_obj
858        OPTIONS_MANAGER.full_img_list = partitions_list
859        OPTIONS_MANAGER.full_image_path_list = partitions_file_path_list
860
861    if incremental_processing(
862            no_zip, partition_file, source_package, verse_script) is False:
863        clear_resource(err_clear=True)
864        return
865
866    # Full processing
867    if len(OPTIONS_MANAGER.full_img_list) != 0:
868        verse_script.add_command("\n# ---- full image ----\n")
869        full_update_image = \
870            FullUpdateImage(OPTIONS_MANAGER.target_package_dir,
871                            OPTIONS_MANAGER.full_img_list,
872                            OPTIONS_MANAGER.full_img_name_list,
873                            verse_script, OPTIONS_MANAGER.full_image_path_list,
874                            no_zip=OPTIONS_MANAGER.no_zip)
875        full_image_content_len_list, full_image_file_obj_list = \
876            full_update_image.update_full_image()
877        if full_image_content_len_list is False or \
878                full_image_file_obj_list is False:
879            clear_resource(err_clear=True)
880            return
881        OPTIONS_MANAGER.full_image_content_len_list, \
882            OPTIONS_MANAGER.full_image_file_obj_list = \
883            full_image_content_len_list, full_image_file_obj_list
884
885    # Generate the update package.
886    build_re = build_update_package(no_zip, update_package,
887                                    prelude_script, verse_script,
888                                    refrain_script, ending_script)
889    if build_re is False:
890        clear_resource(err_clear=True)
891        return
892    # Clear resources.
893    clear_resource()
894
895
896if __name__ == '__main__':
897    main()
898