Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

layout/blspec.py: Properly implement specification type 1 and type 2 #39

Closed
wants to merge 7 commits into from
8 changes: 4 additions & 4 deletions ecleankernel/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def main(argv: typing.List[str]) -> int:
key=sorter.key,
reverse=True)
for k in ordered:
print(f'{k.version} [{k.real_kv}]')
print(f'{k.layout} {k.version} [{k.real_kv}]')
for kf in sorted(k.all_files, key=lambda f: f.path):
print(f'- {kf.ftype.value}: {kf.path}')
ts = time.strftime("%Y-%m-%d %H:%M:%S",
Expand Down Expand Up @@ -292,7 +292,7 @@ def main(argv: typing.List[str]) -> int:
get_removable_files(removals, kernels))

for k, reason, files in file_removals:
print(f'- {k.version}: {", ".join(reason)}')
print(f'- {k.layout} {k.version}: {", ".join(reason)}')
for kf in k.all_files:
if kf.path in files:
sign = '-'
Expand All @@ -315,7 +315,7 @@ def main(argv: typing.List[str]) -> int:

for k, reason in list(removals.items()):
while args.ask:
ans = input(f'Remove {k.version} '
ans = input(f'Remove {k.layout} {k.version} '
f'({", ".join(reason)})? [Yes/No]'
).lower()
if 'yes'.startswith(ans):
Expand All @@ -330,7 +330,7 @@ def main(argv: typing.List[str]) -> int:
get_removable_files(removals, kernels))

for k, reason, files in file_removals:
print(f'* Removing kernel {k.version} '
print(f'* Removing kernel {k.layout} {k.version} '
f'({", ".join(reason)})')

if has_kernel_install:
Expand Down
13 changes: 9 additions & 4 deletions ecleankernel/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@ class Kernel(object):

all_files: typing.List[GenericFile]
version: str
layout: str

def __init__(self,
version: str
version: str,
layout: str = "other",
) -> None:
self.all_files = []
self.layout = layout
self.version = version

@property
Expand Down Expand Up @@ -68,10 +71,12 @@ def __eq__(self,
if not isinstance(other, Kernel):
return False
return (self.version == other.version
and self.all_files == other.all_files)
and self.all_files == other.all_files
and self.layout == other.layout)

def __hash__(self) -> int:
return hash(self.version)
return hash((self.version, self.layout))

def __repr__(self) -> str:
return f'Kernel({repr(self.version)})'
return f'Kernel(version={repr(self.version)}, ' \
f'layout={repr(self.layout)})'
119 changes: 86 additions & 33 deletions ecleankernel/layout/blspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,52 @@ def __init__(self,
for path in ("etc/kernel/entry-token", "etc/machine-id"):
try:
with open(root / path) as f:
kernel_id = f.read().strip()
self.kernel_id = f.read().strip()
break
except FileNotFoundError:
pass
else:
raise LayoutNotFound("/etc/machine-id not found")

for d in self.potential_dirs:
self.bootdir = root / d / kernel_id
if self.bootdir.is_dir():
bootloaderdir = root / d / "loader"
if bootloaderdir.is_dir():
# Type 1 entries (linux+initrd) are in
# $BOOT/ENTRY-TOKEN/KERNEL-VERSION/
self.blsdir = root / d / self.kernel_id
# Type 2 entries (uki's) are in
# $BOOT/EFI/Linux/ENTRY-TOKEN-KERNEL-VERSION.efi
self.ukidir = root / d / "EFI" / "Linux"
return
else:
raise LayoutNotFound(f"/boot/[EFI/]{kernel_id} not found")
raise LayoutNotFound("/boot/[EFI/]loader not found")
Nowa-Ammerlaan marked this conversation as resolved.
Show resolved Hide resolved

def append_kernel_files(self,
ftype: KernelFileType,
path: Path,
k: Kernel,
ver: str,
module_dict: dict,
exclusions: typing.Container[KernelFileType] = [],
) -> Kernel:
fobj = GenericFile(path, ftype)

if ftype == KernelFileType.KERNEL:
try:
kobj = KernelImage(path)
except UnrecognizedKernelError as err:
logging.debug(
f"Unrecognized potential kernel image: {err}")
else:
fobj = kobj
# associate the module directory
k.all_files.extend(
module_dict.get(kobj.internal_version, []))

if ftype not in exclusions:
k.all_files.append(fobj)

return k

def find_kernels(self,
exclusions: typing.Container[KernelFileType] = [],
Expand All @@ -71,41 +104,61 @@ def find_kernels(self,
module_directory=self.root / 'lib/modules')

# collect from /boot
Nowa-Ammerlaan marked this conversation as resolved.
Show resolved Hide resolved
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
kernels: typing.Dict[typing.Tuple[str, str], Kernel] = {}
if self.blsdir.is_dir():
for ver in os.listdir(self.blsdir):
if ver.startswith('.'):
continue
dir_path = self.blsdir / 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('.'):
k = Kernel(ver, layout="bls")

for fn in os.listdir(dir_path):
if fn.startswith('.'):
continue
kernels[(ver, "bls")] = self.append_kernel_files(
self.name_map.get(fn, KernelFileType.MISC),
dir_path / fn,
k, ver, module_dict, exclusions)
kernels[(ver, "bls")].all_files.append(
EmptyDirectory(dir_path))

# collect from ESP/Linux
if self.ukidir.is_dir():
for file in os.listdir(self.ukidir):
if (not file.startswith(self.kernel_id) or
not file.endswith(".efi")):
# This file is not an efi file or does not belong to us
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 as err:
logging.debug(
f"Unrecognized potential kernel image: {err}")
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
ver = file.removeprefix(self.kernel_id +
"-").removesuffix(".efi")

kernels[(ver, "uki")] = self.append_kernel_files(
KernelFileType.KERNEL,
self.ukidir / file,
Kernel(ver, layout="uki"),
ver, module_dict,
exclusions)

# 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)
# this mkv does not have a corresponding kernel
# with same real_kv in kernels
match_found = False
for (ver, layout), k in kernels.items():
if ver == mkv:
# extend existing entry
k.all_files.extend(fobjs)
match_found = True
if not match_found:
# no real_kv for this mkv, no existing entry in kernels
layout = "modules-only"
kernels.setdefault((mkv, layout),
Kernel(mkv, layout=layout)
).all_files.extend(fobjs)

return list(kernels.values())
Loading