def patch_zip(zip_file, file_info, partition_config): if new_ui: ui = new_ui else: ui = OS.ui if not OS.is_android(): ui.info('--- Please wait. This may take a while ---') files_to_patch = [] if type(file_info.patch) == str and file_info.patch != "": files_to_patch = patch.files_in_patch(file_info.patch) elif type(file_info.patch) == list: for i in file_info.patch: if type(i) == str and i != "": files_to_patch.extend(patch.files_in_patch(i)) if file_info.extract: temp = file_info.extract if type(temp) == str or callable(temp): temp = [temp] for i in temp: if callable(i): output = i() if type(output) == list: files_to_patch.extend(output) elif type(output) == str: files_to_patch.append(output) elif type(i) == list: files_to_patch.extend(i) elif type(i) == str: files_to_patch.append(i) tempdir = tempfile.mkdtemp() exit.remove_on_exit(tempdir) set_task('EXTRACTING_ZIP') ui.details('Loading zip file ...') z = zipfile.ZipFile(zip_file, 'r') for f in files_to_patch: ui.details("Extracting file to be patched: %s" % f) try: z.extract(f, path=tempdir) except: ui.failed('Failed to extract file: %s' % f) exit.exit(1) if file_info.has_boot_image: ui.details("Extracting boot image: %s" % file_info.bootimg) try: z.extract(file_info.bootimg, path=tempdir) except: ui.failed('Failed to extract file: %s' % file_info.bootimg) exit.exit(1) z.close() ui.clear() if file_info.has_boot_image: boot_image = os.path.join(tempdir, file_info.bootimg) new_boot_image = patch_boot_image(boot_image, file_info, partition_config) os.remove(boot_image) shutil.move(new_boot_image, boot_image) else: ui.info('No boot image to patch') set_task('PATCHING_FILES') shutil.copy(os.path.join(OS.patchdir, 'dualboot.sh'), tempdir) autopatcher.insert_partition_info(tempdir, 'dualboot.sh', partition_config) if file_info.patch: ui.details('Running patch functions ...') temp = file_info.patch if type(temp) == str or callable(temp): temp = [temp] for i in temp: if callable(i): i(tempdir, bootimg=file_info.bootimg, device_check=file_info.device_check, partition_config=partition_config) elif type(i) == list: for j in i: if not patch.apply_patch(j, tempdir): ui.failed(patch.error_msg) exit.exit(1) elif type(i) == str: if not patch.apply_patch(i, tempdir): ui.failed(patch.error_msg) exit.exit(1) set_task('COMPRESSING_ZIP_FILE') ui.details('Opening input and output zip files ...') # We can't avoid recompression, unfortunately # Only show progress for this stage on Android, since it's, by far, the # most time consuming part of the process new_zip_file = os.path.join(tempdir, 'complete.zip') z_input = zipfile.ZipFile(zip_file, 'r') z_output = zipfile.ZipFile(new_zip_file, 'w', zipfile.ZIP_DEFLATED) progress_current = 0 progress_total = len(z_input.infolist()) - len(files_to_patch) - 1 if file_info.has_boot_image: progress_total -= 1 for root, dirs, files in os.walk(tempdir): progress_total += len(files) ui.max_progress(progress_total) for i in z_input.infolist(): # Skip patched files if i.filename in files_to_patch: continue # Boot image too elif i.filename == file_info.bootimg: continue ui.details('Adding file to zip: %s' % i.filename) ui.progress() z_output.writestr(i.filename, z_input.read(i.filename)) ui.clear() z_input.close() for root, dirs, files in os.walk(tempdir): for f in files: if f == 'complete.zip': continue ui.details('Adding file to zip: %s' % f) ui.progress() arcdir = os.path.relpath(root, start=tempdir) z_output.write(os.path.join(root, f), arcname=os.path.join(arcdir, f)) ui.clear() z_output.close() return new_zip_file
def patch_boot_image(boot_image, file_info, partition_config): if new_ui: ui = new_ui else: ui = OS.ui set_task('PATCHING_RAMDISK') tempdir = tempfile.mkdtemp() exit.remove_on_exit(tempdir) if file_info.loki: ui.details('Unpacking loki boot image ...') else: ui.details('Unpacking boot image ...') bootimageinfo = bootimage.extract(boot_image, tempdir, file_info.loki) if not bootimageinfo: ui.failed(bootimage.error_msg) exit.exit(1) selinux = config.get_selinux() if selinux is not None: bootimageinfo.cmdline += ' androidboot.selinux=' + selinux target_ramdisk = os.path.join(tempdir, 'ramdisk.cpio') target_kernel = os.path.join(tempdir, 'kernel.img') shutil.move(bootimageinfo.kernel, target_kernel) extracted = os.path.join(tempdir, 'extracted') os.mkdir(extracted) # Decompress ramdisk ui.details('Extracting ramdisk using cpio ...') with gzip.open(bootimageinfo.ramdisk, 'rb') as f_in: if not cpio.extract(extracted, data=f_in.read()): ui.command_error(output=cpio.output, error=cpio.error) ui.failed('Failed to extract ramdisk using cpio') exit.exit(1) os.remove(bootimageinfo.ramdisk) # Patch ramdisk if not file_info.ramdisk: ui.info('No ramdisk patch specified') return None ui.details('Modifying ramdisk ...') ramdisk.process_def( os.path.join(OS.ramdiskdir, file_info.ramdisk), extracted, partition_config ) # Copy init.multiboot.mounting.sh shutil.copy( os.path.join(OS.ramdiskdir, 'init.multiboot.mounting.sh'), extracted ) autopatcher.insert_partition_info( extracted, 'init.multiboot.mounting.sh', partition_config, target_path_only=True ) # Copy busybox shutil.copy( os.path.join(OS.ramdiskdir, 'busybox-static'), os.path.join(extracted, 'sbin') ) # Copy new init if needed if file_info.need_new_init: shutil.copy( os.path.join(OS.ramdiskdir, 'init'), extracted ) # Create gzip compressed ramdisk set_task('COMPRESSING_RAMDISK') ramdisk_files = [] for root, dirs, files in os.walk(extracted): for d in dirs: if os.path.samefile(root, extracted): # cpio, for whatever reason, creates a directory called '.' ramdisk_files.append(fileio.unix_path(d)) ui.details('Adding directory to ramdisk: %s' % d) else: relative_dir = os.path.relpath(root, extracted) ramdisk_files.append(fileio.unix_path( os.path.join(relative_dir, d))) ui.details('Adding directory to ramdisk: %s' % os.path.join(relative_dir, d)) for f in files: if os.path.samefile(root, extracted): # cpio, for whatever reason, creates a directory called '.' ramdisk_files.append(fileio.unix_path(f)) ui.details('Adding file to ramdisk: %s' % f) else: relative_dir = os.path.relpath(root, extracted) ramdisk_files.append(fileio.unix_path( os.path.join(relative_dir, f))) ui.details('Adding file to ramdisk: %s' % os.path.join(relative_dir, f)) ui.details('Creating gzip compressed ramdisk with cpio ...') target_ramdisk = target_ramdisk + ".gz" with gzip.open(target_ramdisk, 'wb') as f_out: data = cpio.create(extracted, ramdisk_files) if data is not None: f_out.write(data) else: ui.command_error(output=cpio.output, error=cpio.error) ui.failed('Failed to create gzip compressed ramdisk') exit.exit(1) set_task('CREATING_BOOT_IMAGE') ui.details('Running mkbootimg ...') bootimageinfo.kernel = target_kernel bootimageinfo.ramdisk = target_ramdisk if not bootimage.create(bootimageinfo, os.path.join(tempdir, 'complete.img')): ui.failed('Failed to create boot image: ' + bootimage.error_msg) exit.exit(1) os.remove(target_kernel) os.remove(target_ramdisk) return os.path.join(tempdir, 'complete.img')
def patch_zip(self): if not OS.is_android(): OS.ui.info('--- Please wait. This may take a while ---') tempdir = tempfile.mkdtemp() self.tempdirs.append(tempdir) OS.ui.set_task(self.tasks['EXTRACTING_ZIP']) OS.ui.details('Loading zip file ...') z = zipfile.ZipFile(self.file_info.filename, 'r') try: z.extract(UPDATER_SCRIPT, path=tempdir) except: OS.ui.failed('Failed to extract updater-script') return None z.close() OS.ui.clear() OS.ui.set_task(self.tasks['PATCHING_FILES']) lines = fileio.all_lines(os.path.join(tempdir, UPDATER_SCRIPT)) i = 0 i += autopatcher.insert_line( i, EXTRACT % (PERM_TOOL, '/tmp/' + PERM_TOOL), lines) i += autopatcher.insert_line( i, EXTRACT % ('dualboot.sh', '/tmp/dualboot.sh'), lines) i += autopatcher.insert_line(i, EXTRACT % ('setfacl', '/tmp/setfacl'), lines) i += autopatcher.insert_line(i, EXTRACT % ('setfattr', '/tmp/setfattr'), lines) i += autopatcher.insert_line(i, EXTRACT % ('getfacl', '/tmp/getfacl'), lines) i += autopatcher.insert_line(i, EXTRACT % ('getfattr', '/tmp/getfattr'), lines) i += autopatcher.insert_line(i, MAKE_EXECUTABLE % ('/tmp/' + PERM_TOOL), lines) i += autopatcher.insert_line(i, MAKE_EXECUTABLE % '/tmp/dualboot.sh', lines) i += autopatcher.insert_line(i, MAKE_EXECUTABLE % '/tmp/setfacl', lines) i += autopatcher.insert_line(i, MAKE_EXECUTABLE % '/tmp/setfattr', lines) i += autopatcher.insert_line(i, MAKE_EXECUTABLE % '/tmp/getfacl', lines) i += autopatcher.insert_line(i, MAKE_EXECUTABLE % '/tmp/getfattr', lines) i += autopatcher.insert_line(i, MOUNT % '/system', lines) i += autopatcher.insert_line(i, PERMS_BACKUP % '/system', lines) i += autopatcher.insert_line(i, UNMOUNT % '/system', lines) i += autopatcher.insert_line(i, MOUNT % '/cache', lines) i += autopatcher.insert_line(i, PERMS_BACKUP % '/cache', lines) i += autopatcher.insert_line(i, UNMOUNT % '/cache', lines) def insert_format_system(index, lines, mount): i = 0 if mount: i += autopatcher.insert_line(index + i, MOUNT % '/system', lines) i += autopatcher.insert_line(index + i, FORMAT_SYSTEM, lines) if mount: i += autopatcher.insert_line(index + i, UNMOUNT % '/system', lines) return i def insert_format_cache(index, lines, mount): i = 0 if mount: i += autopatcher.insert_line(index + i, MOUNT % '/cache', lines) i += autopatcher.insert_line(index + i, FORMAT_CACHE, lines) if mount: i += autopatcher.insert_line(index + i, UNMOUNT % '/cache', lines) return i psystem = config.get_partition(self.file_info.device, 'system') pcache = config.get_partition(self.file_info.device, 'cache') replaced_format_system = False replaced_format_cache = False i = 0 while i < len(lines): if re.search(r"^\s*format\s*\(.*$", lines[i]): if 'system' in lines[i] or (psystem and psystem in lines[i]): replaced_format_system = True del lines[i] i += insert_format_system(i, lines, True) elif 'cache' in lines[i] or (pcache and pcache in lines[i]): replaced_format_cache = True del lines[i] i += insert_format_cache(i, lines, True) else: i += 1 elif re.search(r'delete_recursive\s*\([^\)]*"/system"', lines[i]): replaced_format_system = True del lines[i] i += insert_format_system(i, lines, False) elif re.search(r'delete_recursive\s*\([^\)]*"/cache"', lines[i]): replaced_format_cache = True del lines[i] i += insert_format_cache(i, lines, False) else: i += 1 if not replaced_format_system and not replaced_format_cache: OS.ui.failed('The patcher could not find any /system or /cache' ' formatting lines in the updater-script file.\n\n' 'If the file is a ROM, then something failed. If the' ' file is not a ROM (eg. kernel or mod), it doesn\'t' ' need to be patched.') return None i += autopatcher.insert_line(i, MOUNT % '/system', lines) i += autopatcher.insert_line(i, PERMS_RESTORE % '/system', lines) i += autopatcher.insert_line(i, UNMOUNT % '/system', lines) i += autopatcher.insert_line(i, MOUNT % '/cache', lines) i += autopatcher.insert_line(i, PERMS_RESTORE % '/cache', lines) i += autopatcher.insert_line(i, UNMOUNT % '/cache', lines) i += autopatcher.insert_line( i, 'run_program("/tmp/dualboot.sh", "set-multi-kernel");', lines) fileio.write_lines(os.path.join(tempdir, UPDATER_SCRIPT), lines) shutil.copy(os.path.join(OS.patchdir, 'dualboot.sh'), tempdir) autopatcher.insert_partition_info(os.path.join(tempdir, 'dualboot.sh'), self.file_info.partconfig) OS.ui.set_task(self.tasks['COMPRESSING_ZIP_FILE']) OS.ui.details('Opening input and output zip files ...') # We can't avoid recompression, unfortunately # Only show progress for this stage on Android, since it's, by far, the # most time consuming part of the process new_zip_file = tempfile.mkstemp() z_input = zipfile.ZipFile(self.file_info.filename, 'r') z_output = zipfile.ZipFile(new_zip_file[1], 'w', zipfile.ZIP_DEFLATED) # Five extra files progress_total = len(z_input.infolist()) + 6 OS.ui.max_progress(progress_total) for i in z_input.infolist(): if i.filename == UPDATER_SCRIPT: continue OS.ui.details('Adding file to zip: %s' % i.filename) OS.ui.progress() in_info = z_input.getinfo(i.filename) z_output.writestr(in_info, z_input.read(in_info)) OS.ui.clear() z_input.close() OS.ui.details('Adding file to zip: ' + UPDATER_SCRIPT) OS.ui.progress() z_output.write(os.path.join(tempdir, UPDATER_SCRIPT), arcname=UPDATER_SCRIPT) OS.ui.details('Adding file to zip: ' + 'dualboot.sh') OS.ui.progress() z_output.write(os.path.join(tempdir, 'dualboot.sh'), arcname='dualboot.sh') for f in [PERM_TOOL, 'setfacl', 'setfattr', 'getfacl', 'getfattr']: OS.ui.details('Adding file to zip: ' + f) OS.ui.progress() z_output.write(os.path.join(OS.patchdir, f), arcname=f) OS.ui.clear() z_output.close() os.close(new_zip_file[0]) return new_zip_file[1]
def patch_boot_image(self): path = self.file_info.filename OS.ui.set_task(self.tasks['PATCHING_RAMDISK']) tempdir = tempfile.mkdtemp() self.tempdirs.append(tempdir) OS.ui.details('Unpacking boot image ...') try: bootimageinfo = bootimage.extract(path, tempdir) except bootimage.ExtractError as e: OS.ui.failed(str(e)) return None target_ramdisk = os.path.join(tempdir, 'ramdisk.cpio') # Minicpio OS.ui.details('Loading ramdisk with minicpio ...') cf = minicpio.CpioFile() with gzip.open(bootimageinfo.ramdisk, 'rb') as f: cf.load(f.read()) os.remove(bootimageinfo.ramdisk) OS.ui.details('Modifying ramdisk ...') # Update partconfig = self.find_partconfig(cf) if partconfig and cf.get_file('init.multiboot.mounting.sh'): temp = tempfile.mkstemp() shutil.copyfile( os.path.join(OS.ramdiskdir, 'init.multiboot.mounting.sh'), temp[1]) autopatcher.insert_partition_info(temp[1], partconfig, target_path_only=True) cf.add_file(temp[1], name='init.multiboot.mounting.sh', perms=0o750) os.close(temp[0]) os.remove(temp[1]) # Add syncdaemon to init.rc if it doesn't already exist if not cf.get_file('sbin/syncdaemon'): common.add_syncdaemon(cf) else: # Make sure 'oneshot' (added by previous versions) is removed initrc = cf.get_file('init.rc') lines = fileio.bytes_to_lines(initrc.content) buf = bytes() in_syncdaemon = False for line in lines: if line.startswith('service'): in_syncdaemon = '/sbin/syncdaemon' in line buf += fileio.encode(line) elif in_syncdaemon and 'oneshot' in line: continue else: buf += fileio.encode(line) initrc.content = buf # Copy syncdaemon cf.add_file(os.path.join(OS.ramdiskdir, 'syncdaemon'), name='sbin/syncdaemon', perms=0o750) # Create gzip compressed ramdisk OS.ui.details('Creating gzip compressed ramdisk with minicpio ...') target_ramdisk = target_ramdisk + ".gz" with gzip.open(target_ramdisk, 'wb') as f_out: data = cf.create() if data is not None: f_out.write(data) else: OS.ui.failed('Failed to create gzip compressed ramdisk') return None OS.ui.details('Running mkbootimg ...') bootimageinfo.ramdisk = target_ramdisk new_boot_image = tempfile.mkstemp() try: bootimage.create(bootimageinfo, new_boot_image[1]) except bootimage.CreateError as e: OS.ui.failed('Failed to create boot image: ' + str(e)) os.close(new_boot_image[0]) os.remove(new_boot_image[1]) return None os.remove(target_ramdisk) os.close(new_boot_image[0]) return new_boot_image[1]
def patch_boot_image(self, path=None): if not path: path = self.file_info.filename OS.ui.set_task(self.tasks['PATCHING_RAMDISK']) tempdir = tempfile.mkdtemp() self.tempdirs.append(tempdir) OS.ui.details('Unpacking boot image ...') try: bootimageinfo = bootimage.extract(path, tempdir) except bootimage.ExtractError as e: OS.ui.failed(str(e)) return None selinux = config.get_selinux(self.file_info.device) if selinux is not None: bootimageinfo.cmdline += ' androidboot.selinux=' + selinux target_ramdisk = os.path.join(tempdir, 'ramdisk.cpio') target_kernel = os.path.join(tempdir, 'kernel.img') shutil.move(bootimageinfo.kernel, target_kernel) # Minicpio OS.ui.details('Loading ramdisk with minicpio ...') cf = minicpio.CpioFile() with gzip.open(bootimageinfo.ramdisk, 'rb') as f: cf.load(f.read()) os.remove(bootimageinfo.ramdisk) # Patch ramdisk if not self.file_info.patchinfo.ramdisk: OS.ui.info('No ramdisk patch specified') return None OS.ui.details('Modifying ramdisk ...') try: ramdisk.process_def( os.path.join(OS.ramdiskdir, self.file_info.patchinfo.ramdisk), cf, self.file_info.partconfig) except ramdisk.RamdiskPatchError as e: OS.ui.failed('Failed to patch ramdisk: ' + str(e)) return None # Copy init.multiboot.mounting.sh shutil.copy(os.path.join(OS.ramdiskdir, 'init.multiboot.mounting.sh'), tempdir) autopatcher.insert_partition_info(os.path.join( tempdir, 'init.multiboot.mounting.sh'), self.file_info.partconfig, target_path_only=True) cf.add_file(os.path.join(tempdir, 'init.multiboot.mounting.sh'), name='init.multiboot.mounting.sh', perms=0o750) os.remove(os.path.join(tempdir, 'init.multiboot.mounting.sh')) # Copy busybox cf.add_file(os.path.join(OS.ramdiskdir, 'busybox-static'), name='sbin/busybox-static', perms=0o750) # Copy syncdaemon cf.add_file(os.path.join(OS.ramdiskdir, 'syncdaemon'), name='sbin/syncdaemon', perms=0o750) # Copy patched init if needed if self.file_info.patchinfo.patched_init is not None: cf.add_file(os.path.join(OS.ramdiskdir, 'init', self.file_info.patchinfo.patched_init), name='init', perms=0o755) # Create gzip compressed ramdisk OS.ui.details('Creating gzip compressed ramdisk with minicpio ...') target_ramdisk = target_ramdisk + ".gz" with gzip.open(target_ramdisk, 'wb') as f_out: data = cf.create() if data is not None: f_out.write(data) else: OS.ui.failed('Failed to create gzip compressed ramdisk') return None OS.ui.details('Running mkbootimg ...') bootimageinfo.kernel = target_kernel bootimageinfo.ramdisk = target_ramdisk new_boot_image = tempfile.mkstemp() try: bootimage.create(bootimageinfo, new_boot_image[1]) except bootimage.CreateError as e: OS.ui.failed('Failed to create boot image: ' + str(e)) os.close(new_boot_image[0]) os.remove(new_boot_image[1]) return None os.remove(target_kernel) os.remove(target_ramdisk) os.close(new_boot_image[0]) return new_boot_image[1]
def patch_zip(self): if not OS.is_android(): OS.ui.info('--- Please wait. This may take a while ---') ap_instances = [] files_to_patch = [] if self.file_info.patchinfo.autopatchers: for ap in self.file_info.patchinfo.autopatchers: if type(ap) == str: # Special case for patch files ap_instances.append(PatchFilePatcher(patchfile=ap)) # elif issubclass(ap, BasePatcher): elif True: ap_instances.append(ap()) else: raise ValueError('Invalid autopatcher type') for ap in ap_instances: for f in ap.files_list: if f not in files_to_patch: files_to_patch.append(f) tempdir = tempfile.mkdtemp() self.tempdirs.append(tempdir) OS.ui.set_task(self.tasks['EXTRACTING_ZIP']) OS.ui.details('Loading zip file ...') z = zipfile.ZipFile(self.file_info.filename, 'r') for f in files_to_patch: OS.ui.details("Extracting file to be patched: %s" % f) try: z.extract(f, path=tempdir) except: OS.ui.failed('Failed to extract file: %s' % f) return None bootimages = list() if self.file_info.patchinfo.has_boot_image: for i in self.file_info.patchinfo.bootimg: if callable(i): output = i(self.file_info.filename) if not output: continue if type(output) == list: bootimages.extend(output) elif type(output) == str: bootimages.append(output) else: bootimages.append(i) for bootimage in bootimages: OS.ui.details("Extracting boot image: %s" % bootimage) try: z.extract(bootimage, path=tempdir) except: OS.ui.failed('Failed to extract file: %s' % bootimage) return None z.close() OS.ui.clear() if self.file_info.patchinfo.has_boot_image: for bootimage in bootimages: bootimageold = os.path.join(tempdir, bootimage) bootimagenew = self.patch_boot_image(path=bootimageold) if not bootimagenew: return None os.remove(bootimageold) shutil.move(bootimagenew, bootimageold) else: OS.ui.info('No boot images to patch') OS.ui.set_task(self.tasks['PATCHING_FILES']) shutil.copy(os.path.join(OS.patchdir, 'dualboot.sh'), tempdir) autopatcher.insert_partition_info(os.path.join(tempdir, 'dualboot.sh'), self.file_info.partconfig) if ap_instances: OS.ui.details('Running autopatchers ...') for ap in ap_instances: ret = ap.patch(tempdir, self.file_info, bootimages=bootimages) # Auto patchers do not have to return anything if ret is False: OS.ui.failed(ap.error_msg) return None OS.ui.set_task(self.tasks['COMPRESSING_ZIP_FILE']) OS.ui.details('Opening input and output zip files ...') # We can't avoid recompression, unfortunately # Only show progress for this stage on Android, since it's, by far, the # most time consuming part of the process new_zip_file = tempfile.mkstemp() z_input = zipfile.ZipFile(self.file_info.filename, 'r') z_output = zipfile.ZipFile(new_zip_file[1], 'w', zipfile.ZIP_DEFLATED) progress_total = len(z_input.infolist()) - len(files_to_patch) if self.file_info.patchinfo.has_boot_image: progress_total -= len(bootimages) for root, dirs, files in os.walk(tempdir): progress_total += len(files) OS.ui.max_progress(progress_total) for i in z_input.infolist(): # Skip patched files if i.filename in files_to_patch: continue # Boot image too elif self.file_info.patchinfo.has_boot_image \ and i.filename in bootimages: continue OS.ui.details('Adding file to zip: %s' % i.filename) OS.ui.progress() in_info = z_input.getinfo(i.filename) z_output.writestr(in_info, z_input.read(in_info)) OS.ui.clear() z_input.close() for root, dirs, files in os.walk(tempdir): for f in files: OS.ui.details('Adding file to zip: %s' % f) OS.ui.progress() arcdir = os.path.relpath(root, start=tempdir) z_output.write(os.path.join(root, f), arcname=os.path.join(arcdir, f)) OS.ui.clear() z_output.close() os.close(new_zip_file[0]) return new_zip_file[1]
def patch_boot_image(file_info, path=None): # If file_info represents a boot image if not path: path = file_info._filename tempdirs = list() if new_ui: ui = new_ui else: ui = OS.ui set_task('PATCHING_RAMDISK') tempdir = tempfile.mkdtemp() tempdirs.append(tempdir) if file_info.loki: ui.details('Unpacking loki boot image ...') else: ui.details('Unpacking boot image ...') bootimageinfo = bootimage.extract(path, tempdir, file_info.loki) if not bootimageinfo: ui.failed(bootimage.error_msg) cleanup(tempdirs) return None selinux = config.get_selinux() if selinux is not None: bootimageinfo.cmdline += ' androidboot.selinux=' + selinux target_ramdisk = os.path.join(tempdir, 'ramdisk.cpio') target_kernel = os.path.join(tempdir, 'kernel.img') shutil.move(bootimageinfo.kernel, target_kernel) # Minicpio ui.details('Loading ramdisk with minicpio ...') cf = minicpio.CpioFile() with gzip.open(bootimageinfo.ramdisk, 'rb') as f: cf.load(f.read()) os.remove(bootimageinfo.ramdisk) # Patch ramdisk if not file_info.ramdisk: ui.info('No ramdisk patch specified') return None ui.details('Modifying ramdisk ...') ramdisk.process_def( os.path.join(OS.ramdiskdir, file_info.ramdisk), cf, file_info._partconfig ) # Copy init.multiboot.mounting.sh shutil.copy( os.path.join(OS.ramdiskdir, 'init.multiboot.mounting.sh'), tempdir ) autopatcher.insert_partition_info( tempdir, 'init.multiboot.mounting.sh', file_info._partconfig, target_path_only=True ) cf.add_file( os.path.join(tempdir, 'init.multiboot.mounting.sh'), name='init.multiboot.mounting.sh', perms=0o750 ) os.remove(os.path.join(tempdir, 'init.multiboot.mounting.sh')) # Copy busybox cf.add_file( os.path.join(OS.ramdiskdir, 'busybox-static'), name='sbin/busybox-static', perms=0o750 ) # Copy patched init if needed if file_info.patched_init is not None: cf.add_file( os.path.join(OS.ramdiskdir, 'init', file_info.patched_init), name='init', perms=0o755 ) # Create gzip compressed ramdisk set_task('COMPRESSING_RAMDISK') ui.details('Creating gzip compressed ramdisk with minicpio ...') target_ramdisk = target_ramdisk + ".gz" with gzip.open(target_ramdisk, 'wb') as f_out: data = cf.create() if data is not None: f_out.write(data) else: ui.failed('Failed to create gzip compressed ramdisk') cleanup(tempdirs) return None set_task('CREATING_BOOT_IMAGE') ui.details('Running mkbootimg ...') bootimageinfo.kernel = target_kernel bootimageinfo.ramdisk = target_ramdisk new_boot_image = tempfile.mkstemp() if not bootimage.create(bootimageinfo, new_boot_image[1]): ui.failed('Failed to create boot image: ' + bootimage.error_msg) cleanup(tempdirs) os.close(new_boot_image[0]) os.remove(new_boot_image[1]) return None os.remove(target_kernel) os.remove(target_ramdisk) cleanup(tempdirs) os.close(new_boot_image[0]) return new_boot_image[1]
def patch_boot_image(self): path = self.file_info.filename OS.ui.set_task(self.tasks['PATCHING_RAMDISK']) tempdir = tempfile.mkdtemp() self.tempdirs.append(tempdir) OS.ui.details('Unpacking boot image ...') try: bootimageinfo = bootimage.extract(path, tempdir) except bootimage.ExtractError as e: OS.ui.failed(str(e)) return None target_ramdisk = os.path.join(tempdir, 'ramdisk.cpio') # Minicpio OS.ui.details('Loading ramdisk with minicpio ...') cf = minicpio.CpioFile() with gzip.open(bootimageinfo.ramdisk, 'rb') as f: cf.load(f.read()) os.remove(bootimageinfo.ramdisk) OS.ui.details('Modifying ramdisk ...') # Update partconfig = self.find_partconfig(cf) if partconfig and cf.get_file('init.multiboot.mounting.sh'): temp = tempfile.mkstemp() shutil.copyfile( os.path.join(OS.ramdiskdir, 'init.multiboot.mounting.sh'), temp[1] ) autopatcher.insert_partition_info( temp[1], partconfig, target_path_only=True ) cf.add_file(temp[1], name='init.multiboot.mounting.sh', perms=0o750) os.close(temp[0]) os.remove(temp[1]) # Add syncdaemon to init.rc if it doesn't already exist if not cf.get_file('sbin/syncdaemon'): common.add_syncdaemon(cf) else: # Make sure 'oneshot' (added by previous versions) is removed initrc = cf.get_file('init.rc') lines = fileio.bytes_to_lines(initrc.content) buf = bytes() in_syncdaemon = False for line in lines: if line.startswith('service'): in_syncdaemon = '/sbin/syncdaemon' in line buf += fileio.encode(line) elif in_syncdaemon and 'oneshot' in line: continue else: buf += fileio.encode(line) initrc.content = buf # Copy syncdaemon cf.add_file( os.path.join(OS.ramdiskdir, 'syncdaemon'), name='sbin/syncdaemon', perms=0o750 ) # Create gzip compressed ramdisk OS.ui.details('Creating gzip compressed ramdisk with minicpio ...') target_ramdisk = target_ramdisk + ".gz" with gzip.open(target_ramdisk, 'wb') as f_out: data = cf.create() if data is not None: f_out.write(data) else: OS.ui.failed('Failed to create gzip compressed ramdisk') return None OS.ui.details('Running mkbootimg ...') bootimageinfo.ramdisk = target_ramdisk new_boot_image = tempfile.mkstemp() try: bootimage.create(bootimageinfo, new_boot_image[1]) except bootimage.CreateError as e: OS.ui.failed('Failed to create boot image: ' + str(e)) os.close(new_boot_image[0]) os.remove(new_boot_image[1]) return None os.remove(target_ramdisk) os.close(new_boot_image[0]) return new_boot_image[1]
def patch_zip(self): if not OS.is_android(): OS.ui.info('--- Please wait. This may take a while ---') tempdir = tempfile.mkdtemp() self.tempdirs.append(tempdir) OS.ui.set_task(self.tasks['EXTRACTING_ZIP']) OS.ui.details('Loading zip file ...') z = zipfile.ZipFile(self.file_info.filename, 'r') try: z.extract(UPDATER_SCRIPT, path=tempdir) except: OS.ui.failed('Failed to extract updater-script') return None z.close() OS.ui.clear() OS.ui.set_task(self.tasks['PATCHING_FILES']) lines = fileio.all_lines(os.path.join(tempdir, UPDATER_SCRIPT)) i = 0 i += autopatcher.insert_line( i, EXTRACT % (PERM_TOOL, '/tmp/' + PERM_TOOL), lines) i += autopatcher.insert_line( i, EXTRACT % ('dualboot.sh', '/tmp/dualboot.sh'), lines) i += autopatcher.insert_line( i, EXTRACT % ('setfacl', '/tmp/setfacl'), lines) i += autopatcher.insert_line( i, EXTRACT % ('setfattr', '/tmp/setfattr'), lines) i += autopatcher.insert_line( i, EXTRACT % ('getfacl', '/tmp/getfacl'), lines) i += autopatcher.insert_line( i, EXTRACT % ('getfattr', '/tmp/getfattr'), lines) i += autopatcher.insert_line( i, MAKE_EXECUTABLE % ('/tmp/' + PERM_TOOL), lines) i += autopatcher.insert_line( i, MAKE_EXECUTABLE % '/tmp/dualboot.sh', lines) i += autopatcher.insert_line( i, MAKE_EXECUTABLE % '/tmp/setfacl', lines) i += autopatcher.insert_line( i, MAKE_EXECUTABLE % '/tmp/setfattr', lines) i += autopatcher.insert_line( i, MAKE_EXECUTABLE % '/tmp/getfacl', lines) i += autopatcher.insert_line( i, MAKE_EXECUTABLE % '/tmp/getfattr', lines) i += autopatcher.insert_line( i, MOUNT % '/system', lines) i += autopatcher.insert_line( i, PERMS_BACKUP % '/system', lines) i += autopatcher.insert_line( i, UNMOUNT % '/system', lines) i += autopatcher.insert_line( i, MOUNT % '/cache', lines) i += autopatcher.insert_line( i, PERMS_BACKUP % '/cache', lines) i += autopatcher.insert_line( i, UNMOUNT % '/cache', lines) def insert_format_system(index, lines, mount): i = 0 if mount: i += autopatcher.insert_line( index + i, MOUNT % '/system', lines) i += autopatcher.insert_line(index + i, FORMAT_SYSTEM, lines) if mount: i += autopatcher.insert_line( index + i, UNMOUNT % '/system', lines) return i def insert_format_cache(index, lines, mount): i = 0 if mount: i += autopatcher.insert_line( index + i, MOUNT % '/cache', lines) i += autopatcher.insert_line(index + i, FORMAT_CACHE, lines) if mount: i += autopatcher.insert_line( index + i, UNMOUNT % '/cache', lines) return i psystem = config.get_partition(self.file_info.device, 'system') pcache = config.get_partition(self.file_info.device, 'cache') replaced_format_system = False replaced_format_cache = False i = 0 while i < len(lines): if re.search(r"^\s*format\s*\(.*$", lines[i]): if 'system' in lines[i] or (psystem and psystem in lines[i]): replaced_format_system = True del lines[i] i += insert_format_system(i, lines, True) elif 'cache' in lines[i] or (pcache and pcache in lines[i]): replaced_format_cache = True del lines[i] i += insert_format_cache(i, lines, True) else: i += 1 elif re.search(r'delete_recursive\s*\([^\)]*"/system"', lines[i]): replaced_format_system = True del lines[i] i += insert_format_system(i, lines, False) elif re.search(r'delete_recursive\s*\([^\)]*"/cache"', lines[i]): replaced_format_cache = True del lines[i] i += insert_format_cache(i, lines, False) else: i += 1 if not replaced_format_system and not replaced_format_cache: OS.ui.failed('The patcher could not find any /system or /cache' ' formatting lines in the updater-script file.\n\n' 'If the file is a ROM, then something failed. If the' ' file is not a ROM (eg. kernel or mod), it doesn\'t' ' need to be patched.') return None i += autopatcher.insert_line( i, MOUNT % '/system', lines) i += autopatcher.insert_line( i, PERMS_RESTORE % '/system', lines) i += autopatcher.insert_line( i, UNMOUNT % '/system', lines) i += autopatcher.insert_line( i, MOUNT % '/cache', lines) i += autopatcher.insert_line( i, PERMS_RESTORE % '/cache', lines) i += autopatcher.insert_line( i, UNMOUNT % '/cache', lines) i += autopatcher.insert_line( i, 'run_program("/tmp/dualboot.sh", "set-multi-kernel");', lines) fileio.write_lines(os.path.join(tempdir, UPDATER_SCRIPT), lines) shutil.copy(os.path.join(OS.patchdir, 'dualboot.sh'), tempdir) autopatcher.insert_partition_info( os.path.join(tempdir, 'dualboot.sh'), self.file_info.partconfig) OS.ui.set_task(self.tasks['COMPRESSING_ZIP_FILE']) OS.ui.details('Opening input and output zip files ...') # We can't avoid recompression, unfortunately # Only show progress for this stage on Android, since it's, by far, the # most time consuming part of the process new_zip_file = tempfile.mkstemp() z_input = zipfile.ZipFile(self.file_info.filename, 'r') z_output = zipfile.ZipFile(new_zip_file[1], 'w', zipfile.ZIP_DEFLATED) # Five extra files progress_total = len(z_input.infolist()) + 6 OS.ui.max_progress(progress_total) for i in z_input.infolist(): if i.filename == UPDATER_SCRIPT: continue OS.ui.details('Adding file to zip: %s' % i.filename) OS.ui.progress() in_info = z_input.getinfo(i.filename) z_output.writestr(in_info, z_input.read(in_info)) OS.ui.clear() z_input.close() OS.ui.details('Adding file to zip: ' + UPDATER_SCRIPT) OS.ui.progress() z_output.write(os.path.join(tempdir, UPDATER_SCRIPT), arcname=UPDATER_SCRIPT) OS.ui.details('Adding file to zip: ' + 'dualboot.sh') OS.ui.progress() z_output.write(os.path.join(tempdir, 'dualboot.sh'), arcname='dualboot.sh') for f in [PERM_TOOL, 'setfacl', 'setfattr', 'getfacl', 'getfattr']: OS.ui.details('Adding file to zip: ' + f) OS.ui.progress() z_output.write(os.path.join(OS.patchdir, f), arcname=f) OS.ui.clear() z_output.close() os.close(new_zip_file[0]) return new_zip_file[1]
def patch_zip(self): if not OS.is_android(): OS.ui.info('--- Please wait. This may take a while ---') ap_instances = [] files_to_patch = [] if self.file_info.patchinfo.autopatchers: for ap in self.file_info.patchinfo.autopatchers: if type(ap) == str: # Special case for patch files ap_instances.append(PatchFilePatcher(patchfile=ap)) # elif issubclass(ap, BasePatcher): elif True: ap_instances.append(ap()) else: raise ValueError('Invalid autopatcher type') for ap in ap_instances: for f in ap.files_list: if f not in files_to_patch: files_to_patch.append(f) tempdir = tempfile.mkdtemp() self.tempdirs.append(tempdir) OS.ui.set_task(self.tasks['EXTRACTING_ZIP']) OS.ui.details('Loading zip file ...') z = zipfile.ZipFile(self.file_info.filename, 'r') for f in files_to_patch: OS.ui.details("Extracting file to be patched: %s" % f) try: z.extract(f, path=tempdir) except: OS.ui.failed('Failed to extract file: %s' % f) return None bootimages = list() if self.file_info.patchinfo.has_boot_image: for i in self.file_info.patchinfo.bootimg: if callable(i): output = i(self.file_info.filename) if not output: continue if type(output) == list: bootimages.extend(output) elif type(output) == str: bootimages.append(output) else: bootimages.append(i) for bootimage in bootimages: OS.ui.details("Extracting boot image: %s" % bootimage) try: z.extract(bootimage, path=tempdir) except: OS.ui.failed('Failed to extract file: %s' % bootimage) return None z.close() OS.ui.clear() if self.file_info.patchinfo.has_boot_image: for bootimage in bootimages: bootimageold = os.path.join(tempdir, bootimage) bootimagenew = self.patch_boot_image(path=bootimageold) if not bootimagenew: return None os.remove(bootimageold) shutil.move(bootimagenew, bootimageold) else: OS.ui.info('No boot images to patch') OS.ui.set_task(self.tasks['PATCHING_FILES']) shutil.copy(os.path.join(OS.patchdir, 'dualboot.sh'), tempdir) autopatcher.insert_partition_info( os.path.join(tempdir, 'dualboot.sh'), self.file_info.partconfig) if ap_instances: OS.ui.details('Running autopatchers ...') for ap in ap_instances: ret = ap.patch(tempdir, self.file_info, bootimages=bootimages) # Auto patchers do not have to return anything if ret is False: OS.ui.failed(ap.error_msg) return None OS.ui.set_task(self.tasks['COMPRESSING_ZIP_FILE']) OS.ui.details('Opening input and output zip files ...') # We can't avoid recompression, unfortunately # Only show progress for this stage on Android, since it's, by far, the # most time consuming part of the process new_zip_file = tempfile.mkstemp() z_input = zipfile.ZipFile(self.file_info.filename, 'r') z_output = zipfile.ZipFile(new_zip_file[1], 'w', zipfile.ZIP_DEFLATED) progress_total = len(z_input.infolist()) - len(files_to_patch) if self.file_info.patchinfo.has_boot_image: progress_total -= len(bootimages) for root, dirs, files in os.walk(tempdir): progress_total += len(files) OS.ui.max_progress(progress_total) for i in z_input.infolist(): # Skip patched files if i.filename in files_to_patch: continue # Boot image too elif self.file_info.patchinfo.has_boot_image \ and i.filename in bootimages: continue OS.ui.details('Adding file to zip: %s' % i.filename) OS.ui.progress() in_info = z_input.getinfo(i.filename) z_output.writestr(in_info, z_input.read(in_info)) OS.ui.clear() z_input.close() for root, dirs, files in os.walk(tempdir): for f in files: OS.ui.details('Adding file to zip: %s' % f) OS.ui.progress() arcdir = os.path.relpath(root, start=tempdir) z_output.write(os.path.join(root, f), arcname=os.path.join(arcdir, f)) OS.ui.clear() z_output.close() os.close(new_zip_file[0]) return new_zip_file[1]
def patch_boot_image(self, path=None): if not path: path = self.file_info.filename OS.ui.set_task(self.tasks['PATCHING_RAMDISK']) tempdir = tempfile.mkdtemp() self.tempdirs.append(tempdir) OS.ui.details('Unpacking boot image ...') try: bootimageinfo = bootimage.extract(path, tempdir) except bootimage.ExtractError as e: OS.ui.failed(str(e)) return None selinux = config.get_selinux(self.file_info.device) if selinux is not None: bootimageinfo.cmdline += ' androidboot.selinux=' + selinux target_ramdisk = os.path.join(tempdir, 'ramdisk.cpio') target_kernel = os.path.join(tempdir, 'kernel.img') shutil.move(bootimageinfo.kernel, target_kernel) # Minicpio OS.ui.details('Loading ramdisk with minicpio ...') cf = minicpio.CpioFile() with gzip.open(bootimageinfo.ramdisk, 'rb') as f: cf.load(f.read()) os.remove(bootimageinfo.ramdisk) # Patch ramdisk if not self.file_info.patchinfo.ramdisk: OS.ui.info('No ramdisk patch specified') return None OS.ui.details('Modifying ramdisk ...') try: ramdisk.process_def( os.path.join(OS.ramdiskdir, self.file_info.patchinfo.ramdisk), cf, self.file_info.partconfig ) except ramdisk.RamdiskPatchError as e: OS.ui.failed('Failed to patch ramdisk: ' + str(e)) return None # Copy init.multiboot.mounting.sh shutil.copy( os.path.join(OS.ramdiskdir, 'init.multiboot.mounting.sh'), tempdir ) autopatcher.insert_partition_info( os.path.join(tempdir, 'init.multiboot.mounting.sh'), self.file_info.partconfig, target_path_only=True ) cf.add_file( os.path.join(tempdir, 'init.multiboot.mounting.sh'), name='init.multiboot.mounting.sh', perms=0o750 ) os.remove(os.path.join(tempdir, 'init.multiboot.mounting.sh')) # Copy busybox cf.add_file( os.path.join(OS.ramdiskdir, 'busybox-static'), name='sbin/busybox-static', perms=0o750 ) # Copy syncdaemon cf.add_file( os.path.join(OS.ramdiskdir, 'syncdaemon'), name='sbin/syncdaemon', perms=0o750 ) # Copy patched init if needed if self.file_info.patchinfo.patched_init is not None: cf.add_file( os.path.join(OS.ramdiskdir, 'init', self.file_info.patchinfo.patched_init), name='init', perms=0o755 ) # Create gzip compressed ramdisk OS.ui.details('Creating gzip compressed ramdisk with minicpio ...') target_ramdisk = target_ramdisk + ".gz" with gzip.open(target_ramdisk, 'wb') as f_out: data = cf.create() if data is not None: f_out.write(data) else: OS.ui.failed('Failed to create gzip compressed ramdisk') return None OS.ui.details('Running mkbootimg ...') bootimageinfo.kernel = target_kernel bootimageinfo.ramdisk = target_ramdisk new_boot_image = tempfile.mkstemp() try: bootimage.create(bootimageinfo, new_boot_image[1]) except bootimage.CreateError as e: OS.ui.failed('Failed to create boot image: ' + str(e)) os.close(new_boot_image[0]) os.remove(new_boot_image[1]) return None os.remove(target_kernel) os.remove(target_ramdisk) os.close(new_boot_image[0]) return new_boot_image[1]