Пример #1
0
def main(OLD_ZIP, NEW_ZIP, OUT_PATH):
    check_file(OLD_ZIP, NEW_ZIP)
    print('Unpacking %s ...' % OLD_ZIP)
    OLD_ZIP_PATH = extract_zip(OLD_ZIP)
    print('Unpacking %s ...' % NEW_ZIP)
    NEW_ZIP_PATH = extract_zip(NEW_ZIP)

    HAS_IMG = True
    IS_TREBLE = False

    if not os.path.exists(NEW_ZIP_PATH + '/system/app'):
        print('Extracting *.br and *.new.dat ...')
        unpack_to_img(OLD_ZIP_PATH)
        unpack_to_img(NEW_ZIP_PATH)
        print('Extracting system partition EXT4 Image...')
        extract_img(NEW_ZIP_PATH + '/system.img')
        if not os.path.exists(OLD_ZIP_PATH + '/system/app'):
            extract_img(OLD_ZIP_PATH + '/system.img')
    else:
        HAS_IMG = False

    if os.path.exists(NEW_ZIP_PATH + '/vendor.img'):
        print('Found Project-Treble supported device')
        IS_TREBLE = True
        print('Extracting vendor partition EXT4 Image...')
        extract_img(NEW_ZIP_PATH + '/vendor.img')
        if os.path.exists(OLD_ZIP_PATH + '/vendor.img'):
            extract_img(OLD_ZIP_PATH + '/vendor.img')

    # 检查 system-as-root 设备
    if os.path.exists(NEW_ZIP_PATH + '/system/default.prop'):
        IS_SYS_AS_ROOT = True
        print('Found system-as-root device')
        if not is_win():
            print('Found system-as-root device, remounting system partition')
            mkdir(OLD_ZIP_PATH + '/system_root')
            mkdir(NEW_ZIP_PATH + '/system_root')
            os.system(" ".join(('sudo', 'umount', OLD_ZIP_PATH + '/system')))
            os.system(" ".join(('sudo', 'umount', NEW_ZIP_PATH + '/system')))
            os.system(" ".join(
                ('sudo', 'mount', OLD_ZIP_PATH + '/system.img',
                 OLD_ZIP_PATH + '/system_root', '-o', 'rw,loop')))
            os.system(" ".join(
                ('sudo', 'mount', NEW_ZIP_PATH + '/system.img',
                 NEW_ZIP_PATH + '/system_root', '-o', 'rw,loop')))
        else:
            os.rename(OLD_ZIP_PATH + '/system', OLD_ZIP_PATH + '/system_root')
            os.rename(OLD_ZIP_PATH + '/system_statfile.txt',
                      OLD_ZIP_PATH + '/system_root_statfile.txt')
            os.rename(NEW_ZIP_PATH + '/system', NEW_ZIP_PATH + '/system_root')
            os.rename(NEW_ZIP_PATH + '/system_statfile.txt',
                      NEW_ZIP_PATH + '/system_root_statfile.txt')
        SYSTEM_ROOT = "/system_root"
    else:
        IS_SYS_AS_ROOT = False
        SYSTEM_ROOT = '/system'

    # 读取 ROM 中的 build.prop
    print("Getting ROM information...")
    if not IS_SYS_AS_ROOT:
        build_prop_dict = get_build_prop(NEW_ZIP_PATH + SYSTEM_ROOT +
                                         '/build.prop')
    else:
        build_prop_dict = get_build_prop(NEW_ZIP_PATH + SYSTEM_ROOT +
                                         '/system/build.prop')
    info_sdk_version = build_prop_dict.get("ro.build.version.sdk")
    info_build_version_release = build_prop_dict.get(
        'ro.build.version.release')
    info_build_fingerprint = build_prop_dict.get('ro.build.fingerprint')
    info_product_device = build_prop_dict.get('ro.product.device')
    info_build_product = build_prop_dict.get('ro.build.product')
    if info_product_device == "None":
        info_product_device = build_prop_dict.get('ro.product.system.device')
    if info_build_product == "None":
        info_build_product = build_prop_dict.get('ro.system.build.product')

    print('------ ROM Info -------')
    print('Device: %s' % info_product_device)
    print('Product: %s' % info_build_product)
    print('Android Version: %s' % info_build_version_release)
    print('API level: %s' % info_sdk_version)
    print('Fingerprint: %s' % info_build_fingerprint)
    print('')

    # 取得文件列表并存储为集合
    print('Comparing system partition...')
    # 如果是Windows, 取 statfile.txt 作为字典,在get_fileinfo_set中传入
    if HAS_IMG and is_win():
        system_dict = read_statfile(NEW_ZIP_PATH + SYSTEM_ROOT,
                                    def_sys_root=SYSTEM_ROOT)
    else:
        system_dict = {}
    old_system_set = get_fileinfo_set(OLD_ZIP_PATH, OLD_ZIP_PATH + SYSTEM_ROOT,
                                      system_dict)
    new_system_set = get_fileinfo_set(NEW_ZIP_PATH, NEW_ZIP_PATH + SYSTEM_ROOT,
                                      system_dict)
    # 去除相同的文件
    diff_set = old_system_set.symmetric_difference(new_system_set)
    if IS_TREBLE:
        print('Comparing vendor partition...')
        if HAS_IMG and is_win():
            vendor_dict = read_statfile(NEW_ZIP_PATH + '/vendor',
                                        def_sys_root='/vendor')
        else:
            vendor_dict = {}
        old_vendor_set = get_fileinfo_set(OLD_ZIP_PATH,
                                          OLD_ZIP_PATH + '/vendor',
                                          vendor_dict)
        new_vendor_set = get_fileinfo_set(NEW_ZIP_PATH,
                                          NEW_ZIP_PATH + '/vendor',
                                          vendor_dict)
        diff_set = diff_set | old_vendor_set.symmetric_difference(
            new_vendor_set)

    OTA_ZIP_PATH = tempfile.mkdtemp("", "OTA-maker_")

    print('Reading the difference file list...')
    print('Copying files and generating patches...')
    patch_set = set()
    rem_set = set()
    sym_set = set()
    new_set = set()
    old_sha1_dict = {}
    tp_executor = Pool()
    for tmp_item in diff_set:
        if OLD_ZIP_PATH in tmp_item.path:
            if not os.path.exists(NEW_ZIP_PATH + tmp_item.rela_path):
                rem_set.add(tmp_item)
            else:
                old_sha1_dict[tmp_item.rela_path] = tmp_item.sha1
        else:
            if tmp_item.slink:
                sym_set.add(tmp_item)
                rem_set.add(tmp_item)
            elif not os.path.exists(OLD_ZIP_PATH + tmp_item.rela_path
                                    ) or tmp_item.filename in do_not_patch_set:
                new_file_path = OTA_ZIP_PATH + tmp_item.rela_path
                if not os.path.isdir(NEW_ZIP_PATH + tmp_item.rela_path):
                    mkdir(os.path.split(new_file_path)[0])
                    file2file(NEW_ZIP_PATH + tmp_item.rela_path, new_file_path)
                new_set.add(tmp_item)
            else:
                patch_set.add(tmp_item)
                ota_patch_path = OTA_ZIP_PATH + '/patch' + tmp_item.rela_path + '.p'
                mkdir(os.path.split(ota_patch_path)[0])
                tp_executor.apply_async(
                    bsdiff4.file_diff,
                    (OLD_ZIP_PATH + tmp_item.rela_path,
                     NEW_ZIP_PATH + tmp_item.rela_path, ota_patch_path))
    tp_executor.close()
    tp_executor.join()

    print('Reading SELinux context...')
    if not is_win() and HAS_IMG:
        for tmp_item in new_set:
            tmp_item.selabel = get_selabel_linux(tmp_item.path)
    else:
        if IS_SYS_AS_ROOT:
            tmp_root = SYSTEM_ROOT
        else:
            tmp_root = ''
        if os.path.exists(NEW_ZIP_PATH + SYSTEM_ROOT +
                          '/etc/selinux/plat_file_contexts'):
            tmp_file_context = get_file_contexts(
                NEW_ZIP_PATH + SYSTEM_ROOT + '/etc/selinux/plat_file_contexts',
                tmp_root)
        elif os.path.exists(NEW_ZIP_PATH + SYSTEM_ROOT +
                            '/system/etc/selinux/plat_file_contexts'):
            tmp_file_context = get_file_contexts(
                NEW_ZIP_PATH + SYSTEM_ROOT +
                '/system/etc/selinux/plat_file_contexts', tmp_root)
        else:
            boot_out = extract_bootimg(NEW_ZIP_PATH + '/boot.img')
            if os.path.exists(boot_out + '/file_contexts'):
                tmp_file_context = get_file_contexts(boot_out +
                                                     '/file_contexts')
            elif os.path.exists(boot_out + '/file_contexts.bin'):
                tmp_file_context = get_file_contexts(boot_out +
                                                     '/file_contexts.bin')
            else:
                tmp_file_context = {}
        if os.path.exists(NEW_ZIP_PATH +
                          '/vendor/etc/selinux/vendor_file_contexts'):
            tmp_file_context.update(
                get_file_contexts(NEW_ZIP_PATH +
                                  '/vendor/etc/selinux/vendor_file_contexts'))
        if os.path.exists(NEW_ZIP_PATH +
                          '/vendor/etc/selinux/nonplat_file_contexts'):
            tmp_file_context.update(
                get_file_contexts(NEW_ZIP_PATH +
                                  '/vendor/etc/selinux/nonplat_file_contexts'))
        tmp_keys = tmp_file_context.keys()
        for tmp_item in new_set | patch_set:
            tmp_item.selabel = get_selabel_windows(tmp_file_context, tmp_keys,
                                                   tmp_item.rela_path)

    print('Generating updater...')
    tmp_updater = Updater()
    mkdir(OTA_ZIP_PATH + '/install')
    if '64' in build_prop_dict.get('ro.product.cpu.abi'):
        tmp_updater.add("SYS_LD_LIBRARY_PATH=/system/lib64")
        file2file(get_bin('applypatch_64'),
                  OTA_ZIP_PATH + '/install/applypatch')
    else:
        tmp_updater.add("SYS_LD_LIBRARY_PATH=/system/lib")
        file2file(get_bin('applypatch'), OTA_ZIP_PATH + '/install/applypatch')
    tmp_updater.check_device(info_product_device, info_build_product)
    tmp_updater.blank_line()

    tmp_updater.ui_print('This OTA package is made by OTA-maker V.' +
                         __version__)
    tmp_updater.ui_print('Mounting ' + SYSTEM_ROOT)
    tmp_updater.mount(SYSTEM_ROOT)
    if IS_TREBLE:
        tmp_updater.ui_print('Mounting /vendor')
        tmp_updater.mount('/vendor')

    # patch文件
    tmp_updater.ui_print('Checking files...')
    patch_list = list(patch_set)
    patch_list.sort(key=lambda x: x.rela_path)
    for tmp_item in patch_list:
        tmp_updater.apply_patch_check(tmp_item.rela_path,
                                      old_sha1_dict[tmp_item.rela_path],
                                      tmp_item.sha1)
    tmp_updater.blank_line()
    tmp_updater.ui_print('Extracting patch files...')
    tmp_updater.package_extract_dir('patch', '/tmp/patch')
    tmp_updater.ui_print('Patching files...')
    for tmp_item in patch_list:
        ota_patch_path = OTA_ZIP_PATH + '/patch' + tmp_item.rela_path + '.p'
        tmp_updater.apply_patch(tmp_item.rela_path, tmp_item.sha1,
                                len(tmp_item),
                                old_sha1_dict[tmp_item.rela_path],
                                '/tmp/patch' + tmp_item.rela_path + '.p')
    tmp_updater.blank_line()

    # 解包文件
    tmp_updater.ui_print('Extracting files...')
    tmp_updater.package_extract_dir(SYSTEM_ROOT[1:], SYSTEM_ROOT)
    if IS_TREBLE:
        tmp_updater.package_extract_dir('vendor', '/vendor')
    tmp_updater.blank_line()

    # 设置metadata
    tmp_updater.ui_print('Setting metadata...')
    new_list = list(new_set | patch_set)
    new_list.sort(key=lambda x: x.rela_path)
    for tmp_item in new_list:
        tmp_updater.set_metadata(tmp_item.rela_path,
                                 tmp_item.uid,
                                 tmp_item.gid,
                                 tmp_item.perm,
                                 selabel=tmp_item.selabel)
    tmp_updater.blank_line()

    # 移除文件
    tmp_updater.ui_print('Deleting files...')
    rem_list = list(rem_set)
    rem_list.sort(key=lambda x: x.rela_path)
    for tmp_item in rem_list:
        tmp_updater.delete(tmp_item.rela_path)
    tmp_updater.blank_line()

    # 生成symlink
    tmp_updater.ui_print('Making symlinks...')
    sym_list = list(sym_set)
    sym_list.sort(key=lambda x: x.rela_path)
    for tmp_item in sym_list:
        tmp_updater.symlink(tmp_item.rela_path, tmp_item.slink)
    tmp_updater.blank_line()

    # 从原版的updater-script取得操作
    tmp_updater.ui_print('Running updater-script from source zip...')
    list_lines = []
    flag_EOC = True  # EOC: End of command
    with open(NEW_ZIP_PATH + '/META-INF/com/google/android/updater-script',
              "r",
              encoding="UTF-8") as f:
        for line in f.readlines():
            t_line = line.strip()
            if not t_line: continue
            if flag_EOC:
                list_lines.append(t_line)
            else:
                list_lines[-1] = list_lines[-1] + ' ' + t_line
            if t_line[-1] == ";" or t_line[0] == "#":
                flag_EOC = True
            else:
                flag_EOC = False
    for line in list_lines:
        try:
            tmp_line = parameter_split(line)
            us_action = tmp_line[0]
            if us_action == "package_extract_dir":
                if tmp_line[1] == "system" or tmp_line[1] == "vendor": continue
                mkdir(os.path.dirname(OTA_ZIP_PATH + '/' + tmp_line[1]))
                dir2dir(NEW_ZIP_PATH + '/' + tmp_line[1],
                        OTA_ZIP_PATH + '/' + tmp_line[1])
                tmp_updater.package_extract_dir(tmp_line[1], tmp_line[2])
            elif us_action == "package_extract_file":
                mkdir(os.path.dirname(OTA_ZIP_PATH + '/' + tmp_line[1]))
                file2file(NEW_ZIP_PATH + '/' + tmp_line[1],
                          OTA_ZIP_PATH + '/' + tmp_line[1])
                tmp_updater.package_extract_file(tmp_line[1], tmp_line[2])
            elif us_action == "ui_print":
                tmp_updater.ui_print(" ".join(tmp_line[1:]))
            elif us_action == "set_perm":
                tmp_updater.set_perm(tmp_line[1], tmp_line[2], tmp_line[3],
                                     tmp_line[4:])
            elif us_action == "set_perm_recursive":
                tmp_updater.set_perm_recursive(tmp_line[1], tmp_line[2],
                                               tmp_line[3], tmp_line[4],
                                               tutle(tmp_line[5:]))
            elif us_action == "set_metadata":
                tmp_updater.set_metadata(tmp_line[1], tmp_line[3], tmp_line[5],
                                         tmp_line[7])
            elif us_action == "set_metadata_recursive":
                tmp_updater.set_metadata_recursive(tmp_line[1], tmp_line[3],
                                                   tmp_line[5], tmp_line[7],
                                                   tmp_line[9])
            elif us_action == "mount":
                if tmp_line[-1] == "/system" or tmp_line[-1] == "/vendor":
                    continue
                tmp_updater.mount(tmp_line[-1])
            elif us_action == "umount":
                if tmp_line[-1] == "/system" or tmp_line[-1] == "/vendor":
                    continue
                tmp_updater.umount(tmp_line[1])
            elif us_action == "apply_patch_check":
                tmp_updater.apply_patch_check(tmp_line[1], tutle(tmp_line[2:]))
            elif us_action == "apply_patch":
                tmp_updater.add("apply_patch %s:%s" %
                                (" ".join(tmp_line[1:5]), tmp_line[6]))
            elif us_action == "show_progress":
                tmp_updater.add('show_progress "%s" "%s"' %
                                (tmp_line[1], tmp_line[6]))
            elif us_action == "set_progress":
                tmp_updater.add('set_progress "%s"' % tmp_line[1])
            elif us_action == "run_program":
                tmp_updater.add(" ".join(tmp_line[1:]))
            elif us_action == "symlink":
                tmp_updater.add('symlink ' + " ".join(tmp_line[1:]))
            else:
                print("WARNING: failed to analyze " + line.strip())
        except:
            continue

    tmp_updater.blank_line()
    tmp_updater.add("sync")
    tmp_updater.blank_line()
    tmp_updater.ui_print('Unmounting ' + SYSTEM_ROOT)
    tmp_updater.unmount(SYSTEM_ROOT)
    if IS_TREBLE:
        tmp_updater.ui_print('Unmounting /vendor...')
        tmp_updater.unmount("/vendor")
    tmp_updater.blank_line()
    tmp_updater.ui_print("Done!")

    update_script_path = os.path.join(OTA_ZIP_PATH, "META-INF", "com",
                                      "google", "android")
    mkdir(update_script_path)
    new_ub = os.path.join(update_script_path, "update-binary")
    with open(new_ub, "w", encoding="UTF-8", newline="\n") as f:
        for line in tmp_updater.script:
            f.write(line)
    new_uc = os.path.join(update_script_path, "updater-script")
    with open(new_uc, "w", encoding="UTF-8", newline="\n") as f:
        f.write("# Dummy file; update-binary is a shell script.\n")

    print('Making OTA package...')
    make_zip(OTA_ZIP_PATH, OUT_PATH)

    print('Cleaning temp files...')
    if not is_win():
        os.system(" ".join(('sudo', 'umount', OLD_ZIP_PATH + SYSTEM_ROOT)))
        os.system(" ".join(('sudo', 'umount', NEW_ZIP_PATH + SYSTEM_ROOT)))
        if IS_TREBLE:
            os.system(" ".join(('sudo', 'umount', OLD_ZIP_PATH + '/vendor')))
            os.system(" ".join(('sudo', 'umount', NEW_ZIP_PATH + '/vendor')))
    remove_path(OLD_ZIP_PATH)
    remove_path(NEW_ZIP_PATH)
    remove_path(OTA_ZIP_PATH)

    print("\nDone!")
    print("Output OTA package: %s" % OUT_PATH)
Пример #2
0
class main():
    def __init__(self,
                 old_package,
                 new_package,
                 ota_package_name,
                 ext_models=None):
        self.old_package = old_package
        self.new_package = new_package
        self.ota_package_name = ota_package_name
        if ext_models is None:
            self.ext_models = tuple()
        else:
            self.ext_models = ext_models
        self.run()

    def run(self):
        self.clean_temp()

        self.unpack_zip()
        self.p1_simg = self.unpack_dat(self.p1_path, is_new=False)
        self.p2_simg = self.unpack_dat(self.p2_path, is_new=True)
        self.pt_flag = self.is_pt()
        if self.pt_flag:
            self.p1_vimg = self.unpack_dat(self.p1_path,
                                           is_new=False,
                                           is_vendor=True)
            self.p2_vimg = self.unpack_dat(self.p2_path,
                                           is_new=True,
                                           is_vendor=True)

        self.p1_spath = self.unpack_img(self.p1_simg, is_new=False)
        self.p2_spath = self.unpack_img(self.p2_simg, is_new=True)
        if self.pt_flag:
            self.p1_vpath = self.unpack_img(self.p1_vimg,
                                            is_new=False,
                                            is_vendor=True)
            self.p2_vpath = self.unpack_img(self.p2_vimg,
                                            is_new=True,
                                            is_vendor=True)

        self.ota_path = tempfile.mkdtemp("_OTA", "GOTAPGS_")

        self.p1_sfl = self.gen_file_list(self.p1_spath, is_new=False)
        self.p2_sfl = self.gen_file_list(self.p2_spath, is_new=True)
        if self.pt_flag:
            self.p1_vfl = self.gen_file_list(self.p1_vpath,
                                             is_new=False,
                                             is_vendor=True)
            self.p2_vfl = self.gen_file_list(self.p2_vpath,
                                             is_new=True,
                                             is_vendor=True)

        self.do_compare()
        self.get_rom_info()
        self.get_bootimg_block()
        self.updater_init()
        self.cp_files_1st()
        self.diff_files_patch_init()
        self.diff_files_patch_system()
        if self.pt_flag:
            self.diff_files_patch_vendor()
        self.diff_files_patch_write()

        self.remove_init()
        self.remove_dirs()
        self.remove_files()
        self.remove_slink_dirs()
        self.remove_slink_files()

        self.package_extract()
        self.create_symlinks()
        self.set_metadata()

        self.updater_end()
        self.updater_write()

        self.final()

    def unpack_zip(self):
        print("\nUnpacking OLD Rom...")
        self.p1_path = cn.extract_zip(self.old_package)
        print("\nUnpacking NEW Rom...")
        self.p2_path = cn.extract_zip(self.new_package)

    def is_pt(self):
        return all(
            (os.path.exists(os.path.join(self.p1_path, "vendor.new.dat.br")),
             os.path.exists(os.path.join(self.p2_path, "vendor.new.dat.br"))))

    @staticmethod
    def pars_init(bool_1, bool_2):
        str_1 = "OLD"
        if bool_1:
            str_1 = "NEW"
        str_2 = "system"
        if bool_2:
            str_2 = "vendor"
        return str_1, str_2

    def unpack_dat(self, px_path, is_new, is_vendor=False):
        oon, sov = self.pars_init(is_new, is_vendor)
        if os.path.exists(os.path.join(px_path, "%s.new.dat.br" % sov)):
            print("\nUnpacking %s Rom's %s.new.dat.br..." % (oon, sov))
            cn.extract_br(os.path.join(px_path, "%s.new.dat.br" % sov))
        print("\nUnpacking %s Rom's %s.new.dat..." % (oon, sov))
        px_img = cn.extract_sdat(
            os.path.join(px_path, "%s.transfer.list" % sov),
            os.path.join(px_path, "%s.new.dat" % sov),
            os.path.join(px_path, "%s.img" % sov))
        cn.remove_path(os.path.join(px_path, "%s.new.dat.br" % sov))
        cn.remove_path(os.path.join(px_path, "%s.new.dat" % sov))
        return px_img

    def unpack_img(self, img_path, is_new, is_vendor=False):
        oon, sov = self.pars_init(is_new, is_vendor)
        print("\nUnpacking %s Rom's %s.img..." % (oon, sov))
        if cn.is_win():
            spath = cn.extract_img(img_path)
            cn.remove_path(img_path)
        else:
            spath = cn.mount_img(img_path)
        return spath

    def gen_file_list(self, file_path, is_new, is_vendor=False):
        oon, sov = self.pars_init(is_new, is_vendor)
        print("\nRetrieving %s Rom's %s file list..." % (oon, sov))
        px = FL(file_path, is_vendor)
        cn.clean_line()
        print("Search completed\nFound %s files, %s directories" %
              (len(px), len(px.dirlist)))
        return px

    def do_compare(self):
        print("\nStart comparison system files...")
        self.cps = FL_Compare(self.p1_sfl, self.p2_sfl)
        print("\nComparative completion!")
        if self.pt_flag:
            print("\nStart comparison vendor files...")
            self.cpv = FL_Compare(self.p1_vfl, self.p2_vfl)
            print("\nComparative completion!")

    def get_rom_info(self):
        print("\nGetting rom information...")
        self.p1_prop = cn.get_build_prop(
            os.path.join(self.p1_sfl.fullpath, "build.prop"))
        self.p2_prop = cn.get_build_prop(
            os.path.join(self.p2_sfl.fullpath, "build.prop"))
        self.model = self.p2_prop.get("ro.product.device", "Unknown")
        self.arch = "arm"
        self.is_64bit = False
        if self.p2_prop.get("ro.product.cpu.abi") == "x86":
            self.arch = "x86"
        if self.p2_prop.get("ro.product.cpu.abi2") == "x86":
            self.arch = "x86"
        if int(self.p2_prop.get("ro.build.version.sdk", "0")) >= 21:
            if self.p2_prop.get("ro.product.cpu.abi") == "arm64-v8a":
                self.arch = "arm64"
                self.is_64bit = True
            if self.p2_prop.get("ro.product.cpu.abi") == "x86_64":
                self.arch = "x64"
                self.is_64bit = True
        self.verify_info = compare_build_prop(self.p1_prop, self.p2_prop)
        print("\nModel: %s" % self.model)
        print("\nArch: %s" % self.arch)
        print("\nRom verify info: %s=%s" % self.verify_info[:-1])

    def get_bootimg_block(self):
        old_script = os.path.join(self.p2_path, "META-INF", "com", "google",
                                  "android", "updater-script")
        cn.is_exist_path(old_script)
        self.bootimg_block = ""
        with open(old_script, "r", encoding="UTF-8", errors="ignore") as f:
            for line in f.readlines():
                if line.strip().startswith("package_extract_file"):
                    if "\"boot.img\"" in line:
                        self.bootimg_block = cn.parameter_split(
                            line.strip())[-1]
        if not self.bootimg_block:
            raise Exception("Can not get boot.img block!")

    def cp_files_1st(self):
        print("\nCopying files...")
        new_bootimg = os.path.join(self.p2_path, "boot.img")
        cn.is_exist_path(new_bootimg)
        cn.file2dir(new_bootimg, self.ota_path)
        for f in self.cps.FL_2_isolated_files:
            sys.stderr.write("Copying file %-99s\r" % f.spath)
            cn.file2file(f.path,
                         os.path.join(self.ota_path, "system", f.rela_path))
        if self.pt_flag:
            for f in self.cpv.FL_2_isolated_files:
                sys.stderr.write("Copying file %-99s\r" % f.spath)
                cn.file2file(
                    f.path, os.path.join(self.ota_path, "vendor", f.rela_path))
        cn.clean_line()
        cn.file2dir(cn.bin_call("applypatch_old"),
                    os.path.join(self.ota_path, "bin"))
        cn.file2dir(cn.bin_call("applypatch_old_64"),
                    os.path.join(self.ota_path, "bin"))

    def updater_init(self):
        print("\nGenerating script...")
        self.us = Updater(self.is_64bit)
        if self.model != "Unknown":
            self.us.check_device(self.model, self.ext_models)
            self.us.blank_line()
        self.us.ui_print("Updating from %s" % self.verify_info[1])
        self.us.ui_print("to %s" % self.verify_info[2])
        self.us.ui_print("It may take several minutes, please be patient.")
        self.us.ui_print(" ")
        self.us.blank_line()
        self.us.ui_print("Remount /system ...")
        self.us.add("[ $(is_mounted /system) == 1 ] || umount /system")
        self.us.mount("/system")
        self.us.add("[ -f /system/build.prop ] || {", end="\n")
        self.us.ui_print(" ", space_no=2)
        self.us.abort("Failed to mount /system!", space_no=2)
        self.us.add("}")
        self.us.blank_line()
        if self.pt_flag:
            self.us.ui_print("Remount /vendor ...")
            self.us.add("[ $(is_mounted /vendor) == 1 ] || umount /vendor")
            self.us.mount("/vendor")
            self.us.blank_line()
        self.us.ui_print("Verify Rom Version ...")
        self.us.add(
            "[ $(file_getprop /system/build.prop %s) == \"%s\" ] || {" %
            (self.verify_info[0], self.verify_info[1]),
            end="\n")
        self.us.ui_print(" ", space_no=2)
        self.us.abort("Failed! Versions Mismatch!", space_no=2)
        self.us.add("}")

    def diff_files_patch_init(self):
        # patch check命令参数列表
        self.patch_check_script_list = []
        # patch 命令参数列表
        self.patch_do_script_list = []
        # 哈希相同但信息不同的文件
        self.cps.diff_info_files = []
        # 符号链接指向不同的文件
        self.cps.diff_slink_files = []
        # 卡刷时直接替换(而不是打补丁)的文件(名)
        self.cps.ignore_names = {
            "build.prop",
            "recovery-from-boot.p",
            "install-recovery.sh",
            "applypatch",
            "backuptool.functions",
            "backuptool.sh",
        }
        if self.pt_flag:
            self.cpv.diff_info_files = []
            self.cpv.diff_slink_files = []
            self.cpv.ignore_names = set()
        cn.mkdir(os.path.join(self.ota_path, "patch"))

    def diff_files_patch_system(self):
        if len(self.cps.diff_files):
            for f1, f2 in self.cps.diff_files:
                if f1.slink != f2.slink:
                    self.cps.diff_slink_files.append(f2)
                    continue
                if f1.sha1 == f2.sha1:
                    self.cps.diff_info_files.append(f2)
                    continue
                if f2.name in self.cps.ignore_names:
                    self.cps.FL_2_isolated_files.append(f2)
                    cn.file2file(
                        f2.path,
                        os.path.join(self.ota_path, "system", f2.rela_path))
                    continue
                sys.stderr.write("Generating patch file for %-99s\r" %
                                 f2.spath)
                temp_p_file = tempfile.mktemp(".p.tmp")
                if not cn.get_bsdiff(f1, f2, temp_p_file):
                    # 如果生成补丁耗时太长 则取消生成补丁 直接替换文件
                    self.cps.FL_2_isolated_files.append(f2)
                    cn.file2file(
                        f2.path,
                        os.path.join(self.ota_path, "system", f2.rela_path))
                    cn.remove_path(temp_p_file)
                    continue
                p_path = os.path.join(self.ota_path, "patch", "system",
                                      f2.rela_path + ".p")
                p_spath = p_path.replace(self.ota_path, "/tmp",
                                         1).replace("\\", "/")
                cn.file2file(temp_p_file, p_path, move=True)
                self.patch_check_script_list.append(
                    (f2.spath, f1.sha1, f2.sha1))
                self.patch_do_script_list.append(
                    (f2.spath, f2.sha1, len(f2), f1.sha1, p_spath))
            cn.clean_line()

    def diff_files_patch_vendor(self):
        if len(self.cpv.diff_files):
            for f1, f2 in self.cpv.diff_files:
                if f1.slink != f2.slink:
                    self.cpv.diff_slink_files.append(f2)
                    continue
                if f1.sha1 == f2.sha1:
                    self.cpv.diff_info_files.append(f2)
                    continue
                if f2.name in self.cpv.ignore_names:
                    self.cpv.FL_2_isolated_files.append(f2)
                    cn.file2file(
                        f2.path,
                        os.path.join(self.ota_path, "vendor", f2.rela_path))
                    continue
                sys.stderr.write("Generating patch file for %-99s\r" %
                                 f2.spath)
                temp_p_file = tempfile.mktemp(".p.tmp")
                if not cn.get_bsdiff(f1, f2, temp_p_file):
                    self.cpv.FL_2_isolated_files.append(f2)
                    cn.file2file(
                        f2.path,
                        os.path.join(self.ota_path, "vendor", f2.rela_path))
                    cn.remove_path(temp_p_file)
                    continue
                p_path = os.path.join(self.ota_path, "patch", "vendor",
                                      f2.rela_path + ".p")
                p_spath = p_path.replace(self.ota_path, "/tmp",
                                         1).replace("\\", "/")
                cn.file2file(temp_p_file, p_path, move=True)
                self.patch_check_script_list.append(
                    (f2.spath, f1.sha1, f2.sha1))
                self.patch_do_script_list.append(
                    (f2.spath, f2.sha1, len(f2), f1.sha1, p_spath))
            cn.clean_line()

    def diff_files_patch_write(self):
        self.us.blank_line()
        self.us.ui_print("Unpack Patch Files ...")
        self.us.package_extract_dir("patch", "/tmp/patch")
        self.us.package_extract_dir("bin", "/tmp/bin")
        self.us.add("chmod 0755 /tmp/bin/applypatch_old")
        self.us.add("chmod 0755 /tmp/bin/applypatch_old_64")
        self.us.blank_line()
        self.us.ui_print("Check Files ...")
        for arg in self.patch_check_script_list:
            # 差异文件patch check
            self.us.apply_patch_check(*arg)
        self.us.blank_line()
        self.us.ui_print("Patch Files ...")
        for arg in self.patch_do_script_list:
            # 差异文件patch
            self.us.apply_patch(*arg)
        self.us.blank_line()
        self.us.delete_recursive("/tmp/patch")
        self.us.delete_recursive("/tmp/bin")

    def remove_init(self):
        self.us.blank_line()
        self.us.ui_print("Remove Unneeded Files ...")

    def remove_dirs(self):
        for d in self.cps.FL_1_isolated_dirs_spaths:
            # 删除目录
            self.us.delete_recursive(d)
        if self.pt_flag:
            for d in self.cpv.FL_1_isolated_dirs_spaths:
                self.us.delete_recursive(d)

    def remove_files(self):
        for f in self.cps.FL_1_isolated_files_spaths:
            # 删除文件
            if f not in self.cps.ignore_del_files_spaths:
                self.us.delete(f)
        if self.pt_flag:
            for f in self.cpv.FL_1_isolated_files_spaths:
                if f not in self.cpv.ignore_del_files_spaths:
                    self.us.delete(f)

    def remove_slink_dirs(self):
        for d in self.cps.diff_dirs:
            # 差异符号链接目录删除
            if d.slink:
                self.us.delete(d.spath)
        if self.pt_flag:
            for d in self.cpv.diff_dirs:
                if d.slink:
                    self.us.delete(d.spath)

    def remove_slink_files(self):
        for f in self.cps.diff_slink_files:
            # 差异符号链接文件删除
            self.us.delete(f.spath)
        if self.pt_flag:
            for f in self.cpv.diff_slink_files:
                self.us.delete(f.spath)

    def package_extract(self):
        self.us.blank_line()
        self.us.ui_print("Unpack New Files ...")
        if len(self.cps.FL_2_isolated_dirs + self.cps.FL_2_isolated_files):
            self.us.package_extract_dir("system", "/system")
        if self.pt_flag:
            if len(self.cpv.FL_2_isolated_dirs + self.cpv.FL_2_isolated_files):
                self.us.package_extract_dir("vendor", "/vendor")

    def create_symlinks(self):
        self.us.blank_line()
        self.us.ui_print("Create Symlinks ...")
        for f in (self.cps.FL_2_isolated_dirs + self.cps.FL_2_isolated_files +
                  self.cps.diff_dirs + self.cps.diff_slink_files):
            # 新增符号链接目录 & 新增符号链接文件 &
            # 差异符号链接目录 & 差异符号链接文件 建立符号链接
            if f.slink:
                self.us.symlink(f.spath, f.slink)
        if self.pt_flag:
            for f in (self.cpv.FL_2_isolated_dirs +
                      self.cpv.FL_2_isolated_files + self.cpv.diff_dirs +
                      self.cpv.diff_slink_files):
                if f.slink:
                    self.us.symlink(f.spath, f.slink)

    def set_metadata(self):
        self.us.blank_line()
        self.us.ui_print("Set Metadata ...")
        for f in (self.cps.FL_2_isolated_dirs + self.cps.FL_2_isolated_files +
                  self.cps.diff_dirs + self.cps.diff_info_files +
                  self.cps.diff_slink_files):
            # 新增目录 & 新增文件 &
            # 差异目录 & 差异信息文件 & 差异符号链接文件 信息设置
            self.us.set_metadata(f.spath,
                                 f.uid,
                                 f.gid,
                                 f.perm,
                                 selabel=f.selabel)
        if self.pt_flag:
            for f in (self.cpv.FL_2_isolated_dirs +
                      self.cpv.FL_2_isolated_files + self.cpv.diff_dirs +
                      self.cpv.diff_info_files + self.cpv.diff_slink_files):
                self.us.set_metadata(f.spath,
                                     f.uid,
                                     f.gid,
                                     f.perm,
                                     selabel=f.selabel)

    def updater_end(self):
        self.us.blank_line()
        self.us.add("sync")
        self.us.umount("/system")
        if self.pt_flag:
            self.us.umount("/vendor")
        self.us.blank_line()
        self.us.ui_print("Flash boot.img ...")
        self.us.package_extract_file("boot.img", self.bootimg_block)
        self.us.blank_line()
        self.us.delete_recursive("/cache/*")
        self.us.delete_recursive("/data/dalvik-cache")
        self.us.blank_line()
        self.us.ui_print("Done!")

    def updater_write(self):
        update_script_path = os.path.join(self.ota_path, "META-INF", "com",
                                          "google", "android")
        cn.mkdir(update_script_path)
        new_ub = os.path.join(update_script_path, "update-binary")
        with open(new_ub, "w", encoding="UTF-8", newline="\n") as f:
            for line in self.us.script:
                f.write(line)
        new_uc = os.path.join(update_script_path, "updater-script")
        with open(new_uc, "w", encoding="UTF-8", newline="\n") as f:
            f.write("# Dummy file; update-binary is a shell script.\n")

    def final(self):
        print("\nMaking OTA package...")
        ota_zip = cn.make_zip(self.ota_path)
        if self.ota_package_name.startswith("/") or re.match(
                r"[A-Za-z]:\\*", self.ota_package_name):
            ota_zip_real = self.ota_package_name
        else:
            ota_zip_real = os.path.normpath(
                os.path.join(os.getcwd(), self.ota_package_name))
        cn.file2file(ota_zip, ota_zip_real, move=True)

        self.clean_temp()
        print("\nDone!")
        print("\nOutput OTA package: %s !" % ota_zip_real)
        sys.exit()

    @staticmethod
    def clean_temp():
        print("\nCleaning temp files...")
        for d in os.listdir(tempfile.gettempdir()):
            if d.startswith("GOTAPGS_"):
                if not cn.is_win():
                    for mdir in ("system_", "vendor_"):
                        os.system("sudo umount %s > /dev/null" %
                                  os.path.join(tempfile.gettempdir(), d, mdir))
                cn.remove_path(os.path.join(tempfile.gettempdir(), d))