glob += "*"
if (
- # match the full path
- (glob.startswith("/") and fnmatch.fnmatch(f"/{name}", glob))
+ # Match globs starting with / relative to kernel/ first, since in-tree module are the common case
+ (glob.startswith("/") and fnmatch.fnmatch(f"/{name}", f"/kernel{glob}"))
+ # Now match absolute globs relative to lib/modules/KVER/
+ or (glob.startswith("/") and fnmatch.fnmatch(f"/{name}", glob))
# match a subset of the path, at path element boundary
or ("/" in glob and fnmatch.fnmatch(f"/{name}", f"*/{glob}"))
# match the basename
globs = [normalize_module_glob(p) for p in include if not p.startswith("re:")]
for m in modules:
- rel = os.fspath(Path(*m.parts[5:]))
+ rel = os.fspath(m.relative_to(modulesd))
+ # old regexes match relative to modulesd/subdir/ not modulesd/
+ legacy_rel = os.fspath(Path(*m.parts[5:]))
- if (patterns and regex.search(rel)) or globs_match_module(normalize_module_name(rel), globs):
+ if (patterns and regex.search(legacy_rel)) or globs_match_module(
+ normalize_module_name(rel), globs
+ ):
keep.add(rel)
if exclude:
remove = set()
for m in modules:
- rel = os.fspath(Path(*m.parts[5:]))
- if rel not in keep and regex.search(rel):
+ rel = os.fspath(m.relative_to(modulesd))
+ # old regexes match relative to modulesd/subdir/ not modulesd/
+ legacy_rel = os.fspath(Path(*m.parts[5:]))
+
+ if rel not in keep and regex.search(legacy_rel):
remove.add(m)
modules -= remove
if exclude:
logging.debug(f"Firmware exclude: {' '.join(exclude)}")
+ firmwared = Path("usr/lib/firmware")
+
# globs can be also used to exclude firmware, so we we need to apply them
# to the inherited list of firmware files too.
globs = [p for p in include if not p.startswith("re:")]
regex = re.compile("|".join(patterns))
for f in firmware:
- rel = os.fspath(Path(*f.parts[3:]))
+ rel = os.fspath(f.relative_to(firmwared))
if (patterns and regex.search(rel)) or not globs_match_firmware(rel, globs, match_default=True):
remove.add(f)
firmware -= remove
if include:
- firmwared = Path("usr/lib/firmware")
with chdir(root):
all_firmware = {p for p in firmwared.rglob("*") if p.is_file() or p.is_symlink()}
regex = re.compile("|".join(patterns))
for f in all_firmware:
- rel = os.fspath(Path(*f.parts[3:]))
+ rel = os.fspath(f.relative_to(firmwared))
if (patterns and regex.search(rel)) or globs_match_firmware(rel, globs):
firmware.add(f)
The modules that were last matched by a positive pattern are included in the image,
as well as their module and firmware dependencies.
- The module paths are taken relative to the `/usr/lib/modules/<kver>/<subdir>/kernel/` directory,
- and the `.ko` suffix and compression suffix are ignored during matching.
- The patterns may include just the basename (e.g. `loop`),
+ The `.ko` suffix and compression suffix are ignored during matching.
+
+ Globs are matched against module paths relative to `/usr/lib/module/<kver>`,
+ e.g. the module at `/usr/lib/module/<kver>/kernel/foo/bar.ko.xz`
+ becomes `kernel/foo/bar` for matching purposes.
+
+ Globs beginning with "/" are treated specially. The glob is *first* matched
+ against a path relative to `/usr/lib/module/<kver>/kernel`, and only then against
+ `/usr/lib/module/<kver>/`. This is a convenience, since usually only the in-tree
+ modules under `kernel/` are of interest.
+
+ For example, the module at`/usr/lib/module/<kver>/kernel/foo/bar.ko.xz`
+ can be matched by either `/foo/bar` or `/kernel/foo/bar`.
+
+ The glob patterns may include just the basename (e.g. `loop`),
which must match the basename of the module,
the relative path (e.g. `block/loop`),
which must match the final components of the module path up to the basename,
or an absolute path (e.g. `/drivers/block/loop`),
which must match the full path to the module.
+
When suffixed with `/`, the pattern will match all modules underneath that directory.
+
The patterns may include shell-style globs (`*`, `?`, `[…-…]`).
If the special value `default` is used, the default kernel modules
assert not kmod.globs_match_module("drivers/ata/ahci.ko.zst", ["-*"])
assert not kmod.globs_match_module("drivers/ata/ahci.ko.xz", ["-*"])
+ # absolute glob behavior unchanged when paths are relative to /lib/module/<kver>
+ assert kmod.globs_match_module("kernel/drivers/ata/ahci.ko", ["drivers/*"])
+ assert kmod.globs_match_module("kernel/drivers/ata/ahci.ko", ["/drivers/*"])
+ assert not kmod.globs_match_module("kernel/drivers/ata/ahci.ko.xz", ["/ata/*"])
+
+ # absolute globs match both relative to kernel/ and module_dir root
+ assert kmod.globs_match_module("kernel/drivers/ata/ahci.ko.xz", ["/drivers/ata/ahci"])
+ assert kmod.globs_match_module("kernel/drivers/ata/ahci.ko.xz", ["/kernel/drivers/ata/ahci"])
+
def test_normalize_module_glob() -> None:
assert kmod.normalize_module_glob("raid[0-9]") == "raid[0-9]"