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 PARTITION_CHANGE_EVENT
84from utils import DECOUPLED_EVENT
85from utils import get_update_config_softversion
86from vendor_script import create_vendor_script_class
87
88sys.setrecursionlimit(MAXIMUM_RECURSION_DEPTH)
89
90
91def type_check(arg):
92    """
93    Argument check, which is used to check whether the specified arg is a file.
94    :param arg: the arg to check
95    :return:  Check result, which is False if the arg is invalid.
96    """
97    if arg is not None and not os.path.exists(arg):
98        UPDATE_LOGGER.print_log(
99            "FileNotFoundError, path: %s" % arg, UPDATE_LOGGER.ERROR_LOG)
100        return False
101    return arg
102
103
104def private_key_check(arg):
105    """
106    Argument check, which is used to check whether
107    the specified arg is a private_key.
108    :param arg:  The arg to check.
109    :return: Check result, which is False if the arg is invalid.
110    """
111    if arg != "ON_SERVER" and not os.path.isfile(arg):
112        UPDATE_LOGGER.print_log(
113            "FileNotFoundError, path: %s" % arg, UPDATE_LOGGER.ERROR_LOG)
114        return False
115    return arg
116
117
118def check_update_package(arg):
119    """
120    Argument check, which is used to check whether
121    the update package path exists.
122    :param arg: The arg to check.
123    :return: Check result
124    """
125    make_dir_path = None
126    if os.path.exists(arg):
127        if os.path.isfile(arg):
128            UPDATE_LOGGER.print_log(
129                "Update package must be a dir path, not a file path. "
130                "path: %s" % arg, UPDATE_LOGGER.ERROR_LOG)
131            return False
132    else:
133        try:
134            UPDATE_LOGGER.print_log(
135                "Update package path does  not exist. The dir will be created!"
136                "path: %s" % arg, UPDATE_LOGGER.WARNING_LOG)
137            os.makedirs(arg)
138            make_dir_path = arg
139        except OSError:
140            UPDATE_LOGGER.print_log(
141                "Make update package path dir failed! "
142                "path: %s" % arg, UPDATE_LOGGER.ERROR_LOG)
143            return False
144    if make_dir_path is not None:
145        OPTIONS_MANAGER.make_dir_path = make_dir_path
146    OPTIONS_MANAGER.update_package = arg
147    return arg
148
149
150def unpack_check(arg):
151    """
152    Argument check, which is used to check whether
153    the update package path exists.
154    :param arg: The arg to check.
155    :return: Check result
156    """
157    unpack_package = os.path.join(OPTIONS_MANAGER.update_package, arg)
158    if not os.path.isfile(unpack_package):
159        UPDATE_LOGGER.print_log(
160            "FileNotFoundError, path: %s" % unpack_package, UPDATE_LOGGER.ERROR_LOG)
161        OPTIONS_MANAGER.unpack_package_path = None
162        return False
163    OPTIONS_MANAGER.unpack_package_path = unpack_package
164    return arg
165
166
167def create_entrance_args():
168    """
169    Arguments for the tool to create an update package
170    :return source_package : source version package
171            target_package : target version package
172            update_package : update package output path
173            no_zip : whether to enable the update package zip function.
174            partition_file : partition table XML file
175            signing_algorithm : signature algorithm (ECC and RSA (default))
176            private_key : path of the private key file
177    """
178    parser = OPTIONS_MANAGER.parser
179    parser.description = "Tool for creating update package."
180    parser.add_argument("-unpack", "--unpack_package", type=unpack_check,
181                        default=None, help="Unpack updater package.")
182    parser.add_argument("-s", "--source_package", type=type_check,
183                        default=None, help="Source package file path.")
184    parser.add_argument("target_package", type=type_check,
185                        help="Target package file path.")
186    parser.add_argument("update_package", type=check_update_package,
187                        help="Update package file path.")
188    parser.add_argument("-nz", "--no_zip", action='store_true',
189                        help="No zip mode, Output update package without zip.")
190    parser.add_argument("-pf", "--partition_file", default=None,
191                        help="Variable partition mode, "
192                             "Partition list file path.")
193    parser.add_argument("-sa", "--signing_algorithm", default='RSA',
194                        choices=['ECC', 'RSA'],
195                        help="The signing algorithm "
196                             "supported by the tool include ['ECC', 'RSA'].")
197    parser.add_argument("-ha", "--hash_algorithm", default='sha256',
198                        choices=['sha256', 'sha384'],
199                        help="The hash algorithm "
200                             "supported by the tool include "
201                             "['sha256', 'sha384'].")
202    parser.add_argument("-pk", "--private_key", type=private_key_check,
203                        default=None, help="Private key file path.")
204    parser.add_argument("-nl2", "--not_l2", action='store_true',
205                        help="Not L2 mode, Distinguish between L1 and L2.")
206    parser.add_argument("-sl", "--signing_length", default='256',
207                        choices=['256', '384'],
208                        help="The signing content length "
209                             "supported by the tool include "
210                             "['256', '384'].")
211    parser.add_argument("-xp", "--xml_path", type=private_key_check,
212                        default=None, help="XML file path.")
213    parser.add_argument("-sc", "--sd_card", action='store_true',
214                        help="SD Card mode, "
215                             "Create update package for SD Card.")
216
217
218def parse_args():
219    args = OPTIONS_MANAGER.parser.parse_args()
220    OPTIONS_MANAGER.source_package = args.source_package
221    OPTIONS_MANAGER.target_package = args.target_package
222    OPTIONS_MANAGER.update_package = args.update_package
223    OPTIONS_MANAGER.no_zip = args.no_zip
224    OPTIONS_MANAGER.partition_file = args.partition_file
225    OPTIONS_MANAGER.signing_algorithm = args.signing_algorithm
226    OPTIONS_MANAGER.hash_algorithm = args.hash_algorithm
227    OPTIONS_MANAGER.private_key = args.private_key
228    OPTIONS_MANAGER.not_l2 = args.not_l2
229    OPTIONS_MANAGER.signing_length = int(args.signing_length)
230    OPTIONS_MANAGER.xml_path = args.xml_path
231    OPTIONS_MANAGER.sd_card = args.sd_card
232
233
234def get_args():
235    ret_args = \
236        [OPTIONS_MANAGER.source_package,
237        OPTIONS_MANAGER.target_package,
238        OPTIONS_MANAGER.update_package,
239        OPTIONS_MANAGER.no_zip,
240        OPTIONS_MANAGER.not_l2,
241        OPTIONS_MANAGER.partition_file,
242        OPTIONS_MANAGER.signing_algorithm,
243        OPTIONS_MANAGER.hash_algorithm,
244        OPTIONS_MANAGER.private_key]
245    return ret_args
246
247
248def get_script_obj():
249    """
250    Obtain Opera script object
251    :return:
252    """
253    script_obj_list = create_vendor_script_class()
254    if script_obj_list == [None] * len(SCRIPT_KEY_LIST):
255        prelude_script = PreludeScript()
256        verse_script = VerseScript()
257        refrain_script = RefrainScript()
258        ending_script = EndingScript()
259
260        generate_verse_script = \
261            OPTIONS_MANAGER.init.invoke_event(VERSE_SCRIPT_EVENT)
262        if generate_verse_script:
263            verse_script = generate_verse_script()
264    else:
265        UPDATE_LOGGER.print_log(
266            "Get vendor extension object completed!"
267            "The vendor extension script will be generated.")
268        prelude_script = script_obj_list[0]
269        verse_script = script_obj_list[1]
270        refrain_script = script_obj_list[2]
271        ending_script = script_obj_list[3]
272    return prelude_script, verse_script, refrain_script, ending_script
273
274
275def get_source_package_path(source_package):
276    """
277    get_source_package_path.
278    :param source_package: source package path
279    :return:
280    """
281    if os.path.isdir(source_package):
282        OPTIONS_MANAGER.source_package_dir = source_package
283    elif source_package.endswith('.zip'):
284        # Decompress the source package.
285        tmp_dir_obj, unzip_dir = unzip_package(source_package)
286        if tmp_dir_obj is False or unzip_dir is False:
287            clear_resource(err_clear=True)
288            return False
289        OPTIONS_MANAGER.source_package_dir = unzip_dir
290        OPTIONS_MANAGER.source_package_temp_obj = tmp_dir_obj
291    else:
292        UPDATE_LOGGER.print_log("Input Update Package type exception!"
293            "path: %s" % source_package, UPDATE_LOGGER.ERROR_LOG)
294        clear_resource(err_clear=True)
295        return False
296    return True
297
298
299def check_incremental_args(no_zip, partition_file, source_package,
300                           incremental_img_list):
301    """
302    When the incremental list is not empty, incremental processing is required.
303    In this case, check related arguments.
304    :param no_zip: no zip mode
305    :param partition_file:
306    :param source_package:
307    :param incremental_img_list:
308    :return:
309    """
310    if "boot" in incremental_img_list:
311        UPDATE_LOGGER.print_log("boot cannot be incrementally processed!", UPDATE_LOGGER.ERROR_LOG)
312        clear_resource(err_clear=True)
313        return False
314    if source_package is None:
315        UPDATE_LOGGER.print_log("The source package is missing, "
316            "cannot be incrementally processed!", UPDATE_LOGGER.ERROR_LOG)
317        clear_resource(err_clear=True)
318        return False
319    if no_zip:
320        UPDATE_LOGGER.print_log("No ZIP mode, cannot be incrementally processed!", UPDATE_LOGGER.ERROR_LOG)
321        clear_resource(err_clear=True)
322        return False
323    if partition_file is not None:
324        UPDATE_LOGGER.print_log("Partition file is not None, "
325            "cannot be incrementally processed!", UPDATE_LOGGER.ERROR_LOG)
326        clear_resource(err_clear=True)
327        return False
328
329    if not get_source_package_path(source_package):
330        return False
331    partition_change = OPTIONS_MANAGER.init.invoke_event(PARTITION_CHANGE_EVENT)
332    if callable(partition_change) and partition_change() is False:
333        return False
334    UPDATE_LOGGER.print_log("Partition interception check finish.", UPDATE_LOGGER.INFO_LOG)
335    xml_path = ''
336    if OPTIONS_MANAGER.source_package_dir is not False:
337        xml_path = os.path.join(OPTIONS_MANAGER.source_package_dir, UPDATER_CONFIG, XML_FILE_PATH)
338    if OPTIONS_MANAGER.source_package_dir is False:
339        OPTIONS_MANAGER.source_package_temp_obj = None
340        OPTIONS_MANAGER.source_package_dir = None
341    if os.path.exists(xml_path):
342        with open(xml_path, 'r') as xml_file:
343            xml_str = xml_file.read()
344    else:
345        UPDATE_LOGGER.print_log("XML file does not exist! xml path: %s" %
346                                xml_path, UPDATE_LOGGER.ERROR_LOG)
347        return False
348
349    xml_content_dict = xmltodict.parse(xml_str, encoding='utf-8')
350    package_dict = xml_content_dict.get('package', {})
351    get_update_config_softversion(OPTIONS_MANAGER.source_package_dir, package_dict.get('head', {}))
352    head_dict = package_dict.get('head', {}).get('info')
353    OPTIONS_MANAGER.source_package_version = head_dict.get("@softVersion")
354    no_need_version_check_res = OPTIONS_MANAGER.init.invoke_event(DECOUPLED_EVENT)
355    if no_need_version_check_res is False:
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 = [E2FSDROID_PATH, "-B", map_path, "-a", "/%s" % image_name, image_path, "-e"]
489    res = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
490    _, err = res.communicate(timeout=300)
491    if res.returncode != 0:
492        UPDATE_LOGGER.print_log("%s.map generate failed, reason:%s" %
493                                (image_name, err.decode()), UPDATE_LOGGER.ERROR_LOG)
494        return False
495    UPDATE_LOGGER.print_log("%s.map generate success" % image_name, UPDATE_LOGGER.INFO_LOG)
496    return True
497
498
499def get_file_sha256(update_package):
500    sha256obj = hashlib.sha256()
501    maxbuf = 8192
502    with open(update_package, 'rb') as package_file:
503        while True:
504            buf = package_file.read(maxbuf)
505            if not buf:
506                break
507            sha256obj.update(buf)
508    hash_value = sha256obj.hexdigest()
509    return str(hash_value).upper()
510
511
512def write_image_patch_script(partition, src_image_path, tgt_image_path,
513                             script_check_cmd_list, script_write_cmd_list, verse_script):
514    """
515    Add command content to the script.
516    :param partition: image name
517    :param script_check_cmd_list: incremental check command list
518    :param script_write_cmd_list: incremental write command list
519    :param verse_script: verse script object
520    :return:
521    """
522    src_sha = get_file_sha256(src_image_path)
523    src_size = os.path.getsize(src_image_path)
524    tgt_sha = get_file_sha256(tgt_image_path)
525    tgt_size = os.path.getsize(tgt_image_path)
526
527    sha_check_cmd = verse_script.image_sha_check(partition,
528        src_size, src_sha, tgt_size, tgt_sha)
529
530    first_block_check_cmd = verse_script.first_block_check(partition)
531
532    abort_cmd = verse_script.abort(partition)
533
534    cmd = 'if ({sha_check_cmd} != 0)' \
535            '{{\n    {abort_cmd}}}\n'.format(
536            sha_check_cmd=sha_check_cmd,
537            abort_cmd=abort_cmd)
538
539    script_check_cmd_list.append(cmd)
540
541    image_patch_cmd = verse_script.image_patch(partition, os.path.getsize(src_image_path),
542        get_file_sha256(src_image_path), os.path.getsize(tgt_image_path),
543        get_file_sha256(tgt_image_path))
544
545    cmd = '%s_WRITE_FLAG%s' % (partition, image_patch_cmd)
546    script_write_cmd_list.append(cmd)
547    return True
548
549
550def increment_image_diff_processing(
551        partition, src_image_path, tgt_image_path,
552        script_check_cmd_list, script_write_cmd_list, verse_script):
553    """
554    Incremental image processing
555    :param verse_script: verse script
556    :param incremental_img_list: incremental image list
557    :param source_package_dir: source package path
558    :param target_package_dir: target package path
559    :return:
560    """
561    patch_file_obj = tempfile.NamedTemporaryFile(
562            prefix="%s_patch.dat-" % partition, mode='wb')
563    OPTIONS_MANAGER.incremental_image_file_obj_dict[partition] = patch_file_obj
564    cmd = [DIFF_EXE_PATH]
565
566    cmd.extend(['-s', src_image_path, '-d', tgt_image_path,
567                '-p', patch_file_obj.name, '-l', '4096'])
568    sub_p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
569                                stderr=subprocess.STDOUT)
570    try:
571        output, _ = sub_p.communicate(timeout=1800)
572    except subprocess.TimeoutExpired:
573        sub_p.kill()
574
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 add_incremental_command(verse_script, script_check_cmd_list, script_write_cmd_list):
583    """
584    add command for increment_image_progressing to verse_script
585    :param verse_script: verse script
586    :param script_check_cmd_list: verse_script check command list
587    :param script_write_cmd_list: verse_script write command list
588    :return:
589    """
590    verse_script.add_command("\n# ---- start incremental check here ----\n")
591    for each_check_cmd in script_check_cmd_list:
592        verse_script.add_command(each_check_cmd)
593    verse_script.add_command("\n# ---- start incremental write here ----\n")
594    for each_write_cmd in script_write_cmd_list:
595        verse_script.add_command(each_write_cmd)
596
597
598def increment_image_processing(
599        verse_script, incremental_img_list, source_package_dir,
600        target_package_dir):
601    """
602    Incremental image processing
603    :param verse_script: verse script
604    :param incremental_img_list: incremental image list
605    :param source_package_dir: source package path
606    :param target_package_dir: target package path
607    :return:
608    """
609    script_check_cmd_list = []
610    script_write_cmd_list = []
611    patch_process = None
612    for each_img_name in OPTIONS_MANAGER.incremental_img_name_list:
613        each_img = each_img_name[:-4]
614        each_src_image_path = os.path.join(source_package_dir, '%s.img' % each_img)
615        each_src_map_path = os.path.join(source_package_dir, '%s.map' % each_img)
616        each_tgt_image_path = os.path.join(target_package_dir, '%s.img' % each_img)
617        each_tgt_map_path = os.path.join(target_package_dir, '%s.map' % each_img)
618
619        check_make_map_path(each_img)
620
621        if filecmp.cmp(each_src_image_path, each_tgt_image_path):
622            UPDATE_LOGGER.print_log("Source Image is the same as Target Image! src image path: %s, tgt image path: %s"
623                % (each_src_image_path, each_tgt_image_path), UPDATE_LOGGER.INFO_LOG)
624            OPTIONS_MANAGER.incremental_img_list.remove(each_img)
625            continue
626
627        src_generate_map = True
628        tgt_generate_map = True
629        if not os.path.exists(each_src_map_path):
630            src_generate_map = generate_image_map_file(each_src_image_path, each_src_map_path, each_img)
631
632        if not os.path.exists(each_tgt_map_path):
633            tgt_generate_map = generate_image_map_file(each_tgt_image_path, each_tgt_map_path, each_img)
634
635        if not src_generate_map or not tgt_generate_map:
636            if increment_image_diff_processing(each_img, each_src_image_path, each_tgt_image_path,
637                script_check_cmd_list, script_write_cmd_list, verse_script) is True:
638                continue
639            UPDATE_LOGGER.print_log("increment_image_diff_processing %s failed" % each_img)
640            clear_resource(err_clear=True)
641            return False
642
643        inc_image = OPTIONS_MANAGER.init.invoke_event(INC_IMAGE_EVENT)
644        if inc_image:
645            src_image_class, tgt_image_class = inc_image(each_src_image_path, each_src_map_path,
646                                                         each_tgt_image_path, each_tgt_map_path)
647        else:
648            src_image_class = IncUpdateImage(each_src_image_path, each_src_map_path)
649            tgt_image_class = IncUpdateImage(each_tgt_image_path, each_tgt_map_path)
650
651        transfers_manager = TransfersManager(each_img, tgt_image_class, src_image_class)
652        transfers_manager.find_process_needs()
653        actions_list = transfers_manager.get_action_list()
654
655        graph_process = GigraphProcess(actions_list, src_image_class, tgt_image_class)
656        actions_list = graph_process.actions_list
657        patch_process = patch_package_process.PatchProcess(each_img, tgt_image_class, src_image_class, actions_list)
658        patch_process.patch_process()
659        patch_process.write_script(each_img, script_check_cmd_list, script_write_cmd_list, verse_script)
660        OPTIONS_MANAGER.incremental_block_file_obj_dict[each_img] = patch_process.package_patch_zip
661
662        if not check_patch_file(patch_process):
663            UPDATE_LOGGER.print_log('Verify the incremental result failed!', UPDATE_LOGGER.ERROR_LOG)
664            raise RuntimeError
665
666    add_incremental_command(verse_script, script_check_cmd_list, script_write_cmd_list)
667    return True
668
669
670def check_patch_file(patch_process):
671    new_dat_file_obj, patch_dat_file_obj, transfer_list_file_obj = \
672        patch_process.package_patch_zip.get_file_obj()
673    with open(transfer_list_file_obj.name) as f_t:
674        num = 0
675        diff_str = None
676        diff_num = 0
677        for line in f_t:
678            if line.startswith('new '):
679                each_line_list = \
680                    line.strip().replace("new ", "").split(",")[1:]
681                for idx in range(0, len(each_line_list), 2):
682                    num += \
683                        int(each_line_list[idx + 1]) - int(each_line_list[idx])
684                continue
685            if line.startswith('bsdiff ') or line.startswith('pkgdiff '):
686                diff_str = line
687        if diff_str:
688            diff_list = diff_str.split('\n')[0].split(' ')
689            diff_num = int(diff_list[1]) + int(diff_list[2])
690    check_flag = \
691        (os.path.getsize(new_dat_file_obj.name) == num * PER_BLOCK_SIZE) and \
692        (os.path.getsize(patch_dat_file_obj.name) == diff_num)
693    return check_flag
694
695
696def check_make_map_path(each_img):
697    """
698    If env does not exist, the command for map generation does not exist
699    in the environment variable, and False will be returned.
700    """
701    try:
702        cmd = [E2FSDROID_PATH, " -h"]
703        subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE,
704                         stderr=subprocess.STDOUT)
705    except FileNotFoundError:
706        UPDATE_LOGGER.print_log(
707            "Command not found, need check the env! "
708            "Make %s.map failed!" % each_img,
709            UPDATE_LOGGER.ERROR_LOG)
710        clear_resource(err_clear=True)
711        raise RuntimeError
712    return True
713
714
715def incremental_processing(no_zip, partition_file, source_package,
716                           verse_script):
717    """
718    Incremental processing.
719    :param no_zip: no zip mode
720    :param partition_file: partition xml file path
721    :param source_package: source package path
722    :param verse_script: verse script obj
723    :return : processing result
724    """
725    if len(OPTIONS_MANAGER.incremental_img_list) != 0:
726        if check_incremental_args(no_zip, partition_file, source_package,
727                                  OPTIONS_MANAGER.incremental_img_list) \
728                is False:
729            return False
730        if increment_image_processing(
731                verse_script, OPTIONS_MANAGER.incremental_img_list,
732                OPTIONS_MANAGER.source_package_dir,
733                OPTIONS_MANAGER.target_package_dir) is False:
734            return False
735    else:
736        if source_package is not None:
737            UPDATE_LOGGER.print_log(
738                "There is no incremental image, "
739                "the - S parameter is not required!",
740                UPDATE_LOGGER.ERROR_LOG)
741            raise RuntimeError
742
743
744def check_args(private_key, source_package, target_package, update_package):
745    """
746    Input args check.
747    :param private_key: private key path
748    :param source_package: source package path
749    :param target_package: target package path
750    :param update_package: output package path
751    :return : Judgment result
752    """
753    if source_package is False or private_key is False or \
754            target_package is False or update_package is False:
755        return False
756    if check_miss_private_key(private_key) is False:
757        return False
758    if check_target_package_path(target_package) is False:
759        return False
760    if get_update_info() is False:
761        return False
762    if check_images_list() is False:
763        return False
764    return True
765
766
767def unpack_package_processing():
768    if OPTIONS_MANAGER.unpack_package_path:
769        package = UnpackPackage()
770        if not package.unpack_package():
771            UPDATE_LOGGER.print_log("Unpack update package.bin failed!", UPDATE_LOGGER.ERROR_LOG)
772            clear_resource(err_clear=True)
773            sys.exit(1)
774        UPDATE_LOGGER.print_log("Unpack update package.bin success!")
775        clear_resource()
776        sys.exit(0)
777
778
779create_entrance_args()
780
781
782def main():
783    """
784    Entry function.
785    """
786    parse_args()
787
788    OPTIONS_MANAGER.product = PRODUCT
789
790    source_package, target_package, update_package, no_zip, not_l2, \
791        partition_file, signing_algorithm, hash_algorithm, private_key = get_args()
792    if not_l2:
793        no_zip = True
794
795    # Unpack updater package
796    unpack_package_processing()
797
798    if OPTIONS_MANAGER.sd_card:
799        if source_package is not None or OPTIONS_MANAGER.xml_path is not None or partition_file is not None:
800            UPDATE_LOGGER.print_log("SDCard updater:-S/-xp/-pf parameter is not required!", UPDATE_LOGGER.ERROR_LOG)
801            raise RuntimeError
802    if check_args(private_key, source_package, target_package, update_package) is False:
803        clear_resource(err_clear=True)
804        return
805
806    if not OPTIONS_MANAGER.sd_card:
807        if check_userdata_image() is False:
808            clear_resource(err_clear=True)
809            return
810
811    # Create a Script object.
812    prelude_script, verse_script, refrain_script, ending_script = get_script_obj()
813
814    # Create partition.
815    if partition_file is not None:
816        verse_script.add_command("\n# ---- do updater partitions ----\n")
817        updater_partitions_cmd = verse_script.updater_partitions()
818        verse_script.add_command(updater_partitions_cmd)
819
820        partition_file_obj, partitions_list, partitions_file_path_list = parse_partition_file_xml(partition_file)
821        if partition_file_obj is False:
822            clear_resource(err_clear=True)
823            return False
824        OPTIONS_MANAGER.partition_file_obj = partition_file_obj
825        OPTIONS_MANAGER.full_img_list = partitions_list
826        OPTIONS_MANAGER.full_image_path_list = partitions_file_path_list
827
828    if incremental_processing(no_zip, partition_file, source_package, verse_script) is False:
829        clear_resource(err_clear=True)
830        return
831
832    # Full processing
833    if len(OPTIONS_MANAGER.full_img_list) != 0:
834        verse_script.add_command("\n# ---- full image ----\n")
835        full_update_image = FullUpdateImage(OPTIONS_MANAGER.target_package_dir,
836                            OPTIONS_MANAGER.full_img_list, OPTIONS_MANAGER.full_img_name_list, verse_script,
837                            OPTIONS_MANAGER.full_image_path_list, no_zip=OPTIONS_MANAGER.no_zip)
838        full_image_content_len_list, full_image_file_obj_list = full_update_image.update_full_image()
839        if full_image_content_len_list is False or full_image_file_obj_list is False:
840            clear_resource(err_clear=True)
841            return
842        OPTIONS_MANAGER.full_image_content_len_list, OPTIONS_MANAGER.full_image_file_obj_list = \
843            full_image_content_len_list, full_image_file_obj_list
844
845    # Generate the update package.
846    if build_update_package(
847        no_zip, update_package, prelude_script, verse_script, refrain_script, ending_script) is False:
848        clear_resource(err_clear=True)
849        return
850    # Clear resources.
851    clear_resource()
852
853
854if __name__ == '__main__':
855    main()
856