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)
class MkOta(): 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() 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) 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) 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!") self.model, self.verify_info = self.get_rom_info() self.bootimg_block = self.get_bootimg_block() self.us = Updater() self.updater_init() self.cp_files() # patch check命令参数列表 self.patch_check_script_list_sp = [] # patch 命令参数列表 self.patch_do_script_list_sp = [] print("\nGenerating patch files...") self.diff_files_patch() self.remove_items() self.package_extract() self.create_symlinks() self.set_metadata() self.updater_end() self.final() 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)) file_list = FL(file_path, is_vendor) cn.clean_line() print("Search completed\nFound %s files, %s directories" % (len(file_list), len(file_list.dirlist))) return file_list def get_rom_info(self): print("\nGetting rom information...") p1_prop = cn.get_build_prop( os.path.join(self.p1_sfl.fullpath, "build.prop")) p2_prop = cn.get_build_prop( os.path.join(self.p2_sfl.fullpath, "build.prop")) model = p2_prop.get("ro.product.device", "Unknown") arch = "arm" if p2_prop.get("ro.product.cpu.abi") == "x86": arch = "x86" if p2_prop.get("ro.product.cpu.abi2") == "x86": arch = "x86" if int(p2_prop.get("ro.build.version.sdk", "0")) >= 21: if p2_prop.get("ro.product.cpu.abi") == "arm64-v8a": arch = "arm64" if p2_prop.get("ro.product.cpu.abi") == "x86_64": arch = "x64" if arch not in ("arm", "arm64"): raise Exception( "This tool is only available for arm/arm64 devices. " "This rom's device architecture is: %s" % arch) verify_info = compare_build_prop(p1_prop, p2_prop) print("\nModel: %s" % model) print("\nArch: %s" % arch) print("\nRom verify info: %s=%s" % verify_info[:-1]) return model, verify_info 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) 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: bootimg_block = cn.parameter_split(line.strip())[-1] if not bootimg_block: raise Exception("Can not get boot.img block!") return bootimg_block def cp_files(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("busybox"), os.path.join(self.ota_path, "bin")) cn.file2dir(cn.bin_call("bspatch"), os.path.join(self.ota_path, "bin")) def updater_init(self): print("\nGenerating script...") 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_system_fifter(self): diff_files_mp = list() if 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 diff_files_mp.append([f1, f2, tempfile.mktemp(".p.tmp")]) return diff_files_mp def _diff_files_patch_vendor_fifter(self): diff_files_mp = list() if 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 diff_files_mp.append([f1, f2, tempfile.mktemp(".p.tmp")]) return diff_files_mp def diff_files_patch(self): # init # 哈希相同但信息不同的文件 self.cps.diff_info_files = [] # 符号链接指向不同的文件 self.cps.diff_slink_files = [] # 卡刷时直接替换(而不是打补丁)的文件(名) self.cps.ignore_names = { "build.prop", "recovery-from-boot.p", "install-recovery.sh", "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")) # fifter system_diff_files_mp = self._diff_files_patch_system_fifter() if self.pt_flag: vendor_diff_files_mp = self._diff_files_patch_vendor_fifter() else: vendor_diff_files_mp = list() get_bsdiff_list(system_diff_files_mp + vendor_diff_files_mp) for f1, f2, temp_p_file in system_diff_files_mp: if os.path.exists(temp_p_file) and os.stat(temp_p_file).st_size: 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_sp.append( (f2.spath, f1.sha1, f2.sha1)) self.patch_do_script_list_sp.append( (f2.spath, f2.sha1, f1.sha1, p_spath)) else: print("Failed to generate patch file for %s!" % f1.spath) 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) for f1, f2, temp_p_file in vendor_diff_files_mp: if os.path.exists(temp_p_file) and os.stat(temp_p_file).st_size: 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_sp.append( (f2.spath, f1.sha1, f2.sha1)) self.patch_do_script_list_sp.append( (f2.spath, f2.sha1, f1.sha1, p_spath)) else: print("Failed to generate patch file for %s!" % f1.spath) 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) # write lines to updater 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/bspatch") self.us.blank_line() self.us.ui_print("Check Files ...") for arg in self.patch_check_script_list_sp: # 差异文件patch check self.us.apply_patch_check_sp(*arg) self.us.blank_line() self.us.ui_print("Patch Files ...") for arg in self.patch_do_script_list_sp: # 差异文件patch self.us.apply_patch_sp(*arg) self.us.blank_line() self.us.delete_recursive("/tmp/patch") self.us.delete_recursive("/tmp/bin") def remove_items(self): self.us.blank_line() self.us.ui_print("Remove Unneeded Files ...") # 删除目录 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) # 删除文件 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) # 差异符号链接目录删除 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) # 差异符号链接文件删除 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 self.cps.FL_2_isolated_dirs or self.cps.FL_2_isolated_files: self.us.package_extract_dir("system", "/system") if self.pt_flag: if self.cpv.FL_2_isolated_dirs or 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.ui_print("Clean dalvik-cache & cache ...") self.us.delete_recursive("/cache/*") self.us.delete_recursive("/data/dalvik-cache") self.us.blank_line() self.us.ui_print("Done!") 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_"): mount_path = os.path.join(tempfile.gettempdir(), d, mdir) os.system( "mountpoint -q {0} && sudo umount {0}".format( mount_path)) cn.remove_path(os.path.join(tempfile.gettempdir(), d))