def setUp(self) -> None: self.kernels = [ Kernel('1.old'), Kernel('2.new'), Kernel('3.stray'), Kernel('4.stray-files'), ] self.td = tempfile.TemporaryDirectory() td = Path(self.td.name) write_bzImage(td / 'kernel.old', b'old built on test') write_bzImage(td / 'kernel.new', b'new built on test') with open(td / 'config-stray', 'w'): pass with open(td / 'initrd-stray.img', 'w'): pass os.mkdir(td / 'build') build = GenericFile(td / 'build', KernelFileType.BUILD) self.kernels[0].all_files = [ KernelImage(td / 'kernel.old'), build, ] self.kernels[1].all_files = [ KernelImage(td / 'kernel.new'), build, ] self.kernels[2].all_files = [ build, ] self.kernels[3].all_files = [ GenericFile(td / 'config-stray', KernelFileType.CONFIG), GenericFile(td / 'initrd-stray.img', KernelFileType.INITRAMFS), ]
def test_exclude_build(self) -> None: with self.create_layout() as td: path = Path(td) boot = path / 'boot' modules = path / 'lib/modules' self.assertEqual( sorted( kernel_paths( StdLayout(root=path).find_kernels( exclusions=[KFT.BUILD]))), [('1.2.2', [ GenericFile(boot / 'System.map-1.2.2', KFT.SYSTEM_MAP), KernelImage(boot / 'vmlinuz-1.2.2'), ModuleDirectory(modules / '1.2.2'), ], '1.2.2'), ('1.2.3', [ GenericFile(boot / 'System.map-1.2.3', KFT.SYSTEM_MAP), GenericFile(boot / 'config-1.2.3', KFT.CONFIG), GenericFile(boot / 'initrd-1.2.3.img', KFT.INITRAMFS), KernelImage(boot / 'vmlinuz-1.2.3'), ModuleDirectory(modules / '1.2.3'), ], '1.2.3'), ('1.2.3.old', [ GenericFile(boot / 'System.map-1.2.3.old', KFT.SYSTEM_MAP), GenericFile(boot / 'config-1.2.3.old', KFT.CONFIG), GenericFile(boot / 'initrd-1.2.3.img.old', KFT.INITRAMFS), KernelImage(boot / 'vmlinuz-1.2.3.old'), ModuleDirectory(modules / '1.2.3'), ], '1.2.3'), ('1.2.4', [ GenericFile(boot / 'config-1.2.4', KFT.CONFIG), ModuleDirectory(modules / '1.2.4'), ], None)])
def test_missing_decompressor(self) -> None: if util.find_spec('lzo'): self.skipTest('the missing decompressor is installed') path = Path(self.td.name) / 'vmlinuz' with open(path, 'wb') as f: f.write(b'\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a') f.write(0x210 * b'\0') with self.assertRaises(MissingDecompressorError): KernelImage(path).read_internal_version()
def test_short(self) -> None: path = Path(self.td.name) / 'vmlinuz' with open(path, 'wb') as f: f.write(0x202 * b'\0') f.write(b'HdrS') f.write(8 * b'\0') f.write(b'\x10\x00') with self.assertRaises(UnrecognizedKernelError): KernelImage(path).read_internal_version()
def test_missing_decompressor(self, import_module: MagicMock) -> None: path = Path(self.td.name) / 'vmlinuz' with open(path, 'wb') as f: f.write(b'\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a') f.write(0x210 * b'\0') import_module.side_effect = ModuleNotFoundError( "No module named 'lzo'") with self.assertRaises(MissingDecompressorError): KernelImage(path).read_internal_version()
def test_exclude_misc(self) -> None: with self.create_layout() as td: path = Path(td) boot = path / f'boot/{self.machine_id}' modules = path / 'lib/modules' self.assertEqual( sorted( kernel_paths( BlSpecLayout(root=path).find_kernels( exclusions=[KFT.MISC]))), [ ('1.2.1', [ EmptyDirectory(boot / '1.2.1'), GenericFile(boot / '1.2.1/initrd', KFT.INITRAMFS), KernelImage(boot / '1.2.1/linux'), ModuleDirectory(modules / '1.2.1'), ], '1.2.1'), ('1.2.2', [ EmptyDirectory(boot / '1.2.2'), GenericFile(boot / '1.2.2/initrd', KFT.INITRAMFS), KernelImage(boot / '1.2.2/linux'), ModuleDirectory(modules / '1.2.2'), GenericFile(modules / '1.2.2/../../../usr/src/linux', KFT.BUILD), ], '1.2.2'), ('1.2.3', [ EmptyDirectory(boot / '1.2.3'), GenericFile(boot / '1.2.3/initrd', KFT.INITRAMFS), KernelImage(boot / '1.2.3/linux'), ModuleDirectory(modules / '1.2.3'), GenericFile(modules / '1.2.3/../../../usr/src/linux', KFT.BUILD), ], '1.2.3'), ('1.2.4', [ EmptyDirectory(boot / '1.2.4'), GenericFile(boot / '1.2.4/initrd', KFT.INITRAMFS), ModuleDirectory(modules / '1.2.4'), ], None), ])
def find_kernels( self, exclusions: typing.Container[KernelFileType] = [], ) -> typing.List[Kernel]: # this would wreak havok all around the place assert KernelFileType.KERNEL not in exclusions # collect all module directories first module_dict = self.get_module_dict(exclusions=exclusions, module_directory=self.root / 'lib/modules') # collect from /boot kernels: typing.Dict[str, Kernel] = {} for ver in os.listdir(self.bootdir): if ver.startswith('.'): continue dir_path = self.bootdir / ver if dir_path.is_symlink() or not dir_path.is_dir(): continue k = Kernel(ver) for fn in os.listdir(dir_path): if fn.startswith('.'): continue path = dir_path / fn ftype = self.name_map.get(fn, KernelFileType.MISC) fobj = GenericFile(path, ftype) if ftype == KernelFileType.KERNEL: try: kobj = KernelImage(path) except UnrecognizedKernelError: pass else: # associate the module directory k.all_files.extend( module_dict.get(kobj.internal_version, [])) fobj = kobj if ftype not in exclusions: k.all_files.append(fobj) k.all_files.append(EmptyDirectory(dir_path)) kernels[ver] = k # merge unassociated modules into kernel groups for mkv, fobjs in module_dict.items(): if any(mkv == k.real_kv for k in kernels.values()): continue kernels.setdefault(mkv, Kernel(mkv)).all_files.extend(fobjs) return list(kernels.values())
def test_bad_file_magic(self) -> None: path = Path(self.td.name) / 'vmlinuz' with open(path, 'w') as f: f.write('Hello World') with self.assertRaises(UnrecognizedKernelError): KernelImage(path).read_internal_version()
def test_read_internal_version_compress(self) -> None: path = Path(self.td.name) / 'vmlinuz' write_compress(path, b'Linux version 1.2.3 built on test') self.assertEqual(KernelImage(path).read_internal_version(), '1.2.3')
def test_overflow_ver_string_raw(self) -> None: path = Path(self.td.name) / 'vmlinuz' write_raw(path, b'Linux version 1.2.3' + 0xffff * b'\0') with self.assertRaises(UnrecognizedKernelError): KernelImage(path).read_internal_version()
def find_kernels( self, exclusions: typing.Container[KernelFileType] = [], ) -> typing.List[Kernel]: # this would wreak havok all around the place assert KernelFileType.KERNEL not in exclusions # collect all module directories first module_dict = self.get_module_dict(exclusions=exclusions, module_directory=self.root / 'lib/modules') # collect from /boot kernels: typing.Dict[str, typing.Dict[str, Kernel]] = {} other_files: typing.List[typing.Tuple[GenericFile, str]] = [] boot_directory = self.root / 'boot' try: diter = os.listdir(boot_directory) except FileNotFoundError: pass else: for fn in diter: # skip hidden and GRUB signature files if fn.startswith('.') or fn.endswith('.sig'): continue path = boot_directory / fn if path.is_symlink() or not path.is_file(): continue # skip unversioned files ver = fn.partition('-')[2] if not ver: continue # strip suffix from filename to get the correct version for suffix in self.suffixes: if ver.endswith(suffix): ver = ver[:-len(suffix)] break elif ver.endswith(suffix + '.old'): ver = ver[:-len(suffix) - 4] + '.old' # try recognizing kernel image via magic try: kobj = KernelImage(path) except UnrecognizedKernelError: # fall back to filename for ftype, prefix in self.prefixes: if ftype not in exclusions: if fn.startswith(prefix): other_files.append((GenericFile(path, ftype), ver)) break continue # the following is done only for kernel images assert isinstance(kobj, KernelImage) kg = kernels.setdefault(ver, {}) k = kg.setdefault(kobj.internal_version, Kernel(ver)) k.all_files.append(kobj) # associate the module directory k.all_files.extend(module_dict.get(kobj.internal_version, [])) # merge other files into kernel groups for fobj, ver in other_files: kg = kernels.setdefault(ver, {}) # if we had some images with matching apparent version, # append to all of their Kernels; if we had none, create # a single Kernel object if not kg: kg[''] = Kernel(ver) for k in kg.values(): k.all_files.append(fobj) # merge unassociated modules into kernel groups for mkv, fobjs in module_dict.items(): if any(mkv == kv for kg in kernels.values() for kv in kg): continue (kernels.setdefault(mkv, {}).setdefault( '', Kernel(mkv)).all_files.extend(fobjs)) return list( itertools.chain.from_iterable(kg.values() for kg in kernels.values()))