From 197059e8a4bfd7120566bf9480eece4186610433 Mon Sep 17 00:00:00 2001 From: Marco d'Itri Date: Sun, 30 Mar 2025 03:59:19 +0200 Subject: [PATCH] Import kmod_34.2.orig.tar.xz [dgit import orig kmod_34.2.orig.tar.xz] --- .clang-format | 146 + .codespellignore | 6 + .editorconfig | 35 + .github/actions/setup-alpine/action.yml | 32 + .github/actions/setup-archlinux/action.yml | 28 + .github/actions/setup-debian/action.yml | 33 + .github/actions/setup-fedora/action.yml | 41 + .github/actions/setup-ubuntu/action.yml | 34 + .github/dependabot.yml | 21 + .github/print-kdir.sh | 17 + .github/workflows/clang-format.yml | 18 + .github/workflows/codeql.yml | 88 + .github/workflows/codespell.yml | 23 + .github/workflows/coverage.yml | 65 + .github/workflows/main.yml | 153 + .gitignore | 36 + .mailmap | 1 + CODING-STYLE | 67 + CONTRIBUTING.md | 115 + COPYING | 504 +++ DCO.txt | 34 + Makefile.am | 448 +++ NEWS | 1181 ++++++ README.md | 82 + autogen.sh | 88 + build-dev.ini | 18 + configure.ac | 360 ++ libkmod/.gitignore | 6 + libkmod/COPYING | 504 +++ libkmod/Makefile | 13 + libkmod/README | 58 + libkmod/docs/.gitignore | 14 + libkmod/docs/Makefile.am | 34 + libkmod/docs/libkmod-docs.xml | 63 + libkmod/docs/libkmod-sections.txt | 118 + libkmod/docs/meson.build | 29 + libkmod/docs/version.xml.in | 1 + libkmod/libkmod-builtin.c | 191 + libkmod/libkmod-config.c | 1294 +++++++ libkmod/libkmod-elf.c | 1264 +++++++ libkmod/libkmod-file-xz.c | 146 + libkmod/libkmod-file-zlib.c | 112 + libkmod/libkmod-file-zstd.c | 120 + libkmod/libkmod-file.c | 172 + libkmod/libkmod-index.c | 1086 ++++++ libkmod/libkmod-index.h | 34 + libkmod/libkmod-internal-file.h | 48 + libkmod/libkmod-internal.h | 187 + libkmod/libkmod-list.c | 230 ++ libkmod/libkmod-module.c | 2307 ++++++++++++ libkmod/libkmod-signature.c | 344 ++ libkmod/libkmod.c | 856 +++++ libkmod/libkmod.h | 1466 ++++++++ libkmod/libkmod.pc.in | 11 + libkmod/libkmod.sym | 107 + m4/.gitignore | 6 + m4/attributes.m4 | 277 ++ m4/features.m4 | 24 + man/.gitignore | 4 + man/Makefile.am | 28 + man/depmod.8.scd | 181 + man/depmod.d.5.scd | 118 + man/insmod.8.scd | 64 + man/kmod.8.scd | 62 + man/lsmod.8.scd | 53 + man/meson.build | 57 + man/modinfo.8.scd | 87 + man/modprobe.8.scd | 223 ++ man/modprobe.conf.5 | 1 + man/modprobe.d.5.scd | 189 + man/modules.dep.5.scd | 51 + man/modules.dep.bin.5 | 1 + man/rmmod.8.scd | 66 + meson.build | 600 ++++ meson_options.txt | 114 + scripts/autogen-for-tarball.sh | 5 + scripts/build-scdoc.sh | 9 + scripts/sanitizer-env.sh | 51 + scripts/setup-modules.sh | 16 + scripts/setup-rootfs.sh | 191 + scripts/test-gtkdoc.sh | 24 + shared/.gitignore | 5 + shared/array.c | 116 + shared/array.h | 22 + shared/elf-note.h | 53 + shared/hash.c | 325 ++ shared/hash.h | 21 + shared/macro.h | 81 + shared/missing.h | 44 + shared/strbuf.c | 136 + shared/strbuf.h | 120 + shared/util.c | 606 ++++ shared/util.h | 202 ++ shell-completion/bash/insmod | 35 + shell-completion/bash/kmod | 89 + shell-completion/bash/lsmod | 30 + shell-completion/bash/rmmod | 34 + shell-completion/fish/insmod.fish | 21 + shell-completion/fish/lsmod.fish | 12 + shell-completion/fish/rmmod.fish | 16 + shell-completion/zsh/_insmod | 14 + shell-completion/zsh/_lsmod | 12 + shell-completion/zsh/_rmmod | 21 + testsuite/.gitignore | 54 + testsuite/COPYING | 504 +++ testsuite/Makefile | 15 + testsuite/README | 60 + testsuite/delete_module.c | 155 + testsuite/init_module.c | 392 ++ testsuite/meson.build | 124 + testsuite/module-playground/.gitignore | 11 + testsuite/module-playground/Kbuild | 45 + testsuite/module-playground/Makefile | 28 + testsuite/module-playground/Makefile.arch | 14 + testsuite/module-playground/README | 26 + testsuite/module-playground/dummy.pkcs7 | Bin 0 -> 721 bytes testsuite/module-playground/dummy.sha1 | Bin 0 -> 102411 bytes testsuite/module-playground/dummy.sha256 | Bin 0 -> 102411 bytes testsuite/module-playground/mod-fake-cciss.c | 37 + testsuite/module-playground/mod-fake-hpsa.c | 36 + .../module-playground/mod-fake-scsi-mod.c | 23 + testsuite/module-playground/mod-foo-a.c | 22 + testsuite/module-playground/mod-foo-b.c | 22 + testsuite/module-playground/mod-foo-c.c | 22 + testsuite/module-playground/mod-foo.c | 23 + testsuite/module-playground/mod-loop-a.c | 27 + testsuite/module-playground/mod-loop-b.c | 27 + testsuite/module-playground/mod-loop-c.c | 25 + testsuite/module-playground/mod-loop-d.c | 25 + testsuite/module-playground/mod-loop-e.c | 25 + testsuite/module-playground/mod-loop-f.c | 24 + testsuite/module-playground/mod-loop-g.c | 24 + testsuite/module-playground/mod-loop-h.c | 25 + testsuite/module-playground/mod-loop-i.c | 25 + testsuite/module-playground/mod-loop-j.c | 26 + testsuite/module-playground/mod-loop-k.c | 25 + testsuite/module-playground/mod-loop.h | 13 + testsuite/module-playground/mod-simple.c | 32 + testsuite/module-playground/mod-weakdep.c | 19 + testsuite/path.c | 209 ++ .../etc/modprobe.d/modprobe.conf | 2 + .../lib/modules/4.0.20-kmod/modules.alias | 1 + .../lib/modules/4.0.20-kmod/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.0.20-kmod/modules.builtin | 0 .../modules/4.0.20-kmod/modules.builtin.bin | Bin 0 -> 12 bytes .../lib/modules/4.0.20-kmod/modules.dep | 4 + .../lib/modules/4.0.20-kmod/modules.dep.bin | Bin 0 -> 244 bytes .../lib/modules/4.0.20-kmod/modules.devname | 1 + .../lib/modules/4.0.20-kmod/modules.order | 7 + .../lib/modules/4.0.20-kmod/modules.softdep | 1 + .../lib/modules/4.0.20-kmod/modules.symbols | 4 + .../modules/4.0.20-kmod/modules.symbols.bin | Bin 0 -> 102 bytes .../another-moddir/correct-modules.dep | 1 + .../lib/modules/4.4.4/correct-modules.weakdep | 2 + .../test-depmod/detect-loop/correct.txt | 5 + .../lib/modules/4.4.4/modules.order | 0 .../lib/modules/4.4.4/correct-modules.alias | 37 + .../lib/modules/4.4.4/modules.builtin | 0 .../lib/modules/4.4.4/modules.order | 7 + .../modules-outdir/correct-modules.alias | 37 + .../modules-outdir/correct-modules.dep | 3 + .../lib/modules/4.4.4/modules.builtin | 0 .../lib/modules/4.4.4/modules.order | 7 + .../etc/depmod.d/external.conf | 1 + .../etc/depmod.d/search.conf | 1 + .../lib/modules/4.4.4/correct-modules.dep | 1 + .../etc/depmod.d/external.conf | 1 + .../etc/depmod.d/search.conf | 1 + .../lib/modules/4.4.4/correct-modules.dep | 1 + .../etc/depmod.d/override.conf | 1 + .../etc/depmod.d/search.conf | 1 + .../lib/modules/4.4.4/correct-modules.dep | 1 + .../etc/depmod.d/search.conf | 1 + .../lib/modules/4.4.4/correct-modules.dep | 1 + .../etc/depmod.d/search.conf | 1 + .../lib/modules/4.4.4/correct-modules.dep | 1 + .../lib/modules/5.6.0/modules.alias | 1 + .../lib/modules/5.6.0/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/5.6.0/modules.builtin | 1 + .../modules/5.6.0/modules.builtin.alias.bin | Bin 0 -> 12 bytes .../lib/modules/5.6.0/modules.builtin.bin | Bin 0 -> 39 bytes .../lib/modules/5.6.0/modules.dep | 0 .../lib/modules/5.6.0/modules.dep.bin | Bin 0 -> 12 bytes .../lib/modules/5.6.0/modules.devname | 1 + .../lib/modules/5.6.0/modules.softdep | 1 + .../lib/modules/5.6.0/modules.symbols | 1 + .../lib/modules/5.6.0/modules.symbols.bin | Bin 0 -> 12 bytes .../lib/modules/5.6.0/modules.alias | 1 + .../lib/modules/5.6.0/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/5.6.0/modules.builtin | 1 + .../lib/modules/5.6.0/modules.builtin.bin | Bin 0 -> 39 bytes .../lib/modules/5.6.0/modules.dep | 0 .../lib/modules/5.6.0/modules.dep.bin | Bin 0 -> 12 bytes .../lib/modules/5.6.0/modules.devname | 1 + .../lib/modules/5.6.0/modules.softdep | 1 + .../lib/modules/5.6.0/modules.symbols | 1 + .../lib/modules/5.6.0/modules.symbols.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin | 1 + .../lib/modules/4.4.4/modules.builtin.bin | Bin 0 -> 39 bytes .../lib/modules/4.4.4/modules.dep | 0 .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.devname | 1 + .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 1 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 12 bytes .../rootfs-pristine/test-loaded/correct.txt | 2 + .../rootfs-pristine/test-loaded/proc/modules | 1 + .../sys/module/btusb/drivers/usb:btusb | 1 + .../sys/module/btusb/holders/.gitignore | 0 .../test-loaded/sys/module/btusb/initstate | 1 + .../sys/module/btusb/notes/.note.gnu.build-id | Bin 0 -> 36 bytes .../module/btusb/parameters/disable_scofix | 1 + .../sys/module/btusb/parameters/force_scofix | 1 + .../sys/module/btusb/parameters/ignore_csr | 1 + .../sys/module/btusb/parameters/ignore_dga | 1 + .../module/btusb/parameters/ignore_sniffer | 1 + .../sys/module/btusb/parameters/reset | 1 + .../test-loaded/sys/module/btusb/refcnt | 1 + .../sys/module/btusb/sections/.bss | 1 + .../sys/module/btusb/sections/.data | 1 + .../sys/module/btusb/sections/.exit.text | 1 + .../btusb/sections/.gnu.linkonce.this_module | 1 + .../sys/module/btusb/sections/.init.text | 1 + .../module/btusb/sections/.note.gnu.build-id | 1 + .../sys/module/btusb/sections/.rodata | 1 + .../sys/module/btusb/sections/.rodata.str1.1 | 1 + .../sys/module/btusb/sections/.rodata.str1.8 | 1 + .../sys/module/btusb/sections/.smp_locks | 1 + .../sys/module/btusb/sections/.strtab | 1 + .../sys/module/btusb/sections/.symtab | 1 + .../sys/module/btusb/sections/.text | 1 + .../sys/module/btusb/sections/__param | 1 + .../test-loaded/sys/module/btusb/srcversion | 1 + .../test-loaded/sys/module/btusb/version | 1 + .../lib/modules/6.11.0/modules.builtin.bin | Bin 0 -> 11457 bytes .../modules/6.11.0/modules.builtin.modinfo | Bin 0 -> 561 bytes .../test-modinfo/correct-author.txt | 1 + .../test-modinfo/correct-builtin.txt | 6 + .../test-modinfo/correct-depends.txt | 1 + .../test-modinfo/correct-description.txt | 0 .../test-modinfo/correct-external.txt | 1 + .../test-modinfo/correct-filename.txt | 1 + .../test-modinfo/correct-license.txt | 2 + .../test-modinfo/correct-parm.txt | 0 .../correct-sig_hashalgo-openssl.txt | 3 + .../test-modinfo/correct-sig_hashalgo.txt | 4 + .../test-modinfo/correct-sig_key-openssl.txt | 3 + .../test-modinfo/correct-sig_key.txt | 3 + .../test-modinfo/correct-signer-openssl.txt | 3 + .../test-modinfo/correct-signer.txt | 3 + .../rootfs-pristine/test-modinfo/correct.txt | 24 + .../external/lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin.bin | 0 .../external/lib/modules/4.4.4/modules.dep | 1 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 73 bytes .../lib/modules/4.4.4/modules.devname | 0 .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 1 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 12 bytes .../test-modprobe/alias-to-none/correct.txt | 0 .../alias-to-none/etc/modprobe.d/bogus.conf | 1 + .../lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin.bin | 0 .../lib/modules/4.4.4/modules.dep | 1 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 58 bytes .../lib/modules/4.4.4/modules.devname | 1 + .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 1 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 12 bytes .../test-modprobe/alias-to-none/proc/modules | 0 .../test-modprobe/builtin/correct.txt | 1 + .../builtin/lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../builtin/lib/modules/4.4.4/modules.builtin | 1 + .../lib/modules/4.4.4/modules.builtin.bin | Bin 0 -> 31 bytes .../builtin/lib/modules/4.4.4/modules.dep | 0 .../builtin/lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 12 bytes .../builtin/lib/modules/4.4.4/modules.devname | 1 + .../builtin/lib/modules/4.4.4/modules.softdep | 2 + .../builtin/lib/modules/4.4.4/modules.symbols | 1 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 12 bytes .../external/lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin.bin | 0 .../external/lib/modules/4.4.4/modules.dep | 1 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 73 bytes .../lib/modules/4.4.4/modules.devname | 0 .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 1 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 12 bytes .../test-modprobe/external/proc/modules | 0 .../force/lib/modules/4.4.4/modules.alias | 1 + .../force/lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin.bin | 0 .../force/lib/modules/4.4.4/modules.dep | 1 + .../force/lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 58 bytes .../force/lib/modules/4.4.4/modules.devname | 1 + .../force/lib/modules/4.4.4/modules.softdep | 1 + .../force/lib/modules/4.4.4/modules.symbols | 1 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 12 bytes .../test-modprobe/force/proc/modules | 0 .../etc/modprobe.d/dumb-instal-loop.conf | 1 + .../lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin.bin | 0 .../lib/modules/4.4.4/modules.dep | 2 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 118 bytes .../lib/modules/4.4.4/modules.devname | 1 + .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 3 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 78 bytes .../lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin.bin | 0 .../lib/modules/4.4.4/modules.dep | 1 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 73 bytes .../lib/modules/4.4.4/modules.devname | 0 .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 1 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 12 bytes .../module-from-abspath/proc/modules | 0 .../lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin.bin | 0 .../lib/modules/4.4.4/modules.dep | 1 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 73 bytes .../lib/modules/4.4.4/modules.devname | 0 .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 1 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 12 bytes .../module-from-relpath/proc/modules | 0 .../module-param-kcmdline/correct.txt | 1 + .../lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin.bin | 0 .../lib/modules/4.4.4/modules.dep | 1 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 58 bytes .../lib/modules/4.4.4/modules.devname | 1 + .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 1 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 12 bytes .../module-param-kcmdline/proc/cmdline | 1 + .../module-param-kcmdline/proc/modules | 0 .../module-param-kcmdline2/correct.txt | 5 + .../module-param-kcmdline2/proc/cmdline | 1 + .../module-param-kcmdline3/correct.txt | 5 + .../module-param-kcmdline3/proc/cmdline | 1 + .../module-param-kcmdline4/correct.txt | 4 + .../module-param-kcmdline4/proc/cmdline | 1 + .../module-param-kcmdline5/correct.txt | 6 + .../module-param-kcmdline5/proc/cmdline | 1 + .../module-param-kcmdline6/correct.txt | 6 + .../module-param-kcmdline6/proc/cmdline | 1 + .../module-param-kcmdline7/correct.txt | 5 + .../module-param-kcmdline7/proc/cmdline | 1 + .../module-param-kcmdline8/correct.txt | 5 + .../module-param-kcmdline8/proc/cmdline | 1 + .../module-param-kcmdline9/correct.txt | 8 + .../module-param-kcmdline9/proc/cmdline | 1 + .../lib/modules/3.3.3/modules.alias | 1 + .../lib/modules/3.3.3/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/3.3.3/modules.builtin.bin | 0 .../lib/modules/3.3.3/modules.dep | 1 + .../lib/modules/3.3.3/modules.dep.bin | Bin 0 -> 58 bytes .../lib/modules/3.3.3/modules.devname | 1 + .../lib/modules/3.3.3/modules.softdep | 1 + .../lib/modules/3.3.3/modules.symbols | 1 + .../lib/modules/3.3.3/modules.symbols.bin | Bin 0 -> 12 bytes .../oldkernel-force/proc/modules | 0 .../oldkernel/lib/modules/3.3.3/modules.alias | 1 + .../lib/modules/3.3.3/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/3.3.3/modules.builtin.bin | 0 .../oldkernel/lib/modules/3.3.3/modules.dep | 1 + .../lib/modules/3.3.3/modules.dep.bin | Bin 0 -> 58 bytes .../lib/modules/3.3.3/modules.devname | 1 + .../lib/modules/3.3.3/modules.softdep | 1 + .../lib/modules/3.3.3/modules.symbols | 1 + .../lib/modules/3.3.3/modules.symbols.bin | Bin 0 -> 12 bytes .../test-modprobe/oldkernel/proc/modules | 0 .../show-depends/correct-mod-simple.txt | 1 + .../test-modprobe/show-depends/correct.txt | 2 + .../lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin.bin | 0 .../lib/modules/4.4.4/modules.dep | 3 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 188 bytes .../lib/modules/4.4.4/modules.devname | 1 + .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 3 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 78 bytes .../test-modprobe/show-depends/proc/modules | 0 .../test-modprobe/show-exports/correct.txt | 1 + .../etc/modprobe.d/dumb-softdep.conf | 1 + .../lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../lib/modules/4.4.4/modules.builtin.bin | 0 .../lib/modules/4.4.4/modules.dep | 2 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 118 bytes .../lib/modules/4.4.4/modules.devname | 1 + .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 3 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 78 bytes .../etc/modprobe.d/dumb-weakdep.conf | 1 + .../lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../modules/4.4.4/modules.builtin.alias.bin | 0 .../lib/modules/4.4.4/modules.builtin.bin | 0 .../lib/modules/4.4.4/modules.dep | 3 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 188 bytes .../lib/modules/4.4.4/modules.devname | 1 + .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 3 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 78 bytes .../lib/modules/4.4.4/modules.weakdep | 1 + .../test-new-module/from_alias/correct.txt | 2 + .../from_alias/etc/modprobe.d/modprobe.conf | 1 + .../test-new-module/from_name/correct.txt | 6 + .../rootfs-pristine/test-rootfs/lib/modules/a | 1 + .../test-rootfs/testdir/.gitignore | 0 .../test-util/alias-correct.txt | 25 + .../test-util/freadline_wrapped-correct.txt | 12 + .../test-util/freadline_wrapped-input.txt | 9 + .../test-util2/write-str-safe-correct.txt | 1 + .../test-weakdep/correct-weakdep.txt | 2 + .../etc/modprobe.d/dumb-weakdep.conf | 2 + .../lib/modules/4.4.4/modules.alias | 1 + .../lib/modules/4.4.4/modules.alias.bin | Bin 0 -> 12 bytes .../modules/4.4.4/modules.builtin.alias.bin | 0 .../lib/modules/4.4.4/modules.builtin.bin | 0 .../lib/modules/4.4.4/modules.dep | 4 + .../lib/modules/4.4.4/modules.dep.bin | Bin 0 -> 242 bytes .../lib/modules/4.4.4/modules.devname | 0 .../lib/modules/4.4.4/modules.softdep | 1 + .../lib/modules/4.4.4/modules.symbols | 3 + .../lib/modules/4.4.4/modules.symbols.bin | Bin 0 -> 78 bytes .../lib/modules/4.4.4/modules.weakdep | 2 + .../test-weakdep/modprobe-c.txt | 8 + testsuite/stripped-module.h | 27 + testsuite/test-array.c | 190 + testsuite/test-blacklist.c | 89 + testsuite/test-dependencies.c | 76 + testsuite/test-depmod.c | 260 ++ testsuite/test-hash.c | 287 ++ testsuite/test-init.c | 156 + testsuite/test-initstate.c | 111 + testsuite/test-list.c | 210 ++ testsuite/test-loaded.c | 78 + testsuite/test-modinfo.c | 127 + testsuite/test-modprobe.c | 364 ++ testsuite/test-new-module.c | 106 + testsuite/test-strbuf.c | 262 ++ testsuite/test-testsuite.c | 135 + testsuite/test-util.c | 298 ++ testsuite/test-weakdep.c | 108 + testsuite/testsuite.c | 1192 +++++++ testsuite/testsuite.h | 173 + testsuite/uname.c | 62 + tools/.gitignore | 15 + tools/COPYING | 339 ++ tools/Makefile | 13 + tools/depmod.c | 3172 +++++++++++++++++ tools/insmod.c | 151 + tools/kmod.c | 159 + tools/kmod.h | 37 + tools/kmod.pc.in | 10 + tools/log.c | 134 + tools/log.h | 23 + tools/lsmod.c | 140 + tools/modinfo.c | 486 +++ tools/modprobe.c | 980 +++++ tools/opt.c | 66 + tools/opt.h | 6 + tools/rmmod.c | 187 + tools/static-nodes.c | 283 ++ 478 files changed, 32137 insertions(+) create mode 100644 .clang-format create mode 100644 .codespellignore create mode 100644 .editorconfig create mode 100644 .github/actions/setup-alpine/action.yml create mode 100644 .github/actions/setup-archlinux/action.yml create mode 100644 .github/actions/setup-debian/action.yml create mode 100644 .github/actions/setup-fedora/action.yml create mode 100644 .github/actions/setup-ubuntu/action.yml create mode 100644 .github/dependabot.yml create mode 100755 .github/print-kdir.sh create mode 100644 .github/workflows/clang-format.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/codespell.yml create mode 100644 .github/workflows/coverage.yml create mode 100644 .github/workflows/main.yml create mode 100644 .gitignore create mode 100644 .mailmap create mode 100644 CODING-STYLE create mode 100644 CONTRIBUTING.md create mode 100644 COPYING create mode 100644 DCO.txt create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README.md create mode 100755 autogen.sh create mode 100644 build-dev.ini create mode 100644 configure.ac create mode 100644 libkmod/.gitignore create mode 100644 libkmod/COPYING create mode 100644 libkmod/Makefile create mode 100644 libkmod/README create mode 100644 libkmod/docs/.gitignore create mode 100644 libkmod/docs/Makefile.am create mode 100644 libkmod/docs/libkmod-docs.xml create mode 100644 libkmod/docs/libkmod-sections.txt create mode 100644 libkmod/docs/meson.build create mode 100644 libkmod/docs/version.xml.in create mode 100644 libkmod/libkmod-builtin.c create mode 100644 libkmod/libkmod-config.c create mode 100644 libkmod/libkmod-elf.c create mode 100644 libkmod/libkmod-file-xz.c create mode 100644 libkmod/libkmod-file-zlib.c create mode 100644 libkmod/libkmod-file-zstd.c create mode 100644 libkmod/libkmod-file.c create mode 100644 libkmod/libkmod-index.c create mode 100644 libkmod/libkmod-index.h create mode 100644 libkmod/libkmod-internal-file.h create mode 100644 libkmod/libkmod-internal.h create mode 100644 libkmod/libkmod-list.c create mode 100644 libkmod/libkmod-module.c create mode 100644 libkmod/libkmod-signature.c create mode 100644 libkmod/libkmod.c create mode 100644 libkmod/libkmod.h create mode 100644 libkmod/libkmod.pc.in create mode 100644 libkmod/libkmod.sym create mode 100644 m4/.gitignore create mode 100644 m4/attributes.m4 create mode 100644 m4/features.m4 create mode 100644 man/.gitignore create mode 100644 man/Makefile.am create mode 100644 man/depmod.8.scd create mode 100644 man/depmod.d.5.scd create mode 100644 man/insmod.8.scd create mode 100644 man/kmod.8.scd create mode 100644 man/lsmod.8.scd create mode 100644 man/meson.build create mode 100644 man/modinfo.8.scd create mode 100644 man/modprobe.8.scd create mode 100644 man/modprobe.conf.5 create mode 100644 man/modprobe.d.5.scd create mode 100644 man/modules.dep.5.scd create mode 100644 man/modules.dep.bin.5 create mode 100644 man/rmmod.8.scd create mode 100644 meson.build create mode 100644 meson_options.txt create mode 100755 scripts/autogen-for-tarball.sh create mode 100755 scripts/build-scdoc.sh create mode 100755 scripts/sanitizer-env.sh create mode 100755 scripts/setup-modules.sh create mode 100755 scripts/setup-rootfs.sh create mode 100755 scripts/test-gtkdoc.sh create mode 100644 shared/.gitignore create mode 100644 shared/array.c create mode 100644 shared/array.h create mode 100644 shared/elf-note.h create mode 100644 shared/hash.c create mode 100644 shared/hash.h create mode 100644 shared/macro.h create mode 100644 shared/missing.h create mode 100644 shared/strbuf.c create mode 100644 shared/strbuf.h create mode 100644 shared/util.c create mode 100644 shared/util.h create mode 100644 shell-completion/bash/insmod create mode 100644 shell-completion/bash/kmod create mode 100644 shell-completion/bash/lsmod create mode 100644 shell-completion/bash/rmmod create mode 100644 shell-completion/fish/insmod.fish create mode 100644 shell-completion/fish/lsmod.fish create mode 100644 shell-completion/fish/rmmod.fish create mode 100644 shell-completion/zsh/_insmod create mode 100644 shell-completion/zsh/_lsmod create mode 100644 shell-completion/zsh/_rmmod create mode 100644 testsuite/.gitignore create mode 100644 testsuite/COPYING create mode 100644 testsuite/Makefile create mode 100644 testsuite/README create mode 100644 testsuite/delete_module.c create mode 100644 testsuite/init_module.c create mode 100644 testsuite/meson.build create mode 100644 testsuite/module-playground/.gitignore create mode 100644 testsuite/module-playground/Kbuild create mode 100644 testsuite/module-playground/Makefile create mode 100644 testsuite/module-playground/Makefile.arch create mode 100644 testsuite/module-playground/README create mode 100644 testsuite/module-playground/dummy.pkcs7 create mode 100644 testsuite/module-playground/dummy.sha1 create mode 100644 testsuite/module-playground/dummy.sha256 create mode 100644 testsuite/module-playground/mod-fake-cciss.c create mode 100644 testsuite/module-playground/mod-fake-hpsa.c create mode 100644 testsuite/module-playground/mod-fake-scsi-mod.c create mode 100644 testsuite/module-playground/mod-foo-a.c create mode 100644 testsuite/module-playground/mod-foo-b.c create mode 100644 testsuite/module-playground/mod-foo-c.c create mode 100644 testsuite/module-playground/mod-foo.c create mode 100644 testsuite/module-playground/mod-loop-a.c create mode 100644 testsuite/module-playground/mod-loop-b.c create mode 100644 testsuite/module-playground/mod-loop-c.c create mode 100644 testsuite/module-playground/mod-loop-d.c create mode 100644 testsuite/module-playground/mod-loop-e.c create mode 100644 testsuite/module-playground/mod-loop-f.c create mode 100644 testsuite/module-playground/mod-loop-g.c create mode 100644 testsuite/module-playground/mod-loop-h.c create mode 100644 testsuite/module-playground/mod-loop-i.c create mode 100644 testsuite/module-playground/mod-loop-j.c create mode 100644 testsuite/module-playground/mod-loop-k.c create mode 100644 testsuite/module-playground/mod-loop.h create mode 100644 testsuite/module-playground/mod-simple.c create mode 100644 testsuite/module-playground/mod-weakdep.c create mode 100644 testsuite/path.c create mode 100644 testsuite/rootfs-pristine/test-blacklist/etc/modprobe.d/modprobe.conf create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.alias create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.builtin create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.dep create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.devname create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.order create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-dependencies/lib/modules/4.0.20-kmod/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-depmod/another-moddir/correct-modules.dep create mode 100644 testsuite/rootfs-pristine/test-depmod/check-weakdep/lib/modules/4.4.4/correct-modules.weakdep create mode 100644 testsuite/rootfs-pristine/test-depmod/detect-loop/correct.txt create mode 100644 testsuite/rootfs-pristine/test-depmod/detect-loop/lib/modules/4.4.4/modules.order create mode 100644 testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/correct-modules.alias create mode 100644 testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/modules.builtin create mode 100644 testsuite/rootfs-pristine/test-depmod/modules-order-compressed/lib/modules/4.4.4/modules.order create mode 100644 testsuite/rootfs-pristine/test-depmod/modules-outdir/correct-modules.alias create mode 100644 testsuite/rootfs-pristine/test-depmod/modules-outdir/correct-modules.dep create mode 100644 testsuite/rootfs-pristine/test-depmod/modules-outdir/lib/modules/4.4.4/modules.builtin create mode 100644 testsuite/rootfs-pristine/test-depmod/modules-outdir/lib/modules/4.4.4/modules.order create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-external-first/etc/depmod.d/external.conf create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-external-first/etc/depmod.d/search.conf create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-external-first/lib/modules/4.4.4/correct-modules.dep create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-external-last/etc/depmod.d/external.conf create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-external-last/etc/depmod.d/search.conf create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-external-last/lib/modules/4.4.4/correct-modules.dep create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-override/etc/depmod.d/override.conf create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-override/etc/depmod.d/search.conf create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-override/lib/modules/4.4.4/correct-modules.dep create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-same-prefix/etc/depmod.d/search.conf create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-same-prefix/lib/modules/4.4.4/correct-modules.dep create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-simple/etc/depmod.d/search.conf create mode 100644 testsuite/rootfs-pristine/test-depmod/search-order-simple/lib/modules/4.4.4/correct-modules.dep create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.alias create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin.alias.bin create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.dep create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.devname create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-init-load-resources-empty-builtin-aliases-bin/lib/modules/5.6.0/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.alias create mode 100644 testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.builtin create mode 100644 testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.dep create mode 100644 testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.devname create mode 100644 testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-init-load-resources/lib/modules/5.6.0/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.builtin create mode 100644 testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-initstate/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-loaded/correct.txt create mode 100644 testsuite/rootfs-pristine/test-loaded/proc/modules create mode 120000 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/drivers/usb:btusb create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/holders/.gitignore create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/initstate create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/notes/.note.gnu.build-id create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/disable_scofix create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/force_scofix create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_csr create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_dga create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/ignore_sniffer create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/parameters/reset create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/refcnt create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.bss create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.data create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.exit.text create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.gnu.linkonce.this_module create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.init.text create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.note.gnu.build-id create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata.str1.1 create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.rodata.str1.8 create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.smp_locks create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.strtab create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.symtab create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/.text create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/sections/__param create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/srcversion create mode 100644 testsuite/rootfs-pristine/test-loaded/sys/module/btusb/version create mode 100644 testsuite/rootfs-pristine/test-modinfo/builtin/lib/modules/6.11.0/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modinfo/builtin/lib/modules/6.11.0/modules.builtin.modinfo create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-author.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-builtin.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-depends.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-description.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-external.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-filename.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-license.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-parm.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-sig_hashalgo-openssl.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-sig_hashalgo.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-sig_key-openssl.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-sig_key.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-signer-openssl.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct-signer.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modinfo/external/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/etc/modprobe.d/bogus.conf create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/alias-to-none/proc/modules create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.builtin create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/builtin/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/external/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/external/proc/modules create mode 100644 testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/force/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/force/proc/modules create mode 100644 testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/etc/modprobe.d/dumb-instal-loop.conf create mode 100644 testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/install-cmd-loop/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-abspath/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-abspath/proc/modules create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-relpath/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-from-relpath/proc/modules create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/proc/cmdline create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline/proc/modules create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline2/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline2/proc/cmdline create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline3/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline3/proc/cmdline create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline4/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline4/proc/cmdline create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline5/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline5/proc/cmdline create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline6/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline6/proc/cmdline create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/proc/cmdline create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/proc/cmdline create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline9/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline9/proc/cmdline create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel-force/lib/modules/3.3.3/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel-force/proc/modules create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel/lib/modules/3.3.3/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/oldkernel/proc/modules create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/correct-mod-simple.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-depends/proc/modules create mode 100644 testsuite/rootfs-pristine/test-modprobe/show-exports/correct.txt create mode 100644 testsuite/rootfs-pristine/test-modprobe/softdep-loop/etc/modprobe.d/dumb-softdep.conf create mode 100644 testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/softdep-loop/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/etc/modprobe.d/dumb-weakdep.conf create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.builtin.alias.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-modprobe/weakdep-loop/lib/modules/4.4.4/modules.weakdep create mode 100644 testsuite/rootfs-pristine/test-new-module/from_alias/correct.txt create mode 100644 testsuite/rootfs-pristine/test-new-module/from_alias/etc/modprobe.d/modprobe.conf create mode 100644 testsuite/rootfs-pristine/test-new-module/from_name/correct.txt create mode 100644 testsuite/rootfs-pristine/test-rootfs/lib/modules/a create mode 100644 testsuite/rootfs-pristine/test-rootfs/testdir/.gitignore create mode 100644 testsuite/rootfs-pristine/test-util/alias-correct.txt create mode 100644 testsuite/rootfs-pristine/test-util/freadline_wrapped-correct.txt create mode 100644 testsuite/rootfs-pristine/test-util/freadline_wrapped-input.txt create mode 100644 testsuite/rootfs-pristine/test-util2/write-str-safe-correct.txt create mode 100644 testsuite/rootfs-pristine/test-weakdep/correct-weakdep.txt create mode 100644 testsuite/rootfs-pristine/test-weakdep/etc/modprobe.d/dumb-weakdep.conf create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.alias create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.alias.bin create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.builtin.alias.bin create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.builtin.bin create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.dep create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.dep.bin create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.devname create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.softdep create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.symbols create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.symbols.bin create mode 100644 testsuite/rootfs-pristine/test-weakdep/lib/modules/4.4.4/modules.weakdep create mode 100644 testsuite/rootfs-pristine/test-weakdep/modprobe-c.txt create mode 100644 testsuite/stripped-module.h create mode 100644 testsuite/test-array.c create mode 100644 testsuite/test-blacklist.c create mode 100644 testsuite/test-dependencies.c create mode 100644 testsuite/test-depmod.c create mode 100644 testsuite/test-hash.c create mode 100644 testsuite/test-init.c create mode 100644 testsuite/test-initstate.c create mode 100644 testsuite/test-list.c create mode 100644 testsuite/test-loaded.c create mode 100644 testsuite/test-modinfo.c create mode 100644 testsuite/test-modprobe.c create mode 100644 testsuite/test-new-module.c create mode 100644 testsuite/test-strbuf.c create mode 100644 testsuite/test-testsuite.c create mode 100644 testsuite/test-util.c create mode 100644 testsuite/test-weakdep.c create mode 100644 testsuite/testsuite.c create mode 100644 testsuite/testsuite.h create mode 100644 testsuite/uname.c create mode 100644 tools/.gitignore create mode 100644 tools/COPYING create mode 100644 tools/Makefile create mode 100644 tools/depmod.c create mode 100644 tools/insmod.c create mode 100644 tools/kmod.c create mode 100644 tools/kmod.h create mode 100644 tools/kmod.pc.in create mode 100644 tools/log.c create mode 100644 tools/log.h create mode 100644 tools/lsmod.c create mode 100644 tools/modinfo.c create mode 100644 tools/modprobe.c create mode 100644 tools/opt.c create mode 100644 tools/opt.h create mode 100644 tools/rmmod.c create mode 100644 tools/static-nodes.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..7b05896 --- /dev/null +++ b/.clang-format @@ -0,0 +1,146 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 12. +# +# For more information, see: +# +# Documentation/dev-tools/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 90 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false + +# Taken from: +# git grep -h '^#define [^[:space:]]*foreach[^[:space:]]*(' \ +# | sed "s,^#define \([^[:space:]]*foreach[^[:space:]]*\)(.*$, - '\1'," \ +# | LC_ALL=C sort -u +ForEachMacros: + - 'kmod_list_foreach' + - 'kmod_list_foreach_reverse' + +# Taken from: +# git grep -h '^#define .*__attribute__' \ +# | cut -d' ' -f2 \ +# | sed -e 's/(.*//' -e 's/^/ - /' \ +# | LC_ALL=C sort -u +AttributeMacros: + - KMOD_EXPORT + - TS_EXPORT + - _aligned_ + - _alignedptr_ + - _always_inline_ + - _cleanup_ + - _must_check_ + - _nonnull_ + - _nonnull_all_ + - _printf_format_ + - _section_ + - noreturn + +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 8 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 100 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 50 +PenaltyReturnTypeOnItsOwnLine: 110 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatementsExceptForEachMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/.codespellignore b/.codespellignore new file mode 100644 index 0000000..414509a --- /dev/null +++ b/.codespellignore @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +caf +parm diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d1e3045 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +# To use this config on you editor, follow the instructions at: +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +insert_final_newline = true +tab_width = 8 + +[*.{c,h}] +indent_style = tab +max_line_length = 90 + +[{Makefile*,*.mk}] +indent_style = tab + +[*.sh] +indent_style = space +indent_size = 4 + +[*.yml] +indent_style = space +indent_size = 2 + +[*.patch] +trim_trailing_whitespace = false + +[{meson.build,meson_options.txt}] +indent_style = space +indent_size = 2 diff --git a/.github/actions/setup-alpine/action.yml b/.github/actions/setup-alpine/action.yml new file mode 100644 index 0000000..636795b --- /dev/null +++ b/.github/actions/setup-alpine/action.yml @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# SPDX-FileCopyrightText: 2024 Lucas De Marchi +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +name: 'setup Alpine' +description: 'Setup an Alpine container for running CI' +runs: + using: 'composite' + steps: + - name: Install dependencies + shell: sh + run: | + apk update + apk add \ + autoconf \ + automake \ + bash \ + build-base \ + coreutils \ + clang \ + git \ + gtk-doc \ + libtool \ + linux-edge-dev \ + meson \ + openssl-dev \ + scdoc \ + tar \ + xz-dev \ + zlib-dev \ + zstd-dev diff --git a/.github/actions/setup-archlinux/action.yml b/.github/actions/setup-archlinux/action.yml new file mode 100644 index 0000000..5197f2a --- /dev/null +++ b/.github/actions/setup-archlinux/action.yml @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# SPDX-FileCopyrightText: 2024 Lucas De Marchi +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +name: 'setup Archlinux' +description: 'Setup an Archlinux container for running CI' +runs: + using: 'composite' + steps: + - name: Install dependencies + shell: bash + run: | + # Semi-regularly the packager key may have (temporarily) expired. + # Somewhat robust solution is to wipe the local keyring and + # regenerate/reinstall it prior to any other packages on the system. + rm -rf /etc/pacman.d/gnupg + pacman-key --init + pacman-key --populate + pacman --noconfirm -Sy archlinux-keyring + + pacman --noconfirm -Su \ + clang \ + git \ + gtk-doc \ + linux-headers \ + meson \ + scdoc diff --git a/.github/actions/setup-debian/action.yml b/.github/actions/setup-debian/action.yml new file mode 100644 index 0000000..5b58b74 --- /dev/null +++ b/.github/actions/setup-debian/action.yml @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# SPDX-FileCopyrightText: 2024 Lucas De Marchi +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +name: 'setup Debian' +description: 'Setup a Debian container for running CI' +runs: + using: 'composite' + steps: + - name: Install dependencies + shell: bash + run: | + export DEBIAN_FRONTEND=noninteractive + export TZ=Etc/UTC + apt-get update + apt-get install --yes \ + autoconf \ + automake \ + build-essential \ + clang \ + gcc-multilib \ + git \ + gtk-doc-tools \ + liblzma-dev \ + libssl-dev \ + libtool \ + libzstd-dev \ + linux-headers-generic \ + meson \ + scdoc \ + zlib1g-dev \ + zstd diff --git a/.github/actions/setup-fedora/action.yml b/.github/actions/setup-fedora/action.yml new file mode 100644 index 0000000..1bc6b7d --- /dev/null +++ b/.github/actions/setup-fedora/action.yml @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# SPDX-FileCopyrightText: 2024 Lucas De Marchi +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +name: 'setup Fedora' +description: 'Setup a Fedora container for running CI' +runs: + using: 'composite' + steps: + - name: Install dependencies + shell: bash + run: | + dnf update -y + dnf install -y \ + autoconf \ + automake \ + clang \ + compiler-rt \ + gcc \ + git \ + gtk-doc \ + kernel-devel \ + libasan \ + libhwasan \ + liblsan \ + libtool \ + libtsan \ + libubsan \ + libzstd-devel \ + make \ + meson \ + openssl-devel \ + scdoc \ + xz-devel \ + zlib-devel + # CI builds with KDIR pointing to /usr/lib/modules/*/build + # so just a foo/build pointing to the right place, assuming + # just one kernel installed + mkdir -p /usr/lib/modules/foo/ + ln -s /usr/src/kernels/* /usr/lib/modules/foo/build diff --git a/.github/actions/setup-ubuntu/action.yml b/.github/actions/setup-ubuntu/action.yml new file mode 100644 index 0000000..810fc32 --- /dev/null +++ b/.github/actions/setup-ubuntu/action.yml @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# SPDX-FileCopyrightText: 2024 Lucas De Marchi +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +name: 'setup Ubuntu' +description: 'Setup an Ubuntu container for running CI' +runs: + using: 'composite' + steps: + - name: Install dependencies + shell: bash + run: | + export DEBIAN_FRONTEND=noninteractive + export TZ=Etc/UTC + apt-get update + apt-get install --yes \ + autoconf \ + automake \ + build-essential \ + clang \ + gcc-multilib \ + gcovr \ + git \ + gtk-doc-tools \ + liblzma-dev \ + libssl-dev \ + libtool \ + libzstd-dev \ + linux-headers-generic \ + meson \ + scdoc \ + zlib1g-dev \ + zstd diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..923029c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + commit-message: + prefix: "ci" + groups: + all-actions: + patterns: + - "*" diff --git a/.github/print-kdir.sh b/.github/print-kdir.sh new file mode 100755 index 0000000..b3167e7 --- /dev/null +++ b/.github/print-kdir.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -euo pipefail + +for moddir in /usr/lib/modules /lib/modules; do + if [[ -e "$moddir" ]]; then + kernel=$(find "$moddir" -maxdepth 1 -mindepth 1 -type d -exec basename {} \;) + break + fi +done + +# There should be one entry - the kernel we installed +if (( $(echo "$kernel" | wc -l) != 1 )); then + echo >&2 "Error: exactly one kernel must be installed" + exit 1 +fi +echo "KDIR=$moddir/$kernel/build" diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml new file mode 100644 index 0000000..ae3ea88 --- /dev/null +++ b/.github/workflows/clang-format.yml @@ -0,0 +1,18 @@ +name: Check code style with clang-format + +on: + pull_request: + branches: [master] + +permissions: + contents: read + +jobs: + stylecheck: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - run: git fetch --depth=1 origin ${{ github.event.pull_request.base.sha }} + - uses: yshui/git-clang-format-lint@27f3890c6655216edadcc2759110b9c127c74786 # v1.17 + with: + base: ${{ github.event.pull_request.base.sha }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..644c494 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# SPDX-FileCopyrightText: 2024 Lucas De Marchi +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +name: CodeQL + +on: + push: + branches: [master, ci-test] + pull_request: + branches: [master] + schedule: + - cron: "30 2 * * 0" + +permissions: + contents: read + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-24.04 + permissions: + actions: read + security-events: write + + strategy: + fail-fast: false + matrix: + container: + - name: 'ubuntu:24.04' + meson_setup: '-D b_sanitize=none -D build-tests=false' + + container: + image: ${{ matrix.container.name }} + + steps: + - name: Sparse checkout the local actions + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + sparse-checkout: .github + + - uses: ./.github/actions/setup-ubuntu + if: ${{ startsWith(matrix.container.name, 'ubuntu') }} + + - name: Checkout the whole project + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set the environment + run: | + # The second checkout above claims to set safe.directory, yet it + # doesn't quite work. Perhaps our double/sparse checkout is to blame? + git config --global --add safe.directory '*' + + .github/print-kdir.sh >> "$GITHUB_ENV" + + - name: Initialize CodeQL + uses: github/codeql-action/init@4f3212b61783c3c68e8309a0f18a699764811cda # v3.27.1 + with: + languages: cpp + queries: +security-and-quality + + - name: Build + run: | + mkdir build && cd build + meson setup --native-file ../build-dev.ini ${{ matrix.container.meson_setup }} . .. + meson compile + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@4f3212b61783c3c68e8309a0f18a699764811cda # v3.27.1 + with: + category: "/language:cpp" + upload: false + output: sarif-results + + - name: Filter out meson-internal test files + uses: advanced-security/filter-sarif@f3b8118a9349d88f7b1c0c488476411145b6270d # v1.0.1 + with: + patterns: | + -build/meson-private/**/testfile.c + input: sarif-results/cpp.sarif + output: sarif-results/cpp.sarif + + - name: Upload CodeQL results to code scanning + uses: github/codeql-action/upload-sarif@4f3212b61783c3c68e8309a0f18a699764811cda # v3.27.1 + with: + sarif_file: sarif-results/cpp.sarif + category: "/language:cpp" diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml new file mode 100644 index 0000000..828cb0c --- /dev/null +++ b/.github/workflows/codespell.yml @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +name: Check spelling with codespell + +on: + push: + branches: [master, ci-test] + pull_request: + branches: [master] + +permissions: + contents: read + +jobs: + spellcheck: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: codespell-project/actions-codespell@406322ec52dd7b488e48c1c4b82e2a8b3a1bf630 # v2.1 + with: + ignore_words_file: .codespellignore diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..bc8d593 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,65 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# SPDX-FileCopyrightText: 2024 Lucas De Marchi +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +name: Code Coverage + +on: + push: + branches: [master, ci-test] + pull_request: + branches: [master] + schedule: + - cron: "30 2 * * 0" + +permissions: + contents: read + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + container: + - name: 'ubuntu:24.04' + meson_setup: '-D b_sanitize=none -D b_coverage=true' + + container: + image: ${{ matrix.container.name }} + + steps: + - name: Sparse checkout the local actions + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + sparse-checkout: .github + + - uses: ./.github/actions/setup-ubuntu + if: ${{ startsWith(matrix.container.name, 'ubuntu') }} + + - name: Checkout the whole project + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set the environment + run: | + # The second checkout above claims to set safe.directory, yet it + # doesn't quite work. Perhaps our double/sparse checkout is to blame? + git config --global --add safe.directory '*' + + .github/print-kdir.sh >> "$GITHUB_ENV" + + - name: Build + run: | + mkdir build && cd build + meson setup --native-file ../build-dev.ini ${{ matrix.container.meson_setup }} . .. + meson compile + meson test + ninja coverage-xml + + - name: Upload Coverage + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: build/meson-logs/coverage.xml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..08e3db0 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,153 @@ +# SPDX-FileCopyrightText: 2024 Emil Velikov +# SPDX-FileCopyrightText: 2024 Lucas De Marchi +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +name: Build and Test + +on: + push: + branches: [master, ci-test] + pull_request: + branches: [master] + schedule: + - cron: "30 2 * * 0" + +permissions: + contents: read + +jobs: + build: + env: + CC: ${{ matrix.compiler }} + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + build: ['meson', 'autotools'] + compiler: ['clang','gcc'] + container: + - name: 'alpine:latest' + meson_setup: '-D docs=false -D b_sanitize=none' + - name: 'archlinux:multilib-devel' + multilib: 'true' + - name: 'debian:unstable' + multilib: 'true' + - name: 'fedora:latest' + - name: 'fedora:latest' + meson_setup: '-D xz=disabled -D dlopen=all' + - name: 'ubuntu:22.04' + multilib: 'true' + - name: 'ubuntu:22.04' + multilib: 'true' + meson_setup: '-D dlopen=zstd,zlib' + - name: 'ubuntu:24.04' + multilib: 'true' + + container: + image: ${{ matrix.container.name }} + + steps: + - name: Sparse checkout the local actions + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + sparse-checkout: .github + + - uses: ./.github/actions/setup-alpine + if: ${{ startsWith(matrix.container.name, 'alpine') }} + - uses: ./.github/actions/setup-archlinux + if: ${{ startsWith(matrix.container.name, 'archlinux') }} + - uses: ./.github/actions/setup-debian + if: ${{ startsWith(matrix.container.name, 'debian') }} + - uses: ./.github/actions/setup-fedora + if: ${{ startsWith(matrix.container.name, 'fedora') }} + - uses: ./.github/actions/setup-ubuntu + if: ${{ startsWith(matrix.container.name, 'ubuntu') }} + + - name: Checkout the whole project + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set the environment + run: | + # The second checkout above claims to set safe.directory, yet it + # doesn't quite work. Perhaps our double/sparse checkout is to blame? + git config --global --add safe.directory '*' + + .github/print-kdir.sh >> "$GITHUB_ENV" + + - name: configure checks (meson) + if: ${{ matrix.build == 'meson' }} + run: | + should_fail() { + rm -rf build-setup-test/ + if meson setup "$@" build-setup-test/; then + echo Command was expected to fail, but was successful + return 1 + fi + } + should_pass() { + rm -rf build-setup-test/ + meson setup "$@" build-setup-test/ + } + + should_fail -D distconfdir=relative/ + should_fail -D moduledir=relative/ + should_fail -D dlopen=nonexistent + should_fail -D xz=disabled -D dlopen=xz + + should_pass -D dlopen=xz + should_pass -D dlopen=xz -D xz=enabled + + - name: configure (meson) + if: ${{ matrix.build == 'meson' }} + run: mkdir build && cd build && meson setup --native-file ../build-dev.ini ${{ matrix.container.meson_setup }} . .. + + - name: configure (autotools) + if: ${{ matrix.build == 'autotools' }} + run: mkdir build && cd build && ../autogen.sh c + + - name: build (meson) + if: ${{ matrix.build == 'meson' }} + run: cd build && meson compile + + - name: build (autotools) + if: ${{ matrix.build == 'autotools' }} + run: cd build && make -j$(nproc) + + - name: test (meson) + if: ${{ matrix.build == 'meson' }} + run: cd build && meson test || meson test --verbose + + - name: test (autotools) + if: ${{ matrix.build == 'autotools' }} + run: cd build && make -j$(nproc) check + + - name: install (meson) + if: ${{ matrix.build == 'meson' }} + run: cd build && DESTDIR=$PWD/inst meson install + + - name: install (autotools) + if: ${{ matrix.build == 'autotools' }} + run: cd build && DESTDIR=$PWD/inst make install + + - name: distcheck (meson) + if: ${{ matrix.build == 'meson' }} + run: cd build && meson dist + + - name: distcheck (autotools) + if: ${{ matrix.build == 'autotools' }} + run: cd build && make distcheck + + - name: configure (32bit) (meson) + if: ${{ matrix.build == 'meson' && matrix.container.multilib == 'true' }} + run: | + cross_options="-D zstd=disabled -D xz=disabled -D zlib=disabled -D openssl=disabled" + CC="$CC -m32" meson setup $cross_options build32/ + + - name: build (32bit) (meson) + if: ${{ matrix.build == 'meson' && matrix.container.multilib == 'true' }} + run: cd build32 && meson compile + + - name: test (32bit) (meson) + if: ${{ matrix.build == 'meson' && matrix.container.multilib == 'true' }} + run: cd build32 && meson test || meson test --verbose diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5591513 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +*.o +*.gcda +*.gcno +*.pc +/*.tar.xz +/*.md5sum +/*.mbx +/*.cover +.deps/ +.libs/ +/Makefile +/Makefile.in + +/aclocal.m4 +/autom4te.cache/ +/build-aux/ +/config.h +/config.h.in +/config.log +/config.status +/configure +/cov-int +/coverage +/gtk-doc.make +/gtk-doc.m4 +/kmod-*.tar.* +/libtool +/stamp-h1 +/test-suite.log +/build +/build-* + +*~ +.*.swp +cscope.out +tags diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..7a9ca51 --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Emil Velikov diff --git a/CODING-STYLE b/CODING-STYLE new file mode 100644 index 0000000..2cff255 --- /dev/null +++ b/CODING-STYLE @@ -0,0 +1,67 @@ +Every project has its coding style, and kmod is not an exception. This +document describes the preferred coding style for kmod code, in order to keep +some level of consistency among developers so that code can be easily +understood and maintained, and also to help your code survive under +maintainer's fastidious eyes so that you can get a passport for your patch +ASAP. + +First of all, kmod coding style must follow every rule for Linux kernel +(http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool +named checkpatch.pl to help you check the compliance with it. Just type +"checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need +to clean up all the warnings and errors. In certain circumstances one can ignore +the 80 character per line limit. This is generally only allowed if the +alternative would make the code even less readable. + +Besides the kernel coding style above, kmod coding style is heavily based on +oFono's and BlueZ's. Below some basic rules: + +1) Wrap line at 80 char limit. + +There are a few exceptions: + - Headers may or may not wrap + - If it's a string that is hitting the limit, it's preferred not to break + in order to be able to grep for that string. E.g: + + err = my_function(ctx, "this is a long string that will pass the 80chr limit"); + + - If code would become unreadable if line is wrapped + - If there's only one argument to the function, don't put it alone in a + new line. + +Align the wrapped line either with tabs (BlueZ, oFono, etc) or tab + spaces +(kernel), at your discretion. Kernel's is preferred. + +2) It's better to return/exit early in a function than having a really long + "if (...) { }". Example: + + if (x) { // worse | if (!x) // better + ... | return b; + ... | + ... | ... + ... | ... + ... | ... + ... | ... + ... | ... + ... | ... + } else { | ... + return b; | return a; + } | + | + return a; | + +3) Don't initialize variable unnecessarily +When declaring a variable, try not to initialize it unless necessary. + +Example: +int i = 1; // wrong + +for (i = 0; i < 3; i++) { +} + +4) Let the includes in the following order, separated by a new line: + < system headers > + < shared/* > + < libkmod > + < tool > + "local headers" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e35cb84 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,115 @@ +# Contributing to kmod + +Thanks for taking the time to contribute to kmod! + +If you want to submit changes, you can send GitHub [pull requests] as well as +patches sent to the [mailing list]. In addition you can open issues and feature +requests on our [GitHub tracker]. + +In this file you can find: + * [License and SPDX](#license-and-spdx) + * [Commit style and history](#commit-style-and-history) + * [Using commit trailers](#using-commit-trailers) + + [Signed-off-by](#signed-off-by) + + [Issues, feature requests](#issues--feature-requests) + + [Discussions, references](#discussions--references) + + [Link(s)](#link-s-) + + [Bugfixes, regressions](#bugfixes--regressions) + * [Coding style](#coding-style) + * [API documentation](#api-documentation) + * [Tools manual pages](#tools-manual-pages) + * [Tools shell completion](#tools-shell-completion) + +## License and SPDX + +The project uses `GPLv2+` for `tools/` and `LGPLv2.1+` for everything else. +New tools should be licensed under `LGPLv2.1+` to make it easy to migrate code +to other places when/if needed. When adding new files use the respective +`SPDX-License-Identifier:` instead of the complete license text. + +## Commit style and history + +The kmod project uses a [linear, "recipe" style] history. This means that +commits should be small, digestible, stand-alone, and functional. + +Commit messages are in imperative mood and merges are to be avoided. + +When in doubt, or need a refresher, checking through the output of `git log` is +highly recommended. + +## Using commit trailers + +Commit messages, apart from stating why a particular change is made, can include +a range of trailers. + +### Signed-off-by + +By using a `Signed-off-by:` trailer you agree that you comply with the +[Developer Certificate of Origin](DCO.txt). + +### Issues, feature requests + +Whenever a patch resolves a particular issue, be that one on our [GitHub +tracker] or elsewhere, use the `Closes:` trailer followed by the full URL. + + Closes: https://github.com/kmod-project/kmod/issues/65 + +### Discussions, references + +If your commit covers a topic raised in an issue, but does not resolve the issue +itself; or otherwise refers to a more complicated topic, you can use +`Reference:`. + +### Link(s) + +The use of `Link:` trailer is reserved and should be used only to point to the +patch origin. Be that the GitHub pull request, or the mailing list archive. + +You can add it yourself, although usually the maintainer will include it when +merging the patch. + +### Bugfixes, regressions + +Nobody is perfect and regressions happen from time to time. Whenever a commit +addresses a regression caused by another commit, use `Fixes:` as below: + + Fixes: 38943b2 ("shared: use size_t for strbuf") + +## Coding style + +The project uses style practically identical to the kernel style. You can see +the in-tree [CODING-STYLE file](CODING-STYLE) for quick references. + +We also have a [.clang-format file](.clang-format) to ease and enforce the +style. Make sure you run `git-clang-format` against your changes, before +submitting PRs/patches. + +## API documentation + +The official libkmod documentation is generated by [gtk-doc] and a simple test +is wired to `meson test` to catch accidental mistakes or omissions. + +If you're unfamiliar with `gtk-doc` here is a [quick primer]. + +When adding new API, make sure to include the `Since:` token and add a new +api-index section in `libkmod/docs/libkmod-docs.xml`. + +## Tools manual pages + +Our manual pages are written in [scdoc] a simple [markdown-like syntax]. Please +make sure to update them as you add new options to the kmod tools. + +## Tools shell completion + +The project aims to provide `bash`, `zsh` and `fish` shell completions for all +the kmod tools. Currently support is borderline non-existent, so your help is +greatly appreciated. + +[pull requests]: https://github.com/kmod-project/kmod/pull/ +[mailing list]: mailto:linux-modules@vger.kernel.org +[GitHub tracker]: https://github.com/kmod-project/kmod/issues/ +[linear, "recipe" style]: https://www.bitsnbites.eu/git-history-work-log-vs-recipe/ +[gtk-doc]: https://gitlab.gnome.org/GNOME/gtk-doc +[quick primer]: https://gi.readthedocs.io/en/latest/annotations/gtkdoc.html +[scdoc]: https://sr.ht/~sircmpwn/scdoc/ +[markdown-like syntax]: https://man.archlinux.org/man/scdoc.5.en diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..8add30a --- /dev/null +++ b/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/DCO.txt b/DCO.txt new file mode 100644 index 0000000..49b8cb0 --- /dev/null +++ b/DCO.txt @@ -0,0 +1,34 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3401265 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,448 @@ +SUBDIRS = . libkmod/docs + +if BUILD_MANPAGES +SUBDIRS += man +endif + +DISTCLEAN_LOCAL_HOOKS = +EXTRA_DIST = +CLEANFILES = $(BUILT_FILES) +DISTCLEANFILES = +BUILT_FILES = +ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} +AM_MAKEFLAGS = --no-print-directory + +GCC_COLORS ?= 'yes' +export GCC_COLORS + +# meson bits +EXTRA_DIST += \ + libkmod/docs/meson.build \ + man/meson.build \ + meson.build \ + meson_options.txt \ + testsuite/meson.build \ + scripts/build-scdoc.sh \ + scripts/sanitizer-env.sh \ + scripts/test-gtkdoc.sh + +AM_CPPFLAGS = \ + -include $(top_builddir)/config.h \ + -I$(top_srcdir) \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ + -DDISTCONFDIR=\""$(distconfdir)"\" \ + -DMODULE_DIRECTORY=\""$(module_directory)"\" \ + ${zlib_CFLAGS} + +AM_CFLAGS = $(OUR_CFLAGS) +AM_LDFLAGS = $(OUR_LDFLAGS) + +# Rules for libtool versioning (from https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html) +# 1. Start with version information of ‘0:0:0’ for each libtool library. +# 2. Update the version information only immediately before a public release of +# your software. More frequent updates are unnecessary, and only guarantee that +# the current interface number gets larger faster. +# 3. If the library source code has changed at all since the last update, then +# increment revision (‘c:r:a’ becomes ‘c:r+1:a’). +# 4. If any interfaces have been added, removed, or changed since the last +# update, increment current, and set revision to 0. +# 5. If any interfaces have been added since the last public release, then +# increment age. +# 6. If any interfaces have been removed or changed since the last public +# release, then set age to 0. +LIBKMOD_CURRENT=7 +LIBKMOD_REVISION=1 +LIBKMOD_AGE=5 + +noinst_LTLIBRARIES = shared/libshared.la +shared_libshared_la_SOURCES = \ + shared/array.c \ + shared/array.h \ + shared/elf-note.h \ + shared/hash.c \ + shared/hash.h \ + shared/macro.h \ + shared/missing.h \ + shared/strbuf.c \ + shared/strbuf.h \ + shared/util.c \ + shared/util.h + +include_HEADERS = libkmod/libkmod.h +lib_LTLIBRARIES = libkmod/libkmod.la + +libkmod_libkmod_la_SOURCES = \ + libkmod/libkmod-builtin.c \ + libkmod/libkmod.c \ + libkmod/libkmod-config.c \ + libkmod/libkmod-elf.c \ + libkmod/libkmod-file.c \ + libkmod/libkmod.h \ + libkmod/libkmod-index.c \ + libkmod/libkmod-index.h \ + libkmod/libkmod-internal-file.h \ + libkmod/libkmod-internal.h \ + libkmod/libkmod-list.c \ + libkmod/libkmod-module.c \ + libkmod/libkmod-signature.c + +if ENABLE_XZ +libkmod_libkmod_la_SOURCES += libkmod/libkmod-file-xz.c +endif + +if ENABLE_ZLIB +libkmod_libkmod_la_SOURCES += libkmod/libkmod-file-zlib.c +endif + +if ENABLE_ZSTD +libkmod_libkmod_la_SOURCES += libkmod/libkmod-file-zstd.c +endif + +EXTRA_DIST += libkmod/libkmod.sym +EXTRA_DIST += libkmod/README \ + libkmod/COPYING testsuite/COPYING tools/COPYING COPYING + +libkmod_libkmod_la_LDFLAGS = $(AM_LDFLAGS) \ + -version-info $(LIBKMOD_CURRENT):$(LIBKMOD_REVISION):$(LIBKMOD_AGE) \ + -Wl,--version-script=$(top_srcdir)/libkmod/libkmod.sym +libkmod_libkmod_la_DEPENDENCIES = \ + shared/libshared.la \ + ${top_srcdir}/libkmod/libkmod.sym +libkmod_libkmod_la_LIBADD = \ + shared/libshared.la \ + ${libzstd_LIBS} ${liblzma_LIBS} ${zlib_LIBS} ${libcrypto_LIBS} + +noinst_LTLIBRARIES += libkmod/libkmod-internal.la +libkmod_libkmod_internal_la_SOURCES = $(libkmod_libkmod_la_SOURCES) +libkmod_libkmod_internal_la_LDFLAGS = $(AM_LDFLAGS) \ + -Wl,--version-script=$(top_srcdir)/libkmod/libkmod.sym +libkmod_libkmod_internal_la_DEPENDENCIES = $(libkmod_libkmod_la_DEPENDENCIES) +libkmod_libkmod_internal_la_LIBADD = $(libkmod_libkmod_la_LIBADD) + +pkgconfig_DATA = libkmod/libkmod.pc +noarch_pkgconfig_DATA = tools/kmod.pc + +bashcompletiondir=@bashcompletiondir@ +dist_bashcompletion_DATA = \ + shell-completion/bash/insmod \ + shell-completion/bash/kmod \ + shell-completion/bash/lsmod \ + shell-completion/bash/rmmod + +fishcompletiondir=@fishcompletiondir@ +dist_fishcompletion_DATA = \ + shell-completion/fish/insmod.fish \ + shell-completion/fish/lsmod.fish \ + shell-completion/fish/rmmod.fish + +zshcompletiondir=@zshcompletiondir@ +dist_zshcompletion_DATA = \ + shell-completion/zsh/_insmod \ + shell-completion/zsh/_lsmod \ + shell-completion/zsh/_rmmod + +install-exec-hook: +if BUILD_TOOLS + for tool in insmod lsmod rmmod depmod modprobe modinfo; do \ + $(MKDIR_P) $(DESTDIR)$(sbindir); \ + $(LN_S) --force --relative $(DESTDIR)$(bindir)/kmod $(DESTDIR)$(sbindir)/$$tool; \ + done +endif + +uninstall-hook: +if BUILD_TOOLS + for tool in insmod lsmod rmmod depmod modprobe modinfo; do \ + rm -f $(DESTDIR)$(bindir)/$$tool; \ + done +endif + +if BUILD_TOOLS +bin_PROGRAMS = tools/kmod + +noinst_SCRIPTS = \ + tools/insmod tools/rmmod tools/lsmod \ + tools/modprobe tools/modinfo tools/depmod + +tools_kmod_SOURCES = \ + tools/depmod.c \ + tools/insmod.c \ + tools/kmod.c \ + tools/kmod.h \ + tools/log.c \ + tools/log.h \ + tools/lsmod.c \ + tools/modinfo.c \ + tools/modprobe.c \ + tools/opt.c \ + tools/opt.h \ + tools/rmmod.c \ + tools/static-nodes.c + +tools_kmod_LDADD = \ + shared/libshared.la \ + libkmod/libkmod-internal.la + +${noinst_SCRIPTS}: tools/kmod + $(AM_V_GEN) ($(RM) $@; \ + $(LN_S) $(notdir $<) $@) +endif + +# ------------------------------------------------------------------------------ +# TESTSUITE +# ------------------------------------------------------------------------------ + +EXTRA_DIST += scripts/setup-modules.sh +EXTRA_DIST += scripts/setup-rootfs.sh + +MODULE_PLAYGROUND = testsuite/module-playground +BUILD_MODULES = $(AM_V_GEN) $(top_srcdir)/scripts/setup-modules.sh $(top_srcdir) $(top_builddir) $(MODULE_PLAYGROUND) +ROOTFS = testsuite/rootfs +ROOTFS_PRISTINE = $(top_srcdir)/testsuite/rootfs-pristine +CREATE_ROOTFS = $(AM_V_GEN) $(top_srcdir)/scripts/setup-rootfs.sh $(ROOTFS_PRISTINE) $(ROOTFS) $(MODULE_PLAYGROUND) $(top_builddir)/config.h $(sysconfdir) $(module_directory) + +build-module-playground: + $(BUILD_MODULES) + + +rootfs: build-module-playground + $(CREATE_ROOTFS) + +.PHONY: rootfs build-playground + +$(ROOTFS): $(ROOTFS_PRISTINE) + $(CREATE_ROOTFS) + +TESTSUITE_OVERRIDE_LIBS = \ + testsuite/uname.la testsuite/path.la \ + testsuite/init_module.la \ + testsuite/delete_module.la +TESTSUITE_OVERRIDE_LIBS_LDFLAGS = \ + -avoid-version -module -shared -rpath /nowhere -ldl + +check-am: rootfs + + +EXTRA_DIST += \ + testsuite/module-playground/dummy.pkcs7 \ + testsuite/module-playground/dummy.sha1 \ + testsuite/module-playground/dummy.sha256 \ + testsuite/module-playground/Kbuild \ + testsuite/module-playground/Makefile \ + testsuite/module-playground/Makefile.arch \ + testsuite/module-playground/mod-fake-cciss.c \ + testsuite/module-playground/mod-fake-hpsa.c \ + testsuite/module-playground/mod-fake-scsi-mod.c \ + testsuite/module-playground/mod-foo-a.c \ + testsuite/module-playground/mod-foo-b.c \ + testsuite/module-playground/mod-foo.c \ + testsuite/module-playground/mod-foo-c.c \ + testsuite/module-playground/mod-loop-a.c \ + testsuite/module-playground/mod-loop-b.c \ + testsuite/module-playground/mod-loop-c.c \ + testsuite/module-playground/mod-loop-d.c \ + testsuite/module-playground/mod-loop-e.c \ + testsuite/module-playground/mod-loop-f.c \ + testsuite/module-playground/mod-loop-g.c \ + testsuite/module-playground/mod-loop-h.c \ + testsuite/module-playground/mod-loop-i.c \ + testsuite/module-playground/mod-loop-j.c \ + testsuite/module-playground/mod-loop-k.c \ + testsuite/module-playground/mod-loop.h \ + testsuite/module-playground/mod-simple.c \ + testsuite/module-playground/mod-weakdep.c \ + testsuite/module-playground/README + +check_LTLIBRARIES = $(TESTSUITE_OVERRIDE_LIBS) + +testsuite_uname_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS) +testsuite_path_la_CPPFLAGS = $(AM_CPPFLAGS) \ + -DABS_TOP_BUILDDIR=\"$(abs_top_builddir)\" +testsuite_path_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS) + +testsuite_delete_module_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS) +testsuite_init_module_la_LDFLAGS = $(TESTSUITE_OVERRIDE_LIBS_LDFLAGS) +testsuite_init_module_la_SOURCES = testsuite/init_module.c \ + testsuite/stripped-module.h +testsuite_init_module_la_LIBADD = libkmod/libkmod-internal.la + +TESTSUITE_CPPFLAGS = $(AM_CPPFLAGS) \ + -DTESTSUITE_ROOTFS=\"$(abs_top_builddir)/$(ROOTFS)/\" \ + -DTOOLS_DIR=\"$(abs_top_builddir)/tools\" \ + -DOVERRIDE_LIBDIR=\"$(abs_top_builddir)/testsuite/.libs/\" +TESTSUITE_LDADD = \ + testsuite/libtestsuite.la libkmod/libkmod-internal.la \ + shared/libshared.la + +check_LTLIBRARIES += testsuite/libtestsuite.la +testsuite_libtestsuite_la_SOURCES = \ + testsuite/testsuite.c testsuite/testsuite.h +testsuite_libtestsuite_la_DEPENDENCIES = \ + $(ROOTFS) $(TESTSUITE_OVERRIDE_LIBS) +testsuite_libtestsuite_la_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_libtestsuite_la_LIBADD = -lrt + +TESTSUITE = \ + testsuite/test-array \ + testsuite/test-blacklist \ + testsuite/test-dependencies \ + testsuite/test-depmod \ + testsuite/test-hash \ + testsuite/test-init \ + testsuite/test-initstate \ + testsuite/test-list \ + testsuite/test-loaded \ + testsuite/test-modinfo \ + testsuite/test-modprobe \ + testsuite/test-new-module \ + testsuite/test-strbuf \ + testsuite/test-testsuite \ + testsuite/test-util \ + testsuite/test-weakdep + +check_PROGRAMS = $(TESTSUITE) +TESTS = $(TESTSUITE) + +testsuite_test_testsuite_LDADD = \ + testsuite/libtestsuite.la shared/libshared.la +testsuite_test_testsuite_CPPFLAGS = $(TESTSUITE_CPPFLAGS) + +testsuite_test_hash_LDADD = $(TESTSUITE_LDADD) +testsuite_test_hash_CPPFLAGS = $(TESTSUITE_CPPFLAGS) + +testsuite_test_array_LDADD = $(TESTSUITE_LDADD) +testsuite_test_array_CPPFLAGS = $(TESTSUITE_CPPFLAGS) + +testsuite_test_strbuf_LDADD = $(TESTSUITE_LDADD) +testsuite_test_strbuf_CPPFLAGS = $(TESTSUITE_CPPFLAGS) + +testsuite_test_init_LDADD = $(TESTSUITE_LDADD) +testsuite_test_init_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_initstate_LDADD = $(TESTSUITE_LDADD) +testsuite_test_initstate_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_loaded_LDADD = $(TESTSUITE_LDADD) +testsuite_test_loaded_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_modinfo_LDADD = $(TESTSUITE_LDADD) +testsuite_test_modinfo_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_util_LDADD = $(TESTSUITE_LDADD) +testsuite_test_util_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_new_module_LDADD = $(TESTSUITE_LDADD) +testsuite_test_new_module_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_modprobe_LDADD = $(TESTSUITE_LDADD) +testsuite_test_modprobe_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_blacklist_LDADD = $(TESTSUITE_LDADD) +testsuite_test_blacklist_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_dependencies_LDADD = $(TESTSUITE_LDADD) +testsuite_test_dependencies_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_depmod_LDADD = $(TESTSUITE_LDADD) +testsuite_test_depmod_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_list_LDADD = $(TESTSUITE_LDADD) +testsuite_test_list_CPPFLAGS = $(TESTSUITE_CPPFLAGS) +testsuite_test_weakdep_LDADD = $(TESTSUITE_LDADD) +testsuite_test_weakdep_CPPFLAGS = $(TESTSUITE_CPPFLAGS) + +testsuite-distclean: + $(RM) -r $(ROOTFS) + $(RM) testsuite/stamp-rootfs + $(MAKE) -C $(MODULE_PLAYGROUND) clean + if test "$(top_srcdir)" != "$(top_builddir)"; then \ + $(RM) -rf testsuite/module-playground; \ + fi + +DISTCLEAN_LOCAL_HOOKS += testsuite-distclean +EXTRA_DIST += testsuite/rootfs-pristine + +AM_DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc --sysconfdir=/etc \ + --with-zlib --with-zstd --with-xz --with-openssl \ + --with-bashcompletiondir=$$dc_install_base/$(bashcompletiondir) \ + --with-fishcompletiondir=$$dc_install_base/$(fishcompletiondir) \ + --with-zshcompletiondir=$$dc_install_base/$(zshcompletiondir) + +distclean-local: $(DISTCLEAN_LOCAL_HOOKS) + +buildtest-TESTS: + $(MAKE) $(AM_MAKEFLAGS) $(check_LTLIBRARIES) $(check_PROGRAMS) + +# ------------------------------------------------------------------------------ +# coverage +# ------------------------------------------------------------------------------ + +# .PHONY so it always rebuilds it +.PHONY: coverage lcov-run lcov-report coverage-sync + +# run lcov from scratch, always +coverage: all + $(MAKE) lcov-run + $(MAKE) lcov-report + +coverage_dir = coverage +coverage_opts = --base-directory $(srcdir) --directory $(builddir) \ + --rc 'geninfo_adjust_src_path=$(abspath $(srcdir))=>$(abspath $(builddir))' + +if ENABLE_COVERAGE +# reset run coverage tests +lcov-run: + @rm -rf $(coverage_dir) + lcov $(coverage_opts) --zerocounters + -$(MAKE) check + +# generate report based on current coverage data +lcov-report: + $(MKDIR_P) $(coverage_dir) + lcov $(coverage_opts) --capture --no-external --ignore-errors graph \ + | sed 's|$(abspath $(builddir))|$(abspath $(srcdir))|' > $(coverage_dir)/.lcov.info + lcov --remove $(coverage_dir)/.lcov.info --output-file $(coverage_dir)/.lcov-clean.info 'test-*' + genhtml -t "kmod test coverage" -o $(coverage_dir) $(coverage_dir)/.lcov-clean.info + @echo "Coverage report generated in $(abs_builddir)/$(coverage_dir)/index.html" + +else + +lcov-run lcov-report: + echo "Need to reconfigure with --enable-coverage" + +endif + +# ------------------------------------------------------------------------------ +# coverity +# ------------------------------------------------------------------------------ + +kmod-coverity-%.tar.xz: + rm -rf $< cov-int + ./autogen.sh c --disable-manpages + make clean + cov-build --dir cov-int make -j 4 + tar caf $@ cov-int + +coverity-tar: kmod-coverity-$(shell git describe 2>/dev/null).tar.xz + +coverity-sync: kmod-coverity-$(shell git describe 2>/dev/null).tar.xz + @echo "uploading coverity tarball" + @curl --form token=$(COVERITY_KMOD_TOKEN) \ + --form email=lucas.de.marchi@gmail.com \ + --form file=@$< \ + --form version="$(shell git describe)" \ + --form description="" \ + https://scan.coverity.com/builds?project=kmod + +coverity-clean: + rm -rf kmod-coverity-*.tar.xz cov-int + +# ------------------------------------------------------------------------------ +# custom release helpers +# ------------------------------------------------------------------------------ + +git-release: + head -1 NEWS | grep -q "kmod $(VERSION)" + git commit -a -s -m "kmod $(VERSION)" + git tag -m "kmod $(VERSION)" -s v$(VERSION) + git gc --prune=0 + +kmod-$(VERSION).tar.xz: + make distcheck + +kmod-$(VERSION).tar.sign: + xz -d -c kmod-$(VERSION).tar.xz | gpg --armor --detach-sign --output kmod-$(VERSION).tar.sign + +tar: kmod-$(VERSION).tar.xz kmod-$(VERSION).tar.sign + +tar-sync: kmod-$(VERSION).tar.xz kmod-$(VERSION).tar.sign + kup put kmod-$(VERSION).tar.xz kmod-$(VERSION).tar.sign /pub/linux/utils/kernel/kmod/ diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..08c85f0 --- /dev/null +++ b/NEWS @@ -0,0 +1,1181 @@ +kmod 34 +======= + +- Improvements + + - Drop pre-built .ko modules from git - distros/packages will need the + linux-headers to be able to run the testsuite. There was limited use + of the feature, while linters complained about "source-not-included" + or "source-contains-prebuilt-binary". + + - Switch build system to meson: autotools is still supported but slated + for removal on next release. This is the transition release to help + distros and integrators to move to the new build system. Default options + target distros while developers can use the build-dev.ini configuration + file. + + - Allow to load decompression libraries on demand: liblzma.so, libz.so + and libzstd.so can now be loaded on demand, only when there is + such a need. For use during early boot for loading modules, if + configured well it means none of these libraries are loaded: the + module loading logic via finit_module() will just hand over to kernel + the open file descriptor and kernel will use its own decompress routine. + + If kernel doesn't handle decompression or if the module is compressed + with a different algorithm than the one configured in the kernel, + libkmod can still open the module by dynamically loading the + correspondent library. + + Tools inspecting the module contents, like modinfo, will load that + single decompression library instead all of them. + + For distros building with meson it's possible to choose the behavior + per library. Examples: a) -Ddlopen=all uses dlopen behavior for all + the libraries; b) -Ddlopen=xz, will make only xz to be dlopen'ed + while other enabled libraries will be linked in at build time. + + The use of dlopen is annotated in the ELF file by using the ELF + Package Metadata spec as documented in + https://systemd.io/ELF_PACKAGE_METADATA/. Example: + + $ dlopen-notes.py libkmod.so + # build/libkmod.so + [ + { + "feature": "xz", + "description": "Support for uncompressing xz-compressed modules", + "priority": "recommended", + "soname": [ + "liblzma.so.5" + ] + } + ] + + - Add -m / --moduledir to depmod to override in runtime the module + directory that was already possible to set on build time. Document + the interaction between the dir options: base, module and output. + + - Better error propagation in libkmod for its internal APIs and libc + functions up to the callers. + + - Improve libkmod API documentation by adding new sections, documenting + functions previously missing, rewording existing ones, adding version + information, cross-referencing, etc. + + - Remove deprecated arguments for depmod: --unresolved-error, --quiet, + --root and --map. + + - Remove deprecated arguments for rmmod: -w. + + - Remove deprecated arguments for insmod: -p and -s. + + - Add --syslog and --force for insmod to normalize it with other tools. + + - Add bash, fish and zsh shell-completion for insmod, rmmod and lsmod. + + - Remove depmod_module_directory_override from .pc as the kernel side + is not making use of it and will likely not need it. + + - Improve builtin module listing and retrieving information from its + modinfo index which reduces the amount of needed syscalls by 90%. + + - Improve zstd decompression by using streaming bufferless mode which + reduces the amount of syscalls by 65%. + + - Increase use of pread while parsing ELF and indexes in order to reduce + syscalls and improve performance. + + - Improve module sorting in depmod to speedup the use of the + modules.order index and support duplicate lines in it. + + - Avoid misaligned memory access while reading module signature in + libkmod. + + - Add more documentation for contributing to kmod. New developers are + welcome to look at the new README.md and CONTRIBUTING.md files for + information on process, coding style, build/installation, etc. + + - Overhaul man pages with multiple clarifications, section rewrites and + additional documentation. + + - Drop --with-rootlibdir as it's seldom used and was partially broken. + + - Drop strndupa() and alloca() for increased libc compatibility. + + - Better handling of LFS for increased compatibility with libc. + + - Protect kmod_get_dirname() and kmod_new() against NULL argument. + + - Normalize --version / --help output across all tools. + + - Always include log priority in messages, even when building with debug. + + - Optimize index reading by lazily reading nodes on demand, reducing + FILE overhead and reducing code duplication wrt FILE vs mmap + implementations, etc. + + - Switch index to pre-order to improve performance in both read and + write, meaning faster lookup and faster depmod. Some examples: + a) traversing all indexes via configuration dump shows a 9% + improvement on Raspberry Pi 2. b) writing the indexes takes 90% less + lseek() calls, leading to a performance gain of 13%. + + - Make symlink install locations more similar to what distros are + using: by default it installs the kmod binary as bin/kmod and the + symlinks are located in e.g. `sbin/depmod -> ../bin/kmod`. Changing + the sbin location is sufficient to move the symlinks to the + appropriate place, so distros using `--sbin /usr/bin` will have them + installed in that directory. This avoids distros having to remove the + symlink and add the symlinks by themselves. (meson only) + + - Install configuration directories, + /{etc,usr/lib}/{depmod,modprobe}.d/ as part of installation, matching + what several distros do during packaging. (meson only) + +- Bug fixes + + - Fix testsuite using when using configurable module dir. + + - Fix typos on documentation and source code. + + - Fix out of bound access in multiple places when using long paths, + synthetic huge files, or handling memory allocation errors, or + inconsistent variable types, particularly on 32b builds. + + - Fix internal array APIs, with better error checking: improve execution on + very memory-constrained scenarios or very long paths. + + - Fix absolute path handling in depmod. + + - Fix libkmod memory leaks on error handling when getting builtin + module list. + + - Do not crash on invalid modules.builtin.modinfo file. + + - Fix link with lld resulting in empty testsuite. + + - Fix testsuite build/execution with musl. + +- Others + + - Adopt clang-format and editorconfig for coding style and setup CI + action to keep the codebase consistent. + + - Adopt codespell in CI. + + - Adopt CodeQL integration in CI. + + - Adopt Codecov in CI. + + - Adopt SPDX copyright and license identifiers throughout the project. + + - Add more distros to CI, 32b builds, clang as compiler and lld as + linker. + + - Add support for clang sanitizers and squelch warnings. + + - Add tests for builtin modules from modinfo index file. + + - Multiple testsuite refactors and fixes to make it simpler to write tests. + + - Add CI coverage for docs + + - Improve strbuf implementation with more error checks and generalize + it to cover the role of scratchbuf. This allows to remove the + scratchbuf implementation. + + - Use common array and strbuf code in depmod to remove duplication. + + - Add abstraction and use more compiler builtins for addition and + multiplication with overflow checking. + + - Normalize use of C attributes throughout the project. + +kmod 33 +======= + +- Improvements + + - Allow to handle compressed modules even without decompression + libraries linked in. Previously we would detect if the kernel + supported the decompression algorithm and pass the module directly + through finit_module(). However it wouldn't consider the file if + the respective decompression library was compiled out. Now it's + possible to completely disable all libraries and still have module + load working with libkmod. + + Tools that inspect module content themselves like modinfo and depmod + won't work if the decompression library is not enabled. + + - Add weak dependencies - these are similar to pre softdep, but they + don't cause the dependency to be loaded by libkmod: when a module has + a weak dependency, it is expected that module may or may not be used, + with decision happening in runtime by the kernel. It's purpose is to + be informational for other tools like ones used to create initramfs, + so the module is made available before switch_root(), but doesn't + imply it to be loaded when not needed. + + - Improve compatibility with non-gnu libc like musl and uClibc. Now it's + possible to build and use libkmod and tools without any additional compat + patches. + + - Move manpages from xsltproc to scdoc, which is now needed during build. + + - Improve documentation in manpages, fixing typos, rewording sentences, + detailing how configuration files are handled with precedence order + and making all the manpages more consistent on how to reference options, + environment variables, configuration, authors, etc. + + - Speed up zstd decompression, particularly when not using glibc. + + - Stop parsing .alias files from modprobe.d directories. Configuration files + were always documented as needing the .conf extension. For compatibility + reason with module-init-tools, kmod also parsed .alias files. However that + was also done in module-init-tools for compatibility reasons and not + documented anywhere. From inspection on what distros are using, none use + .alias files in practice, so stop parsing those files and follow what's + documented. + + - Adopt SPDX for license and cleanup comments on individual files. + + - Since kmod 29 there's a github mirror for the repository. Now it's + also used for issues and improvement tracking. With that, the old + TODO file has been removed and distros/users are encouraged to file + issues in github. + +- Bug fixes + + - Move kmod.pc to the right dir, ${datadir}/pkgconfig, as it's related + to kmod, not libkmod. + + - Fix error handling while loading a file and mmap fails. + + - Fix error handling while handling errors from the decompression + libraries. + + - Add missing documentation for KMOD_INDEX_MODULES_BUILTIN that was + added in v27 breaking the ABI. A wide search has found one external + tool using it, which hasn't been updated in the past 12 years. It + was deemed safe to simply update the documentation to include the + missing enum. + + - Move kmod_module_new_from_name_lookup() to the correct symbol + version. It was added by mistake to @LIBKMOD_5 when v30 got released. + No external user of this API was found, so it was considered safe + to just move it. + +- Others + + - Overwrite symlinks when installing tools. + + - General cleanup of how (de)compression libraries are integrated. + + - Add CI infrastructure to automatically test in several distros + before applying commit series. Currently the latest versions of + Alpine, Archlinux, Fedora and Ubuntu are covered. More distros are + easy to add as they are all containerized. + +kmod 32 +======= + +- Improvements + + - Use any hash algo known by kernel/openssl instead of keep needing + to update the mapping + + - Teach kmod to load modprobe.d/depmod.d configuration from ${prefix}/lib + and allow it to be overridden during build with --with-distconfdir=DIR + + - Make kernel modules directory configurable. This allows distro to + make kmod use only files from /usr regardless of having a compat + symlink in place. + + - Install kmod.pc containing the features selected at build time. + + - Install all tools and symlinks by default. Previously kmod relied on + distro packaging to set up the symlinks in place like modprobe, + depmod, lsmod, etc. Now those symlinks are created by kmod itself + and they are always placed in $bindir. + +- Bug Fixes + + - Fix warnings due to -Walloc-size + +- Others + + - Drop python bindings. Those were not update in ages and not compatible + with latest python releases. + + - Cleanup test infra, dropping what was not used anymore + + - Drop experimental tools `kmod insert` / `kmod remove`. Building those + was protected by a configure option never set by distros. They also + didn't gain enough traction to replace the older interfaces via + modprobe/insmod/rmmod. + +kmod 31 +======= + +- Improvements + + - Allow passing a path to modprobe so the module is loaded from + anywhere from the filesystem, but still handling the module + dependencies recorded in the indexes. This is mostly intended for kernel + developers to speedup testing their kernel modules without having to load the + dependencies manually or override the module in /usr/lib/modules/. + Now it's possible to do: + + # modprobe ./drivers/gpu/drm/i915/i915.ko + + As long as the dependencies didn't change, this should do the right thing + + - Use in-kernel decompression if available. This will check the runtime support + in the kernel for decompressing modules and use it through finit_module(). + Previously kmod would fallback to the older init_module() when using + compressed modules since there wasn't a way to instruct the kernel to + uncompress it on load or check if the kernel supported it or not. + This requires a recent kernel (>= 6.4) to have that support and + in-kernel decompression properly working in the kernel. + + - Make modprobe fallback to syslog when stderr is not available, as was + documented in the man page, but not implemented + + - Better explaining `modprobe -r` and how it differentiates from rmmod + + - depmod learned a `-o ` option to allow using a separate output + directory. With this, it's possible to split the output files from + the ones used as input from the kernel build system + + - Add compat with glibc >= 2.32.9000 that dropped __xstat + + - Improve testsuite to stop skipping tests when sysconfdir is something + other than /etc + + - Build system improvements and updates + + - Change a few return codes from -ENOENT to -ENODATA to avoid confusing output + in depmod when the module itself lacks a particular ELF section due to e.g. + CONFIG_MODVERSIONS=n in the kernel. + + +- Bug Fixes + + - Fix testsuite using uninitialized memory when testing module removal + with --wait + + - Fix testsuite not correctly overriding the stat syscall on 32-bit + platforms. For most architectures this was harmless, but for MIPS it + was causing some tests to fail. + + - Fix handling unknown signature algorithm + + - Fix linking with a static liblzma, libzstd or zlib + + - Fix memory leak when removing module holders + + - Fix out-of-bounds access when using very long paths as argument to rmmod + + - Fix warnings reported by UBSan + +kmod 30 +======= + +- Improvements + - Stop adding duplicate information on modules.builtin.alias.bin, just use + the modules.builtin.bin index + + - Speedup depmod, particularly under qemu with emulated arch, by + avoiding a lot of open/read/close of modules.alias.bin. On an + emulated ARM rootfs, depmod with only 2 modules was taking ~32s + vs ~0.07s now. + + - Add kmod_module_new_from_name_lookup() which allows doing a lookup by + module name, without considering the aliases. Other than that search + order is similar to kmod_module_new_from_lookup(). + + - modinfo learned the --modname option to explicitly show information + about the module, even if there is an alias with the same name. This + allows showing information about e.g. kernel/lib/crc32.ko, even if + kernel also exports a crc32 alias in modules.alias: + + alias crc32 crc32_pclmul + alias crc32 crc32_generic + + Same behavior will be used to other modules and to aliases provided + by user/distro. + + - depmod.conf learned a new "excludedir" directive so distro/user can + configure more directories to be excluded from its search, besides + the hardcoded values "build" and "source". + + - Better group modprobe options on help output under "Management, Query and General". + + - modprobe learned a --wait option to be used together with -r + when removing a module. This allows modprobe to keep trying the + removal if it fails because the module is still in use. An exponential backoff + time is used for further retries. + + The wait behavior provided by the kernel when not passing O_NONBLOCK + to delete_module() was removed in v3.13 due to not be used and the + consequences of having to support it in the kernel. However there may + be some users, particularly on testsuites for individual subsystems, that + would want that. So provide a userspace implementation inside modprobe for + such users. "rmmod" doesn't have a --wait as it remains a bare minimal over + the API provided by the kernel. In future the --wait behavior can be added + to libkmod for testsuites not exec'ing modprobe for module removal. + + - kmod_module_remove_module() learned a new flag to silence output when + caller wants to handle them - this is particularly important for the + --wait flag to modprobe, as it's not desired to keep seeing error messages + while waiting for the module to be unused. + + - Add SM3 hash algo support to modinfo output, as already available in the kernel. + +- Bug Fixes + - Fix modinfo output when showing information for a .ko module when running + on a kernel that has that module as builtin. + + - Fix kmod_module_new_from_lookup() returning > 0 rather than 0 + when it matches an alias. + + - Fix modinfo segfault when module doesn't exist. + + - Add missing function in the html documentation: kmod_get_dirname(). + + - Fix modprobe incorrectly handling number of arguments when prepending values from + MODPROBE_OPTIONS environment variable. + + - Fix modprobe -r --remove-dependencies and since "dependencies" was a + misnomer, add the preferred argument option: "--remove-holders". This + is the same name used by the kernel. It allows users to also remove + other modules holding the one that is being removed. + + - Fix off-by-one in max module name length in depmod. + +- Infra/internal + - Start some changes in the out-of-tree test modules in kmod so they are useful + for being really inserted in the kernel rather than relying on kmod's mock + interface. This helps manual testing and may be used to exercise to test + changes in the kernel. + +kmod 29 +======= + +- Improvements + - Add support to use /usr/local as a place for configuration files. This makes it easier + to install locally without overriding distro files. + +- Bug fixes + - Fix `modinfo -F` when module is builtin: when we asked by a specific field from modinfo, + it was not working correctly if the module was builtin + + - Documentation fixes on precedence order of /etc and /run: the correct order is + /etc/modprobe.d, /run/modprobe.d, /lib/modprobe.d + + - Fix the priority order that we use for searching configuration files. The + correct one is /etc, /run, /usr/local/lib, /lib, for both modprobe.d + and depmo.d + + - Fix kernel command line parsing when there are quotes present. Grub + mangles the command line and changes it from 'module.option="val with + spaces"' to '"module.option=val with spaces"'. Although this is weird + behavior and grub could have been fixed, the kernel understands it + correctly for builtin modules. So change libkmod to also parse it + correctly. This also brings another hidden behavior from the kernel: + newline in the kernel command line is also allowed and can be used to + separate options. + + - Fix a memory leak, overflow and double free on error path + + - Fix documentation for return value from kmod_module_get_info(): we + return the number of entries we added to the list + + - Fix output of modules.builtin.alias.bin index: we were writing an empty file due to + the misuse of kmod_module_get_info() + +- Infra/internal + - Retire integration with semaphoreci + + - Declare the github mirror also as an official upstream source: now besides accepting + patches via mailing list, PRs on github are also acceptable + + - Misc improvements to testsuite, so we can use it reliably regardless + of the configuration used: now tests will skip if we don't have the + build dependencies) + +kmod 28 +======= + +- Improvements + - Add Zstandard to the supported compression formats using libzstd + (pass --with-zstd to configure) + +- Bug fixes + - Ignore ill-formed kernel command line, e.g. with "ivrs_acpihid[00:14.5]=AMD0020:0" + option in it + - Fix some memory leaks + - Fix 0-length builtin.alias.bin: it needs at least the index header + +kmod 27 +======= + +- Improvements + - Link to libcrypto rather than requiring openssl + + - Print a better error message when kernel doesn't support module unload + + - Use PKCS#7 instead of CMS for parsing module signature to be + compatible with LibreSSL and OpenSSL < 1.1.0 + + - Teach modinfo to parse modules.builtin.modinfo. When using Linux kernel + >= v5.2-rc1 it's possible to get module information from this new file. Now + modinfo is able to show it instead of an error message that the module is + built-in: + + Before: + $ modinfo ext4 + modinfo: ERROR: Module ext4 not found. + + After: + $ modinfo ext4 + name: ext4 + filename: (builtin) + softdep: pre: crc32c + license: GPL + description: Fourth Extended Filesystem + author: Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others + alias: fs-ext4 + alias: ext3 + alias: fs-ext3 + alias: ext2 + alias: fs-ext2 + +- Bug fixes + - Do not link python bindings with libpython to be compatible with + python3.8 + + - Fix module removal with `modprobe -r` when a dependency is built-in. + Now it properly ignores them and proceed with removal of other + dependencies + + - Fix propagation of return code from install/remove commands to the + the probe function. The return values of kmod_module_probe_insert_module() + have very specific meanings, do not confuse the caller by return codes + from system() + + - Fix softdep config parsing leading to buffer overflow + +kmod 26 +======= + +- Improvements + - Add more error-checking in library functions and remove warnings on newer + toolchains + + - Depmod now handles parallel invoctions better by protecting the temporary + files being used + + - Improvements to testsuite and added tests to check the our behavior + regardless of the features enabled in the kernel, or libraries we link to + + - Teach the --show-exports option to modprobe. This works similarly to + --show-modversions, but it reports the exported symbols from that module. + Under the hood this reads the .symtab and .strtab section rather than + __versions so it shows useful data even if kernel is configured without + modversions (CONFIG_MODVERSIONS) + + - Teach pkcs7 parsing to modinfo by using openssl. This allows modinfo to + correctly parse the signature appended to a module by the kernel build + system when configured with CONFIG_MODULE_SIG_ALL, or when externally + signed by the distro. Traditionally modules were signed and a struct + was appended together with the signature to the end of the module. + This has changed on the kernel for pkcs#7 and now the structure isn't + filled out with useful information. So we have to parse the signature + block in order to return useful data to the user. + + If kmod is linked with openssl we parse the signature and return the + fields as we do for other signatures. An example of the relevant part + on the output of modinfo is below: + + Before: + sig_id: PKCS#7 + signer: + sig_key: + sig_hashalgo: md4 + After: + sig_id: PKCS#7 + signer: Fedora kernel signing key + sig_key: 51:C4:0C:6D:7E:A5:6C:D8:8F:B4:3A:DF:91:78:4F:18:BC:D5:E4:C5 + sig_hashalgo: sha256 + + If kmod is not linked to openssl we just start printing "unknown" in the + sig_hashalgo field rather than the bogus value. + + +kmod 25 +======= + +- Improvements + - Add module signature to modinfo output + + - Add support for external directories in depmod: now there's a new + "external" keyword parsed by depmod when calculating the dependencies. + It allows to add modules to other directories which are not relative + to where the modules are commonly installed. This results in + modules.dep and friends now understanding absolute paths rather than + relative paths only. For more information see depmod.d(1). + + - Add support for CONFIG_MODULE_REL_CRCS + + - Add missing documentation references in man pages + + - Handle the case in which module has a .TOC symbol already while + calculating dependencies + + - Improve testsuite and allow to use mkosi to run testsuite in different + distros + +kmod 24 +======= + +- Improvements: + - Add more information on dependency loop + + - Sanitize use of strcpy and allow to grow from small strings on stack + (common case) to bigger strings on heap when needed + +- Bug fixes + - Fix wrong dependency loops being reported by depmod + + - Fix crashes when reporting dependency loops + + - Fix parsing kernel command line containing quotes + + - Fix leaks on error paths + +kmod 23 +======= + +- Improvements: + - Don't add comment to modules.devname if it would otherwise be empty + to play nice with tools detecting empty files + + - Allow building with BSD sed, that doesn't have -E flag + + - Ignore .TOC. symbols in depmod parsing as it's for PPC64 the + equivalent of _GLOBAL_OFFSET_TABLE_ + + - Teach modinfo about PKCS#7 module signatures: it doesn't add any + other info besides telling the user the module is signed since + kernel doesn't add other info on the module section + +- Bug fixes + + - Fix -s and -p compat options to insmod triggering force flag + + - Fix long lines from /proc/modules not being handled correctly by + kmod_module_new_from_loaded() and kmod_module_get_size() and several + other library functions that use them + + - Fix crash on modinfo while checking for available signature of + unknown type + + - Fix documentation generation with gtk-doc + +kmod 22 +======= + +- Tools: + - Change default log level for tools to WARNING rather than ERROR and update + some log levels for current messages + + - depmod doesn't fallback to uname if a bad version is passed in the command + line anymore. We just exit with an error. + + - insmod was taught the -f flag, just like in modprobe. It was previously + silently ignoring it. + +- libkmod + - New kmod_get_dirname() API to get the module directory set in the + context + +- Bug fixes: + - Fix return code in error path of kmod_module_insert_module(). We were + previously returning ENOSYS rather than ENOENT. + +kmod 21 +======= + +- New features: + - kmod tool started to learn the "insert" and "remove" commands that + are the simplified versions of the older modprobe tool. These + commands are still work in progress so they are hidden behind a + --enable-experimental flag during build. It should not be enabled + unless you know what you're doing. + - kmod tool now prints the relevant configuration options it was built + with when the "--version" argument is passed. This helps to mitigate + problems for example when the user is trying to load a compressed + module but kmod was built without support for the compression method. + +- Improvements to testsuite: + - Cache built modules so it is easier to run "make check" on build + servers by distro maintainers. If kmod is configured with + --disable-test-modules the modules from cache will be used by + "make check". No changes to the tests are needed and all of them + can run fine. + +kmod 20 +======= +- Bug fixes: + - Handle bogus values from ELF, making sure they don't overflow while + parsing the file + - Fix leak in depmod when -b flag is passed multiple times + - Multiple minor fixes from static analysis by coverity and + clang-analyze + - Fix race between loading modules and checking if it's loaded in the + kernel + +- New features: + - There's a change in behavior regarding builtin modules: we now only + consider as builtin those that are present in modules.builtin index. + Previously we were also checking the presence of + /sys/module/, but this is racy and only modules that + contain parameters are the ones creating a directory in sysfs. + + Now some commands will start to fail, e.g. "modprobe vt". Since vt + can't be compiled as a module it's not present in modules.builtin + index. Previously we would report at as builtin, but now we fail + because we couldn't find the module. + +- Improvements: + - Integration of gcov into the build. Currently libkmod is at ~70% + covered and tools at ~50% by tests in the testsuite. Utility + functions and structures in shared have more than 90% of coverage. + - Upload build to coverity + +- Improvements to testsuite: + - Fix parsing return codes of init_module() calls + - Add tests for utility functions in shared/ + - Add tests for kmod_module_remove_module() + - Add playground, in which our own modules are compiled + - Port all tests to use modules from module-playground instead of + copying prebuilt modules to the repository + - Properly handle binaries that exit with no output + - Besides comparing the output of commands, allow to copy to + stdout/stderr + +kmod 19 +======= + +- Bug fixes: + - Fix missing CLOEXEC in library + - Fix error message while opening kmod's index + +- New features: + - Add kmod(8) man page + - Allow to build with libc's without be32toh() + - Move code around separating common code and data structures into a + shared directory. This allows to share more code between library and + tools, making the binary size of tools smaller. + - Clarify tools vs library licenses + - static-nodes: when writing in tmpfiles format, indicate that + creation of static nodes should only happen at boot. This is used and + required by systemd-217+. + +- Improvements to testsuite: + - Add tests for newly created shared/ code + - Improve how tests are declared so there's less boilerplate code for + each test. + +kmod 18 +======= + +- Bug fixes: + - Fix leaks in error paths + - Fix use-after-free in hash implementation causing a wrong index to be + generated by depmod with out-of-tree modules + +- New features: + - Calling depmod with modules creating a dependency loop will now make + depmod return an error and not update the indexes. This is to protect + the current index not being overridden by another index that may cause + a boot failure, depending on the buggy module. It's a necessary + change in behavior regarding previous kmod releases and + module-init-tools. The error message was also improved to output + the modules that caused the dependency cycle. + +- Improvements to testsuite: + - Fix and improve expected-fail test + - Add tests for hashmap implementation + +kmod 17 +======= + +- Bug fixes: + - Fix matching a "." in kernel cmdline, making garbage in the command + line be parsed as kmod options + - Fix man pages to clarify we don't fallback to parsing modules.dep + but instead we depend on modules.dep.bin (generated by depmod) to + be present + - Fix ELF parsing on 32 bit systems assigning the wrong class. + - Fix partial matches of search directives in depmod. Previously having + a line in depmod.conf such as "search foo foobar built-in" would cause + unpretictable results because foo is a partial match of foobar as well. + - Fix unaligned access in modinfo when getting the signature from a + module + - Make sure softdeps are treated as optional dependencies + +- New features: + - Accept special files given to "-C" switch in modprobe. This way it's + possible to skip system configuration with "modprobe -C /dev/null" + - Do not require xsltproc on released tarballs + - Don't use Werror anymore + - Add experimental python bindings, merged from python-kmod repository + (https://github.com/agrover/python-kmod) + - Parse softdeps exported by the kernel as + /lib/modules/`uname -r`/modules.softdep + +- Improvements to testsuite: + - Check the list of loaded modules after a test + +kmod 16 +======= + +- Bug fixes: + - Fix usage of readdir_r() + - Add some missing checks for memory allocation errors + +- New features: + - Remove option from libkmod to allow waiting on module removal if + the module is being used. It's dangerous since it can block the + caller indefinitely. + - Improve compatibility with musl libc + - Add fallback implementation for compilers without _Static_assert(), + e.g. gcc < 4.6 + - Minor optimizations to the hash table + - Make depmod warn if a module has incorrect devname specification + - Use cleanup attribute + +kmod 15 +======= + +- Bug fixes: + - kmod static-nodes doesn't fail if modules.devname isn't available + - Fix getting boolean parameter from kernel cmdline in case the value + is omitted + - Fix some mkdir_p() corner cases (used in testsuite and static-nodes) + +- New features: + - kmod static-nodes creates parent directories if given a -o option + - kmod binary statically links to libkmod - if distro is only interested + in the kmod tool (for example in an initrd) it can refrain from + installing the library + - Add shell completion for kmod tool + +kmod 14 +======= + +- Bug fixes: + - Fix some format strings + - Protect against NULL being passed around to index + - Avoid calling syscall() with -1 when finit_module() is not available, + since this doesn't always work + - Fix not being able to remove alias due to checking the module's + refcount + - Minor fixes and refactors + +- New features: + - Improve libkmod documentation, particularly on how flags are dealt + with. + - Remove ability to build a static libkmod + - Add static-nodes command to kmod that parses modules.devname + generating output in useful formats + +kmod 13 +======= + +- Bug fixes: + - Add the long option --symbol-prefix option to depmod (it was absent) + and fix its behavior + - Don't abort if there's a bogus line in configuration file like "alias + psmouse off". Some distros are carrying this since the days of + modutils + +- New features: + - Add support for finit_module(2). If the module is load straight from + the disk and without compression we use finit_module() syscall when + available, falling back to init_module() otherwise + - kmod_module_get_info() also returns the signature if the module is + signed and modinfo uses it + - Use secure_getenv if available + - rmmod understands builtin modules, just like modprobe does + - Improve compatibility with musl-libc + - Test cases exit with success when receiving a signal if they are + xfail tests + +kmod 12 +======= + +- Bug fixes: + - Fix removing vermagic from module when told to force load a module + - Fix removing __versions section when told to force load a module: we + need to mangle the section header, not the section. + - modinfo no longer fails while loading a module from file when path + contains ".ko" substring + +kmod 11 +======= + +- Improvements to testsuite: + - Fix testsuite defining symbols twice on 32 bit systems + - Allow to check generated files against correct ones + +- New features: + - libkmod now keeps a file opened after the first call to + kmod_module_get_{info,versions,symbols,dependency_symbols}. This + reduces significantly the amount of time depmod tool takes to + execute. Particularly if compressed modules are used. + - Remove --with-rootprefix from build system. It was not a great idea + after all and should not be use since it causes more harm then + benefits. + - Hide --wait option on rmmod. This feature is being targeted for + removal from kernel. rmmod still accepts this option, but it's hidden + now: man page and usage() say nothing about it and if it's used, + user will get a 10s sleep. This way we can check and help if anyone + is using this feature. + - Refactor message logging on all tools, giving proper prefix, routing + everything to syslog when asked for, etc. + +- Bug fixes: + - Fix parsing of modules.order when using compressed modules + - Usage messages go to stdout instead of stderr + - Fix memory leak in hash implementation + +kmod 10 +======= + +- New features: + - Read coresize from /sys if supported + + - Add flag to kmod_module_probe_insert() to apply blacklisting during + probe only if mod is an alias. Now modprobe uses this flag by default. + This is needed to fix a change in behavior regarding module-init-tools + and ultimately makes us loading a blacklisted module. + +- Better formatting in man pages + +- Add option to disable building man pages at build time + +- Fixes in the testsuite and refactoring of LDPRELOAD'ed libraries + +- Re-licensing testsuite as LGPL + +kmod 9 +====== + +- Improvements to the testsuite: + - Check for correct handling of softdep loops + - Check for correct handling of install command loops + +- Bug fixes: + - Fix build with compilers that don't support --gc-sections + - Handle errors when dealing with gzipped modules + - depmod now handles errors while writing indices, so it doesn't end up + with a corrupted index without telling the user + +kmod 8 +====== + +- No new features, small bug fixes only. + - Fix a bug in "modprobe -c" output: be compatible with + module-init-tools + + - Give a useful error message when init_module fails due to bad + parameter or unknown symbols + + - Fix doc generation + +kmod 7 +====== + +- Re-order dirs for configuration files to match the change in systemd and + udev: now the priority is: + 1. /etc/modprobe.d + 2. /run/modprobe.d + 3. /lib/modprobe.d + +- Fix setting CFLAGS/LDFLAGS in build system. This prevented us from not + allowing the user to set his preferences. + +- Bug fixes: + - Return same error codes of module-init-tools when removing modules + with modprobe + - Fix builtin output in "--show-depends" when target kernel is not the + same of the running kernel + - 'modprobe -r' always look at all command line arguments + - Fix '-q' usage in modprobe + +kmod 6 +====== + +- New API in libkmod: + - kmod_module_apply_filter(): a generic function to apply filters in a + list of modules. This deprecates the use of + kmod_module_get_filtered_blacklist() + +- More tests in testsuite + +- Add compatibility with uClibc again + +- Lookup modules.builtin.bin to decide if a module is built in kernel + +- Downgrade some log messages so we don't annoy people with useless messages + +- Bug fixes: + - Flag --ignore-loaded was not being properly handled + - Infinite loop with softdeps + - Infinite loop with dumb user configuration with install commands + - Fix leak in index when there's a partial match + +- Move repository and tarballs to kernel.org + +kmod 5 +====== + +- Break libkmod's API to insert a module like modprobe does. It now accepts + extra an extra argument to print its action and acceptable flags were + sanitized. + +- Share more code between modprobe and libkmod: using the new version of + kmod_module_probe_insert_module() it's possible to share a great amount of + code between modprobe and libkmod + +- modprobe no longer works with paths: it only accepts module names and/or + aliases now. + +- testsuite was added to repository, allowing automated tests to be run and + easing the way bugs are reproduced. + +- modprobe: when dumping configuration ('-c' option) separate config + and indexes by adding a commented line between them. + +- Fix bugs wrt normalizing aliases and module names + +- Fix bug wrt inserting an alias that resolves to multiple modules: we should + not stop on the first error, but rather continue to try loading other + modules. + +- Fix unaligned memory access in hash function, causing depmod to output wrong + information in ARMv5 + +- Fix man page build and install: now they are only installed if tools are + enabled + +kmod 4 +====== + +- New APIs in libkmod to: + - Get configuration lists: blacklists, install commands, remove + commands, aliases, options and softdeps + - Dump indexes + +- Several bugs fixed in libkmod, modprobe, depmod and modinfo + +- API documentation: if configure with run with --enable-gtk-doc, the API doc + will be generated by make. Gtk-doc is required for that. + +- Man pages are built, which replace man pages from module-init-tools + +- 'include' and 'config' options in *.conf files were deprecated + +- configure is not run by autogen.sh. Instead, a common set of options is + printed. If you are hacking on kmod, consider using bootstrap-configure + script. + +- 'modprobe -c' works as expected now. As opposed to module-init-tools, it + dumps the parsed configuration, not only the file contents. + +kmod 3 +====== + +- New APIs in libkmod to: + - Get symbols from module, parsing the ELF section + - Get dependency symbols + - Check if resources are still valid or if libkmod must be reloaded + - Insert module like modprobe, checking (soft-)dependencies, commands, + blacklist. It can run commands by itself and to call a callback + function. + +- Support to load modules compressed with xz + +- Tools are now bundled together in a single tool called kmod. It can be + called using symlinks with the same names as tools from module-init-tools. + E.g: /usr/bin/lsmod -> /usr/bin/kmod. With this we are aiming to complete a + 1:1 replacement of module-init-tools. + +- The only missing tool, depmod, was added to kmod together with the necessary + APIs in libkmod. + +- If a program using libkmod runs for a long time, as for example udev, it must + check if it doesn't have to re-load libkmod. A new helper function was added + in libkmod to check if context is still valid and udev is already using it. + +- An 'unaligned access' bug was fixed. So those architecture that does not + handle unaligned access can use kmod, too. + +kmod 2 +====== + +Some bugs fixed: the worst of them was with an infinite loop when an alias +matched more than one module. + +- New APIs in libkmod to: + - Get soft dependencies + - Get info from module files parsing ELF + - Get modversions from files parsing ELF + +- Support to load gzipped kernel modules: kmod can be compiled with support to + gzipped modules by giving the --enable-zlib flag + +- Support to forcefully load modules, both vermagic and modversion + +- Support to force and nowait removal flags + +- Configuration files are parsed in the same order as modprobe: files are + sorted alphabetically (independently of their dir) and files with the same + name obey a precedence order + +- New tool: kmod-modinfo + +- kmod-modprobe gained several features to be a 1:1 replacement for modprobe. + The only missing things are the options '--showconfig' and '-t / -l'. These + last ones have been deprecated long ago and they will be removed from + modprobe. A lot of effort has been put on kmod-modprobe to ensure it + maintains compatibility with modprobe. + +- linux-modules@vger.kernel.org became the official mailing list for kmod + +kmod 1 +====== + +First version of kmod and its library, libkmod. + +In the libkmod it's currently possible to: + - List modules currently loaded + - Get information about loaded modules such as initstate, refcount, + holders, sections, address and size + - Lookup modules by alias, module name or path + - Insert modules: options from configuration and extra options can be + passed, but flags are not implemented, yet + - Remove modules + - Filter list of modules using blacklist + - For each module, get the its list of options and install/remove + commands + - Indexes can be loaded on startup to speedup lookups later + +Tools provided with the same set of options as in module-init-tools: + - kmod-lsmod + - kmod-insmod + - kmod-rmmod + - kmod-modprobe, with some functionality still missing (use of softdep, + dump configuration, show modversions) diff --git a/README.md b/README.md new file mode 100644 index 0000000..5169890 --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +## kmod - Linux kernel module handling + +OVERVIEW +======== + +kmod is a set of tools to handle common tasks with Linux kernel modules like +insert, remove, list, check properties, resolve dependencies and aliases. + +These tools are designed on top of libkmod, a library that is shipped with +kmod. See libkmod/README for more details on this library and how to use it. +The aim is to be compatible with tools, configurations and indexes from +module-init-tools project. + + +Links +===== +- Mailing list (no subscription needed): linux-modules@vger.kernel.org +- Mailing list archives: https://lore.kernel.org/linux-modules/ + +- Signed packages: http://www.kernel.org/pub/linux/utils/kernel/kmod/ + +- Git: + - Official: https://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git + - Mirror: https://github.com/kmod-project/kmod + - Mirror: https://kernel.googlesource.com/pub/scm/utils/kernel/kmod/kmod.git + +- License: + - LGPLv2.1+ for libkmod, testsuite and helper libraries + - GPLv2+ for tools/* + +- Irc: `#kmod` on irc.oftc.net + +- Issues: https://github.com/kmod-project/kmod/issues + + +Compilation and installation +============================ + +In order to compile the source code you need the following software packages: +- GCC/CLANG compiler +- GNU C library / musl / uClibc + +Optional dependencies, required with the default build configuration: +- ZLIB library +- LZMA library +- ZSTD library +- OPENSSL library (signature handling in modinfo) + +Typical configuration and installation + + meson setup builddir/ + meson compile -C builddir/ + sudo meson install -C builddir/ + +For end-user and distributions builds, it's recommended to use: + + meson setup --buildtype release builddir/ + +Alternatively you can try autotools build. +NOTE: The autotools build is slated for removal with kmod v35 + + ./configure CFLAGS="-g -O2" --prefix=/usr \ + --sysconfdir=/etc --libdir=/usr/lib + make && make install + +Hacking +======= + +When working on kmod, use the included `build-dev.ini` file, as: + + meson setup --native-file build-dev.ini builddir/ + +Make sure to read [our contributing guide](CONTRIBUTING.md) and the other +READMEs: [libkmod](libkmod/README) and [testsuite](testsuite/README). + +Compatibility with module-init-tools +==================================== + +kmod replaced module-init-tools, which was EOL'ed in 2011. All the tools were +rewritten on top of libkmod and they can be used as drop in replacements. +Along the years there were a few behavior changes and new features implemented, +following feedback from Linux kernel community and distros. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..e42a535 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,88 @@ +#!/bin/sh + +set -e + +oldpwd=$(pwd) + +if [ -n "$MESON_DIST_ROOT" ]; then + topdir="$MESON_DIST_ROOT" + gtkdocize_args="--copy" + autoreconf_args= +else + topdir=$(dirname $0) + gtkdocize_args= + autoreconf_args="--symlink" +fi + +cd $topdir + +gtkdocize ${gtkdocize_args} --docdir libkmod/docs || NO_GTK_DOC="yes" +if [ "x$NO_GTK_DOC" = "xyes" ] +then + for f in libkmod/docs/gtk-doc.make m4/gtk-doc.m4 + do + rm -f $f + touch $f + done +fi + +autoreconf --force --install ${autoreconf_args} + +libdir() { + echo $(cd "$1/$(gcc -print-multi-os-directory)"; pwd) +} + +args="\ +--prefix=/usr \ +--sysconfdir=/etc \ +--libdir=$(libdir /usr/lib) \ +" + +if [ -f "$topdir/.config.args" ]; then + args="$args $(cat $topdir/.config.args)" +fi + +cd $oldpwd + +hackargs="\ +--enable-debug \ +--enable-gtk-doc \ +--with-zstd \ +--with-xz \ +--with-zlib \ +--with-openssl \ +" + +if [ "x$1" = "xc" ]; then + shift + $topdir/configure CFLAGS='-g -O2' $args $hackargs "$@" + make clean +elif [ "x$1" = "xg" ]; then + shift + $topdir/configure CFLAGS='-g -Og' $args "$@" + make clean +elif [ "x$1" = "xl" ]; then + shift + $topdir/configure CC=clang CXX=clang++ $args "$@" + make clean +elif [ "x$1" = "xa" ]; then + shift + $topdir/configure CFLAGS='-g -O2 -Wsuggest-attribute=pure -Wsuggest-attribute=const' $args "$@" + make clean +elif [ "x$1" = "xs" ]; then + shift + scan-build $topdir/configure CFLAGS='-g -O0 -std=gnu11' $args "$@" + scan-build make +else + echo + echo "----------------------------------------------------------------" + echo "Initialized build system. For a common configuration please run:" + echo "----------------------------------------------------------------" + echo + echo "$topdir/configure CFLAGS='-g -O2' $args" + echo + echo If you are debugging or hacking on kmod, consider configuring + echo like below: + echo + echo "$topdir/configure CFLAGS='-g -O2' $args $hackargs" +fi diff --git a/build-dev.ini b/build-dev.ini new file mode 100644 index 0000000..804893f --- /dev/null +++ b/build-dev.ini @@ -0,0 +1,18 @@ +; SPDX-FileCopyrightText: 2024 Emil Velikov +; SPDX-FileCopyrightText: 2024 Lucas De Marchi +; +; SPDX-License-Identifier: LGPL-2.1-or-later + +[project options] +build-tests = true +debug-messages = true +docs = true +zstd = 'enabled' +xz = 'enabled' +zlib = 'enabled' +openssl = 'enabled' +werror = true +b_sanitize = 'address,undefined' + +[built-in options] +buildtype = 'debugoptimized' diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..9443268 --- /dev/null +++ b/configure.ac @@ -0,0 +1,360 @@ +AC_PREREQ(2.64) +AC_INIT([kmod], + [34.2], + [linux-modules@vger.kernel.org], + [kmod], + [http://git.kernel.org/?p=utils/kernel/kmod/kmod.git]) + +AC_CONFIG_SRCDIR([libkmod/libkmod.c]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS(config.h) +AC_CONFIG_AUX_DIR([build-aux]) + +AC_USE_SYSTEM_EXTENSIONS +AC_SYS_LARGEFILE +AC_PREFIX_DEFAULT([/usr]) +AM_MAINTAINER_MODE([enable]) +AM_INIT_AUTOMAKE([foreign 1.11 silent-rules tar-pax no-dist-gzip dist-xz subdir-objects color-tests parallel-tests]) +AM_SILENT_RULES([yes]) +LT_INIT([disable-static pic-only]) + +AS_IF([test "x$enable_static" = "xyes"], [AC_MSG_ERROR([--enable-static is not supported by kmod])]) +AS_IF([test "x$enable_largefile" = "xno"], [AC_MSG_ERROR([--disable-largefile is not supported by kmod])]) + +module_compressions="" +module_signatures="legacy" + +##################################################################### +# Program checks and configurations +##################################################################### + +AC_PROG_SED +AC_PROG_MKDIR_P +AC_PROG_LN_S +PKG_PROG_PKG_CONFIG + +AC_PROG_CC_C99 + +##################################################################### +# Function and structure checks +##################################################################### + +AC_CHECK_FUNCS_ONCE([open64 stat64 fopen64 __stat64_time64]) +AC_CHECK_FUNCS_ONCE([secure_getenv]) + +CC_CHECK_FUNC_BUILTIN([__builtin_clz]) +CC_CHECK_FUNC_BUILTIN([__builtin_types_compatible_p]) +CC_CHECK_FUNC_BUILTIN([__builtin_uadd_overflow], [ ], [ ]) +CC_CHECK_FUNC_BUILTIN([__builtin_uaddl_overflow], [ ], [ ]) +CC_CHECK_FUNC_BUILTIN([__builtin_uaddll_overflow], [ ], [ ]) +CC_CHECK_FUNC_BUILTIN([__builtin_umul_overflow], [ ], [ ]) +CC_CHECK_FUNC_BUILTIN([__builtin_umull_overflow], [ ], [ ]) +CC_CHECK_FUNC_BUILTIN([__builtin_umulll_overflow], [ ], [ ]) + +# dietlibc doesn't have st.st_mtim struct member +AC_CHECK_MEMBERS([struct stat.st_mtim], [], [], [#include ]) + +# basename may be only available in libgen.h with the POSIX behavior, +# not desired here +AC_CHECK_DECLS_ONCE([[basename]], [], [], [[#include ]]) +AC_CHECK_DECLS_ONCE([[__xstat]], [], [], [[#include + _Noreturn int foo(void) { exit(0); }]])], + [AC_DEFINE([HAVE_NORETURN], [1], [Define if _Noreturn is available]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) + + +##################################################################### +# --with- +##################################################################### + +AC_ARG_WITH([distconfdir], AS_HELP_STRING([--with-distconfdir=DIR], [directory to search for distribution configuration files]), + [], [with_distconfdir='${prefix}/lib']) +AC_SUBST([distconfdir], [$with_distconfdir]) + +# Ideally this would be $prefix/lib/modules but default to /lib/modules for compatibility with earlier versions +AC_ARG_WITH([module_directory], + AS_HELP_STRING([--with-module-directory=DIR], [directory in which to look for kernel modules @<:@default=/lib/modules@:>@]), + [], [with_module_directory=/lib/modules]) +AC_SUBST([module_directory], [$with_module_directory]) + +# Check all directory arguments for consistency. +for ac_var in distconfdir module_directory +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*@<:@^/@:>@\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + @<:@\\/$@:>@* | ?:@<:@\\/@:>@* ) continue;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +AC_ARG_WITH([zstd], + AS_HELP_STRING([--with-zstd], [handle Zstandard-compressed modules @<:@default=disabled@:>@]), + [], [with_zstd=no]) +AS_IF([test "x$with_zstd" != "xno"], [ + PKG_CHECK_MODULES([libzstd], [libzstd >= 1.4.4], [LIBS="$LIBS $libzstd_LIBS"]) + AC_DEFINE([ENABLE_ZSTD], [1], [Zstandard for modules.]) + module_compressions="zstd $module_compressions" +], [ + AC_DEFINE([ENABLE_ZSTD], [0], [Zstandard for modules.]) + AC_MSG_NOTICE([Zstandard support not requested]) +]) +CC_FEATURE_APPEND([with_features], [with_zstd], [ZSTD]) +AM_CONDITIONAL([ENABLE_ZSTD], [test "x$with_zstd" != "xno"]) +AC_DEFINE([ENABLE_ZSTD_DLOPEN], [0], [dlopen zstd]) + +AC_ARG_WITH([xz], + AS_HELP_STRING([--with-xz], [handle Xz-compressed modules @<:@default=disabled@:>@]), + [], [with_xz=no]) +AS_IF([test "x$with_xz" != "xno"], [ + PKG_CHECK_MODULES([liblzma], [liblzma >= 4.99], [LIBS="$LIBS $liblzma_LIBS"]) + AC_DEFINE([ENABLE_XZ], [1], [xz for modules.]) + module_compressions="xz $module_compressions" +], [ + AC_DEFINE([ENABLE_XZ], [0], [xz for modules.]) + AC_MSG_NOTICE([Xz support not requested]) +]) +CC_FEATURE_APPEND([with_features], [with_xz], [XZ]) +AM_CONDITIONAL([ENABLE_XZ], [test "x$with_xz" != "xno"]) +AC_DEFINE([ENABLE_XZ_DLOPEN], [0], [dlopen xz]) + +AC_ARG_WITH([zlib], + AS_HELP_STRING([--with-zlib], [handle gzipped modules @<:@default=disabled@:>@]), + [], [with_zlib=no]) +AS_IF([test "x$with_zlib" != "xno"], [ + PKG_CHECK_MODULES([zlib], [zlib], [LIBS="$LIBS $zlib_LIBS"]) + AC_DEFINE([ENABLE_ZLIB], [1], [zlib for modules.]) + module_compressions="gzip $module_compressions" +], [ + AC_DEFINE([ENABLE_ZLIB], [0], [zlib for modules.]) + AC_MSG_NOTICE([zlib support not requested]) +]) +CC_FEATURE_APPEND([with_features], [with_zlib], [ZLIB]) +AM_CONDITIONAL([ENABLE_ZLIB], [test "x$with_zlib" != "xno"]) +AC_DEFINE([ENABLE_ZLIB_DLOPEN], [0], [dlopen zlib]) + +AC_ARG_WITH([openssl], + AS_HELP_STRING([--with-openssl], [handle PKCS7 signatures @<:@default=disabled@:>@]), + [], [with_openssl=no]) +AS_IF([test "x$with_openssl" != "xno"], [ + PKG_CHECK_MODULES([libcrypto], [libcrypto >= 1.1.0], [LIBS="$LIBS $libcrypto_LIBS"]) + AC_DEFINE([ENABLE_OPENSSL], [1], [openssl for modinfo.]) + module_signatures="PKCS7 $module_signatures" +], [ + AC_DEFINE([ENABLE_OPENSSL], [0], [openssl for modinfo.]) + AC_MSG_NOTICE([openssl support not requested]) +]) +CC_FEATURE_APPEND([with_features], [with_openssl], [LIBCRYPTO]) + +AC_ARG_WITH([bashcompletiondir], + AS_HELP_STRING([--with-bashcompletiondir=DIR], [Bash completions directory]), + [], + [AS_IF([$($PKG_CONFIG --exists bash-completion)], [ + with_bashcompletiondir=$($PKG_CONFIG --variable=completionsdir bash-completion) + ] , [ + with_bashcompletiondir=${datadir}/bash-completion/completions + ])]) +AC_SUBST([bashcompletiondir], [$with_bashcompletiondir]) + +AC_ARG_WITH([fishcompletiondir], + AS_HELP_STRING([--with-fishcompletiondir=DIR], [Fish completions directory]), + [], + [AS_IF([$($PKG_CONFIG --exists fish)], [ + with_fishcompletiondir=$($PKG_CONFIG --variable=completionsdir fish) + ] , [ + with_fishcompletiondir=${datadir}/fish/vendor_functions.d + ])]) +AC_SUBST([fishcompletiondir], [$with_fishcompletiondir]) + +AC_ARG_WITH([zshcompletiondir], + AS_HELP_STRING([--with-zshcompletiondir=DIR], [Zsh completions directory]), + [], + [with_zshcompletiondir=${datadir}/zsh/site-functions]) +AC_SUBST([zshcompletiondir], [$with_zshcompletiondir]) + +##################################################################### +# --enable- +##################################################################### + +AC_ARG_ENABLE([tools], + AS_HELP_STRING([--disable-tools], [disable building tools that provide same functionality as module-init-tools @<:@default=enabled@:>@]), + [], enable_tools=yes) +AM_CONDITIONAL([BUILD_TOOLS], [test "x$enable_tools" = "xyes"]) + +AC_ARG_ENABLE([manpages], + AS_HELP_STRING([--disable-manpages], [disable manpages @<:@default=enabled@:>@]), + [], enable_manpages=yes) +AS_IF([test "x$enable_manpages" = "xyes"], [ + AC_PATH_PROG([SCDOC], [scdoc]) + AS_IF([test "x$SCDOC" = "x"],[ + AC_MSG_ERROR([*** scdoc needed for building manpages. Either install it or pass --disable-manpages]) + ])]) +AM_CONDITIONAL([BUILD_MANPAGES], [test "x$enable_manpages" = "xyes"]) + +AC_ARG_ENABLE([logging], + AS_HELP_STRING([--disable-logging], [disable system logging @<:@default=enabled@:>@]), + [], enable_logging=yes) +AS_IF([test "x$enable_logging" = "xyes"], [ + AC_DEFINE(ENABLE_LOGGING, [1], [System logging.]) +], [ + AC_DEFINE(ENABLE_LOGGING, [0], [System logging.]) +]) + +AC_ARG_ENABLE([debug], + AS_HELP_STRING([--enable-debug], [enable debug messages @<:@default=disabled@:>@]), + [], [enable_debug=no]) +AS_IF([test "x$enable_debug" = "xyes"], [ + AC_DEFINE(ENABLE_DEBUG, [1], [Debug messages.]) +], [ + AC_DEFINE(ENABLE_DEBUG, [0], [Debug messages.]) +]) +AC_DEFINE(ENABLE_ELFDBG, [0], [Debug elf parsing messages.]) + +AC_ARG_ENABLE([coverage], + AS_HELP_STRING([--enable-coverage], [enable test coverage @<:@default=disabled@:>@]), + [], [enable_coverage=no]) +AS_IF([test "x$enable_coverage" = "xyes"], [ + AC_CHECK_PROG(have_coverage, [lcov], [yes], [no]) + AS_IF([test "x$have_coverage" = xno],[ + AC_MSG_ERROR([*** lcov support requested but the program was not found]) + ], [ + lcov_version_major="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 1`" + lcov_version_minor="`lcov --version | cut -d ' ' -f 4 | cut -d '.' -f 2`" + AS_IF([test "$lcov_version_major" -lt 1 -o "$lcov_version_minor" -lt 10], [ + AC_MSG_ERROR([*** lcov version is too old. 1.10 required]) + ], [ + have_coverage=yes + CC_CHECK_FLAGS_APPEND([with_coverage_cflags], [CFLAGS], [\ + -fprofile-arcs \ + -ftest-coverage]) + ]) + ]) +]) +AM_CONDITIONAL([ENABLE_COVERAGE], [test "x$enable_coverage" = "xyes"]) + +m4_ifdef([GTK_DOC_CHECK], [ +GTK_DOC_CHECK([1.14],[--flavour no-tmpl-flat]) +], [ +AM_CONDITIONAL([ENABLE_GTK_DOC], false)]) + +PKG_INSTALLDIR +PKG_NOARCH_INSTALLDIR + +##################################################################### +# Default CFLAGS and LDFLAGS +##################################################################### + +CC_CHECK_FLAGS_APPEND(with_cflags, [CFLAGS], [\ + -pipe \ + -fdata-sections \ + -fdiagnostics-show-option \ + -ffunction-sections \ + -fno-common \ + -fvisibility=hidden \ + -W \ + -Wall \ + -Wchar-subscripts \ + -Wdeclaration-after-statement \ + -Wendif-labels \ + -Wextra \ + -Wfloat-equal \ + -Wformat=2 \ + -Wformat-nonliteral \ + -Wformat-security \ + -Winit-self \ + -Wlogical-op \ + -Wmissing-declarations \ + -Wmissing-include-dirs \ + -Wmissing-noreturn \ + -Wmissing-prototypes \ + -Wnested-externs \ + -Wno-attributes=clang::suppress \ + -Wno-unused-parameter \ + -Wold-style-definition \ + -Wpointer-arith \ + -Wredundant-decls \ + -Wshadow \ + -Wsign-compare \ + -Wstrict-aliasing=3 \ + -Wstrict-prototypes \ + -Wtype-limits \ + -Wundef \ + -Wuninitialized \ + -Wvla \ + -Wwrite-strings]) +AC_SUBST([OUR_CFLAGS], "$with_cflags $with_coverage_cflags") + + +CC_CHECK_FLAGS_APPEND([with_ldflags], [LDFLAGS], [ \ + -Wl,--as-needed \ + -Wl,--no-undefined \ + -Wl,--gc-sections]) +AC_SUBST([OUR_LDFLAGS], $with_ldflags) + +AC_DEFINE_UNQUOTED(KMOD_FEATURES, ["$with_features"], [Features in this build]) + +##################################################################### +# Generate files from *.in +##################################################################### + +AC_SUBST([module_compressions], $module_compressions) +AC_SUBST([module_signatures], $module_signatures) + +AC_CONFIG_FILES([ + Makefile + man/Makefile + libkmod/docs/Makefile + libkmod/docs/version.xml + libkmod/libkmod.pc + tools/kmod.pc +]) + + +##################################################################### + +AC_OUTPUT +AC_MSG_RESULT([ + $PACKAGE $VERSION + ======= + + module_directory: ${module_directory} + prefix: ${prefix} + sysconfdir: ${sysconfdir} + distconfdir: ${distconfdir} + libdir: ${libdir} + includedir: ${includedir} + bindir: ${bindir} + Bash completions dir: ${with_bashcompletiondir} + + compiler: ${CC} + cflags: ${with_cflags} ${CFLAGS} + ldflags: ${with_ldflags} ${LDFLAGS} + + tools: ${enable_tools} + logging: ${enable_logging} + compression: zstd=${with_zstd} xz=${with_xz} zlib=${with_zlib} + debug: ${enable_debug} + coverage: ${enable_coverage} + doc: ${enable_gtk_doc} + man: ${enable_manpages} + + features: ${with_features} +]) diff --git a/libkmod/.gitignore b/libkmod/.gitignore new file mode 100644 index 0000000..826fd62 --- /dev/null +++ b/libkmod/.gitignore @@ -0,0 +1,6 @@ +.dirstamp +.deps/ +.libs/ +*.la +*.lo +libkmod.pc diff --git a/libkmod/COPYING b/libkmod/COPYING new file mode 100644 index 0000000..8add30a --- /dev/null +++ b/libkmod/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/libkmod/Makefile b/libkmod/Makefile new file mode 100644 index 0000000..223bec2 --- /dev/null +++ b/libkmod/Makefile @@ -0,0 +1,13 @@ +# Copyright 2010 Lennart Poettering +# +# This file has been copied from systemd. It is a dirty trick to simplify +# compilation when CWD is not the root of the source tree. This file is not +# intended to be distributed. So, don't touch it, even better ignore it! + +all: + $(MAKE) -C .. + +clean: + $(MAKE) -C .. clean + +.PHONY: all clean diff --git a/libkmod/README b/libkmod/README new file mode 100644 index 0000000..3e1c8dc --- /dev/null +++ b/libkmod/README @@ -0,0 +1,58 @@ +libkmod - linux kernel module handling library + +ABSTRACT +======== + +libkmod was created to allow programs to easily insert, remove and +list modules, also checking its properties, dependencies and aliases. + +there is no shared/global context information and it can be used by +multiple sites on a single program, also being able to be used from +threads, although it's not thread safe (you must lock explicitly). + + +OVERVIEW +======== + +Every user should create and manage it's own library context with: + + struct kmod_ctx *ctx = kmod_new(kernel_dirname); + kmod_unref(ctx); + + +Modules can be created by various means: + + struct kmod_module *mod; + int err; + + err = kmod_module_new_from_path(ctx, path, &mod); + if (err < 0) { + /* code */ + } else { + /* code */ + kmod_module_unref(mod); + } + + err = kmod_module_new_from_name(ctx, name, &mod); + if (err < 0) { + /* code */ + } else { + /* code */ + kmod_module_unref(mod); + } + + +Or could be resolved from a known alias to a list of alternatives: + + struct kmod_list *list, *itr; + int err; + err = kmod_module_new_from_lookup(ctx, alias, &list); + if (err < 0) { + /* code */ + } else { + kmod_list_foreach(itr, list) { + struct kmod_module *mod = kmod_module_get_module(itr); + /* code */ + } + } + diff --git a/libkmod/docs/.gitignore b/libkmod/docs/.gitignore new file mode 100644 index 0000000..7514b08 --- /dev/null +++ b/libkmod/docs/.gitignore @@ -0,0 +1,14 @@ +*.bak +*.stamp +*.sgml +libkmod.* +libkmod-*.xml +!libkmod-docs.xml +libkmod-*.txt +!libkmod-sections.txt +version.xml +xml +html +gtk-doc.make +Makefile +Makefile.in diff --git a/libkmod/docs/Makefile.am b/libkmod/docs/Makefile.am new file mode 100644 index 0000000..41482e9 --- /dev/null +++ b/libkmod/docs/Makefile.am @@ -0,0 +1,34 @@ + +AUTOMAKE_OPTIONS = 1.11 + +DOC_MODULE = libkmod + +DOC_MODULE_VERSION = 3 + +DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.xml + +DOC_SOURCE_DIR = $(top_srcdir)/libkmod + +SCAN_OPTIONS = --ignore-decorators="KMOD_EXPORT" + +MKDB_OPTIONS = --xml-mode --output-format=xml --name-space kmod + +MKHTML_OPTIONS = --path=$(abs_srcdir)/doc --path=$(abs_builddir)/doc + +HFILE_GLOB = $(top_srcdir)/libkmod/libkmod.h +CFILE_GLOB = $(top_srcdir)/libkmod/libkmod.c $(top_srcdir)/libkmod/libkmod-module.c $(top_srcdir)/libkmod/libkmod-list.c + +IGNORE_HFILES = \ + libkmod-index.h \ + libkmod-internal-file.h \ + libkmod-internal.h + +content_files = version.xml + +EXTRA_DIST = + +if ENABLE_GTK_DOC +include $(top_srcdir)/libkmod/docs/gtk-doc.make +else +EXTRA_DIST += libkmod-docs.xml libkmod-sections.txt +endif diff --git a/libkmod/docs/libkmod-docs.xml b/libkmod/docs/libkmod-docs.xml new file mode 100644 index 0000000..674fe3a --- /dev/null +++ b/libkmod/docs/libkmod-docs.xml @@ -0,0 +1,63 @@ + + + +]> + + + libkmod Reference Manual + for libkmod version &version; + + + + libkmod + + + + + + + + + API Index + + + + Index of deprecated symbols + + + + Index of new symbols in 1 + + + + Index of new symbols in 2 + + + + Index of new symbols in 3 + + + + Index of new symbols in 4 + + + + Index of new symbols in 6 + + + + Index of new symbols in 22 + + + + Index of new symbols in 30 + + + + Index of new symbols in 33 + + + diff --git a/libkmod/docs/libkmod-sections.txt b/libkmod/docs/libkmod-sections.txt new file mode 100644 index 0000000..dd50264 --- /dev/null +++ b/libkmod/docs/libkmod-sections.txt @@ -0,0 +1,118 @@ +
+libkmod +kmod_ctx +kmod_new +kmod_ref +kmod_unref + +kmod_load_resources +kmod_unload_resources +kmod_resources +kmod_validate_resources +kmod_index +kmod_dump_index + +kmod_set_log_priority +kmod_get_log_priority +kmod_set_log_fn +kmod_get_userdata +kmod_set_userdata +kmod_get_dirname +
+ +
+libkmod-list +kmod_list +kmod_list_foreach +kmod_list_foreach_reverse +kmod_list_last +kmod_list_next +kmod_list_prev +
+ +
+libkmod-config +kmod_config_iter +kmod_config_get_blacklists +kmod_config_get_install_commands +kmod_config_get_remove_commands +kmod_config_get_aliases +kmod_config_get_options +kmod_config_get_softdeps +kmod_config_get_weakdeps +kmod_config_iter_get_key +kmod_config_iter_get_value +kmod_config_iter_next +kmod_config_iter_free_iter +
+ +
+libkmod-module +kmod_module +kmod_module_new_from_lookup +kmod_module_new_from_name_lookup +kmod_module_new_from_name +kmod_module_new_from_path + +kmod_module_ref +kmod_module_unref +kmod_module_unref_list + +kmod_insert +kmod_module_insert_module +kmod_probe +kmod_module_probe_insert_module +kmod_remove +kmod_module_remove_module + +kmod_module_get_module +kmod_module_get_dependencies +kmod_module_get_softdeps +kmod_module_get_weakdeps +kmod_filter +kmod_module_apply_filter +kmod_module_get_filtered_blacklist +kmod_module_get_install_commands +kmod_module_get_remove_commands +kmod_module_get_name +kmod_module_get_options +kmod_module_get_path + +kmod_module_get_dependency_symbols +kmod_symbol_bind +kmod_module_dependency_symbol_get_bind +kmod_module_dependency_symbol_get_crc +kmod_module_dependency_symbol_get_symbol +kmod_module_dependency_symbols_free_list + +kmod_module_get_sections +kmod_module_section_get_address +kmod_module_section_get_name +kmod_module_section_free_list + +kmod_module_get_symbols +kmod_module_symbol_get_crc +kmod_module_symbol_get_symbol +kmod_module_symbols_free_list + +kmod_module_get_versions +kmod_module_version_get_crc +kmod_module_version_get_symbol +kmod_module_versions_free_list + +kmod_module_get_info +kmod_module_info_get_key +kmod_module_info_get_value +kmod_module_info_free_list +
+ +
+libkmod-loaded +kmod_module_new_from_loaded +kmod_module_initstate +kmod_module_get_initstate +kmod_module_initstate_str +kmod_module_get_size +kmod_module_get_refcnt +kmod_module_get_holders +
diff --git a/libkmod/docs/meson.build b/libkmod/docs/meson.build new file mode 100644 index 0000000..b4d41cc --- /dev/null +++ b/libkmod/docs/meson.build @@ -0,0 +1,29 @@ +gnome = import('gnome') + +version_file = configure_file( + input: 'version.xml.in', + output: 'version.xml', + configuration: cdata, +) + +built_docs = gnome.gtkdoc( + 'libkmod', + content_files : version_file, + ignore_headers : [ + '@0@/libkmod/libkmod-index.h'.format(meson.project_source_root()), + '@0@/libkmod/libkmod-internal-file.h'.format(meson.project_source_root()), + '@0@/libkmod/libkmod-internal.h'.format(meson.project_source_root()), + ], + scan_args : '--ignore-decorators="KMOD_EXPORT"', + src_dir : '@0@/libkmod/'.format(meson.project_source_root()), + namespace : 'kmod', + module_version : '3', + main_xml : 'libkmod-docs.xml', +) + +test( + 'test-gtkdoc', + test_gtkdoc, + args : meson.current_build_dir(), + depends : built_docs, +) diff --git a/libkmod/docs/version.xml.in b/libkmod/docs/version.xml.in new file mode 100644 index 0000000..d78bda9 --- /dev/null +++ b/libkmod/docs/version.xml.in @@ -0,0 +1 @@ +@VERSION@ diff --git a/libkmod/libkmod-builtin.c b/libkmod/libkmod-builtin.c new file mode 100644 index 0000000..f354d45 --- /dev/null +++ b/libkmod/libkmod-builtin.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2019 Alexey Gladkov + * Copyright (C) 2024 Tobias Stöckmann + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libkmod.h" +#include "libkmod-internal.h" + +#define MODULES_BUILTIN_MODINFO "modules.builtin.modinfo" + +struct kmod_builtin_info { + struct kmod_ctx *ctx; + + // The file handle. + FILE *fp; + + // Internal buffer and its size. Used by getdelim. + size_t bufsz; + char *buf; +}; + +static bool kmod_builtin_info_init(struct kmod_builtin_info *info, struct kmod_ctx *ctx) +{ + char path[PATH_MAX]; + FILE *fp = NULL; + const char *dirname = kmod_get_dirname(ctx); + size_t len = strlen(dirname); + + if ((len + 1 + strlen(MODULES_BUILTIN_MODINFO) + 1) >= PATH_MAX) { + errno = ENAMETOOLONG; + return false; + } + snprintf(path, PATH_MAX, "%s/" MODULES_BUILTIN_MODINFO, dirname); + + fp = fopen(path, "r"); + if (fp == NULL) + return false; + + info->ctx = ctx; + info->fp = fp; + info->bufsz = 0; + info->buf = NULL; + + return true; +} + +static void kmod_builtin_info_release(struct kmod_builtin_info *info) +{ + free(info->buf); + fclose(info->fp); +} + +static ssize_t get_string(struct kmod_builtin_info *info) +{ + ssize_t len; + + len = getdelim(&info->buf, &info->bufsz, '\0', info->fp); + if (len > 0 && info->buf[len] != '\0') { + errno = EINVAL; + len = -1; + } + + return len; +} + +static ssize_t get_strings(struct kmod_builtin_info *info, const char *modname, + struct strbuf *buf) +{ + const size_t modlen = strlen(modname); + ssize_t count, n; + + for (count = 0; count < INTPTR_MAX;) { + char *dot, *line; + + n = get_string(info); + if (n == -1) { + if (!feof(info->fp)) { + count = -errno; + ERR(info->ctx, "get_string: %s\n", strerror(errno)); + } + break; + } + + line = info->buf; + dot = strchr(line, '.'); + if (dot == NULL) { + count = -EINVAL; + ERR(info->ctx, "get_strings: " + "unexpected string without modname prefix\n"); + return count; + } + if (strncmp(line, modname, modlen) || line[modlen] != '.') { + /* + * If no string matched modname yet, keep searching. + * Otherwise this indicates that no further string will + * match again. + */ + if (count == 0) + continue; + break; + } + if (!strbuf_pushchars(buf, dot + 1) || !strbuf_pushchar(buf, '\0')) { + count = -errno; + ERR(info->ctx, "get_strings: " + "failed to append modinfo string\n"); + return count; + } + count++; + } + + if (count == INTPTR_MAX) { + count = -ENOMEM; + ERR(info->ctx, "get_strings: " + "too many modinfo strings encountered\n"); + return count; + } + + return count; +} + +static char **strbuf_to_vector(struct strbuf *buf, size_t count) +{ + size_t vec_size, total_size; + char **vector; + char *s; + size_t n; + + /* (string vector + NULL) * sizeof(char *) + strbuf_used() */ + if (uaddsz_overflow(count, 1, &n) || + umulsz_overflow(sizeof(char *), n, &vec_size) || + uaddsz_overflow(strbuf_used(buf), vec_size, &total_size)) { + errno = ENOMEM; + return NULL; + } + + vector = realloc(buf->bytes, total_size); + if (vector == NULL) + return NULL; + + memmove(vector + count + 1, vector, strbuf_used(buf)); + s = (char *)(vector + count + 1); + strbuf_init(buf); + + for (n = 0; n < count; n++) { + vector[n] = s; + s += strlen(s) + 1; + } + vector[n] = NULL; + + return vector; +} + +/* array will be allocated with strings in a single malloc, just free *array */ +ssize_t kmod_builtin_get_modinfo(struct kmod_ctx *ctx, const char *modname, + char ***modinfo) +{ + DECLARE_STRBUF(buf); + struct kmod_builtin_info info; + ssize_t count; + + if (!kmod_builtin_info_init(&info, ctx)) + return -errno; + + count = get_strings(&info, modname, &buf); + if (count == 0) + *modinfo = NULL; + else if (count > 0) { + *modinfo = strbuf_to_vector(&buf, (size_t)count); + if (*modinfo == NULL) { + count = -errno; + ERR(ctx, "strbuf_to_vector: %s\n", strerror(errno)); + } + } + + kmod_builtin_info_release(&info); + return count; +} diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c new file mode 100644 index 0000000..ee1cb94 --- /dev/null +++ b/libkmod/libkmod-config.c @@ -0,0 +1,1294 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2011-2013 ProFUSION embedded systems + * Copyright (C) 2013 Intel Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libkmod.h" +#include "libkmod-internal.h" + +struct kmod_alias { + char *name; + char modname[]; +}; + +struct kmod_options { + char *options; + char modname[]; +}; + +struct kmod_command { + char *command; + char modname[]; +}; + +struct kmod_softdep { + char *name; + const char **pre; + const char **post; + unsigned int n_pre; + unsigned int n_post; +}; + +struct kmod_weakdep { + char *name; + const char **weak; + unsigned int n_weak; +}; + +const char *kmod_blacklist_get_modname(const struct kmod_list *l) +{ + return l->data; +} + +const char *kmod_alias_get_name(const struct kmod_list *l) +{ + const struct kmod_alias *alias = l->data; + return alias->name; +} + +const char *kmod_alias_get_modname(const struct kmod_list *l) +{ + const struct kmod_alias *alias = l->data; + return alias->modname; +} + +const char *kmod_option_get_options(const struct kmod_list *l) +{ + const struct kmod_options *alias = l->data; + return alias->options; +} + +const char *kmod_option_get_modname(const struct kmod_list *l) +{ + const struct kmod_options *alias = l->data; + return alias->modname; +} + +const char *kmod_command_get_command(const struct kmod_list *l) +{ + const struct kmod_command *alias = l->data; + return alias->command; +} + +const char *kmod_command_get_modname(const struct kmod_list *l) +{ + const struct kmod_command *alias = l->data; + return alias->modname; +} + +const char *kmod_softdep_get_name(const struct kmod_list *l) +{ + const struct kmod_softdep *dep = l->data; + return dep->name; +} + +const char *const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) +{ + const struct kmod_softdep *dep = l->data; + *count = dep->n_pre; + return dep->pre; +} + +const char *const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count) +{ + const struct kmod_softdep *dep = l->data; + *count = dep->n_post; + return dep->post; +} + +const char *kmod_weakdep_get_name(const struct kmod_list *l) +{ + const struct kmod_weakdep *dep = l->data; + return dep->name; +} + +const char *const *kmod_weakdep_get_weak(const struct kmod_list *l, unsigned int *count) +{ + const struct kmod_weakdep *dep = l->data; + *count = dep->n_weak; + return dep->weak; +} +static int kmod_config_add_command(struct kmod_config *config, const char *modname, + const char *command, const char *command_name, + struct kmod_list **list) +{ + _cleanup_free_ struct kmod_command *cmd; + struct kmod_list *l; + size_t modnamelen = strlen(modname) + 1; + size_t commandlen = strlen(command) + 1; + + DBG(config->ctx, "modname='%s' cmd='%s %s'\n", modname, command_name, command); + + cmd = malloc(sizeof(*cmd) + modnamelen + commandlen); + if (!cmd) + return -ENOMEM; + + cmd->command = sizeof(*cmd) + modnamelen + (char *)cmd; + memcpy(cmd->modname, modname, modnamelen); + memcpy(cmd->command, command, commandlen); + + l = kmod_list_append(*list, cmd); + if (!l) + return -ENOMEM; + + TAKE_PTR(cmd); + *list = l; + + return 0; +} + +static int kmod_config_add_options(struct kmod_config *config, const char *modname, + const char *options) +{ + _cleanup_free_ struct kmod_options *opt; + struct kmod_list *list; + size_t modnamelen = strlen(modname) + 1; + size_t optionslen = strlen(options) + 1; + + DBG(config->ctx, "modname='%s' options='%s'\n", modname, options); + + opt = malloc(sizeof(*opt) + modnamelen + optionslen); + if (!opt) + return -ENOMEM; + + opt->options = sizeof(*opt) + modnamelen + (char *)opt; + + memcpy(opt->modname, modname, modnamelen); + memcpy(opt->options, options, optionslen); + strchr_replace(opt->options, '\t', ' '); + + list = kmod_list_append(config->options, opt); + if (!list) + return -ENOMEM; + + TAKE_PTR(opt); + config->options = list; + + return 0; +} + +static int kmod_config_add_alias(struct kmod_config *config, const char *name, + const char *modname) +{ + _cleanup_free_ struct kmod_alias *alias; + struct kmod_list *list; + size_t namelen = strlen(name) + 1, modnamelen = strlen(modname) + 1; + + DBG(config->ctx, "name=%s modname=%s\n", name, modname); + + alias = malloc(sizeof(*alias) + namelen + modnamelen); + if (!alias) + return -ENOMEM; + + alias->name = sizeof(*alias) + modnamelen + (char *)alias; + + memcpy(alias->modname, modname, modnamelen); + memcpy(alias->name, name, namelen); + + list = kmod_list_append(config->aliases, alias); + if (!list) + return -ENOMEM; + + TAKE_PTR(alias); + config->aliases = list; + + return 0; +} + +static int kmod_config_add_blacklist(struct kmod_config *config, const char *modname) +{ + _cleanup_free_ char *p; + struct kmod_list *list; + + DBG(config->ctx, "modname=%s\n", modname); + + _clang_suppress_alloc_ p = strdup(modname); + if (!p) + return -ENOMEM; + + list = kmod_list_append(config->blacklists, p); + if (!list) + return -ENOMEM; + + TAKE_PTR(p); + config->blacklists = list; + + return 0; +} + +static int kmod_config_add_softdep(struct kmod_config *config, const char *modname, + const char *line) +{ + struct kmod_list *list; + struct kmod_softdep *dep; + const char *s, *p; + char *itr; + unsigned int n_pre = 0, n_post = 0; + size_t modnamelen = strlen(modname) + 1; + size_t buflen = 0, size, size_pre, size_post; + bool was_space = false; + enum { S_NONE, S_PRE, S_POST } mode = S_NONE; + + DBG(config->ctx, "modname=%s\n", modname); + + /* analyze and count */ + for (p = s = line;; s++) { + size_t plen; + + if (*s != '\0') { + if (!isspace(*s)) { + was_space = false; + continue; + } + + if (was_space) { + p = s + 1; + continue; + } + was_space = true; + + if (p >= s) + continue; + } + plen = s - p; + + if (plen == sizeof("pre:") - 1 && + memcmp(p, "pre:", sizeof("pre:") - 1) == 0) + mode = S_PRE; + else if (plen == sizeof("post:") - 1 && + memcmp(p, "post:", sizeof("post:") - 1) == 0) + mode = S_POST; + else if (*s != '\0' || (*s == '\0' && !was_space)) { + if (mode == S_PRE) { + buflen += plen + 1; + if (uadd32_overflow(n_pre, 1, &n_pre)) { + ERR(config->ctx, + "too many pre softdeps for modname=%s\n", + modname); + return -EINVAL; + } + } else if (mode == S_POST) { + buflen += plen + 1; + if (uadd32_overflow(n_post, 1, &n_post)) { + ERR(config->ctx, + "too many post softdeps for modname=%s\n", + modname); + return -EINVAL; + } + } + } + p = s + 1; + if (*s == '\0') + break; + } + + DBG(config->ctx, "%u pre, %u post\n", n_pre, n_post); + + /* + * sizeof(struct kmod_softdep) + modnamelen + + * n_pre * sizeof(const char *) + n_post * sizeof(const char *) + buflen + */ + if (uaddsz_overflow(sizeof(struct kmod_softdep), modnamelen, &size) || + umulsz_overflow(n_pre, sizeof(const char *), &size_pre) || + uaddsz_overflow(size, size_pre, &size) || + umulsz_overflow(n_post, sizeof(const char *), &size_post) || + uaddsz_overflow(size, size_post, &size) || + uaddsz_overflow(size, buflen, &size)) { + ERR(config->ctx, "out-of-memory modname=%s\n", modname); + return -ENOMEM; + } + + dep = malloc(size); + if (dep == NULL) { + ERR(config->ctx, "out-of-memory modname=%s\n", modname); + return -ENOMEM; + } + dep->n_pre = n_pre; + dep->n_post = n_post; + dep->pre = (const char **)((char *)dep + sizeof(struct kmod_softdep)); + dep->post = dep->pre + n_pre; + dep->name = (char *)(dep->post + n_post); + + memcpy(dep->name, modname, modnamelen); + + /* copy strings */ + itr = dep->name + modnamelen; + n_pre = 0; + n_post = 0; + mode = S_NONE; + was_space = false; + for (p = s = line;; s++) { + size_t plen; + + if (*s != '\0') { + if (!isspace(*s)) { + was_space = false; + continue; + } + + if (was_space) { + p = s + 1; + continue; + } + was_space = true; + + if (p >= s) + continue; + } + plen = s - p; + + if (plen == sizeof("pre:") - 1 && + memcmp(p, "pre:", sizeof("pre:") - 1) == 0) + mode = S_PRE; + else if (plen == sizeof("post:") - 1 && + memcmp(p, "post:", sizeof("post:") - 1) == 0) + mode = S_POST; + else if (*s != '\0' || (*s == '\0' && !was_space)) { + if (mode == S_PRE) { + dep->pre[n_pre] = itr; + memcpy(itr, p, plen); + itr[plen] = '\0'; + itr += plen + 1; + n_pre++; + } else if (mode == S_POST) { + dep->post[n_post] = itr; + memcpy(itr, p, plen); + itr[plen] = '\0'; + itr += plen + 1; + n_post++; + } + } + p = s + 1; + if (*s == '\0') + break; + } + + list = kmod_list_append(config->softdeps, dep); + if (list == NULL) { + free(dep); + return -ENOMEM; + } + config->softdeps = list; + + return 0; +} + +static int kmod_config_add_weakdep(struct kmod_config *config, const char *modname, + const char *line) +{ + struct kmod_list *list; + struct kmod_weakdep *dep; + const char *s, *p; + char *itr; + unsigned int n_weak = 0; + size_t modnamelen = strlen(modname) + 1; + size_t buflen = 0, size, size_weak; + bool was_space = false; + + DBG(config->ctx, "modname=%s\n", modname); + + /* analyze and count */ + for (p = s = line;; s++) { + size_t plen; + + if (*s != '\0') { + if (!isspace(*s)) { + was_space = false; + continue; + } + + if (was_space) { + p = s + 1; + continue; + } + was_space = true; + + if (p >= s) + continue; + } + plen = s - p; + + if (*s != '\0' || (*s == '\0' && !was_space)) { + buflen += plen + 1; + if (uadd32_overflow(n_weak, 1, &n_weak)) { + ERR(config->ctx, "too many weakdeps for modname=%s\n", + modname); + return -EINVAL; + } + } + p = s + 1; + if (*s == '\0') + break; + } + + DBG(config->ctx, "%u weak\n", n_weak); + + /* sizeof(struct kmod_weakdep) + modnamelen + n_weak * sizeof(const char *) + buflen */ + if (uaddsz_overflow(sizeof(struct kmod_weakdep), modnamelen, &size) || + umulsz_overflow(n_weak, sizeof(const char *), &size_weak) || + uaddsz_overflow(size, size_weak, &size) || + uaddsz_overflow(size, buflen, &size)) { + ERR(config->ctx, "out-of-memory modname=%s\n", modname); + return -ENOMEM; + } + + dep = malloc(size); + if (dep == NULL) { + ERR(config->ctx, "out-of-memory modname=%s\n", modname); + return -ENOMEM; + } + dep->n_weak = n_weak; + dep->weak = (const char **)((char *)dep + sizeof(struct kmod_weakdep)); + dep->name = (char *)(dep->weak + n_weak); + + memcpy(dep->name, modname, modnamelen); + + /* copy strings */ + itr = dep->name + modnamelen; + n_weak = 0; + was_space = false; + for (p = s = line;; s++) { + size_t plen; + + if (*s != '\0') { + if (!isspace(*s)) { + was_space = false; + continue; + } + + if (was_space) { + p = s + 1; + continue; + } + was_space = true; + + if (p >= s) + continue; + } + plen = s - p; + + if (*s != '\0' || (*s == '\0' && !was_space)) { + dep->weak[n_weak] = itr; + memcpy(itr, p, plen); + itr[plen] = '\0'; + itr += plen + 1; + n_weak++; + } + p = s + 1; + if (*s == '\0') + break; + } + + list = kmod_list_append(config->weakdeps, dep); + if (list == NULL) { + free(dep); + return -ENOMEM; + } + config->weakdeps = list; + + return 0; +} + +static char *softdep_to_char(struct kmod_softdep *dep) +{ + const size_t sz_preprefix = sizeof("pre: ") - 1; + const size_t sz_postprefix = sizeof("post: ") - 1; + size_t sz = 1; /* at least '\0' */ + size_t sz_pre, sz_post; + const char *start, *end; + char *s, *itr; + + /* + * Rely on the fact that dep->pre[] and dep->post[] are strv's that + * point to a contiguous buffer + */ + if (dep->n_pre > 0) { + start = dep->pre[0]; + end = dep->pre[dep->n_pre - 1] + strlen(dep->pre[dep->n_pre - 1]); + sz_pre = end - start; + sz += sz_pre + sz_preprefix; + } else + sz_pre = 0; + + if (dep->n_post > 0) { + start = dep->post[0]; + end = dep->post[dep->n_post - 1] + strlen(dep->post[dep->n_post - 1]); + sz_post = end - start; + sz += sz_post + sz_postprefix; + } else + sz_post = 0; + + itr = s = malloc(sz); + if (s == NULL) + return NULL; + + if (sz_pre) { + char *p; + + memcpy(itr, "pre: ", sz_preprefix); + itr += sz_preprefix; + + /* include last '\0' */ + memcpy(itr, dep->pre[0], sz_pre + 1); + for (p = itr; p < itr + sz_pre; p++) { + if (*p == '\0') + *p = ' '; + } + itr = p; + } + + if (sz_post) { + char *p; + + memcpy(itr, "post: ", sz_postprefix); + itr += sz_postprefix; + + /* include last '\0' */ + memcpy(itr, dep->post[0], sz_post + 1); + for (p = itr; p < itr + sz_post; p++) { + if (*p == '\0') + *p = ' '; + } + itr = p; + } + + *itr = '\0'; + + return s; +} + +static char *weakdep_to_char(struct kmod_weakdep *dep) +{ + size_t sz = 1; /* at least '\0' */ + size_t sz_weak; + const char *start, *end; + char *s, *itr; + + /* Rely on the fact that dep->weak[] is a strv that points to a contiguous buffer */ + if (dep->n_weak > 0) { + start = dep->weak[0]; + end = dep->weak[dep->n_weak - 1] + strlen(dep->weak[dep->n_weak - 1]); + sz_weak = end - start; + sz += sz_weak; + } else + sz_weak = 0; + + itr = s = malloc(sz); + if (s == NULL) + return NULL; + + if (sz_weak) { + char *p; + + /* include last '\0' */ + memcpy(itr, dep->weak[0], sz_weak); + for (p = itr; p < itr + sz_weak; p++) { + if (*p == '\0') + *p = ' '; + } + itr = p; + } + + *itr = '\0'; + + return s; +} + +static void kcmdline_parse_result(struct kmod_config *config, char *modname, char *param, + char *value) +{ + if (modname == NULL || param == NULL) + return; + + DBG(config->ctx, "%s %s\n", modname, param); + + if (streq(modname, "modprobe") && !strncmp(param, "blacklist=", 10)) { + for (;;) { + char *t = strsep(&value, ","); + if (t == NULL) + break; + + kmod_config_add_blacklist(config, t); + } + } else { + if (underscores(modname) < 0) { + ERR(config->ctx, + "Ignoring bad option on kernel command line while parsing module name: '%s'\n", + modname); + } else { + kmod_config_add_options(config, modname, param); + } + } +} + +static int kmod_config_parse_kcmdline(struct kmod_config *config) +{ + char buf[KCMD_LINE_SIZE]; + int fd, err; + char *p, *p_quote_start, *modname, *param = NULL, *value = NULL; + bool is_quoted = false, iter = true; + enum state { + STATE_IGNORE, + STATE_MODNAME, + STATE_PARAM, + STATE_VALUE, + STATE_COMPLETE, + } state; + + fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC); + if (fd < 0) { + err = -errno; + DBG(config->ctx, "could not open '/proc/cmdline' for reading: %m\n"); + return err; + } + + err = read_str_safe(fd, buf, sizeof(buf)); + close(fd); + if (err < 0) { + ERR(config->ctx, "could not read from '/proc/cmdline': %s\n", + strerror(-err)); + return err; + } + + state = STATE_MODNAME; + p_quote_start = NULL; + for (p = buf, modname = buf; iter; p++) { + switch (*p) { + case '"': + is_quoted = !is_quoted; + + /* + * only allow starting quote as first char when looking + * for a modname: anything else is considered ill-formed + */ + if (is_quoted && state == STATE_MODNAME && p == modname) { + p_quote_start = p; + modname = p + 1; + } else if (state != STATE_VALUE) { + state = STATE_IGNORE; + } + + break; + case '\0': + iter = false; + /* fall-through */ + case ' ': + case '\n': + case '\t': + case '\v': + case '\f': + case '\r': + if (is_quoted && state == STATE_VALUE) { + /* no state change*/; + } else if (is_quoted) { + /* spaces are only allowed in the value part */ + state = STATE_IGNORE; + } else if (state == STATE_VALUE || state == STATE_PARAM) { + *p = '\0'; + state = STATE_COMPLETE; + } else { + /* + * go to next option, ignoring any possible + * partial match we have + */ + modname = p + 1; + state = STATE_MODNAME; + p_quote_start = NULL; + } + break; + case '.': + if (state == STATE_MODNAME) { + *p = '\0'; + param = p + 1; + state = STATE_PARAM; + } else if (state == STATE_PARAM) { + state = STATE_IGNORE; + } + break; + case '=': + if (state == STATE_PARAM) { + /* + * Don't set *p to '\0': the value var shadows + * param + */ + value = p + 1; + state = STATE_VALUE; + } else if (state == STATE_MODNAME) { + state = STATE_IGNORE; + } + break; + } + + if (state == STATE_COMPLETE) { + /* + * We may need to re-quote to unmangle what the + * bootloader passed. Example: grub passes the option as + * "parport.dyndbg=file drivers/parport/ieee1284_ops.c +mpf" + * instead of + * parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf" + */ + if (p_quote_start && p_quote_start < modname) { + /* + * p_quote_start + * | + * |modname param value + * || | | + * vv v v + * "parport\0dyndbg=file drivers/parport/ieee1284_ops.c +mpf" */ + memmove(p_quote_start, modname, value - modname); + value--; + modname--; + param--; + *value = '"'; + } + kcmdline_parse_result(config, modname, param, value); + /* start over on next iteration */ + modname = p + 1; + state = STATE_MODNAME; + p_quote_start = NULL; + } + } + + return 0; +} + +/* + * Take an fd and own it. It will be closed on return. filename is used only + * for debug messages + */ +static int kmod_config_parse(struct kmod_config *config, int fd, const char *filename) +{ + struct kmod_ctx *ctx = config->ctx; + char *line; + FILE *fp; + unsigned int linenum = 0; + int err; + + fp = fdopen(fd, "r"); + if (fp == NULL) { + err = -errno; + ERR(config->ctx, "fd %d: %m\n", fd); + close(fd); + return err; + } + + while ((line = freadline_wrapped(fp, &linenum)) != NULL) { + char *cmd, *saveptr; + + if (line[0] == '\0' || line[0] == '#') + goto done_next; + + cmd = strtok_r(line, "\t ", &saveptr); + if (cmd == NULL) + goto done_next; + + if (streq(cmd, "alias")) { + char *alias = strtok_r(NULL, "\t ", &saveptr); + char *modname = strtok_r(NULL, "\t ", &saveptr); + + if (underscores(alias) < 0 || underscores(modname) < 0) + goto syntax_error; + + kmod_config_add_alias(config, alias, modname); + } else if (streq(cmd, "blacklist")) { + char *modname = strtok_r(NULL, "\t ", &saveptr); + + if (underscores(modname) < 0) + goto syntax_error; + + kmod_config_add_blacklist(config, modname); + } else if (streq(cmd, "options")) { + char *modname = strtok_r(NULL, "\t ", &saveptr); + char *options = strtok_r(NULL, "\0", &saveptr); + + if (underscores(modname) < 0 || options == NULL) + goto syntax_error; + + kmod_config_add_options(config, modname, options); + } else if (streq(cmd, "install")) { + char *modname = strtok_r(NULL, "\t ", &saveptr); + char *installcmd = strtok_r(NULL, "\0", &saveptr); + + if (underscores(modname) < 0 || installcmd == NULL) + goto syntax_error; + + kmod_config_add_command(config, modname, installcmd, cmd, + &config->install_commands); + } else if (streq(cmd, "remove")) { + char *modname = strtok_r(NULL, "\t ", &saveptr); + char *removecmd = strtok_r(NULL, "\0", &saveptr); + + if (underscores(modname) < 0 || removecmd == NULL) + goto syntax_error; + + kmod_config_add_command(config, modname, removecmd, cmd, + &config->remove_commands); + } else if (streq(cmd, "softdep")) { + char *modname = strtok_r(NULL, "\t ", &saveptr); + char *softdeps = strtok_r(NULL, "\0", &saveptr); + + if (underscores(modname) < 0 || softdeps == NULL) + goto syntax_error; + + kmod_config_add_softdep(config, modname, softdeps); + } else if (streq(cmd, "weakdep")) { + char *modname = strtok_r(NULL, "\t ", &saveptr); + char *weakdeps = strtok_r(NULL, "\0", &saveptr); + + if (underscores(modname) < 0 || weakdeps == NULL) + goto syntax_error; + + kmod_config_add_weakdep(config, modname, weakdeps); + } else if (streq(cmd, "include") || streq(cmd, "config")) { + ERR(ctx, "%s: command %s is deprecated and not parsed anymore\n", + filename, cmd); + } else { +syntax_error: + ERR(ctx, "%s line %u: ignoring bad line starting with '%s'\n", + filename, linenum, cmd); + } + +done_next: + free(line); + } + + fclose(fp); + + return 0; +} + +void kmod_config_free(struct kmod_config *config) +{ + kmod_list_release(config->aliases, free); + kmod_list_release(config->blacklists, free); + kmod_list_release(config->options, free); + kmod_list_release(config->install_commands, free); + kmod_list_release(config->remove_commands, free); + kmod_list_release(config->softdeps, free); + kmod_list_release(config->weakdeps, free); + kmod_list_release(config->paths, free); + free(config); +} + +static bool conf_files_filter_out(struct kmod_ctx *ctx, DIR *d, const char *path, + const char *fn) +{ + size_t len = strlen(fn); + struct stat st; + + if (fn[0] == '.') + return true; + + if (len < 6 || !streq(&fn[len - 5], ".conf")) + return true; + + if (fstatat(dirfd(d), fn, &st, 0) < 0) { + ERR(ctx, "Cannot stat directory entry: %s/%s\n", path, fn); + return true; + } + + if (S_ISDIR(st.st_mode)) { + ERR(ctx, + "Directories inside directories are not supported: " + "%s/%s\n", + path, fn); + return true; + } + + return false; +} + +struct conf_file { + const char *path; + bool is_single; + char name[]; +}; + +static int conf_files_insert_sorted(struct kmod_ctx *ctx, struct kmod_list **list, + const char *path, const char *name) +{ + struct kmod_list *lpos, *tmp; + struct conf_file *cf; + size_t namelen; + int cmp = -1; + bool is_single = false; + + if (name == NULL) { + name = basename(path); + is_single = true; + } + + kmod_list_foreach(lpos, *list) { + cf = lpos->data; + + if ((cmp = strcmp(name, cf->name)) <= 0) + break; + } + + if (cmp == 0) { + DBG(ctx, "Ignoring duplicate config file: %s/%s\n", path, name); + return -EEXIST; + } + + namelen = strlen(name); + cf = malloc(sizeof(*cf) + namelen + 1); + if (cf == NULL) + return -ENOMEM; + + memcpy(cf->name, name, namelen + 1); + cf->path = path; + cf->is_single = is_single; + + if (lpos == NULL) + tmp = kmod_list_append(*list, cf); + else if (lpos == *list) + tmp = kmod_list_prepend(*list, cf); + else + tmp = kmod_list_insert_before(lpos, cf); + + if (tmp == NULL) { + free(cf); + return -ENOMEM; + } + + if (lpos == NULL || lpos == *list) + *list = tmp; + + return 0; +} + +/* + * Insert configuration files in @list, ignoring duplicates + */ +static int conf_files_list(struct kmod_ctx *ctx, struct kmod_list **list, + const char *path, unsigned long long *path_stamp) +{ + DIR *d; + int err; + struct stat st; + struct dirent *dent; + + if (stat(path, &st) != 0) { + err = -errno; + DBG(ctx, "could not stat '%s': %m\n", path); + return err; + } + + *path_stamp = stat_mstamp(&st); + + if (!S_ISDIR(st.st_mode)) { + conf_files_insert_sorted(ctx, list, path, NULL); + return 0; + } + + d = opendir(path); + if (d == NULL) { + ERR(ctx, "opendir(%s): %m\n", path); + return -EINVAL; + } + + for (dent = readdir(d); dent != NULL; dent = readdir(d)) { + if (conf_files_filter_out(ctx, d, path, dent->d_name)) + continue; + + conf_files_insert_sorted(ctx, list, path, dent->d_name); + } + + closedir(d); + return 0; +} + +int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config, + const char *const *config_paths) +{ + struct kmod_config *config; + struct kmod_list *list = NULL; + struct kmod_list *path_list = NULL; + size_t i; + + conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.softdep"); + conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.weakdep"); + + for (i = 0; config_paths[i] != NULL; i++) { + const char *path = config_paths[i]; + unsigned long long path_stamp = 0; + size_t pathlen; + struct kmod_list *tmp; + struct kmod_config_path *cf; + + if (conf_files_list(ctx, &list, path, &path_stamp) < 0) + continue; + + pathlen = strlen(path) + 1; + cf = malloc(sizeof(*cf) + pathlen); + if (cf == NULL) + goto oom; + + cf->stamp = path_stamp; + memcpy(cf->path, path, pathlen); + + tmp = kmod_list_append(path_list, cf); + if (tmp == NULL) { + free(cf); + goto oom; + } + path_list = tmp; + } + + *p_config = config = calloc(1, sizeof(struct kmod_config)); + if (config == NULL) + goto oom; + + config->paths = path_list; + config->ctx = ctx; + + for (; list != NULL; list = kmod_list_remove(list)) { + char buf[PATH_MAX]; + const char *fn = buf; + struct conf_file *cf = list->data; + int fd; + + if (cf->is_single) { + fn = cf->path; + } else if (snprintf(buf, sizeof(buf), "%s/%s", cf->path, cf->name) >= + (int)sizeof(buf)) { + ERR(ctx, "Error parsing %s/%s: path too long\n", cf->path, + cf->name); + free(cf); + continue; + } + + fd = open(fn, O_RDONLY | O_CLOEXEC); + DBG(ctx, "parsing file '%s' fd=%d\n", fn, fd); + + if (fd >= 0) + kmod_config_parse(config, fd, fn); + + free(cf); + } + + kmod_config_parse_kcmdline(config); + + return 0; + +oom: + kmod_list_release(list, free); + kmod_list_release(path_list, free); + + return -ENOMEM; +} + +/********************************************************************** + * struct kmod_config_iter functions + **********************************************************************/ + +enum config_type { + CONFIG_TYPE_BLACKLIST = 0, + CONFIG_TYPE_INSTALL, + CONFIG_TYPE_REMOVE, + CONFIG_TYPE_ALIAS, + CONFIG_TYPE_OPTION, + CONFIG_TYPE_SOFTDEP, + CONFIG_TYPE_WEAKDEP, +}; + +struct kmod_config_iter { + enum config_type type; + bool intermediate; + const struct kmod_list *list; + const struct kmod_list *curr; + void *data; + const char *(*get_key)(const struct kmod_list *l); + const char *(*get_value)(const struct kmod_list *l); +}; + +static const char *softdep_get_plain_softdep(const struct kmod_list *l) +{ + char *s = softdep_to_char(l->data); + return s; +} + +static const char *weakdep_get_plain_weakdep(const struct kmod_list *l) +{ + char *s = weakdep_to_char(l->data); + return s; +} + +static struct kmod_config_iter *kmod_config_iter_new(const struct kmod_ctx *ctx, + enum config_type type) +{ + struct kmod_config_iter *iter = calloc(1, sizeof(*iter)); + const struct kmod_config *config = kmod_get_config(ctx); + + if (iter == NULL) + return NULL; + + iter->type = type; + + switch (type) { + case CONFIG_TYPE_BLACKLIST: + iter->list = config->blacklists; + iter->get_key = kmod_blacklist_get_modname; + break; + case CONFIG_TYPE_INSTALL: + iter->list = config->install_commands; + iter->get_key = kmod_command_get_modname; + iter->get_value = kmod_command_get_command; + break; + case CONFIG_TYPE_REMOVE: + iter->list = config->remove_commands; + iter->get_key = kmod_command_get_modname; + iter->get_value = kmod_command_get_command; + break; + case CONFIG_TYPE_ALIAS: + iter->list = config->aliases; + iter->get_key = kmod_alias_get_name; + iter->get_value = kmod_alias_get_modname; + break; + case CONFIG_TYPE_OPTION: + iter->list = config->options; + iter->get_key = kmod_option_get_modname; + iter->get_value = kmod_option_get_options; + break; + case CONFIG_TYPE_SOFTDEP: + iter->list = config->softdeps; + iter->get_key = kmod_softdep_get_name; + iter->get_value = softdep_get_plain_softdep; + iter->intermediate = true; + break; + case CONFIG_TYPE_WEAKDEP: + iter->list = config->weakdeps; + iter->get_key = kmod_weakdep_get_name; + iter->get_value = weakdep_get_plain_weakdep; + iter->intermediate = true; + break; + } + + return iter; +} + +KMOD_EXPORT struct kmod_config_iter *kmod_config_get_blacklists(const struct kmod_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + return kmod_config_iter_new(ctx, CONFIG_TYPE_BLACKLIST); +} + +// clang-format off +KMOD_EXPORT struct kmod_config_iter *kmod_config_get_install_commands(const struct kmod_ctx *ctx) +// clang-format on +{ + if (ctx == NULL) + return NULL; + + return kmod_config_iter_new(ctx, CONFIG_TYPE_INSTALL); +} + +// clang-format off +KMOD_EXPORT struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *ctx) +// clang-format on +{ + if (ctx == NULL) + return NULL; + + return kmod_config_iter_new(ctx, CONFIG_TYPE_REMOVE); +} + +KMOD_EXPORT struct kmod_config_iter *kmod_config_get_aliases(const struct kmod_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + return kmod_config_iter_new(ctx, CONFIG_TYPE_ALIAS); +} + +KMOD_EXPORT struct kmod_config_iter *kmod_config_get_options(const struct kmod_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + return kmod_config_iter_new(ctx, CONFIG_TYPE_OPTION); +} + +KMOD_EXPORT struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + return kmod_config_iter_new(ctx, CONFIG_TYPE_SOFTDEP); +} + +KMOD_EXPORT struct kmod_config_iter *kmod_config_get_weakdeps(const struct kmod_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + return kmod_config_iter_new(ctx, CONFIG_TYPE_WEAKDEP); +} + +KMOD_EXPORT const char *kmod_config_iter_get_key(const struct kmod_config_iter *iter) +{ + if (iter == NULL || iter->curr == NULL) + return NULL; + + return iter->get_key(iter->curr); +} + +KMOD_EXPORT const char *kmod_config_iter_get_value(const struct kmod_config_iter *iter) +{ + const char *s; + + if (iter == NULL || iter->curr == NULL) + return NULL; + + if (iter->get_value == NULL) + return NULL; + + if (iter->intermediate) { + struct kmod_config_iter *i = (struct kmod_config_iter *)iter; + + free(i->data); + s = i->data = (void *)iter->get_value(iter->curr); + } else + s = iter->get_value(iter->curr); + + return s; +} + +KMOD_EXPORT bool kmod_config_iter_next(struct kmod_config_iter *iter) +{ + if (iter == NULL) + return false; + + if (iter->curr == NULL) { + iter->curr = iter->list; + return iter->curr != NULL; + } + + iter->curr = kmod_list_next(iter->list, iter->curr); + + return iter->curr != NULL; +} + +KMOD_EXPORT void kmod_config_iter_free_iter(struct kmod_config_iter *iter) +{ + if (iter) + free(iter->data); + free(iter); +} diff --git a/libkmod/libkmod-elf.c b/libkmod/libkmod-elf.c new file mode 100644 index 0000000..f215f1e --- /dev/null +++ b/libkmod/libkmod-elf.c @@ -0,0 +1,1264 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2011-2013 ProFUSION embedded systems + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libkmod.h" +#include "libkmod-internal.h" + +/* as defined in module-init-tools */ +struct kmod_modversion32 { + uint32_t crc; + char name[64 - sizeof(uint32_t)]; +}; + +struct kmod_modversion64 { + uint64_t crc; + char name[64 - sizeof(uint64_t)]; +}; + +enum kmod_elf_section { + KMOD_ELF_SECTION_KSYMTAB, + KMOD_ELF_SECTION_MODINFO, + KMOD_ELF_SECTION_STRTAB, + KMOD_ELF_SECTION_SYMTAB, + KMOD_ELF_SECTION_VERSIONS, + KMOD_ELF_SECTION_MAX, +}; + +static const char *const section_name_map[] = { + [KMOD_ELF_SECTION_KSYMTAB] = "__ksymtab_strings", + [KMOD_ELF_SECTION_MODINFO] = ".modinfo", + [KMOD_ELF_SECTION_STRTAB] = ".strtab", + [KMOD_ELF_SECTION_SYMTAB] = ".symtab", + [KMOD_ELF_SECTION_VERSIONS] = "__versions", +}; + +struct kmod_elf { + const uint8_t *memory; + uint64_t size; + bool x32; + bool msb; + struct { + struct { + uint64_t offset; + uint16_t count; + uint16_t entry_size; + } section; + struct { + uint16_t section; /* index of the strings section */ + uint64_t size; + uint64_t offset; + } strings; + uint16_t machine; + } header; + struct { + uint64_t offset; + uint64_t size; + } sections[KMOD_ELF_SECTION_MAX]; +}; + +//#undef ENABLE_ELFDBG +//#define ENABLE_ELFDBG 1 + +#define ELFDBG(elf, ...) \ + do { \ + if (ENABLE_LOGGING == 1 && ENABLE_ELFDBG == 1) \ + _elf_dbg(elf, __FILE__, __LINE__, __func__, __VA_ARGS__); \ + } while (0); + +_printf_format_(5, 6) static inline void _elf_dbg(const struct kmod_elf *elf, + const char *fname, unsigned line, + const char *func, const char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "ELFDBG-%d%c: %s:%u %s() ", elf->x32 ? 32 : 64, + elf->msb ? 'M' : 'L', fname, line, func); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +static int elf_identify(struct kmod_elf *elf, const void *memory, uint64_t size) +{ + const uint8_t *p = memory; + + if (size <= EI_NIDENT || memcmp(p, ELFMAG, SELFMAG) != 0) + return -ENOEXEC; + + switch (p[EI_CLASS]) { + case ELFCLASS32: + if (size <= sizeof(Elf32_Ehdr)) + return -EINVAL; + elf->x32 = true; + break; + case ELFCLASS64: + if (size <= sizeof(Elf64_Ehdr)) + return -EINVAL; + elf->x32 = false; + break; + default: + return -EINVAL; + } + + switch (p[EI_DATA]) { + case ELFDATA2LSB: + elf->msb = false; + break; + case ELFDATA2MSB: + elf->msb = true; + break; + default: + return -EINVAL; + } + + return 0; +} + +static inline bool elf_range_valid(const struct kmod_elf *elf, uint64_t offset, + uint64_t size) +{ + uint64_t min_size; + + if (uadd64_overflow(offset, size, &min_size) || min_size > elf->size) { + ELFDBG(elf, + "out of bounds: %" PRIu64 " + %" PRIu64 " > %" PRIu64 + " (ELF size)\n", + offset, size, elf->size); + return false; + } + return true; +} + +static inline uint64_t elf_get_uint(const struct kmod_elf *elf, uint64_t offset, + uint16_t size) +{ + const uint8_t *p; + uint64_t ret = 0; + + assert(size <= sizeof(uint64_t)); + + p = elf->memory + offset; + + if (elf->msb) { + memcpy((char *)&ret + sizeof(ret) - size, p, size); + ret = be64toh(ret); + } else { + memcpy(&ret, p, size); + ret = le64toh(ret); + } + + ELFDBG(elf, "size=%" PRIu16 " offset=%" PRIu64 " value=%" PRIu64 "\n", size, + offset, ret); + + return ret; +} + +static inline int elf_set_uint(const struct kmod_elf *elf, uint64_t offset, uint64_t size, + uint64_t value, uint8_t *changed) +{ + uint8_t *p; + size_t i; + + ELFDBG(elf, + "size=%" PRIu64 " offset=%" PRIu64 " value=%" PRIu64 " write memory=%p\n", + size, offset, value, changed); + + assert(size <= sizeof(uint64_t)); + + p = changed + offset; + if (elf->msb) { + for (i = 1; i <= size; i++) { + p[size - i] = value & 0xff; + value = (value & 0xffffffffffffff00) >> 8; + } + } else { + for (i = 0; i < size; i++) { + p[i] = value & 0xff; + value = (value & 0xffffffffffffff00) >> 8; + } + } + + return 0; +} + +static inline const void *elf_get_mem(const struct kmod_elf *elf, uint64_t offset) +{ + return elf->memory + offset; +} + +/* + * Returns offset to section header for section with given index or 0 on error + * (offset 0 cannot be a valid section offset because ELF header is located there). + */ +static inline uint64_t elf_get_section_header_offset(const struct kmod_elf *elf, + uint16_t idx) +{ + assert(idx != SHN_UNDEF); + assert(idx < elf->header.section.count); + if (idx == SHN_UNDEF || idx >= elf->header.section.count) { + ELFDBG(elf, "invalid section number: %" PRIu16 ", last=%" PRIu16 "\n", + idx, elf->header.section.count); + return 0; + } + return elf->header.section.offset + + (uint64_t)(idx * elf->header.section.entry_size); +} + +static inline int elf_get_section_info(const struct kmod_elf *elf, uint16_t idx, + uint64_t *offset, uint64_t *size, + const char **name) +{ + uint64_t nameoff; + uint64_t off = elf_get_section_header_offset(elf, idx); + + if (off == 0) { + ELFDBG(elf, "no section at %" PRIu16 "\n", idx); + goto fail; + } + +#define READV(field) \ + elf_get_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field)) + + if (elf->x32) { + Elf32_Shdr *hdr; + + if (!elf_range_valid(elf, off, sizeof(*hdr))) + goto fail; + *size = READV(sh_size); + *offset = READV(sh_offset); + nameoff = READV(sh_name); + } else { + Elf64_Shdr *hdr; + + if (!elf_range_valid(elf, off, sizeof(*hdr))) + goto fail; + *size = READV(sh_size); + *offset = READV(sh_offset); + nameoff = READV(sh_name); + } +#undef READV + + if (!elf_range_valid(elf, *offset, *size)) + goto fail; + + if (nameoff >= elf->header.strings.size) + goto fail; + *name = elf_get_mem(elf, elf->header.strings.offset + nameoff); + + ELFDBG(elf, + "section=%" PRIu16 " is: offset=%" PRIu64 " size=%" PRIu64 " name=%s\n", + idx, *offset, *size, *name); + + return 0; +fail: + *offset = 0; + *size = 0; + *name = NULL; + return -EINVAL; +} + +static void kmod_elf_save_sections(struct kmod_elf *elf) +{ + const uint16_t all_sec = (1 << KMOD_ELF_SECTION_MAX) - 1; + uint16_t found_sec = 0; + enum kmod_elf_section sec; + + for (uint16_t i = 1; i < elf->header.section.count && found_sec != all_sec; i++) { + uint64_t off, size; + const char *n; + int err = elf_get_section_info(elf, i, &off, &size, &n); + if (err < 0) + continue; + + for (sec = KMOD_ELF_SECTION_KSYMTAB; sec < KMOD_ELF_SECTION_MAX; sec++) { + if (found_sec & (1 << sec)) + continue; + + if (streq(section_name_map[sec], n)) { + elf->sections[sec].offset = off; + elf->sections[sec].size = size; + found_sec |= 1 << sec; + break; + } + } + } + + for (sec = KMOD_ELF_SECTION_KSYMTAB; sec < KMOD_ELF_SECTION_MAX; sec++) { + if (found_sec & (1 << sec)) + continue; + + ELFDBG(elf, "section %s not found\n", section_name_map[sec]); + elf->sections[sec].offset = 0; + elf->sections[sec].size = 0; + } +} + +struct kmod_elf *kmod_elf_new(const void *memory, off_t size) +{ + struct kmod_elf *elf; + size_t shdrs_size, shdr_size; + int err; + const char *name; + + assert_cc(sizeof(uint16_t) == sizeof(Elf32_Half)); + assert_cc(sizeof(uint16_t) == sizeof(Elf64_Half)); + assert_cc(sizeof(uint32_t) == sizeof(Elf32_Word)); + assert_cc(sizeof(uint32_t) == sizeof(Elf64_Word)); + + if (!memory) { + errno = -EINVAL; + return NULL; + } + + elf = malloc(sizeof(struct kmod_elf)); + if (elf == NULL) { + return NULL; + } + + err = elf_identify(elf, memory, size); + if (err < 0) { + free(elf); + errno = -err; + return NULL; + } + + elf->memory = memory; + elf->size = size; + +#define READV(field) elf_get_uint(elf, offsetof(typeof(*hdr), field), sizeof(hdr->field)) + +#define LOAD_HEADER \ + elf->header.section.offset = READV(e_shoff); \ + elf->header.section.count = READV(e_shnum); \ + elf->header.section.entry_size = READV(e_shentsize); \ + elf->header.strings.section = READV(e_shstrndx); \ + elf->header.machine = READV(e_machine) + if (elf->x32) { + Elf32_Ehdr *hdr; + + shdr_size = sizeof(Elf32_Shdr); + if (!elf_range_valid(elf, 0, sizeof(*hdr))) + goto invalid; + LOAD_HEADER; + } else { + Elf64_Ehdr *hdr; + + shdr_size = sizeof(Elf64_Shdr); + if (!elf_range_valid(elf, 0, sizeof(*hdr))) + goto invalid; + LOAD_HEADER; + } +#undef LOAD_HEADER +#undef READV + + ELFDBG(elf, + "section: offset=%" PRIu64 " count=%" PRIu16 " entry_size=%" PRIu16 + " strings index=%" PRIu16 "\n", + elf->header.section.offset, elf->header.section.count, + elf->header.section.entry_size, elf->header.strings.section); + + if (elf->header.section.entry_size != shdr_size) { + ELFDBG(elf, "unexpected section entry size: %" PRIu16 ", expected %zu\n", + elf->header.section.entry_size, shdr_size); + goto invalid; + } + shdrs_size = shdr_size * elf->header.section.count; + if (!elf_range_valid(elf, elf->header.section.offset, shdrs_size)) + goto invalid; + + if (elf_get_section_info(elf, elf->header.strings.section, + &elf->header.strings.offset, &elf->header.strings.size, + &name) < 0) { + ELFDBG(elf, "could not get strings section\n"); + goto invalid; + } else { + uint64_t slen = elf->header.strings.size; + const char *s = elf_get_mem(elf, elf->header.strings.offset); + if (slen == 0 || s[slen - 1] != '\0') { + ELFDBG(elf, "strings section does not end with \\0\n"); + goto invalid; + } + } + + kmod_elf_save_sections(elf); + return elf; + +invalid: + free(elf); + errno = EINVAL; + return NULL; +} + +void kmod_elf_unref(struct kmod_elf *elf) +{ + free(elf); +} + +const void *kmod_elf_get_memory(const struct kmod_elf *elf) +{ + return elf->memory; +} + +/* + * Returns section index on success, negative value otherwise. + * On success, sec_off and sec_size are range checked and valid. + */ +int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, + uint64_t *sec_off, uint64_t *sec_size) +{ + uint16_t i; + + *sec_off = 0; + *sec_size = 0; + + for (i = 1; i < elf->header.section.count; i++) { + uint64_t off, size; + const char *n; + int err = elf_get_section_info(elf, i, &off, &size, &n); + if (err < 0) + continue; + if (!streq(section, n)) + continue; + + *sec_off = off; + *sec_size = size; + return i; + } + + return -ENODATA; +} + +/* array will be allocated with strings in a single malloc, just free *array */ +int kmod_elf_get_modinfo_strings(const struct kmod_elf *elf, char ***array) +{ + size_t i, j, count; + size_t tmp_size, vec_size, total_size; + uint64_t off, size; + const char *strings; + char *s, **a; + + *array = NULL; + + off = elf->sections[KMOD_ELF_SECTION_MODINFO].offset; + size = elf->sections[KMOD_ELF_SECTION_MODINFO].size; + if (off == 0) + return -ENODATA; + + strings = elf_get_mem(elf, off); + + /* skip zero padding */ + while (size > 1 && strings[0] == '\0') { + strings++; + size--; + } + + if (size <= 1) + return 0; + + for (i = 0, count = 0; i < size;) { + if (strings[i] != '\0') { + i++; + continue; + } + + while (strings[i] == '\0' && i < size) + i++; + + count++; + } + + if (strings[i - 1] != '\0') + count++; + + /* (string vector + NULL) * sizeof(char *) + size + NUL */ + if (uaddsz_overflow(count, 1, &tmp_size) || + umulsz_overflow(sizeof(char *), tmp_size, &vec_size) || + uaddsz_overflow(size, vec_size, &tmp_size) || + uaddsz_overflow(1, tmp_size, &total_size)) { + return -ENOMEM; + } + + *array = a = malloc(total_size); + if (*array == NULL) + return -errno; + + s = (char *)(a + count + 1); + memcpy(s, strings, size); + + /* make sure the last string is NULL-terminated */ + s[size] = '\0'; + a[count] = NULL; + a[0] = s; + + for (i = 0, j = 1; j < count && i < size;) { + if (s[i] != '\0') { + i++; + continue; + } + + while (i < size && s[i] == '\0') + i++; + + a[j] = &s[i]; + j++; + } + + return count; +} + +static inline void elf_get_modversion_lengths(const struct kmod_elf *elf, size_t *verlen, + size_t *crclen, size_t *namlen) +{ + assert_cc(sizeof(struct kmod_modversion64) == sizeof(struct kmod_modversion32)); + + if (elf->x32) { + struct kmod_modversion32 *mv; + + *verlen = sizeof(*mv); + *crclen = sizeof(mv->crc); + *namlen = sizeof(mv->name); + } else { + struct kmod_modversion64 *mv; + + *verlen = sizeof(*mv); + *crclen = sizeof(mv->crc); + *namlen = sizeof(mv->name); + } +} + +/* array will be allocated with strings in a single malloc, just free *array */ +int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array) +{ + size_t i, count, crclen, namlen, verlen; + uint64_t off, sec_off, size; + struct kmod_modversion *a; + + elf_get_modversion_lengths(elf, &verlen, &crclen, &namlen); + + *array = NULL; + + sec_off = elf->sections[KMOD_ELF_SECTION_VERSIONS].offset; + size = elf->sections[KMOD_ELF_SECTION_VERSIONS].size; + if (sec_off == 0) + return -ENODATA; + + if (size == 0) + return 0; + + if (size % verlen != 0) + return -EINVAL; + + count = size / verlen; + if (count > INT_MAX) { + ELFDBG(elf, "too many modversions: %zu\n", count); + return -EINVAL; + } + + *array = a = malloc(sizeof(struct kmod_modversion) * count); + if (*array == NULL) + return -errno; + + for (i = 0, off = sec_off; i < count; i++, off += verlen) { + uint64_t crc = elf_get_uint(elf, off, crclen); + const char *symbol = elf_get_mem(elf, off + crclen); + size_t nlen = strnlen(symbol, namlen); + + if (nlen == namlen) { + ELFDBG(elf, "symbol name at index %zu too long\n", i); + return -EINVAL; + } + + if (symbol[0] == '.') + symbol++; + + a[i].crc = crc; + a[i].bind = KMOD_SYMBOL_UNDEF; + a[i].symbol = symbol; + } + + return count; +} + +static int elf_strip_versions_section(const struct kmod_elf *elf, uint8_t *changed) +{ + uint64_t off, size; + const void *buf; + /* the off and size values are not used, supply them as dummies */ + int idx = kmod_elf_get_section(elf, "__versions", &off, &size); + uint64_t val; + + if (idx < 0) + return idx == -ENODATA ? 0 : idx; + + off = elf_get_section_header_offset(elf, idx); + + if (elf->x32) { + off += offsetof(Elf32_Shdr, sh_flags); + size = sizeof(((Elf32_Shdr *)buf)->sh_flags); + } else { + off += offsetof(Elf64_Shdr, sh_flags); + size = sizeof(((Elf64_Shdr *)buf)->sh_flags); + } + + val = elf_get_uint(elf, off, size); + val &= ~(uint64_t)SHF_ALLOC; + + return elf_set_uint(elf, off, size, val, changed); +} + +static int elf_strip_vermagic(const struct kmod_elf *elf, uint8_t *changed) +{ + uint64_t i, sec_off, size; + const char *strings; + + sec_off = elf->sections[KMOD_ELF_SECTION_MODINFO].offset; + size = elf->sections[KMOD_ELF_SECTION_MODINFO].size; + if (sec_off == 0) + return 0; + strings = elf_get_mem(elf, sec_off); + + /* skip zero padding */ + while (size > 1 && strings[0] == '\0') { + strings++; + size--; + } + if (size <= 1) + return 0; + + for (i = 0; i < size; i++) { + const char *s; + size_t off, len; + + if (strings[i] == '\0') + continue; + if (i + 1 >= size) + continue; + + s = strings + i; + len = sizeof("vermagic=") - 1; + if (i + len >= size) + continue; + if (strncmp(s, "vermagic=", len) != 0) { + i += strlen(s); + continue; + } + off = (const uint8_t *)s - elf->memory; + + len = strlen(s); + ELFDBG(elf, "clear .modinfo vermagic \"%s\" (%zu bytes)\n", s, len); + memset(changed + off, '\0', len); + return 0; + } + + ELFDBG(elf, "no vermagic found in .modinfo\n"); + return -ENODATA; +} + +const void *kmod_elf_strip(const struct kmod_elf *elf, unsigned int flags) +{ + uint8_t *changed; + int err = 0; + + assert(flags & (KMOD_INSERT_FORCE_MODVERSION | KMOD_INSERT_FORCE_VERMAGIC)); + + changed = memdup(elf->memory, elf->size); + if (changed == NULL) + return NULL; + + ELFDBG(elf, "copied memory to allow writing.\n"); + + if (flags & KMOD_INSERT_FORCE_MODVERSION) { + err = elf_strip_versions_section(elf, changed); + if (err < 0) { + errno = -err; + goto fail; + } + } + + if (flags & KMOD_INSERT_FORCE_VERMAGIC) { + err = elf_strip_vermagic(elf, changed); + if (err < 0) { + errno = -err; + goto fail; + } + } + + return changed; +fail: + free(changed); + return NULL; +} + +static int kmod_elf_get_symbols_symtab(const struct kmod_elf *elf, + struct kmod_modversion **array) +{ + uint64_t i, last, off, size; + const char *strings; + struct kmod_modversion *a; + size_t count, total_size; + + *array = NULL; + + off = elf->sections[KMOD_ELF_SECTION_KSYMTAB].offset; + size = elf->sections[KMOD_ELF_SECTION_KSYMTAB].size; + if (off == 0) + return -ENODATA; + strings = elf_get_mem(elf, off); + + /* skip zero padding */ + while (size > 1 && strings[0] == '\0') { + strings++; + size--; + } + if (size <= 1) + return 0; + + if (strings[size - 1] != '\0') { + ELFDBG(elf, "section __ksymtab_strings does not end with \\0 byte"); + return -EINVAL; + } + + last = 0; + for (i = 0, count = 0; i < size; i++) { + if (strings[i] == '\0') { + if (last == i) { + last = i + 1; + continue; + } + count++; + last = i + 1; + } + } + + if (count > INT_MAX) { + ELFDBG(elf, "too many symbols: %zu\n", count); + return -EINVAL; + } + + /* sizeof(struct kmod_modversion) * count */ + if (umulsz_overflow(sizeof(struct kmod_modversion), count, &total_size)) { + return -ENOMEM; + } + + *array = a = malloc(total_size); + if (*array == NULL) + return -errno; + + last = 0; + for (i = 0, count = 0; i < size; i++) { + if (strings[i] == '\0') { + if (last == i) { + last = i + 1; + continue; + } + a[count].crc = 0; + a[count].bind = KMOD_SYMBOL_GLOBAL; + a[count].symbol = strings + last; + count++; + last = i + 1; + } + } + + return count; +} + +static inline uint8_t kmod_symbol_bind_from_elf(uint8_t elf_value) +{ + switch (elf_value) { + case STB_LOCAL: + return KMOD_SYMBOL_LOCAL; + case STB_GLOBAL: + return KMOD_SYMBOL_GLOBAL; + case STB_WEAK: + return KMOD_SYMBOL_WEAK; + default: + return KMOD_SYMBOL_NONE; + } +} + +static uint64_t kmod_elf_resolve_crc(const struct kmod_elf *elf, uint64_t crc, + uint16_t shndx) +{ + int err; + uint64_t off, size; + const char *name; + + if (shndx == SHN_ABS || shndx == SHN_UNDEF) + return crc; + + err = elf_get_section_info(elf, shndx, &off, &size, &name); + if (err < 0) { + ELFDBG(elf, "Could not find section index %" PRIu16 " for crc", shndx); + return (uint64_t)-1; + } + + if (size < sizeof(uint32_t) || crc > (size - sizeof(uint32_t))) { + ELFDBG(elf, + "CRC offset %" PRIu64 " is too big, section %" PRIu16 + " size is %" PRIu64 "\n", + crc, shndx, size); + return (uint64_t)-1; + } + + crc = elf_get_uint(elf, off + crc, sizeof(uint32_t)); + return crc; +} + +/* array will be allocated with strings in a single malloc, just free *array */ +int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array) +{ + static const char crc_str[] = "__crc_"; + static const size_t crc_strlen = sizeof(crc_str) - 1; + uint64_t strtablen, symtablen, str_sec_off, sym_sec_off, str_off, sym_off; + struct kmod_modversion *a; + size_t i, count, symcount, symlen; + + str_sec_off = elf->sections[KMOD_ELF_SECTION_STRTAB].offset; + strtablen = elf->sections[KMOD_ELF_SECTION_STRTAB].size; + if (str_sec_off == 0) { + ELFDBG(elf, "no .strtab found.\n"); + goto fallback; + } + + sym_sec_off = elf->sections[KMOD_ELF_SECTION_SYMTAB].offset; + symtablen = elf->sections[KMOD_ELF_SECTION_SYMTAB].size; + if (sym_sec_off == 0) { + ELFDBG(elf, "no .symtab found.\n"); + goto fallback; + } + + if (elf->x32) + symlen = sizeof(Elf32_Sym); + else + symlen = sizeof(Elf64_Sym); + + if (symtablen % symlen != 0) { + ELFDBG(elf, + "unexpected .symtab of length %" PRIu64 + ", not multiple of %zu as expected.\n", + symtablen, symlen); + goto fallback; + } + + symcount = symtablen / symlen; + count = 0; + str_off = str_sec_off; + sym_off = sym_sec_off + symlen; + for (i = 1; i < symcount; i++, sym_off += symlen) { + const char *name; + uint32_t name_off; + +#define READV(field) \ + elf_get_uint(elf, sym_off + offsetof(typeof(*s), field), sizeof(s->field)) + if (elf->x32) { + Elf32_Sym *s; + + name_off = READV(st_name); + } else { + Elf64_Sym *s; + + name_off = READV(st_name); + } +#undef READV + if (name_off >= strtablen) { + ELFDBG(elf, + ".strtab is %" PRIu64 + " bytes, but .symtab entry %zu wants to access offset %" PRIu32 + ".\n", + strtablen, i, name_off); + goto fallback; + } + + name = elf_get_mem(elf, str_off + name_off); + + if (strncmp(name, crc_str, crc_strlen) != 0) + continue; + count++; + } + + if (count == 0) + goto fallback; + + *array = a = malloc(sizeof(struct kmod_modversion) * count); + if (*array == NULL) + return -errno; + + count = 0; + str_off = str_sec_off; + sym_off = sym_sec_off + symlen; + for (i = 1; i < symcount; i++, sym_off += symlen) { + const char *name; + uint32_t name_off; + uint64_t crc; + uint8_t info, bind; + uint16_t shndx; + +#define READV(field) \ + elf_get_uint(elf, sym_off + offsetof(typeof(*s), field), sizeof(s->field)) + if (elf->x32) { + Elf32_Sym *s; + + name_off = READV(st_name); + crc = READV(st_value); + info = READV(st_info); + shndx = READV(st_shndx); + } else { + Elf64_Sym *s; + + name_off = READV(st_name); + crc = READV(st_value); + info = READV(st_info); + shndx = READV(st_shndx); + } +#undef READV + name = elf_get_mem(elf, str_off + name_off); + if (strncmp(name, crc_str, crc_strlen) != 0) + continue; + name += crc_strlen; + + if (elf->x32) + bind = ELF32_ST_BIND(info); + else + bind = ELF64_ST_BIND(info); + + a[count].crc = kmod_elf_resolve_crc(elf, crc, shndx); + a[count].bind = kmod_symbol_bind_from_elf(bind); + a[count].symbol = name; + count++; + } + return count; + +fallback: + ELFDBG(elf, "Falling back to __ksymtab_strings!\n"); + return kmod_elf_get_symbols_symtab(elf, array); +} + +static int kmod_elf_crc_find(const struct kmod_elf *elf, uint64_t off, + uint64_t versionslen, const char *name, uint64_t *crc) +{ + size_t namlen, verlen, crclen; + uint64_t i; + + elf_get_modversion_lengths(elf, &verlen, &crclen, &namlen); + + for (i = 0; i < versionslen; i += verlen) { + const char *symbol = elf_get_mem(elf, off + i + crclen); + if (strnlen(symbol, namlen) == namlen || !streq(name, symbol)) { + ELFDBG(elf, "symbol name at index %" PRIu64 " too long\n", i); + continue; + } + *crc = elf_get_uint(elf, off + i, crclen); + return i / verlen; + } + + ELFDBG(elf, "could not find crc for symbol '%s'\n", name); + *crc = 0; + return -1; +} + +/* from module-init-tools:elfops_core.c */ +#ifndef STT_REGISTER +#define STT_REGISTER 13 /* Global register reserved to app. */ +#endif + +/* array will be allocated with strings in a single malloc, just free *array */ +int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf, + struct kmod_modversion **array) +{ + uint64_t versionslen, strtablen, symtablen, str_off, sym_off, ver_off; + uint64_t str_sec_off, sym_sec_off; + struct kmod_modversion *a; + size_t i, count, namlen, vercount, verlen, symcount, symlen, crclen; + bool handle_register_symbols; + uint8_t *visited_versions; + uint64_t *symcrcs; + + ver_off = elf->sections[KMOD_ELF_SECTION_VERSIONS].offset; + versionslen = elf->sections[KMOD_ELF_SECTION_VERSIONS].size; + if (ver_off == 0) { + versionslen = 0; + verlen = 0; + crclen = 0; + namlen = 0; + } else { + elf_get_modversion_lengths(elf, &verlen, &crclen, &namlen); + if (versionslen % verlen != 0) { + ELFDBG(elf, + "unexpected __versions of length %" PRIu64 + ", not multiple of %zu as expected.\n", + versionslen, verlen); + ver_off = 0; + versionslen = 0; + } + } + + str_sec_off = elf->sections[KMOD_ELF_SECTION_STRTAB].offset; + strtablen = elf->sections[KMOD_ELF_SECTION_STRTAB].size; + if (str_sec_off == 0) { + ELFDBG(elf, "no .strtab found.\n"); + return -EINVAL; + } + + sym_sec_off = elf->sections[KMOD_ELF_SECTION_SYMTAB].offset; + symtablen = elf->sections[KMOD_ELF_SECTION_SYMTAB].size; + if (sym_sec_off == 0) { + ELFDBG(elf, "no .symtab found.\n"); + return -EINVAL; + } + + if (elf->x32) + symlen = sizeof(Elf32_Sym); + else + symlen = sizeof(Elf64_Sym); + + if (symtablen % symlen != 0) { + ELFDBG(elf, + "unexpected .symtab of length %" PRIu64 + ", not multiple of %zu as expected.\n", + symtablen, symlen); + return -EINVAL; + } + + if (versionslen == 0) { + vercount = 0; + visited_versions = NULL; + } else { + vercount = versionslen / verlen; + visited_versions = calloc(vercount, sizeof(uint8_t)); + if (visited_versions == NULL) + return -ENOMEM; + } + + handle_register_symbols = + (elf->header.machine == EM_SPARC || elf->header.machine == EM_SPARCV9); + + symcount = symtablen / symlen; + count = 0; + str_off = str_sec_off; + sym_off = sym_sec_off + symlen; + + symcrcs = calloc(symcount, sizeof(uint64_t)); + if (symcrcs == NULL) { + free(visited_versions); + return -ENOMEM; + } + + for (i = 1; i < symcount; i++, sym_off += symlen) { + const char *name; + uint64_t crc; + uint32_t name_off; + uint16_t secidx; + uint8_t info; + int idx; + +#define READV(field) \ + elf_get_uint(elf, sym_off + offsetof(typeof(*s), field), sizeof(s->field)) + if (elf->x32) { + Elf32_Sym *s; + + name_off = READV(st_name); + secidx = READV(st_shndx); + info = READV(st_info); + } else { + Elf64_Sym *s; + + name_off = READV(st_name); + secidx = READV(st_shndx); + info = READV(st_info); + } +#undef READV + if (secidx != SHN_UNDEF) + continue; + + if (handle_register_symbols) { + uint8_t type; + if (elf->x32) + type = ELF32_ST_TYPE(info); + else + type = ELF64_ST_TYPE(info); + + /* Not really undefined: sparc gcc 3.3 creates + * U references when you have global asm + * variables, to avoid anyone else misusing + * them. + */ + if (type == STT_REGISTER) + continue; + } + + if (name_off >= strtablen) { + ELFDBG(elf, + ".strtab is %" PRIu64 + " bytes, but .symtab entry %zu wants to access offset %" PRIu32 + ".\n", + strtablen, i, name_off); + free(visited_versions); + free(symcrcs); + return -EINVAL; + } + + name = elf_get_mem(elf, str_off + name_off); + if (name[0] == '\0') { + ELFDBG(elf, "empty symbol name at index %zu\n", i); + continue; + } + + count++; + + idx = kmod_elf_crc_find(elf, ver_off, versionslen, name, &crc); + if (idx >= 0 && visited_versions != NULL) + visited_versions[idx] = 1; + symcrcs[i] = crc; + } + + if (visited_versions != NULL) { + /* module_layout/struct_module are not visited, but needed */ + for (i = 0; i < vercount; i++) { + if (visited_versions[i] == 0) { + const char *name; + size_t nlen; + + name = elf_get_mem(elf, ver_off + i * verlen + crclen); + nlen = strnlen(name, namlen); + + if (nlen == namlen) { + ELFDBG(elf, "symbol name at index %zu too long\n", + i); + free(visited_versions); + free(symcrcs); + return -EINVAL; + } + + count++; + } + } + } + + if (count > INT_MAX) { + ELFDBG(elf, "too many symbols: %zu\n", count); + free(visited_versions); + free(symcrcs); + *array = NULL; + return -EINVAL; + } + + if (count == 0) { + free(visited_versions); + free(symcrcs); + *array = NULL; + return 0; + } + + *array = a = malloc(sizeof(struct kmod_modversion) * count); + if (*array == NULL) { + free(visited_versions); + free(symcrcs); + return -errno; + } + + count = 0; + str_off = str_sec_off; + sym_off = sym_sec_off + symlen; + for (i = 1; i < symcount; i++, sym_off += symlen) { + const char *name; + uint64_t crc; + uint32_t name_off; + uint16_t secidx; + uint8_t info, bind; + +#define READV(field) \ + elf_get_uint(elf, sym_off + offsetof(typeof(*s), field), sizeof(s->field)) + if (elf->x32) { + Elf32_Sym *s; + + name_off = READV(st_name); + secidx = READV(st_shndx); + info = READV(st_info); + } else { + Elf64_Sym *s; + + name_off = READV(st_name); + secidx = READV(st_shndx); + info = READV(st_info); + } +#undef READV + if (secidx != SHN_UNDEF) + continue; + + if (handle_register_symbols) { + uint8_t type; + if (elf->x32) + type = ELF32_ST_TYPE(info); + else + type = ELF64_ST_TYPE(info); + + /* Not really undefined: sparc gcc 3.3 creates + * U references when you have global asm + * variables, to avoid anyone else misusing + * them. + */ + if (type == STT_REGISTER) + continue; + } + + name = elf_get_mem(elf, str_off + name_off); + if (name[0] == '\0') { + ELFDBG(elf, "empty symbol name at index %zu\n", i); + continue; + } + + if (elf->x32) + bind = ELF32_ST_BIND(info); + else + bind = ELF64_ST_BIND(info); + if (bind == STB_WEAK) + bind = KMOD_SYMBOL_WEAK; + else + bind = KMOD_SYMBOL_UNDEF; + + crc = symcrcs[i]; + + a[count].crc = crc; + a[count].bind = bind; + a[count].symbol = name; + + count++; + } + + free(symcrcs); + + if (visited_versions == NULL) + return count; + + /* add unvisited (module_layout/struct_module) */ + for (i = 0; i < vercount; i++) { + const char *name; + uint64_t crc; + + if (visited_versions[i] != 0) + continue; + + name = elf_get_mem(elf, ver_off + i * verlen + crclen); + crc = elf_get_uint(elf, ver_off + i * verlen, crclen); + + a[count].crc = crc; + a[count].bind = KMOD_SYMBOL_UNDEF; + a[count].symbol = name; + + count++; + } + free(visited_versions); + return count; +} diff --git a/libkmod/libkmod-file-xz.c b/libkmod/libkmod-file-xz.c new file mode 100644 index 0000000..f693877 --- /dev/null +++ b/libkmod/libkmod-file-xz.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright © 2024 Intel Corporation + */ + +#define DLSYM_LOCALLY_ENABLED ENABLE_XZ_DLOPEN + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libkmod.h" +#include "libkmod-internal.h" +#include "libkmod-internal-file.h" + +#define DL_SYMBOL_TABLE(M) \ + M(lzma_stream_decoder) \ + M(lzma_code) \ + M(lzma_end) + +DL_SYMBOL_TABLE(DECLARE_SYM) + +static int dlopen_lzma(void) +{ +#if !DLSYM_LOCALLY_ENABLED + return 0; +#else + static void *dl = NULL; + + ELF_NOTE_DLOPEN("xz", "Support for uncompressing xz-compressed modules", + ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "liblzma.so.5"); + + return dlsym_many(&dl, "liblzma.so.5", DL_SYMBOL_TABLE(DLSYM_ARG) NULL); +#endif +} + +static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret) +{ + switch (ret) { + case LZMA_MEM_ERROR: + ERR(file->ctx, "xz: %s\n", strerror(ENOMEM)); + break; + case LZMA_FORMAT_ERROR: + ERR(file->ctx, "xz: File format not recognized\n"); + break; + case LZMA_OPTIONS_ERROR: + ERR(file->ctx, "xz: Unsupported compression options\n"); + break; + case LZMA_DATA_ERROR: + ERR(file->ctx, "xz: File is corrupt\n"); + break; + case LZMA_BUF_ERROR: + ERR(file->ctx, "xz: Unexpected end of input\n"); + break; + default: + ERR(file->ctx, "xz: Internal error (bug)\n"); + break; + } +} + +static int xz_uncompress(lzma_stream *strm, struct kmod_file *file) +{ + uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ]; + lzma_action action = LZMA_RUN; + lzma_ret ret; + void *p = NULL; + size_t total = 0; + + strm->avail_in = 0; + strm->next_out = out_buf; + strm->avail_out = sizeof(out_buf); + + while (true) { + if (strm->avail_in == 0) { + ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf)); + if (rdret < 0) { + ret = -errno; + goto out; + } + strm->next_in = in_buf; + strm->avail_in = rdret; + if (rdret == 0) + action = LZMA_FINISH; + } + ret = sym_lzma_code(strm, action); + if (strm->avail_out == 0 || ret != LZMA_OK) { + size_t write_size = BUFSIZ - strm->avail_out; + char *tmp = realloc(p, total + write_size); + if (tmp == NULL) { + ret = -errno; + goto out; + } + memcpy(tmp + total, out_buf, write_size); + total += write_size; + p = tmp; + strm->next_out = out_buf; + strm->avail_out = BUFSIZ; + } + if (ret == LZMA_STREAM_END) + break; + if (ret != LZMA_OK) { + xz_uncompress_belch(file, ret); + ret = -EINVAL; + goto out; + } + } + file->memory = p; + file->size = total; + return 0; +out: + free(p); + return ret; +} + +int kmod_file_load_xz(struct kmod_file *file) +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_ret lzret; + int ret; + + ret = dlopen_lzma(); + if (ret < 0) { + ERR(file->ctx, "xz: can't load and resolve symbols (%s)", strerror(-ret)); + return -EINVAL; + } + + lzret = sym_lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED); + if (lzret == LZMA_MEM_ERROR) { + ERR(file->ctx, "xz: %s\n", strerror(ENOMEM)); + return -ENOMEM; + } else if (lzret != LZMA_OK) { + ERR(file->ctx, "xz: Internal error (bug)\n"); + return -EINVAL; + } + ret = xz_uncompress(&strm, file); + sym_lzma_end(&strm); + return ret; +} diff --git a/libkmod/libkmod-file-zlib.c b/libkmod/libkmod-file-zlib.c new file mode 100644 index 0000000..b26c7d1 --- /dev/null +++ b/libkmod/libkmod-file-zlib.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright © 2024 Intel Corporation + */ + +#define DLSYM_LOCALLY_ENABLED ENABLE_ZLIB_DLOPEN + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libkmod.h" +#include "libkmod-internal.h" +#include "libkmod-internal-file.h" + +#define READ_STEP (4 * 1024 * 1024) + +#define DL_SYMBOL_TABLE(M) \ + M(gzclose) \ + M(gzdopen) \ + M(gzerror) \ + M(gzread) + +DL_SYMBOL_TABLE(DECLARE_SYM) + +static int dlopen_zlib(void) +{ +#if !DLSYM_LOCALLY_ENABLED + return 0; +#else + static void *dl = NULL; + + ELF_NOTE_DLOPEN("zlib", "Support for uncompressing zlib-compressed modules", + ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libz.so.1"); + + return dlsym_many(&dl, "libz.so.1", DL_SYMBOL_TABLE(DLSYM_ARG) NULL); +#endif +} + +int kmod_file_load_zlib(struct kmod_file *file) +{ + _cleanup_free_ unsigned char *p = NULL; + int ret = 0; + off_t did = 0, total = 0; + gzFile gzf; + int gzfd; + + ret = dlopen_zlib(); + if (ret < 0) { + ERR(file->ctx, "zlib: can't load and resolve symbols (%s)", + strerror(-ret)); + return -EINVAL; + } + + errno = 0; + gzfd = fcntl(file->fd, F_DUPFD_CLOEXEC, 3); + if (gzfd < 0) + return -errno; + + gzf = sym_gzdopen(gzfd, "rb"); /* takes ownership of the fd */ + if (gzf == NULL) { + close(gzfd); + return -errno; + } + + for (;;) { + int r; + + if (did == total) { + void *tmp = realloc(p, total + READ_STEP); + if (tmp == NULL) { + ret = -errno; + goto error; + } + total += READ_STEP; + p = tmp; + } + + r = sym_gzread(gzf, p + did, total - did); + if (r == 0) + break; + else if (r < 0) { + int gzerr; + const char *gz_errmsg = sym_gzerror(gzf, &gzerr); + + ERR(file->ctx, "gzip: %s\n", gz_errmsg); + + /* gzip might not set errno here */ + ret = gzerr == Z_ERRNO ? -errno : -EINVAL; + goto error; + } + did += r; + } + + file->memory = TAKE_PTR(p); + file->size = did; + sym_gzclose(gzf); + + return 0; + +error: + sym_gzclose(gzf); + return ret; +} diff --git a/libkmod/libkmod-file-zstd.c b/libkmod/libkmod-file-zstd.c new file mode 100644 index 0000000..f403a01 --- /dev/null +++ b/libkmod/libkmod-file-zstd.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright © 2024 Intel Corporation + */ + +#define DLSYM_LOCALLY_ENABLED ENABLE_ZSTD_DLOPEN + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libkmod.h" +#include "libkmod-internal.h" +#include "libkmod-internal-file.h" + +#define DL_SYMBOL_TABLE(M) \ + M(ZSTD_decompress) \ + M(ZSTD_getErrorName) \ + M(ZSTD_getFrameContentSize) \ + M(ZSTD_isError) + +DL_SYMBOL_TABLE(DECLARE_SYM) + +static int dlopen_zstd(void) +{ +#if !DLSYM_LOCALLY_ENABLED + return 0; +#else + static void *dl = NULL; + + ELF_NOTE_DLOPEN("zstd", "Support for uncompressing zstd-compressed modules", + ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, "libzstd.so.1"); + + return dlsym_many(&dl, "libzstd.so.1", DL_SYMBOL_TABLE(DLSYM_ARG) NULL); +#endif +} + +int kmod_file_load_zstd(struct kmod_file *file) +{ + void *src_buf = MAP_FAILED, *dst_buf = NULL; + size_t src_size, dst_size; + unsigned long long frame_size; + struct stat st; + int ret; + + ret = dlopen_zstd(); + if (ret < 0) { + ERR(file->ctx, "zstd: can't load and resolve symbols (%s)", + strerror(-ret)); + return -EINVAL; + } + + if (fstat(file->fd, &st) < 0) { + ret = -errno; + ERR(file->ctx, "zstd: %m\n"); + goto out; + } + + if ((uintmax_t)st.st_size > SIZE_MAX) { + ret = -ENOMEM; + goto out; + } + + src_size = st.st_size; + src_buf = mmap(NULL, src_size, PROT_READ, MAP_PRIVATE, file->fd, 0); + if (src_buf == MAP_FAILED) { + ret = -errno; + goto out; + } + + frame_size = sym_ZSTD_getFrameContentSize(src_buf, src_size); + if (frame_size == 0 || frame_size == ZSTD_CONTENTSIZE_UNKNOWN || + frame_size == ZSTD_CONTENTSIZE_ERROR) { + ret = -EINVAL; + ERR(file->ctx, "zstd: Failed to determine decompression size\n"); + goto out; + } + + if (frame_size > SIZE_MAX) { + ret = -ENOMEM; + goto out; + } + + dst_size = frame_size; + dst_buf = malloc(dst_size); + if (dst_buf == NULL) { + ret = -errno; + goto out; + } + + dst_size = sym_ZSTD_decompress(dst_buf, dst_size, src_buf, src_size); + if (sym_ZSTD_isError(dst_size)) { + ERR(file->ctx, "zstd: %s\n", sym_ZSTD_getErrorName(dst_size)); + ret = -EINVAL; + goto out; + } + + file->memory = dst_buf; + file->size = dst_size; + + ret = 0; + dst_buf = NULL; + +out: + free(dst_buf); + + if (src_buf != MAP_FAILED) + munmap(src_buf, src_size); + + return ret; +} diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c new file mode 100644 index 0000000..89f7464 --- /dev/null +++ b/libkmod/libkmod-file.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2011-2013 ProFUSION embedded systems + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libkmod.h" +#include "libkmod-internal.h" +#include "libkmod-internal-file.h" + +static const char magic_zstd[] = { 0x28, 0xB5, 0x2F, 0xFD }; +static const char magic_xz[] = { 0xfd, '7', 'z', 'X', 'Z', 0 }; +static const char magic_zlib[] = { 0x1f, 0x8b }; + +static int load_reg(struct kmod_file *file) +{ + struct stat st; + + if (fstat(file->fd, &st) < 0) + return -errno; + + file->size = st.st_size; + if ((uintmax_t)file->size > SIZE_MAX) + return -ENOMEM; + + file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0); + if (file->memory == MAP_FAILED) { + file->memory = NULL; + return -errno; + } + + return 0; +} + +static const struct comp_type { + size_t magic_size; + enum kmod_file_compression_type compression; + const char *magic_bytes; + int (*load)(struct kmod_file *file); +} comp_types[] = { + // clang-format off + { sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, kmod_file_load_zstd }, + { sizeof(magic_xz), KMOD_FILE_COMPRESSION_XZ, magic_xz, kmod_file_load_xz }, + { sizeof(magic_zlib), KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, kmod_file_load_zlib }, + { 0, KMOD_FILE_COMPRESSION_NONE, NULL, load_reg }, + // clang-format on +}; + +struct kmod_elf *kmod_file_get_elf(struct kmod_file *file) +{ + int err; + + if (file->elf) + return file->elf; + + err = kmod_file_load_contents(file); + if (err) { + errno = -err; + return NULL; + } + + file->elf = kmod_elf_new(file->memory, file->size); + return file->elf; +} + +struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx, const char *filename) +{ + struct kmod_file *file; + char buf[7]; + ssize_t sz; + + assert_cc(sizeof(magic_zstd) < sizeof(buf)); + assert_cc(sizeof(magic_xz) < sizeof(buf)); + assert_cc(sizeof(magic_zlib) < sizeof(buf)); + + file = calloc(1, sizeof(struct kmod_file)); + if (file == NULL) + return NULL; + + file->fd = open(filename, O_RDONLY | O_CLOEXEC); + if (file->fd < 0) { + free(file); + return NULL; + } + + sz = pread_str_safe(file->fd, buf, sizeof(buf), 0); + if (sz != (sizeof(buf) - 1)) { + if (sz < 0) + errno = -sz; + else + errno = EINVAL; + + close(file->fd); + free(file); + return NULL; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(comp_types); i++) { + const struct comp_type *itr = &comp_types[i]; + + file->load = itr->load; + file->compression = itr->compression; + if (itr->magic_size && + memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) { + break; + } + } + + file->ctx = ctx; + + return file; +} + +/* + * Callers should just check file->memory got updated + */ +int kmod_file_load_contents(struct kmod_file *file) +{ + if (file->memory) + return 0; + + /* The load functions already log possible errors. */ + return file->load(file); +} + +void *kmod_file_get_contents(const struct kmod_file *file) +{ + return file->memory; +} + +off_t kmod_file_get_size(const struct kmod_file *file) +{ + return file->size; +} + +enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file) +{ + return file->compression; +} + +int kmod_file_get_fd(const struct kmod_file *file) +{ + return file->fd; +} + +void kmod_file_unref(struct kmod_file *file) +{ + if (file->elf) + kmod_elf_unref(file->elf); + + if (file->compression == KMOD_FILE_COMPRESSION_NONE) { + if (file->memory) + munmap(file->memory, file->size); + } else { + free(file->memory); + } + + close(file->fd); + free(file); +} diff --git a/libkmod/libkmod-index.c b/libkmod/libkmod-index.c new file mode 100644 index 0000000..40199a5 --- /dev/null +++ b/libkmod/libkmod-index.c @@ -0,0 +1,1086 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2011-2013 ProFUSION embedded systems + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libkmod-internal.h" +#include "libkmod-index.h" + +/* libkmod-index.c: module index file implementation + * + * Integers are stored as 32 bit unsigned in "network" order, i.e. MSB first. + * All files start with a magic number. + * + * Magic spells "BOOTFAST", where the exact encoding varies across versions. + * + * The original implementation in modutils/module-init-tools used 0xB007FA57, but also + * lacked the version fields. Thus later on, with module-init-tools commit 44d7ac4 + * ("add versioning to the binary module files"), the encoding was changed to 0xB007F457 + * (A -> 4) and the version fields were introduced. Shortly afterwards the format was + * changed in backwards incompatible way and the major version was bumped to 2. + * + * We use a version string to keep track of changes to the binary format. + * This is stored in the form: INDEX_MAJOR (hi) INDEX_MINOR (lo) just in + * case we ever decide to have minor changes that are not incompatible. + */ +#define INDEX_MAGIC 0xB007F457 +#define INDEX_VERSION_MAJOR 0x0002 +#define INDEX_VERSION_MINOR 0x0001 +#define INDEX_VERSION ((INDEX_VERSION_MAJOR << 16) | INDEX_VERSION_MINOR) + +/* The index file maps keys to values. Both keys and values are ASCII strings. + * Each key can have multiple values. Values are sorted by an integer priority. + * + * The reader also implements a wildcard search (including range expressions) + * where the keys in the index are treated as patterns. + * This feature is required for module aliases. + */ +#define INDEX_CHILDMAX 128u + +/* Disk format: + * + * uint32_t magic = INDEX_MAGIC; + * uint32_t version = INDEX_VERSION; + * uint32_t root_offset; + * + * (node_offset & INDEX_NODE_MASK) specifies the file offset of nodes: + * + * char[] prefix; // nul terminated + * + * uint8_t first; + * uint8_t last; + * uint32_t children[last - first + 1]; + * + * uint32_t value_count; + * struct { + * uint32_t priority; + * char[] value; // nul terminated + * } values[value_count]; + * + * (node_offset & INDEX_NODE_FLAGS) indicates which fields are present. + * Empty prefixes are omitted, leaf nodes omit the three child-related fields. + * + * This could be optimised further by adding a sparse child format + * (indicated using a new flag). + * + * + * Implementation is based on a radix tree, or "trie". + * Each arc from parent to child is labelled with a character. + * Each path from the root represents a string. + * + * == Example strings == + * + * ask + * ate + * on + * once + * one + * + * == Key == + * + Normal node + * * Marked node, representing a key and its values. + * + * + + * |-a-+-s-+-k-* + * | | + * | `-t-+-e-* + * | + * `-o-+-n-*-c-+-e-* + * | + * `-e-* + * + * Naive implementations tend to be very space inefficient; child pointers + * are stored in arrays indexed by character, but most child pointers are null. + * + * Our implementation uses a scheme described by Wikipedia as a Patricia trie, + * + * "easiest to understand as a space-optimized trie where + * each node with only one child is merged with its child" + * + * + + * |-a-+-sk-* + * | | + * | `-te-* + * | + * `-on-*-ce-* + * | + * `-e-* + * + * We still use arrays of child pointers indexed by a single character; + * the remaining characters of the label are stored as a "prefix" in the child. + * + * The paper describing the original Patricia trie works on individual bits - + * each node has a maximum of two children, which increases space efficiency. + * However for this application it is simpler to use the ASCII character set. + * Since the index file is read-only, it can be compressed by omitting null + * child pointers at the start and end of arrays. + */ + +/* Format of node offsets within index file */ +enum node_offset { + INDEX_NODE_FLAGS = 0xF0000000, /* Flags in high nibble */ + INDEX_NODE_PREFIX = 0x80000000, + INDEX_NODE_VALUES = 0x40000000, + INDEX_NODE_CHILDS = 0x20000000, + + INDEX_NODE_MASK = 0x0FFFFFFF, /* Offset value */ +}; + +struct wrtbuf { + char bytes[4096]; + size_t len; + int fd; +}; + +static void wrtbuf_init(struct wrtbuf *buf, int fd) +{ + buf->len = 0; + buf->fd = fd; +} + +static void wrtbuf_flush(struct wrtbuf *buf) +{ + if (buf->len > 0) { + write_str_safe(buf->fd, buf->bytes, buf->len); + buf->len = 0; + } +} + +static void wrtbuf_write(struct wrtbuf *buf, const char *p, size_t len) +{ + size_t todo = len; + size_t done = 0; + + do { + size_t n = MIN(sizeof(buf->bytes) - buf->len, todo); + + memcpy(buf->bytes + buf->len, p + done, n); + buf->len += n; + todo -= n; + done += n; + + if (buf->len == sizeof(buf->bytes)) + wrtbuf_flush(buf); + } while (todo > 0); +} + +void index_values_free(struct index_value *values) +{ + while (values) { + struct index_value *value = values; + + values = value->next; + free(value); + } +} + +static int add_value(struct index_value **values, const char *value, size_t len, + uint32_t priority) +{ + struct index_value *v; + + /* find position to insert value */ + while (*values && (*values)->priority < priority) + values = &(*values)->next; + + v = malloc(sizeof(struct index_value) + len + 1); + if (!v) + return -1; + v->next = *values; + v->priority = priority; + v->len = len; + memcpy(v->value, value, len); + v->value[len] = '\0'; + *values = v; + + return 0; +} + +static inline int read_char(FILE *in) +{ + return getc_unlocked(in); +} + +static bool read_u32s(FILE *in, uint32_t *l, size_t n) +{ + size_t i; + + if (fread_unlocked(l, sizeof(uint32_t), n, in) != n) { + return false; + } + for (i = 0; i < n; i++) + l[i] = ntohl(l[i]); + return true; +} + +static inline bool read_u32(FILE *in, uint32_t *l) +{ + return read_u32s(in, l, 1); +} + +/* + * Index file searching + */ +struct index_file { + FILE *file; + uint32_t root_offset; + char *tmp; + size_t tmp_size; +}; + +struct index_node_f { + struct index_file *idx; + char *prefix; /* path compression */ + struct index_value *values; + uint8_t first; /* range of child nodes */ + uint8_t last; + uint32_t children[0]; +}; + +static struct index_node_f *index_read(struct index_file *idx, uint32_t offset) +{ + struct index_node_f *node = NULL; + char *prefix = NULL; + size_t child_count = 0; + FILE *fp = idx->file; + + if ((offset & INDEX_NODE_MASK) == 0) + return NULL; + + if (fseek(fp, offset & INDEX_NODE_MASK, SEEK_SET) < 0) + return NULL; + + if (offset & INDEX_NODE_PREFIX) { + if (getdelim(&idx->tmp, &idx->tmp_size, '\0', fp) < 0) + return NULL; + prefix = strdup(idx->tmp); + } else + prefix = strdup(""); + + if (prefix == NULL) + goto err; + + if (offset & INDEX_NODE_CHILDS) { + int first = read_char(fp); + int last = read_char(fp); + + if (first == EOF || last == EOF || first > last) + goto err; + + child_count = last - first + 1; + + node = malloc(sizeof(struct index_node_f) + + sizeof(uint32_t) * child_count); + if (node == NULL) + goto err; + + node->first = (uint8_t)first; + node->last = (uint8_t)last; + + if (!read_u32s(fp, node->children, child_count)) + goto err; + } else { + node = malloc(sizeof(struct index_node_f)); + if (node == NULL) + goto err; + + node->first = INDEX_CHILDMAX; + node->last = 0; + } + + node->values = NULL; + if (offset & INDEX_NODE_VALUES) { + uint32_t value_count; + uint32_t priority; + + if (!read_u32(fp, &value_count)) + goto err; + + while (value_count--) { + ssize_t n; + + if (!read_u32(fp, &priority)) + goto err; + n = getdelim(&idx->tmp, &idx->tmp_size, '\0', fp); + if (n < 0) + goto err; + add_value(&node->values, idx->tmp, n, priority); + } + } + + node->prefix = prefix; + node->idx = idx; + return node; +err: + free(prefix); + free(node); + return NULL; +} + +static void index_close(struct index_node_f *node) +{ + free(node->prefix); + index_values_free(node->values); + free(node); +} + +struct index_file *index_file_open(const char *filename) +{ + FILE *file; + uint32_t magic, version; + struct index_file *new; + + file = fopen(filename, "re"); + if (!file) + return NULL; + + if (!read_u32(file, &magic) || magic != INDEX_MAGIC) + goto err; + + if (!read_u32(file, &version) || version >> 16 != INDEX_VERSION_MAJOR) + goto err; + + new = malloc(sizeof(struct index_file)); + if (new == NULL) + goto err; + + new->file = file; + if (!read_u32(new->file, &new->root_offset)) { + free(new); + goto err; + } + new->tmp = NULL; + new->tmp_size = 0; + + return new; +err: + fclose(file); + return NULL; +} + +void index_file_close(struct index_file *idx) +{ + fclose(idx->file); + free(idx->tmp); + free(idx); +} + +static struct index_node_f *index_readroot(struct index_file *in) +{ + return index_read(in, in->root_offset); +} + +static struct index_node_f *index_readchild(const struct index_node_f *parent, uint8_t ch) +{ + if (parent->first <= ch && ch <= parent->last) { + return index_read(parent->idx, parent->children[ch - parent->first]); + } + + return NULL; +} + +static void index_dump_node(struct index_node_f *node, struct strbuf *buf, + struct wrtbuf *wbuf) +{ + struct index_value *v; + size_t pushed; + + pushed = strbuf_pushchars(buf, node->prefix); + + for (v = node->values; v != NULL; v = v->next) { + wrtbuf_write(wbuf, buf->bytes, strbuf_used(buf)); + wrtbuf_write(wbuf, " ", 1); + wrtbuf_write(wbuf, v->value, strlen(v->value)); + wrtbuf_write(wbuf, "\n", 1); + } + + for (uint8_t ch = node->first; ch <= node->last; ch++) { + struct index_node_f *child = index_readchild(node, ch); + + if (!child) + continue; + + if (strbuf_pushchar(buf, ch)) { + index_dump_node(child, buf, wbuf); + strbuf_popchar(buf); + } + } + + strbuf_popchars(buf, pushed); + index_close(node); +} + +void index_dump(struct index_file *in, int fd, bool alias_prefix) +{ + struct index_node_f *root; + struct strbuf buf; + struct wrtbuf wbuf; + + root = index_readroot(in); + if (root == NULL) + return; + + strbuf_init(&buf); + wrtbuf_init(&wbuf, fd); + if (!alias_prefix || strbuf_pushchars(&buf, "alias ")) + index_dump_node(root, &buf, &wbuf); + wrtbuf_flush(&wbuf); + strbuf_release(&buf); +} + +static char *index_search__node(struct index_node_f *node, const char *key, int i) +{ + char *value; + struct index_node_f *child; + int j; + + while (node) { + for (j = 0; node->prefix[j]; j++) { + uint8_t ch = node->prefix[j]; + + if (ch != key[i + j]) { + index_close(node); + return NULL; + } + } + + i += j; + + if (key[i] == '\0') { + value = node->values != NULL ? strdup(node->values[0].value) : + NULL; + + index_close(node); + return value; + } + + child = index_readchild(node, key[i]); + index_close(node); + node = child; + i++; + } + + return NULL; +} + +/* + * Search the index for a key + * + * Returns the value of the first match + * + * The recursive functions free their node argument (using index_close). + */ +char *index_search(struct index_file *in, const char *key) +{ + // FIXME: return value by reference instead of strdup + struct index_node_f *root; + char *value; + + root = index_readroot(in); + value = index_search__node(root, key, 0); + + return value; +} + +/* Level 4: add all the values from a matching node */ +static void index_searchwild__allvalues(struct index_node_f *node, + struct index_value **out) +{ + struct index_value *v; + + for (v = node->values; v != NULL; v = v->next) + add_value(out, v->value, v->len, v->priority); + + index_close(node); +} + +/* + * Level 3: traverse a sub-keyspace which starts with a wildcard, + * looking for matches. + */ +static void index_searchwild__all(struct index_node_f *node, int j, struct strbuf *buf, + const char *subkey, struct index_value **out) +{ + size_t pushed; + + pushed = strbuf_pushchars(buf, &node->prefix[j]); + + for (uint8_t ch = node->first; ch <= node->last; ch++) { + struct index_node_f *child = index_readchild(node, ch); + + if (!child) + continue; + + if (strbuf_pushchar(buf, ch)) { + index_searchwild__all(child, 0, buf, subkey, out); + strbuf_popchar(buf); + } + } + + if (node->values) { + const char *s = strbuf_str(buf); + + if (s != NULL && fnmatch(s, subkey, 0) == 0) + index_searchwild__allvalues(node, out); + else + index_close(node); + } else { + index_close(node); + } + + strbuf_popchars(buf, pushed); +} + +/* Level 2: descend the tree (until we hit a wildcard) */ +static void index_searchwild__node(struct index_node_f *node, struct strbuf *buf, + const char *key, struct index_value **out) +{ + struct index_node_f *child; + int j; + + while (node) { + for (j = 0; node->prefix[j]; j++) { + uint8_t ch = node->prefix[j]; + + if (ch == '*' || ch == '?' || ch == '[') { + index_searchwild__all(node, j, buf, &key[j], out); + return; + } + + if (ch != key[j]) { + index_close(node); + return; + } + } + + key += j; + + child = index_readchild(node, '*'); + if (child) { + if (strbuf_pushchar(buf, '*')) { + index_searchwild__all(child, 0, buf, key, out); + strbuf_popchar(buf); + } + } + + child = index_readchild(node, '?'); + if (child) { + if (strbuf_pushchar(buf, '?')) { + index_searchwild__all(child, 0, buf, key, out); + strbuf_popchar(buf); + } + } + + child = index_readchild(node, '['); + if (child) { + if (strbuf_pushchar(buf, '[')) { + index_searchwild__all(child, 0, buf, key, out); + strbuf_popchar(buf); + } + } + + if (*key == '\0') { + index_searchwild__allvalues(node, out); + + return; + } + + child = index_readchild(node, *key); + index_close(node); + node = child; + key++; + } +} + +/* + * Search the index for a key. The index may contain wildcards. + * + * Returns a list of all the values of matching keys. + */ +struct index_value *index_searchwild(struct index_file *in, const char *key) +{ + struct index_node_f *root = index_readroot(in); + struct strbuf buf; + struct index_value *out = NULL; + + strbuf_init(&buf); + index_searchwild__node(root, &buf, key, &out); + strbuf_release(&buf); + return out; +} + +/**************************************************************************/ +/* + * Alternative implementation, using mmap to map all the file to memory when + * starting + */ +#include +#include +#include + +struct index_mm { + const struct kmod_ctx *ctx; + void *mm; + uint32_t root_offset; + size_t size; +}; + +struct index_mm_value { + uint32_t priority; + size_t len; + const char *value; +}; + +struct index_mm_node { + const struct index_mm *idx; + const char *prefix; /* mmap'ed value */ + unsigned char first; + unsigned char last; + const void *children; /* mmap'ed value */ + size_t value_count; + const void *values; /* mmap'ed value */ +}; + +static inline uint32_t read_u32_mm(const void **p) +{ + const uint8_t *addr = *(const uint8_t **)p; + uint32_t v; + + /* addr may be unaligned to uint32_t */ + v = get_unaligned((const uint32_t *)addr); + + *p = addr + sizeof(uint32_t); + return ntohl(v); +} + +static inline uint8_t read_char_mm(const void **p) +{ + const uint8_t *addr = *(const uint8_t **)p; + uint8_t v = *addr; + *p = addr + sizeof(uint8_t); + return v; +} + +static inline const char *read_chars_mm(const void **p, size_t *rlen) +{ + const char *addr = *(const char **)p; + size_t len = *rlen = strlen(addr); + *p = addr + len + 1; + return addr; +} + +static inline void read_value_mm(const void **p, struct index_mm_value *v) +{ + v->priority = read_u32_mm(p); + v->value = read_chars_mm(p, &v->len); +} + +/* reads node into given node struct and returns its address on success or NULL on error. */ +static struct index_mm_node *index_mm_read_node(const struct index_mm *idx, + uint32_t offset, + struct index_mm_node *node) +{ + const void *p; + + if ((offset & INDEX_NODE_MASK) == 0 || (offset & INDEX_NODE_MASK) >= idx->size) + return NULL; + + p = (const char *)idx->mm + (offset & INDEX_NODE_MASK); + + if (offset & INDEX_NODE_PREFIX) { + size_t len; + node->prefix = read_chars_mm(&p, &len); + } else { + node->prefix = ""; + } + + if (offset & INDEX_NODE_CHILDS) { + size_t child_count; + + node->first = read_char_mm(&p); + node->last = read_char_mm(&p); + + if (node->first > node->last || node->first >= INDEX_CHILDMAX || + node->last >= INDEX_CHILDMAX) + return NULL; + + node->children = p; + + child_count = node->last - node->first + 1; + p = (const char *)p + sizeof(uint32_t) * child_count; + } else { + node->first = INDEX_CHILDMAX; + node->last = 0; + node->children = NULL; + } + + if (offset & INDEX_NODE_VALUES) { + node->value_count = read_u32_mm(&p); + node->values = p; + } else { + node->value_count = 0; + node->values = NULL; + } + + node->idx = idx; + + return node; +} + +int index_mm_open(const struct kmod_ctx *ctx, const char *filename, + unsigned long long *stamp, struct index_mm **pidx) +{ + int fd, err; + struct stat st; + struct index_mm *idx; + struct { + uint32_t magic; + uint32_t version; + uint32_t root_offset; + } hdr; + const void *p; + + assert(pidx != NULL); + + DBG(ctx, "file=%s\n", filename); + + idx = malloc(sizeof(*idx)); + if (idx == NULL) { + ERR(ctx, "malloc: %m\n"); + return -ENOMEM; + } + + if ((fd = open(filename, O_RDONLY | O_CLOEXEC)) < 0) { + DBG(ctx, "open(%s, O_RDONLY|O_CLOEXEC): %m\n", filename); + err = -errno; + goto fail_open; + } + + if (fstat(fd, &st) < 0 || st.st_size < (off_t)sizeof(hdr)) { + err = -EINVAL; + goto fail_nommap; + } + + if ((uintmax_t)st.st_size > SIZE_MAX) { + err = -ENOMEM; + goto fail_nommap; + } + + idx->mm = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (idx->mm == MAP_FAILED) { + ERR(ctx, "mmap(NULL, %" PRIu64 ", PROT_READ, MAP_PRIVATE, %d, 0): %m\n", + (uint64_t)st.st_size, fd); + err = -errno; + goto fail_nommap; + } + + p = idx->mm; + hdr.magic = read_u32_mm(&p); + hdr.version = read_u32_mm(&p); + hdr.root_offset = read_u32_mm(&p); + + if (hdr.magic != INDEX_MAGIC) { + ERR(ctx, "magic check fail: %x instead of %x\n", hdr.magic, INDEX_MAGIC); + err = -EINVAL; + goto fail; + } + + if (hdr.version >> 16 != INDEX_VERSION_MAJOR) { + ERR(ctx, "major version check fail: %u instead of %u\n", + hdr.version >> 16, INDEX_VERSION_MAJOR); + err = -EINVAL; + goto fail; + } + + idx->root_offset = hdr.root_offset; + idx->size = st.st_size; + idx->ctx = ctx; + close(fd); + + *stamp = stat_mstamp(&st); + *pidx = idx; + + return 0; + +fail: + munmap(idx->mm, st.st_size); +fail_nommap: + close(fd); +fail_open: + free(idx); + return err; +} + +void index_mm_close(struct index_mm *idx) +{ + munmap(idx->mm, idx->size); + free(idx); +} + +static struct index_mm_node *index_mm_readroot(const struct index_mm *idx, + struct index_mm_node *root) +{ + return index_mm_read_node(idx, idx->root_offset, root); +} + +static struct index_mm_node *index_mm_readchild(const struct index_mm_node *parent, + uint8_t ch, struct index_mm_node *child) +{ + if (parent->first <= ch && ch <= parent->last) { + const void *p; + uint32_t off; + + p = (const char *)parent->children + + sizeof(uint32_t) * (ch - parent->first); + off = read_u32_mm(&p); + + return index_mm_read_node(parent->idx, off, child); + } + + return NULL; +} + +static void index_mm_dump_node(struct index_mm_node *node, struct strbuf *buf, + struct wrtbuf *wbuf) +{ + const void *p; + size_t i, pushed; + + pushed = strbuf_pushchars(buf, node->prefix); + + for (i = 0, p = node->values; i < node->value_count; i++) { + struct index_mm_value v; + + read_value_mm(&p, &v); + wrtbuf_write(wbuf, buf->bytes, strbuf_used(buf)); + wrtbuf_write(wbuf, " ", 1); + wrtbuf_write(wbuf, v.value, v.len); + wrtbuf_write(wbuf, "\n", 1); + } + + for (uint8_t ch = node->first; ch <= node->last; ch++) { + struct index_mm_node *child, nbuf; + + child = index_mm_readchild(node, ch, &nbuf); + if (child == NULL) + continue; + + if (strbuf_pushchar(buf, ch)) { + index_mm_dump_node(child, buf, wbuf); + strbuf_popchar(buf); + } + } + + strbuf_popchars(buf, pushed); +} + +void index_mm_dump(const struct index_mm *idx, int fd, bool alias_prefix) +{ + struct index_mm_node nbuf, *root; + struct strbuf buf; + struct wrtbuf wbuf; + + root = index_mm_readroot(idx, &nbuf); + if (root == NULL) + return; + + strbuf_init(&buf); + wrtbuf_init(&wbuf, fd); + if (!alias_prefix || strbuf_pushchars(&buf, "alias ")) + index_mm_dump_node(root, &buf, &wbuf); + wrtbuf_flush(&wbuf); + strbuf_release(&buf); +} + +static char *index_mm_search_node(struct index_mm_node *node, const char *key) +{ + char *value; + int j; + + while (node) { + for (j = 0; node->prefix[j]; j++) { + uint8_t ch = node->prefix[j]; + + if (ch != key[j]) + return NULL; + } + + key += j; + + if (*key == '\0') { + if (node->value_count > 0) { + const char *p = node->values; + + /* return first value without priority */ + p += sizeof(uint32_t); + value = strdup(p); + } else { + value = NULL; + } + + return value; + } + + node = index_mm_readchild(node, *key, node); + key++; + } + + return NULL; +} + +/* + * Search the index for a key + * + * Returns the value of the first match + */ +char *index_mm_search(const struct index_mm *idx, const char *key) +{ + // FIXME: return value by reference instead of strdup + struct index_mm_node nbuf, *root; + char *value; + + root = index_mm_readroot(idx, &nbuf); + value = index_mm_search_node(root, key); + + return value; +} + +/* Level 4: add all the values from a matching node */ +static void index_mm_searchwild_allvalues(struct index_mm_node *node, + struct index_value **out) +{ + const void *p; + size_t i; + + for (i = 0, p = node->values; i < node->value_count; i++) { + struct index_mm_value v; + + read_value_mm(&p, &v); + add_value(out, v.value, v.len, v.priority); + } +} + +/* + * Level 3: traverse a sub-keyspace which starts with a wildcard, + * looking for matches. + */ +static void index_mm_searchwild_all(struct index_mm_node *node, int j, struct strbuf *buf, + const char *subkey, struct index_value **out) +{ + size_t pushed; + + pushed = strbuf_pushchars(buf, &node->prefix[j]); + + for (uint8_t ch = node->first; ch <= node->last; ch++) { + struct index_mm_node *child, nbuf; + + child = index_mm_readchild(node, ch, &nbuf); + if (!child) + continue; + + if (strbuf_pushchar(buf, ch)) { + index_mm_searchwild_all(child, 0, buf, subkey, out); + strbuf_popchar(buf); + } + } + + if (node->value_count > 0) { + const char *s = strbuf_str(buf); + + if (s != NULL && fnmatch(s, subkey, 0) == 0) + index_mm_searchwild_allvalues(node, out); + } + + strbuf_popchars(buf, pushed); +} + +/* Level 2: descend the tree (until we hit a wildcard) */ +static void index_mm_searchwild_node(struct index_mm_node *node, struct strbuf *buf, + const char *key, struct index_value **out) +{ + while (node) { + struct index_mm_node *child, nbuf; + int j; + + for (j = 0; node->prefix[j]; j++) { + uint8_t ch = node->prefix[j]; + + if (ch == '*' || ch == '?' || ch == '[') { + index_mm_searchwild_all(node, j, buf, key + j, out); + return; + } + + if (ch != key[j]) + return; + } + + key += j; + + child = index_mm_readchild(node, '*', &nbuf); + if (child) { + if (strbuf_pushchar(buf, '*')) { + index_mm_searchwild_all(child, 0, buf, key, out); + strbuf_popchar(buf); + } + } + + child = index_mm_readchild(node, '?', &nbuf); + if (child) { + if (strbuf_pushchar(buf, '?')) { + index_mm_searchwild_all(child, 0, buf, key, out); + strbuf_popchar(buf); + } + } + + child = index_mm_readchild(node, '[', &nbuf); + if (child) { + if (strbuf_pushchar(buf, '[')) { + index_mm_searchwild_all(child, 0, buf, key, out); + strbuf_popchar(buf); + } + } + + if (*key == '\0') { + index_mm_searchwild_allvalues(node, out); + + return; + } + + node = index_mm_readchild(node, *key, node); + key++; + } +} + +/* + * Search the index for a key. The index may contain wildcards. + * + * Returns a list of all the values of matching keys. + */ +struct index_value *index_mm_searchwild(const struct index_mm *idx, const char *key) +{ + struct index_mm_node nbuf, *root; + struct strbuf buf; + struct index_value *out = NULL; + + root = index_mm_readroot(idx, &nbuf); + strbuf_init(&buf); + index_mm_searchwild_node(root, &buf, key, &out); + strbuf_release(&buf); + return out; +} diff --git a/libkmod/libkmod-index.h b/libkmod/libkmod-index.h new file mode 100644 index 0000000..47c50d3 --- /dev/null +++ b/libkmod/libkmod-index.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011-2013 ProFUSION embedded systems + */ + +#pragma once + +#include + +struct index_value { + struct index_value *next; + uint32_t priority; + size_t len; + char value[0]; +}; + +/* In-memory index (depmod only) */ +struct index_file; +struct index_file *index_file_open(const char *filename); +void index_file_close(struct index_file *idx); +char *index_search(struct index_file *idx, const char *key); +void index_dump(struct index_file *in, int fd, bool alias_prefix); +struct index_value *index_searchwild(struct index_file *idx, const char *key); + +void index_values_free(struct index_value *values); + +/* Implementation using mmap */ +struct index_mm; +int index_mm_open(const struct kmod_ctx *ctx, const char *filename, + unsigned long long *stamp, struct index_mm **pidx); +void index_mm_close(struct index_mm *index); +char *index_mm_search(const struct index_mm *idx, const char *key); +struct index_value *index_mm_searchwild(const struct index_mm *idx, const char *key); +void index_mm_dump(const struct index_mm *idx, int fd, bool alias_prefix); diff --git a/libkmod/libkmod-internal-file.h b/libkmod/libkmod-internal-file.h new file mode 100644 index 0000000..a50d06e --- /dev/null +++ b/libkmod/libkmod-internal-file.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright © 2024 Intel Corporation + */ + +#include + +#include + +struct kmod_ctx; +struct kmod_elf; + +struct kmod_file { + int fd; + enum kmod_file_compression_type compression; + off_t size; + void *memory; + int (*load)(struct kmod_file *file); + const struct kmod_ctx *ctx; + struct kmod_elf *elf; +}; + +#if ENABLE_XZ +int kmod_file_load_xz(struct kmod_file *file); +#else +static inline int kmod_file_load_xz(struct kmod_file *file) +{ + return -ENOSYS; +} +#endif + +#if ENABLE_ZLIB +int kmod_file_load_zlib(struct kmod_file *file); +#else +static inline int kmod_file_load_zlib(struct kmod_file *file) +{ + return -ENOSYS; +} +#endif + +#if ENABLE_ZSTD +int kmod_file_load_zstd(struct kmod_file *file); +#else +static inline int kmod_file_load_zstd(struct kmod_file *file) +{ + return -ENOSYS; +} +#endif diff --git a/libkmod/libkmod-internal.h b/libkmod/libkmod-internal.h new file mode 100644 index 0000000..4706071 --- /dev/null +++ b/libkmod/libkmod-internal.h @@ -0,0 +1,187 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "libkmod.h" + +#define kmod_log_cond(ctx, prio, arg...) \ + do { \ + if (ENABLE_LOGGING == 1 && \ + (ENABLE_DEBUG == 1 || (!ENABLE_DEBUG && prio != LOG_DEBUG)) && \ + kmod_get_log_priority(ctx) >= prio) \ + kmod_log(ctx, prio, __FILE__, __LINE__, __func__, ##arg); \ + } while (0) + +#define DBG(ctx, arg...) kmod_log_cond(ctx, LOG_DEBUG, ##arg) +#define NOTICE(ctx, arg...) kmod_log_cond(ctx, LOG_NOTICE, ##arg) +#define INFO(ctx, arg...) kmod_log_cond(ctx, LOG_INFO, ##arg) +#define ERR(ctx, arg...) kmod_log_cond(ctx, LOG_ERR, ##arg) + +#define KMOD_EXPORT __attribute__((visibility("default"))) + +#define KCMD_LINE_SIZE 4096 + +#ifndef HAVE_SECURE_GETENV +#warning secure_getenv is not available +#define secure_getenv getenv +#endif + +_printf_format_(6, 7) _nonnull_(1, 3, 5) void kmod_log(const struct kmod_ctx *ctx, + int priority, const char *file, + int line, const char *fn, + const char *format, ...); + +struct list_node { + struct list_node *next, *prev; +}; + +struct kmod_list { + struct list_node node; + void *data; +}; + +enum kmod_file_compression_type { + KMOD_FILE_COMPRESSION_NONE = 0, + KMOD_FILE_COMPRESSION_ZSTD, + KMOD_FILE_COMPRESSION_XZ, + KMOD_FILE_COMPRESSION_ZLIB, +}; + +// clang-format off +_must_check_ _nonnull_(2) struct kmod_list *kmod_list_append(struct kmod_list *list, const void *data); +_must_check_ _nonnull_(2) struct kmod_list *kmod_list_prepend(struct kmod_list *list, const void *data); +_must_check_ struct kmod_list *kmod_list_remove(struct kmod_list *list); +_must_check_ _nonnull_(2) struct kmod_list *kmod_list_remove_data(struct kmod_list *list, const void *data); +_nonnull_(2) struct kmod_list *kmod_list_insert_after(struct kmod_list *list, const void *data); +_nonnull_(2) struct kmod_list *kmod_list_insert_before(struct kmod_list *list, const void *data); +_must_check_ struct kmod_list *kmod_list_append_list(struct kmod_list *list1, struct kmod_list *list2); +#define kmod_list_release(list, free_data) \ + while (list) { \ + free_data((list)->data); \ + list = kmod_list_remove(list); \ + } + +/* libkmod.c */ +_nonnull_all_ int kmod_lookup_alias_from_config(struct kmod_ctx *ctx, const char *name, struct kmod_list **list); +_nonnull_all_ int kmod_lookup_alias_from_symbols_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list); +_nonnull_all_ int kmod_lookup_alias_from_aliases_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list); +_nonnull_all_ int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list); +_nonnull_all_ int kmod_lookup_alias_from_kernel_builtin_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list); +_nonnull_all_ int kmod_lookup_alias_from_builtin_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list); +_nonnull_all_ bool kmod_lookup_alias_is_builtin(struct kmod_ctx *ctx, const char *name); +_nonnull_all_ int kmod_lookup_alias_from_commands(struct kmod_ctx *ctx, const char *name, struct kmod_list **list); +_nonnull_(1) void kmod_set_modules_visited(struct kmod_ctx *ctx, bool visited); +_nonnull_(1) void kmod_set_modules_required(struct kmod_ctx *ctx, bool required); + +_nonnull_all_ char *kmod_search_moddep(struct kmod_ctx *ctx, const char *name); + +_nonnull_all_ struct kmod_module *kmod_pool_get_module(struct kmod_ctx *ctx, const char *key); +_nonnull_all_ int kmod_pool_add_module(struct kmod_ctx *ctx, struct kmod_module *mod, const char *key); +_nonnull_all_ void kmod_pool_del_module(struct kmod_ctx *ctx, struct kmod_module *mod, const char *key); + +_nonnull_all_ const struct kmod_config *kmod_get_config(const struct kmod_ctx *ctx); +_nonnull_all_ enum kmod_file_compression_type kmod_get_kernel_compression(const struct kmod_ctx *ctx); + +/* libkmod-config.c */ +struct kmod_config_path { + unsigned long long stamp; + char path[]; +}; + +struct kmod_config { + struct kmod_ctx *ctx; + struct kmod_list *aliases; + struct kmod_list *blacklists; + struct kmod_list *options; + struct kmod_list *remove_commands; + struct kmod_list *install_commands; + struct kmod_list *softdeps; + struct kmod_list *weakdeps; + + struct kmod_list *paths; +}; + +_nonnull_all_ int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **config, const char * const *config_paths); +_nonnull_all_ void kmod_config_free(struct kmod_config *config); +_nonnull_all_ const char *kmod_blacklist_get_modname(const struct kmod_list *l); +_nonnull_all_ const char *kmod_alias_get_name(const struct kmod_list *l); +_nonnull_all_ const char *kmod_alias_get_modname(const struct kmod_list *l); +_nonnull_all_ const char *kmod_option_get_options(const struct kmod_list *l); +_nonnull_all_ const char *kmod_option_get_modname(const struct kmod_list *l); +_nonnull_all_ const char *kmod_command_get_command(const struct kmod_list *l); +_nonnull_all_ const char *kmod_command_get_modname(const struct kmod_list *l); + +_nonnull_all_ const char *kmod_softdep_get_name(const struct kmod_list *l); +_nonnull_all_ const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count); +const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count); + +_nonnull_all_ const char * kmod_weakdep_get_name(const struct kmod_list *l); +_nonnull_all_ const char * const *kmod_weakdep_get_weak(const struct kmod_list *l, unsigned int *count); + +/* libkmod-module.c */ +int kmod_module_new_from_alias(struct kmod_ctx *ctx, const char *alias, const char *name, struct kmod_module **mod); +_nonnull_all_ void kmod_module_parse_depline(struct kmod_module *mod, char *line); +_nonnull_(1) void kmod_module_set_install_commands(struct kmod_module *mod, const char *cmd); +_nonnull_(1) void kmod_module_set_remove_commands(struct kmod_module *mod, const char *cmd); +_nonnull_(1)void kmod_module_set_visited(struct kmod_module *mod, bool visited); +_nonnull_(1) void kmod_module_set_builtin(struct kmod_module *mod, bool builtin); +_nonnull_(1) void kmod_module_set_required(struct kmod_module *mod, bool required); +_nonnull_all_ bool kmod_module_is_builtin(struct kmod_module *mod); + +/* libkmod-file.c */ +_must_check_ _nonnull_all_ struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx, const char *filename); +_nonnull_all_ struct kmod_elf *kmod_file_get_elf(struct kmod_file *file); +_nonnull_all_ int kmod_file_load_contents(struct kmod_file *file); +_must_check_ _nonnull_all_ void *kmod_file_get_contents(const struct kmod_file *file); +_must_check_ _nonnull_all_ off_t kmod_file_get_size(const struct kmod_file *file); +_must_check_ _nonnull_all_ enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file); +_must_check_ _nonnull_all_ int kmod_file_get_fd(const struct kmod_file *file); +_nonnull_all_ void kmod_file_unref(struct kmod_file *file); + +/* libkmod-elf.c */ +struct kmod_elf; +struct kmod_modversion { + uint64_t crc; + enum kmod_symbol_bind bind; + const char *symbol; +}; + +struct kmod_elf *kmod_elf_new(const void *memory, off_t size); +_nonnull_all_ void kmod_elf_unref(struct kmod_elf *elf); +_must_check_ _nonnull_all_ const void *kmod_elf_get_memory(const struct kmod_elf *elf); +_must_check_ _nonnull_all_ int kmod_elf_get_modinfo_strings(const struct kmod_elf *elf, char ***array); +_must_check_ _nonnull_all_ int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array); +_must_check_ _nonnull_all_ int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array); +_must_check_ _nonnull_all_ int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf, struct kmod_modversion **array); +_must_check_ _nonnull_all_ const void *kmod_elf_strip(const struct kmod_elf *elf, unsigned int flags); + +/* + * Debug mock lib need to find section ".gnu.linkonce.this_module" in order to + * get modname + */ +_must_check_ _nonnull_all_ int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, uint64_t *sec_off, uint64_t *sec_size); + +/* libkmod-signature.c */ +struct kmod_signature_info { + const char *signer; + size_t signer_len; + const char *key_id; + size_t key_id_len; + const char *algo, *hash_algo, *id_type; + const char *sig; + size_t sig_len; + void (*free)(void *); + void *private; +}; +_must_check_ _nonnull_all_ bool kmod_module_signature_info(const struct kmod_file *file, struct kmod_signature_info *sig_info); +_nonnull_all_ void kmod_module_signature_info_free(struct kmod_signature_info *sig_info); + +/* libkmod-builtin.c */ +_nonnull_all_ ssize_t kmod_builtin_get_modinfo(struct kmod_ctx *ctx, const char *modname, char ***modinfo); +// clang-format on diff --git a/libkmod/libkmod-list.c b/libkmod/libkmod-list.c new file mode 100644 index 0000000..5b5899c --- /dev/null +++ b/libkmod/libkmod-list.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2011-2013 ProFUSION embedded systems + */ + +#include + +#include "libkmod.h" +#include "libkmod-internal.h" + +static inline struct list_node *list_node_init(struct list_node *node) +{ + node->next = node; + node->prev = node; + + return node; +} + +static inline void list_node_append(struct list_node *list, struct list_node *node) +{ + if (list == NULL) { + list_node_init(node); + return; + } + + node->prev = list->prev; + list->prev->next = node; + list->prev = node; + node->next = list; +} + +static inline struct list_node *list_node_remove(struct list_node *node) +{ + if (node->prev == node || node->next == node) + return NULL; + + node->prev->next = node->next; + node->next->prev = node->prev; + + return node->next; +} + +static inline void list_node_insert_after(struct list_node *list, struct list_node *node) +{ + if (list == NULL) { + list_node_init(node); + return; + } + + node->prev = list; + node->next = list->next; + list->next->prev = node; + list->next = node; +} + +static inline void list_node_insert_before(struct list_node *list, struct list_node *node) +{ + if (list == NULL) { + list_node_init(node); + return; + } + + node->next = list; + node->prev = list->prev; + list->prev->next = node; + list->prev = node; +} + +static inline void list_node_append_list(struct list_node *list1, struct list_node *list2) +{ + struct list_node *list1_last; + + if (list1 == NULL) { + list_node_init(list2); + return; + } + + list1->prev->next = list2; + list2->prev->next = list1; + + /* cache the last, because we will lose the pointer */ + list1_last = list1->prev; + + list1->prev = list2->prev; + list2->prev = list1_last; +} + +struct kmod_list *kmod_list_append(struct kmod_list *list, const void *data) +{ + struct kmod_list *new; + + new = malloc(sizeof(*new)); + if (new == NULL) + return NULL; + + new->data = (void *)data; + list_node_append(list ? &list->node : NULL, &new->node); + + return list ? list : new; +} + +struct kmod_list *kmod_list_insert_after(struct kmod_list *list, const void *data) +{ + struct kmod_list *new; + + if (list == NULL) + return kmod_list_append(list, data); + + new = malloc(sizeof(*new)); + if (new == NULL) + return NULL; + + new->data = (void *)data; + list_node_insert_after(&list->node, &new->node); + + return list; +} + +struct kmod_list *kmod_list_insert_before(struct kmod_list *list, const void *data) +{ + struct kmod_list *new; + + if (list == NULL) + return kmod_list_append(list, data); + + new = malloc(sizeof(*new)); + if (new == NULL) + return NULL; + + new->data = (void *)data; + list_node_insert_before(&list->node, &new->node); + + return new; +} + +struct kmod_list *kmod_list_append_list(struct kmod_list *list1, struct kmod_list *list2) +{ + if (list1 == NULL) + return list2; + + if (list2 == NULL) + return list1; + + list_node_append_list(&list1->node, &list2->node); + + return list1; +} + +struct kmod_list *kmod_list_prepend(struct kmod_list *list, const void *data) +{ + struct kmod_list *new; + + new = malloc(sizeof(*new)); + if (new == NULL) + return NULL; + + new->data = (void *)data; + list_node_append(list ? &list->node : NULL, &new->node); + + return new; +} + +struct kmod_list *kmod_list_remove(struct kmod_list *list) +{ + struct list_node *node; + + if (list == NULL) + return NULL; + + node = list_node_remove(&list->node); + free(list); + + if (node == NULL) + return NULL; + + return container_of(node, struct kmod_list, node); +} + +struct kmod_list *kmod_list_remove_data(struct kmod_list *list, const void *data) +{ + struct kmod_list *itr; + struct list_node *node; + + for (itr = list; itr != NULL; itr = kmod_list_next(list, itr)) { + if (itr->data == data) + break; + } + + if (itr == NULL) + return list; + + node = list_node_remove(&itr->node); + free(itr); + + if (node == NULL) + return NULL; + + return container_of(node, struct kmod_list, node); +} + +KMOD_EXPORT struct kmod_list *kmod_list_prev(const struct kmod_list *list, + const struct kmod_list *curr) +{ + if (list == NULL || curr == NULL) + return NULL; + + if (list == curr) + return NULL; + + return container_of(curr->node.prev, struct kmod_list, node); +} + +KMOD_EXPORT struct kmod_list *kmod_list_next(const struct kmod_list *list, + const struct kmod_list *curr) +{ + if (list == NULL || curr == NULL) + return NULL; + + if (curr->node.next == &list->node) + return NULL; + + return container_of(curr->node.next, struct kmod_list, node); +} + +KMOD_EXPORT struct kmod_list *kmod_list_last(const struct kmod_list *list) +{ + if (list == NULL) + return NULL; + return container_of(list->node.prev, struct kmod_list, node); +} diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c new file mode 100644 index 0000000..0d74100 --- /dev/null +++ b/libkmod/libkmod-module.c @@ -0,0 +1,2307 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2011-2013 ProFUSION embedded systems + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libkmod.h" +#include "libkmod-internal.h" + +enum kmod_module_builtin { + KMOD_MODULE_BUILTIN_UNKNOWN, + KMOD_MODULE_BUILTIN_NO, + KMOD_MODULE_BUILTIN_YES, +}; + +struct kmod_module { + struct kmod_ctx *ctx; + char *hashkey; + char *name; + char *path; + struct kmod_list *dep; + char *options; + const char *install_commands; /* owned by kmod_config */ + const char *remove_commands; /* owned by kmod_config */ + char *alias; /* only set if this module was created from an alias */ + struct kmod_file *file; + int refcount; + struct { + bool dep : 1; + bool options : 1; + bool install_commands : 1; + bool remove_commands : 1; + } init; + + /* + * mark if module is builtin, i.e. it's present on modules.builtin + * file. This is set as soon as it is needed or as soon as we know + * about it, i.e. the module was created from builtin lookup. + */ + enum kmod_module_builtin builtin; + + /* + * private field used by kmod_module_get_probe_list() to detect + * dependency loops + */ + bool visited : 1; + + /* + * set by kmod_module_get_probe_list: indicates for probe_insert() + * whether the module's command and softdep should be ignored + */ + bool ignorecmd : 1; + + /* + * set by kmod_module_get_probe_list: indicates whether this is the + * module the user asked for or its dependency, or whether this + * is a softdep only + */ + bool required : 1; +}; + +static inline const char *path_join(const char *path, size_t prefixlen, char buf[PATH_MAX]) +{ + size_t pathlen; + + if (path[0] == '/') + return path; + + pathlen = strlen(path); + if (prefixlen + pathlen + 1 >= PATH_MAX) + return NULL; + + memcpy(buf + prefixlen, path, pathlen + 1); + return buf; +} + +static inline bool module_is_inkernel(struct kmod_module *mod) +{ + int state = kmod_module_get_initstate(mod); + + if (state == KMOD_MODULE_LIVE || state == KMOD_MODULE_BUILTIN) + return true; + + return false; +} + +void kmod_module_parse_depline(struct kmod_module *mod, char *line) +{ + struct kmod_ctx *ctx = mod->ctx; + struct kmod_list *list = NULL; + const char *dirname; + char buf[PATH_MAX]; + char *p, *saveptr; + size_t n = 0; + size_t dirnamelen; + + if (mod->init.dep) + return; + assert(mod->dep == NULL); + mod->init.dep = true; + + p = strchr(line, ':'); + if (p == NULL) + return; + + *p = '\0'; + dirname = kmod_get_dirname(mod->ctx); + dirnamelen = strlen(dirname); + if (dirnamelen + 2 >= PATH_MAX) + return; + + memcpy(buf, dirname, dirnamelen); + buf[dirnamelen] = '/'; + dirnamelen++; + buf[dirnamelen] = '\0'; + + if (mod->path == NULL) { + const char *str = path_join(line, dirnamelen, buf); + if (str == NULL) + return; + mod->path = strdup(str); + if (mod->path == NULL) + return; + } + + p++; + for (p = strtok_r(p, " \t", &saveptr); p != NULL; + p = strtok_r(NULL, " \t", &saveptr)) { + struct kmod_list *l_new; + struct kmod_module *depmod = NULL; + const char *path; + int err; + + path = path_join(p, dirnamelen, buf); + if (path == NULL) { + ERR(ctx, "could not join path '%s' and '%s'.\n", dirname, p); + goto fail; + } + + err = kmod_module_new_from_path(ctx, path, &depmod); + if (err < 0) { + ERR(ctx, "ctx=%p path=%s error=%s\n", ctx, path, strerror(-err)); + goto fail; + } + + DBG(ctx, "add dep: %s\n", path); + + l_new = kmod_list_prepend(list, depmod); + if (l_new == NULL) { + ERR(ctx, "could not add dependency for %s\n", mod->name); + goto fail; + } + list = l_new; + } + + DBG(ctx, "%zu dependencies for %s\n", n, mod->name); + + mod->dep = list; + return; + +fail: + kmod_module_unref_list(list); + mod->init.dep = false; +} + +void kmod_module_set_visited(struct kmod_module *mod, bool visited) +{ + mod->visited = visited; +} + +void kmod_module_set_builtin(struct kmod_module *mod, bool builtin) +{ + mod->builtin = builtin ? KMOD_MODULE_BUILTIN_YES : KMOD_MODULE_BUILTIN_NO; +} + +void kmod_module_set_required(struct kmod_module *mod, bool required) +{ + mod->required = required; +} + +bool kmod_module_is_builtin(struct kmod_module *mod) +{ + if (mod->builtin == KMOD_MODULE_BUILTIN_UNKNOWN) { + kmod_module_set_builtin(mod, kmod_lookup_alias_is_builtin(mod->ctx, + mod->name)); + } + + return mod->builtin == KMOD_MODULE_BUILTIN_YES; +} +/* + * Memory layout with alias: + * + * struct kmod_module { + * hashkey -----. + * alias -----. | + * name ----. | | + * } | | | + * name <----------' | | + * alias <-----------' | + * name\alias <--------' + * + * Memory layout without alias: + * + * struct kmod_module { + * hashkey ---. + * alias -----|----> NULL + * name ----. | + * } | | + * name <----------'-' + * + * @key is "name\alias" or "name" (in which case alias == NULL) + */ +static int kmod_module_new(struct kmod_ctx *ctx, const char *key, const char *name, + size_t namelen, const char *alias, size_t aliaslen, + struct kmod_module **mod) +{ + struct kmod_module *m; + size_t keylen; + int err; + + m = kmod_pool_get_module(ctx, key); + if (m != NULL) { + *mod = kmod_module_ref(m); + return 0; + } + + if (alias == NULL) + keylen = namelen; + else + keylen = namelen + aliaslen + 1; + + m = malloc(sizeof(*m) + (alias == NULL ? 1 : 2) * (keylen + 1)); + if (m == NULL) + return -ENOMEM; + + memset(m, 0, sizeof(*m)); + + m->ctx = kmod_ref(ctx); + m->name = (char *)m + sizeof(*m); + memcpy(m->name, key, keylen + 1); + if (alias == NULL) { + m->hashkey = m->name; + m->alias = NULL; + } else { + m->name[namelen] = '\0'; + m->alias = m->name + namelen + 1; + m->hashkey = m->name + keylen + 1; + memcpy(m->hashkey, key, keylen + 1); + } + + m->refcount = 1; + err = kmod_pool_add_module(ctx, m, m->hashkey); + if (err < 0) { + free(m); + return err; + } + *mod = m; + + return 0; +} + +KMOD_EXPORT int kmod_module_new_from_name(struct kmod_ctx *ctx, const char *name, + struct kmod_module **mod) +{ + size_t namelen; + char name_norm[PATH_MAX]; + + if (ctx == NULL || name == NULL || mod == NULL) + return -ENOENT; + + modname_normalize(name, name_norm, &namelen); + + return kmod_module_new(ctx, name_norm, name_norm, namelen, NULL, 0, mod); +} + +int kmod_module_new_from_alias(struct kmod_ctx *ctx, const char *alias, const char *name, + struct kmod_module **mod) +{ + char key[PATH_MAX]; + size_t namelen = strlen(name); + size_t aliaslen = strlen(alias); + + if (namelen + aliaslen + 2 > PATH_MAX) + return -ENAMETOOLONG; + + memcpy(key, name, namelen); + memcpy(key + namelen + 1, alias, aliaslen + 1); + key[namelen] = '\\'; + + return kmod_module_new(ctx, key, name, namelen, alias, aliaslen, mod); +} + +KMOD_EXPORT int kmod_module_new_from_path(struct kmod_ctx *ctx, const char *path, + struct kmod_module **mod) +{ + struct kmod_module *m; + int err; + struct stat st; + char name[PATH_MAX]; + char *abspath; + size_t namelen; + + if (ctx == NULL || path == NULL || mod == NULL) + return -ENOENT; + + abspath = path_make_absolute_cwd(path); + if (abspath == NULL) { + DBG(ctx, "no absolute path for %s\n", path); + return -ENOMEM; + } + + err = stat(abspath, &st); + if (err < 0) { + err = -errno; + DBG(ctx, "stat %s: %s\n", path, strerror(errno)); + free(abspath); + return err; + } + + if (path_to_modname(path, name, &namelen) == NULL) { + DBG(ctx, "could not get modname from path %s\n", path); + free(abspath); + return -ENOENT; + } + + err = kmod_module_new(ctx, name, name, namelen, NULL, 0, &m); + if (err < 0) { + free(abspath); + return err; + } + if (m->path == NULL) + m->path = abspath; + else if (streq(m->path, abspath)) + free(abspath); + else { + kmod_module_unref(m); + ERR(ctx, + "kmod_module '%s' already exists with different path: new-path='%s' old-path='%s'\n", + name, abspath, m->path); + free(abspath); + return -EEXIST; + } + + m->builtin = KMOD_MODULE_BUILTIN_NO; + *mod = m; + + return 0; +} + +KMOD_EXPORT struct kmod_module *kmod_module_unref(struct kmod_module *mod) +{ + if (mod == NULL) + return NULL; + + if (--mod->refcount > 0) + return mod; + + DBG(mod->ctx, "kmod_module %p released\n", mod); + + kmod_pool_del_module(mod->ctx, mod, mod->hashkey); + kmod_module_unref_list(mod->dep); + + if (mod->file) + kmod_file_unref(mod->file); + + kmod_unref(mod->ctx); + free(mod->options); + free(mod->path); + free(mod); + return NULL; +} + +KMOD_EXPORT struct kmod_module *kmod_module_ref(struct kmod_module *mod) +{ + if (mod == NULL) + return NULL; + + mod->refcount++; + + return mod; +} + +typedef _nonnull_all_ int (*lookup_func)(struct kmod_ctx *ctx, const char *name, + struct kmod_list **list); + +static int __kmod_module_new_from_lookup(struct kmod_ctx *ctx, const lookup_func lookup[], + size_t lookup_count, const char *s, + struct kmod_list **list) +{ + unsigned int i; + + for (i = 0; i < lookup_count; i++) { + int err; + + err = lookup[i](ctx, s, list); + if (err < 0 && err != -ENOSYS) + return err; + else if (*list != NULL) + return 0; + } + + return 0; +} + +KMOD_EXPORT int kmod_module_new_from_lookup(struct kmod_ctx *ctx, const char *given_alias, + struct kmod_list **list) +{ + static const lookup_func lookup[] = { + kmod_lookup_alias_from_config, + kmod_lookup_alias_from_moddep_file, + kmod_lookup_alias_from_symbols_file, + kmod_lookup_alias_from_commands, + kmod_lookup_alias_from_aliases_file, + kmod_lookup_alias_from_builtin_file, + kmod_lookup_alias_from_kernel_builtin_file, + }; + char alias[PATH_MAX]; + int err; + + if (ctx == NULL || given_alias == NULL) + return -ENOENT; + + if (list == NULL || *list != NULL) { + ERR(ctx, "An empty list is needed to create lookup\n"); + return -ENOSYS; + } + + if (alias_normalize(given_alias, alias, NULL) < 0) { + DBG(ctx, "invalid alias: %s\n", given_alias); + return -EINVAL; + } + + DBG(ctx, "input alias=%s, normalized=%s\n", given_alias, alias); + + err = __kmod_module_new_from_lookup(ctx, lookup, ARRAY_SIZE(lookup), alias, list); + + DBG(ctx, "lookup=%s found=%d\n", alias, err >= 0 && *list); + + if (err < 0) { + kmod_module_unref_list(*list); + *list = NULL; + } + + return err; +} + +KMOD_EXPORT int kmod_module_new_from_name_lookup(struct kmod_ctx *ctx, + const char *modname, + struct kmod_module **mod) +{ + static const lookup_func lookup[] = { + kmod_lookup_alias_from_moddep_file, + kmod_lookup_alias_from_builtin_file, + kmod_lookup_alias_from_kernel_builtin_file, + }; + char name_norm[PATH_MAX]; + struct kmod_list *list = NULL; + int err; + + if (ctx == NULL || modname == NULL || mod == NULL) + return -ENOENT; + + modname_normalize(modname, name_norm, NULL); + + DBG(ctx, "input modname=%s, normalized=%s\n", modname, name_norm); + + err = __kmod_module_new_from_lookup(ctx, lookup, ARRAY_SIZE(lookup), name_norm, + &list); + + DBG(ctx, "lookup=%s found=%d\n", name_norm, err >= 0 && list); + + if (err >= 0 && list != NULL) + *mod = kmod_module_get_module(list); + + kmod_module_unref_list(list); + + return err; +} + +KMOD_EXPORT int kmod_module_unref_list(struct kmod_list *list) +{ + kmod_list_release(list, kmod_module_unref); + + return 0; +} + +KMOD_EXPORT int kmod_module_get_filtered_blacklist(const struct kmod_ctx *ctx, + const struct kmod_list *input, + struct kmod_list **output) +{ + return kmod_module_apply_filter(ctx, KMOD_FILTER_BLACKLIST, input, output); +} + +static void module_get_dependencies_noref(struct kmod_module *mod) +{ + if (!mod->init.dep) { + /* lazy init */ + char *line = kmod_search_moddep(mod->ctx, mod->name); + + if (line != NULL) { + kmod_module_parse_depline(mod, line); + free(line); + } + } +} + +KMOD_EXPORT struct kmod_list *kmod_module_get_dependencies(const struct kmod_module *mod) +{ + struct kmod_list *l, *l_new, *list_new = NULL; + + if (mod == NULL) + return NULL; + + module_get_dependencies_noref((struct kmod_module *)mod); + + kmod_list_foreach(l, mod->dep) { + l_new = kmod_list_append(list_new, kmod_module_ref(l->data)); + if (l_new == NULL) { + kmod_module_unref(l->data); + goto fail; + } + + list_new = l_new; + } + + return list_new; + +fail: + ERR(mod->ctx, "out of memory\n"); + kmod_module_unref_list(list_new); + return NULL; +} + +KMOD_EXPORT struct kmod_module *kmod_module_get_module(const struct kmod_list *entry) +{ + if (entry == NULL) + return NULL; + + return kmod_module_ref(entry->data); +} + +KMOD_EXPORT const char *kmod_module_get_name(const struct kmod_module *mod) +{ + if (mod == NULL) + return NULL; + + return mod->name; +} + +KMOD_EXPORT const char *kmod_module_get_path(const struct kmod_module *mod) +{ + if (mod == NULL) + return NULL; + + DBG(mod->ctx, "name='%s' path='%s'\n", mod->name, mod->path); + + if (mod->path != NULL) + return mod->path; + if (mod->init.dep) + return NULL; + + /* lazy init */ + module_get_dependencies_noref((struct kmod_module *)mod); + + return mod->path; +} + +extern long delete_module(const char *name, unsigned int flags); + +KMOD_EXPORT int kmod_module_remove_module(struct kmod_module *mod, unsigned int flags) +{ + unsigned int libkmod_flags = flags & 0xff; + + int err; + + if (mod == NULL) + return -ENOENT; + + /* Filter out other flags and force ONONBLOCK */ + flags &= KMOD_REMOVE_FORCE; + flags |= KMOD_REMOVE_NOWAIT; + + err = delete_module(mod->name, flags); + if (err != 0) { + err = -errno; + if (!(libkmod_flags & KMOD_REMOVE_NOLOG)) + ERR(mod->ctx, "could not remove '%s': %m\n", mod->name); + } + + return err; +} + +extern long init_module(const void *mem, unsigned long len, const char *args); + +static int do_finit_module(struct kmod_module *mod, unsigned int flags, const char *args) +{ + enum kmod_file_compression_type compression, kernel_compression; + unsigned int kernel_flags = 0; + int err; + + /* + * When module is not compressed or its compression type matches the + * one in use by the kernel, there is no need to read the file + * in userspace. Otherwise, reuse ENOSYS to trigger the same fallback + * as when finit_module() is not supported. + */ + compression = kmod_file_get_compression(mod->file); + kernel_compression = kmod_get_kernel_compression(mod->ctx); + if (!(compression == KMOD_FILE_COMPRESSION_NONE || + compression == kernel_compression)) + return -ENOSYS; + + if (compression != KMOD_FILE_COMPRESSION_NONE) + kernel_flags |= MODULE_INIT_COMPRESSED_FILE; + + if (flags & KMOD_INSERT_FORCE_VERMAGIC) + kernel_flags |= MODULE_INIT_IGNORE_VERMAGIC; + if (flags & KMOD_INSERT_FORCE_MODVERSION) + kernel_flags |= MODULE_INIT_IGNORE_MODVERSIONS; + + err = finit_module(kmod_file_get_fd(mod->file), args, kernel_flags); + if (err < 0) + err = -errno; + + return err; +} + +static int do_init_module(struct kmod_module *mod, unsigned int flags, const char *args) +{ + _cleanup_free_ const void *stripped = NULL; + struct kmod_elf *elf; + const void *mem; + off_t size; + int err; + + if (flags & (KMOD_INSERT_FORCE_VERMAGIC | KMOD_INSERT_FORCE_MODVERSION)) { + elf = kmod_file_get_elf(mod->file); + if (elf == NULL) { + err = -errno; + return err; + } + + stripped = kmod_elf_strip(elf, flags); + if (stripped == NULL) { + ERR(mod->ctx, "Failed to strip version information: %s\n", + strerror(errno)); + return -errno; + } + mem = stripped; + } else { + err = kmod_file_load_contents(mod->file); + if (err) + return err; + + mem = kmod_file_get_contents(mod->file); + } + size = kmod_file_get_size(mod->file); + + err = init_module(mem, size, args); + if (err < 0) + err = -errno; + + return err; +} + +KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod, unsigned int flags, + const char *options) +{ + int err; + const char *path; + const char *args = options ? options : ""; + + if (mod == NULL) + return -ENOENT; + + path = kmod_module_get_path(mod); + if (path == NULL) { + ERR(mod->ctx, "could not find module by name='%s'\n", mod->name); + return -ENOENT; + } + + if (!mod->file) { + mod->file = kmod_file_open(mod->ctx, path); + if (mod->file == NULL) { + err = -errno; + return err; + } + } + + err = do_finit_module(mod, flags, args); + if (err == -ENOSYS) + err = do_init_module(mod, flags, args); + + if (err < 0) + INFO(mod->ctx, "Failed to insert module '%s': %s\n", path, strerror(-err)); + + return err; +} + +static bool module_is_blacklisted(const struct kmod_module *mod) +{ + const struct kmod_ctx *ctx = mod->ctx; + const struct kmod_config *config = kmod_get_config(ctx); + const struct kmod_list *bl = config->blacklists; + const struct kmod_list *l; + + kmod_list_foreach(l, bl) { + const char *modname = kmod_blacklist_get_modname(l); + + if (streq(modname, mod->name)) + return true; + } + + return false; +} + +KMOD_EXPORT int kmod_module_apply_filter(const struct kmod_ctx *ctx, + enum kmod_filter filter_type, + const struct kmod_list *input, + struct kmod_list **output) +{ + const struct kmod_list *li; + + if (ctx == NULL || output == NULL) + return -ENOENT; + + *output = NULL; + if (input == NULL) + return 0; + + kmod_list_foreach(li, input) { + struct kmod_module *mod = li->data; + struct kmod_list *node; + + if ((filter_type & KMOD_FILTER_BLACKLIST) && module_is_blacklisted(mod)) + continue; + + if ((filter_type & KMOD_FILTER_BUILTIN) && kmod_module_is_builtin(mod)) + continue; + + node = kmod_list_append(*output, mod); + if (node == NULL) + goto fail; + + *output = node; + kmod_module_ref(mod); + } + + return 0; + +fail: + kmod_module_unref_list(*output); + *output = NULL; + return -ENOMEM; +} + +static int command_do(struct kmod_module *mod, const char *type, const char *cmd) +{ + const char *modname = kmod_module_get_name(mod); + int err; + + DBG(mod->ctx, "%s %s\n", type, cmd); + + setenv("MODPROBE_MODULE", modname, 1); + err = system(cmd); + unsetenv("MODPROBE_MODULE"); + + if (err == -1) { + ERR(mod->ctx, "Could not run %s command '%s' for module %s: %m\n", type, + cmd, modname); + return -EINVAL; + } + + if (WEXITSTATUS(err)) { + ERR(mod->ctx, "Error running %s command '%s' for module %s: retcode %d\n", + type, cmd, modname, WEXITSTATUS(err)); + return -EINVAL; + } + + return 0; +} + +struct probe_insert_cb { + int (*run_install)(struct kmod_module *m, const char *cmd, void *data); + void *data; +}; + +static int module_do_install_commands(struct kmod_module *mod, const char *options, + struct probe_insert_cb *cb) +{ + const char *command = kmod_module_get_install_commands(mod); + char *p; + _cleanup_free_ char *cmd; + int err; + size_t cmdlen, options_len, varlen; + + assert(command); + + if (options == NULL) + options = ""; + + options_len = strlen(options); + cmdlen = strlen(command); + varlen = sizeof("$CMDLINE_OPTS") - 1; + + cmd = memdup(command, cmdlen + 1); + if (cmd == NULL) + return -ENOMEM; + + while ((p = strstr(cmd, "$CMDLINE_OPTS")) != NULL) { + size_t prefixlen = p - cmd; + size_t suffixlen = cmdlen - prefixlen - varlen; + size_t slen = cmdlen - varlen + options_len; + char *suffix = p + varlen; + _clang_suppress_alloc_ char *s = malloc(slen + 1); + if (!s) + return -ENOMEM; + + memcpy(s, cmd, p - cmd); + memcpy(s + prefixlen, options, options_len); + memcpy(s + prefixlen + options_len, suffix, suffixlen); + s[slen] = '\0'; + + free(cmd); + cmd = s; + cmdlen = slen; + } + + if (cb->run_install != NULL) + err = cb->run_install(mod, cmd, cb->data); + else + err = command_do(mod, "install", cmd); + + return err; +} + +static char *module_options_concat(const char *opt, const char *xopt) +{ + // TODO: we might need to check if xopt overrides options on opt + size_t optlen = opt == NULL ? 0 : strlen(opt); + size_t xoptlen = xopt == NULL ? 0 : strlen(xopt); + char *r; + + if (optlen == 0 && xoptlen == 0) + return NULL; + + r = malloc(optlen + xoptlen + 2); + + if (opt != NULL) { + memcpy(r, opt, optlen); + r[optlen] = ' '; + optlen++; + } + + if (xopt != NULL) + memcpy(r + optlen, xopt, xoptlen); + + r[optlen + xoptlen] = '\0'; + + return r; +} + +static int __kmod_module_get_probe_list(struct kmod_module *mod, bool required, + bool ignorecmd, struct kmod_list **list); + +/* re-entrant */ +static int __kmod_module_fill_softdep(struct kmod_module *mod, struct kmod_list **list) +{ + struct kmod_list *pre = NULL, *post = NULL, *l; + int err; + + err = kmod_module_get_softdeps(mod, &pre, &post); + if (err < 0) { + ERR(mod->ctx, "could not get softdep: %s\n", strerror(-err)); + goto fail; + } + + kmod_list_foreach(l, pre) { + struct kmod_module *m = l->data; + err = __kmod_module_get_probe_list(m, false, false, list); + if (err < 0) + goto fail; + } + + l = kmod_list_append(*list, kmod_module_ref(mod)); + if (l == NULL) { + kmod_module_unref(mod); + err = -ENOMEM; + goto fail; + } + *list = l; + mod->ignorecmd = (pre != NULL || post != NULL); + + kmod_list_foreach(l, post) { + struct kmod_module *m = l->data; + err = __kmod_module_get_probe_list(m, false, false, list); + if (err < 0) + goto fail; + } + +fail: + kmod_module_unref_list(pre); + kmod_module_unref_list(post); + + return err; +} + +/* re-entrant */ +static int __kmod_module_get_probe_list(struct kmod_module *mod, bool required, + bool ignorecmd, struct kmod_list **list) +{ + struct kmod_list *dep, *l; + int err = 0; + + if (mod->visited) { + DBG(mod->ctx, "Ignore module '%s': already visited\n", mod->name); + return 0; + } + mod->visited = true; + + dep = kmod_module_get_dependencies(mod); + if (required) { + /* + * Called from kmod_module_probe_insert_module(); set the + * ->required flag on mod and all its dependencies before + * they are possibly visited through some softdeps. + */ + mod->required = true; + kmod_list_foreach(l, dep) { + struct kmod_module *m = l->data; + m->required = true; + } + } + + kmod_list_foreach(l, dep) { + struct kmod_module *m = l->data; + err = __kmod_module_fill_softdep(m, list); + if (err < 0) + goto finish; + } + + if (ignorecmd) { + l = kmod_list_append(*list, kmod_module_ref(mod)); + if (l == NULL) { + kmod_module_unref(mod); + err = -ENOMEM; + goto finish; + } + *list = l; + mod->ignorecmd = true; + } else + err = __kmod_module_fill_softdep(mod, list); + +finish: + kmod_module_unref_list(dep); + return err; +} + +static int kmod_module_get_probe_list(struct kmod_module *mod, bool ignorecmd, + struct kmod_list **list) +{ + int err; + + assert(mod != NULL); + assert(list != NULL && *list == NULL); + + /* + * Make sure we don't get screwed by previous calls to this function + */ + kmod_set_modules_visited(mod->ctx, false); + kmod_set_modules_required(mod->ctx, false); + + err = __kmod_module_get_probe_list(mod, true, ignorecmd, list); + if (err < 0) { + kmod_module_unref_list(*list); + *list = NULL; + } + + return err; +} + +KMOD_EXPORT int kmod_module_probe_insert_module( + struct kmod_module *mod, unsigned int flags, const char *extra_options, + int (*run_install)(struct kmod_module *m, const char *cmd, void *data), + const void *data, + void (*print_action)(struct kmod_module *m, bool install, const char *options)) +{ + struct kmod_list *list = NULL, *l; + struct probe_insert_cb cb; + int err; + + if (mod == NULL) + return -ENOENT; + + if (!(flags & KMOD_PROBE_IGNORE_LOADED) && module_is_inkernel(mod)) { + if (flags & KMOD_PROBE_FAIL_ON_LOADED) + return -EEXIST; + else + return 0; + } + + if (module_is_blacklisted(mod)) { + if (mod->alias != NULL && (flags & KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY)) + return KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY; + + if (flags & KMOD_PROBE_APPLY_BLACKLIST_ALL) + return KMOD_PROBE_APPLY_BLACKLIST_ALL; + + if (flags & KMOD_PROBE_APPLY_BLACKLIST) + return KMOD_PROBE_APPLY_BLACKLIST; + } + + err = kmod_module_get_probe_list(mod, !!(flags & KMOD_PROBE_IGNORE_COMMAND), + &list); + if (err < 0) + return err; + + if (flags & KMOD_PROBE_APPLY_BLACKLIST_ALL) { + struct kmod_list *filtered = NULL; + + err = kmod_module_apply_filter(mod->ctx, KMOD_FILTER_BLACKLIST, list, + &filtered); + if (err < 0) + return err; + + kmod_module_unref_list(list); + if (filtered == NULL) + return KMOD_PROBE_APPLY_BLACKLIST_ALL; + + list = filtered; + } + + cb.run_install = run_install; + cb.data = (void *)data; + + kmod_list_foreach(l, list) { + struct kmod_module *m = l->data; + const char *moptions = kmod_module_get_options(m); + const char *cmd = kmod_module_get_install_commands(m); + char *options; + + if (!(flags & KMOD_PROBE_IGNORE_LOADED) && module_is_inkernel(m)) { + DBG(mod->ctx, "Ignoring module '%s': already loaded\n", m->name); + err = -EEXIST; + goto finish_module; + } + + options = + module_options_concat(moptions, m == mod ? extra_options : NULL); + + if (cmd != NULL && !m->ignorecmd) { + if (print_action != NULL) + print_action(m, true, options ?: ""); + + if (!(flags & KMOD_PROBE_DRY_RUN)) + err = module_do_install_commands(m, options, &cb); + } else { + if (print_action != NULL) + print_action(m, false, options ?: ""); + + if (!(flags & KMOD_PROBE_DRY_RUN)) + err = kmod_module_insert_module(m, flags, options); + } + + free(options); + +finish_module: + /* + * Treat "already loaded" error. If we were told to stop on + * already loaded and the module being loaded is not a softdep + * or dep, bail out. Otherwise, just ignore and continue. + * + * We need to check here because of race conditions. We + * checked first if module was already loaded but it may have + * been loaded between the check and the moment we try to + * insert it. + */ + if (err == -EEXIST && m == mod && (flags & KMOD_PROBE_FAIL_ON_LOADED)) + break; + + /* + * Ignore errors from softdeps + */ + if (err == -EEXIST || !m->required) + err = 0; + + else if (err < 0) + break; + } + + kmod_module_unref_list(list); + return err; +} + +KMOD_EXPORT const char *kmod_module_get_options(const struct kmod_module *mod) +{ + if (mod == NULL) + return NULL; + + if (!mod->init.options) { + /* lazy init */ + struct kmod_module *m = (struct kmod_module *)mod; + const struct kmod_list *l; + const struct kmod_config *config; + char *opts = NULL; + size_t optslen = 0; + + config = kmod_get_config(mod->ctx); + + kmod_list_foreach(l, config->options) { + const char *modname = kmod_option_get_modname(l); + const char *str; + size_t len; + void *tmp; + + DBG(mod->ctx, "modname=%s mod->name=%s mod->alias=%s\n", modname, + mod->name, mod->alias); + if (!(streq(modname, mod->name) || + (mod->alias != NULL && streq(modname, mod->alias)))) + continue; + + DBG(mod->ctx, "passed = modname=%s mod->name=%s mod->alias=%s\n", + modname, mod->name, mod->alias); + str = kmod_option_get_options(l); + len = strlen(str); + if (len < 1) + continue; + + tmp = realloc(opts, optslen + len + 2); + if (tmp == NULL) { + free(opts); + goto failed; + } + + opts = tmp; + + if (optslen > 0) { + opts[optslen] = ' '; + optslen++; + } + + memcpy(opts + optslen, str, len); + optslen += len; + opts[optslen] = '\0'; + } + + m->init.options = true; + m->options = opts; + } + + return mod->options; + +failed: + ERR(mod->ctx, "out of memory\n"); + return NULL; +} + +KMOD_EXPORT const char *kmod_module_get_install_commands(const struct kmod_module *mod) +{ + if (mod == NULL) + return NULL; + + if (!mod->init.install_commands) { + /* lazy init */ + struct kmod_module *m = (struct kmod_module *)mod; + const struct kmod_list *l; + const struct kmod_config *config; + + config = kmod_get_config(mod->ctx); + + kmod_list_foreach(l, config->install_commands) { + const char *modname = kmod_command_get_modname(l); + + if (fnmatch(modname, mod->name, 0) != 0) + continue; + + m->install_commands = kmod_command_get_command(l); + + /* + * find only the first command, as modprobe from + * module-init-tools does + */ + break; + } + + m->init.install_commands = true; + } + + return mod->install_commands; +} + +void kmod_module_set_install_commands(struct kmod_module *mod, const char *cmd) +{ + mod->init.install_commands = true; + mod->install_commands = cmd; +} + +static struct kmod_list *lookup_dep(struct kmod_ctx *ctx, const char *const *array, + unsigned int count) +{ + struct kmod_list *ret = NULL; + unsigned i; + + for (i = 0; i < count; i++) { + const char *depname = array[i]; + struct kmod_list *lst = NULL; + int err; + + err = kmod_module_new_from_lookup(ctx, depname, &lst); + if (err < 0) { + ERR(ctx, "failed to lookup dependency '%s', continuing anyway.\n", + depname); + continue; + } else if (lst != NULL) + ret = kmod_list_append_list(ret, lst); + } + return ret; +} + +KMOD_EXPORT int kmod_module_get_softdeps(const struct kmod_module *mod, + struct kmod_list **pre, struct kmod_list **post) +{ + const struct kmod_list *l; + const struct kmod_config *config; + + if (mod == NULL || pre == NULL || post == NULL) + return -ENOENT; + + assert(*pre == NULL); + assert(*post == NULL); + + config = kmod_get_config(mod->ctx); + + kmod_list_foreach(l, config->softdeps) { + const char *modname = kmod_softdep_get_name(l); + const char *const *array; + unsigned count; + + if (fnmatch(modname, mod->name, 0) != 0) + continue; + + array = kmod_softdep_get_pre(l, &count); + *pre = lookup_dep(mod->ctx, array, count); + array = kmod_softdep_get_post(l, &count); + *post = lookup_dep(mod->ctx, array, count); + + /* + * find only the first command, as modprobe from + * module-init-tools does + */ + break; + } + + return 0; +} + +KMOD_EXPORT int kmod_module_get_weakdeps(const struct kmod_module *mod, + struct kmod_list **weak) +{ + const struct kmod_list *l; + const struct kmod_config *config; + + if (mod == NULL || weak == NULL) + return -ENOENT; + + assert(*weak == NULL); + + config = kmod_get_config(mod->ctx); + + kmod_list_foreach(l, config->weakdeps) { + const char *modname = kmod_weakdep_get_name(l); + const char *const *array; + unsigned count; + + if (fnmatch(modname, mod->name, 0) != 0) + continue; + + array = kmod_weakdep_get_weak(l, &count); + *weak = lookup_dep(mod->ctx, array, count); + + /* + * find only the first command, as modprobe from + * module-init-tools does + */ + break; + } + + return 0; +} + +KMOD_EXPORT const char *kmod_module_get_remove_commands(const struct kmod_module *mod) +{ + if (mod == NULL) + return NULL; + + if (!mod->init.remove_commands) { + /* lazy init */ + struct kmod_module *m = (struct kmod_module *)mod; + const struct kmod_list *l; + const struct kmod_config *config; + + config = kmod_get_config(mod->ctx); + + kmod_list_foreach(l, config->remove_commands) { + const char *modname = kmod_command_get_modname(l); + + if (fnmatch(modname, mod->name, 0) != 0) + continue; + + m->remove_commands = kmod_command_get_command(l); + + /* + * find only the first command, as modprobe from + * module-init-tools does + */ + break; + } + + m->init.remove_commands = true; + } + + return mod->remove_commands; +} + +void kmod_module_set_remove_commands(struct kmod_module *mod, const char *cmd) +{ + mod->init.remove_commands = true; + mod->remove_commands = cmd; +} + +KMOD_EXPORT int kmod_module_new_from_loaded(struct kmod_ctx *ctx, struct kmod_list **list) +{ + struct kmod_list *l = NULL; + FILE *fp; + char line[4096]; + + if (ctx == NULL || list == NULL) + return -ENOENT; + + fp = fopen("/proc/modules", "re"); + if (fp == NULL) { + int err = -errno; + ERR(ctx, "could not open /proc/modules: %s\n", strerror(errno)); + return err; + } + + while (fgets(line, sizeof(line), fp)) { + struct kmod_module *m; + struct kmod_list *node; + int err; + size_t len = strlen(line); + char *saveptr, *name = strtok_r(line, " \t", &saveptr); + + err = kmod_module_new_from_name(ctx, name, &m); + if (err < 0) { + ERR(ctx, "could not get module from name '%s': %s\n", name, + strerror(-err)); + goto eat_line; + } + + node = kmod_list_append(l, m); + if (node) + l = node; + else { + ERR(ctx, "out of memory\n"); + kmod_module_unref(m); + } +eat_line: + while (len > 0 && line[len - 1] != '\n' && fgets(line, sizeof(line), fp)) + len = strlen(line); + } + + fclose(fp); + *list = l; + + return 0; +} + +KMOD_EXPORT const char *kmod_module_initstate_str(enum kmod_module_initstate state) +{ + switch (state) { + case KMOD_MODULE_BUILTIN: + return "builtin"; + case KMOD_MODULE_LIVE: + return "live"; + case KMOD_MODULE_COMING: + return "coming"; + case KMOD_MODULE_GOING: + return "going"; + default: + return NULL; + } +} + +KMOD_EXPORT int kmod_module_get_initstate(const struct kmod_module *mod) +{ + char path[PATH_MAX], buf[32]; + int fd, err, pathlen; + + if (mod == NULL) + return -ENOENT; + + /* remove const: this can only change internal state */ + if (kmod_module_is_builtin((struct kmod_module *)mod)) + return KMOD_MODULE_BUILTIN; + + pathlen = snprintf(path, sizeof(path), "/sys/module/%s/initstate", mod->name); + if (pathlen >= (int)sizeof(path)) { + /* Too long path was truncated */ + return -ENAMETOOLONG; + } + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + err = -errno; + + DBG(mod->ctx, "could not open '%s': %s\n", path, strerror(-err)); + + if (pathlen > (int)sizeof("/initstate") - 1) { + struct stat st; + path[pathlen - (sizeof("/initstate") - 1)] = '\0'; + if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) + return KMOD_MODULE_COMING; + } + + DBG(mod->ctx, "could not open '%s': %s\n", path, strerror(-err)); + return err; + } + + err = read_str_safe(fd, buf, sizeof(buf)); + close(fd); + if (err < 0) { + ERR(mod->ctx, "could not read from '%s': %s\n", path, strerror(-err)); + return err; + } + + if (streq(buf, "live\n")) + return KMOD_MODULE_LIVE; + else if (streq(buf, "coming\n")) + return KMOD_MODULE_COMING; + else if (streq(buf, "going\n")) + return KMOD_MODULE_GOING; + + ERR(mod->ctx, "unknown %s: '%s'\n", path, buf); + return -EINVAL; +} + +KMOD_EXPORT long kmod_module_get_size(const struct kmod_module *mod) +{ + FILE *fp; + char line[4096]; + int lineno = 0; + long size = -ENOENT; + int dfd, cfd; + + if (mod == NULL) + return -ENOENT; + + /* try to open the module dir in /sys. If this fails, don't + * bother trying to find the size as we know the module isn't + * loaded. + */ + snprintf(line, sizeof(line), "/sys/module/%s", mod->name); + dfd = open(line, O_RDONLY | O_CLOEXEC); + if (dfd < 0) + return -errno; + + /* available as of linux 3.3.x */ + cfd = openat(dfd, "coresize", O_RDONLY | O_CLOEXEC); + if (cfd >= 0) { + if (read_str_long(cfd, &size, 10) < 0) + ERR(mod->ctx, "failed to read coresize from %s\n", line); + close(cfd); + goto done; + } + + /* fall back on parsing /proc/modules */ + fp = fopen("/proc/modules", "re"); + if (fp == NULL) { + int err = -errno; + ERR(mod->ctx, "could not open /proc/modules: %s\n", strerror(errno)); + close(dfd); + return err; + } + + while (fgets(line, sizeof(line), fp)) { + size_t len = strlen(line); + char *saveptr, *endptr, *tok = strtok_r(line, " \t", &saveptr); + long value; + + lineno++; + if (tok == NULL || !streq(tok, mod->name)) + goto eat_line; + + tok = strtok_r(NULL, " \t", &saveptr); + if (tok == NULL) { + ERR(mod->ctx, "invalid line format at /proc/modules:%d\n", lineno); + break; + } + + value = strtol(tok, &endptr, 10); + if (endptr == tok || *endptr != '\0') { + ERR(mod->ctx, "invalid line format at /proc/modules:%d\n", lineno); + break; + } + + size = value; + break; +eat_line: + while (len > 0 && line[len - 1] != '\n' && fgets(line, sizeof(line), fp)) + len = strlen(line); + } + fclose(fp); + +done: + close(dfd); + return size; +} + +KMOD_EXPORT int kmod_module_get_refcnt(const struct kmod_module *mod) +{ + char path[PATH_MAX]; + long refcnt; + int fd, err; + + if (mod == NULL) + return -ENOENT; + + snprintf(path, sizeof(path), "/sys/module/%s/refcnt", mod->name); + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + err = -errno; + DBG(mod->ctx, "could not open '%s': %s\n", path, strerror(errno)); + return err; + } + + err = read_str_long(fd, &refcnt, 10); + close(fd); + if (err < 0) { + ERR(mod->ctx, "could not read integer from '%s': '%s'\n", path, + strerror(-err)); + return err; + } + + return (int)refcnt; +} + +KMOD_EXPORT struct kmod_list *kmod_module_get_holders(const struct kmod_module *mod) +{ + char dname[PATH_MAX]; + struct kmod_list *list = NULL; + struct dirent *dent; + DIR *d; + + if (mod == NULL || mod->ctx == NULL) + return NULL; + + snprintf(dname, sizeof(dname), "/sys/module/%s/holders", mod->name); + + d = opendir(dname); + if (d == NULL) { + ERR(mod->ctx, "could not open '%s': %s\n", dname, strerror(errno)); + return NULL; + } + + for (dent = readdir(d); dent != NULL; dent = readdir(d)) { + struct kmod_module *holder; + struct kmod_list *l; + int err; + + if (dent->d_name[0] == '.') { + if (dent->d_name[1] == '\0' || + (dent->d_name[1] == '.' && dent->d_name[2] == '\0')) + continue; + } + + err = kmod_module_new_from_name(mod->ctx, dent->d_name, &holder); + if (err < 0) { + ERR(mod->ctx, "could not create module for '%s': %s\n", + dent->d_name, strerror(-err)); + goto fail; + } + + l = kmod_list_append(list, holder); + if (l != NULL) { + list = l; + } else { + ERR(mod->ctx, "out of memory\n"); + kmod_module_unref(holder); + goto fail; + } + } + + closedir(d); + return list; + +fail: + closedir(d); + kmod_module_unref_list(list); + return NULL; +} + +struct kmod_module_section { + unsigned long address; + char name[]; +}; + +static void kmod_module_section_free(struct kmod_module_section *section) +{ + free(section); +} + +KMOD_EXPORT struct kmod_list *kmod_module_get_sections(const struct kmod_module *mod) +{ + char dname[PATH_MAX]; + struct kmod_list *list = NULL; + struct dirent *dent; + DIR *d; + int dfd; + + if (mod == NULL) + return NULL; + + snprintf(dname, sizeof(dname), "/sys/module/%s/sections", mod->name); + + d = opendir(dname); + if (d == NULL) { + ERR(mod->ctx, "could not open '%s': %s\n", dname, strerror(errno)); + return NULL; + } + + dfd = dirfd(d); + + for (dent = readdir(d); dent; dent = readdir(d)) { + struct kmod_module_section *section; + struct kmod_list *l; + unsigned long address; + size_t namesz; + int fd, err; + + if (dent->d_name[0] == '.') { + if (dent->d_name[1] == '\0' || + (dent->d_name[1] == '.' && dent->d_name[2] == '\0')) + continue; + } + + fd = openat(dfd, dent->d_name, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ERR(mod->ctx, "could not open '%s/%s': %m\n", dname, dent->d_name); + goto fail; + } + + err = read_str_ulong(fd, &address, 16); + close(fd); + if (err < 0) { + ERR(mod->ctx, "could not read long from '%s/%s': %m\n", dname, + dent->d_name); + goto fail; + } + + namesz = strlen(dent->d_name) + 1; + section = malloc(sizeof(*section) + namesz); + + if (section == NULL) { + ERR(mod->ctx, "out of memory\n"); + goto fail; + } + + section->address = address; + memcpy(section->name, dent->d_name, namesz); + + l = kmod_list_append(list, section); + if (l != NULL) { + list = l; + } else { + ERR(mod->ctx, "out of memory\n"); + free(section); + goto fail; + } + } + + closedir(d); + return list; + +fail: + closedir(d); + kmod_module_unref_list(list); + return NULL; +} + +KMOD_EXPORT const char *kmod_module_section_get_name(const struct kmod_list *entry) +{ + struct kmod_module_section *section; + + if (entry == NULL) + return NULL; + + section = entry->data; + return section->name; +} + +KMOD_EXPORT unsigned long kmod_module_section_get_address(const struct kmod_list *entry) +{ + struct kmod_module_section *section; + + if (entry == NULL) + return (unsigned long)-1; + + section = entry->data; + return section->address; +} + +KMOD_EXPORT void kmod_module_section_free_list(struct kmod_list *list) +{ + kmod_list_release(list, kmod_module_section_free); +} + +static struct kmod_elf *kmod_module_get_elf(const struct kmod_module *mod) +{ + if (mod->file == NULL) { + const char *path = kmod_module_get_path(mod); + + if (path == NULL) { + errno = ENOENT; + return NULL; + } + + ((struct kmod_module *)mod)->file = kmod_file_open(mod->ctx, path); + if (mod->file == NULL) + return NULL; + } + + return kmod_file_get_elf(mod->file); +} + +struct kmod_module_info { + char *key; + char value[]; +}; + +static struct kmod_module_info *kmod_module_info_new(const char *key, size_t keylen, + const char *value, size_t valuelen) +{ + struct kmod_module_info *info; + + info = malloc(sizeof(struct kmod_module_info) + keylen + valuelen + 2); + if (info == NULL) + return NULL; + + info->key = (char *)info + sizeof(struct kmod_module_info) + valuelen + 1; + memcpy(info->key, key, keylen); + info->key[keylen] = '\0'; + memcpy(info->value, value, valuelen); + info->value[valuelen] = '\0'; + return info; +} + +static void kmod_module_info_free(struct kmod_module_info *info) +{ + free(info); +} + +static struct kmod_list *kmod_module_info_append(struct kmod_list **list, const char *key, + size_t keylen, const char *value, + size_t valuelen) +{ + struct kmod_module_info *info; + struct kmod_list *n; + + info = kmod_module_info_new(key, keylen, value, valuelen); + if (info == NULL) + return NULL; + n = kmod_list_append(*list, info); + if (n != NULL) + *list = n; + else + kmod_module_info_free(info); + return n; +} + +static char *kmod_module_hex_to_str(const char *hex, size_t len) +{ + static const char digits[] = "0123456789ABCDEF"; + _cleanup_strbuf_ struct strbuf sbuf; + const size_t line_limit = 20; + + strbuf_init(&sbuf); + + for (size_t i = 0; i < len; i++) { + if (!strbuf_pushchar(&sbuf, digits[(hex[i] >> 4) & 0x0F]) || + !strbuf_pushchar(&sbuf, digits[hex[i] & 0x0F])) + return NULL; + + if (i < len - 1) { + if (!strbuf_pushchar(&sbuf, ':')) + return NULL; + + if ((i + 1) % line_limit == 0 && + !strbuf_pushchars(&sbuf, "\n\t\t")) + return NULL; + } + } + + return strbuf_steal(&sbuf); +} + +static struct kmod_list *kmod_module_info_append_hex(struct kmod_list **list, + const char *key, size_t keylen, + const char *value, size_t valuelen) +{ + char *hex; + struct kmod_list *n; + + if (valuelen > 0) { + /* Display as 01:12:DE:AD:BE:EF:... */ + hex = kmod_module_hex_to_str(value, valuelen); + if (hex == NULL) + goto list_error; + n = kmod_module_info_append(list, key, keylen, hex, strlen(hex)); + free(hex); + if (n == NULL) + goto list_error; + } else { + n = kmod_module_info_append(list, key, keylen, NULL, 0); + if (n == NULL) + goto list_error; + } + + return n; + +list_error: + return NULL; +} + +KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, + struct kmod_list **list) +{ + struct kmod_elf *elf; + char **strings; + int i, count, ret = -ENOMEM; + struct kmod_signature_info sig_info = {}; + + if (mod == NULL || list == NULL) + return -ENOENT; + + assert(*list == NULL); + + /* remove const: this can only change internal state */ + if (kmod_module_is_builtin((struct kmod_module *)mod)) { + count = kmod_builtin_get_modinfo(mod->ctx, kmod_module_get_name(mod), + &strings); + if (count < 0) + return count; + } else { + elf = kmod_module_get_elf(mod); + if (elf == NULL) + return -errno; + + count = kmod_elf_get_modinfo_strings(elf, &strings); + if (count < 0) + return count; + } + + for (i = 0; i < count; i++) { + struct kmod_list *n; + const char *key, *value; + size_t keylen, valuelen; + + key = strings[i]; + value = strchr(key, '='); + if (value == NULL) { + keylen = strlen(key); + valuelen = 0; + value = key; + } else { + keylen = value - key; + value++; + valuelen = strlen(value); + } + + n = kmod_module_info_append(list, key, keylen, value, valuelen); + if (n == NULL) + goto list_error; + } + + if (mod->file && kmod_module_signature_info(mod->file, &sig_info)) { + struct kmod_list *n; + + n = kmod_module_info_append(list, "sig_id", strlen("sig_id"), + sig_info.id_type, strlen(sig_info.id_type)); + if (n == NULL) + goto list_error; + count++; + + n = kmod_module_info_append(list, "signer", strlen("signer"), + sig_info.signer, sig_info.signer_len); + if (n == NULL) + goto list_error; + count++; + + n = kmod_module_info_append_hex(list, "sig_key", strlen("sig_key"), + sig_info.key_id, sig_info.key_id_len); + if (n == NULL) + goto list_error; + count++; + + n = kmod_module_info_append(list, "sig_hashalgo", strlen("sig_hashalgo"), + sig_info.hash_algo, + strlen(sig_info.hash_algo)); + if (n == NULL) + goto list_error; + count++; + + /* + * Omit sig_info.algo for now, as these + * are currently constant. + */ + n = kmod_module_info_append_hex(list, "signature", strlen("signature"), + sig_info.sig, sig_info.sig_len); + + if (n == NULL) + goto list_error; + count++; + } + ret = count; + +list_error: + /* aux structures freed in normal case also */ + kmod_module_signature_info_free(&sig_info); + + if (ret < 0) { + kmod_module_info_free_list(*list); + *list = NULL; + } + free(strings); + return ret; +} + +KMOD_EXPORT const char *kmod_module_info_get_key(const struct kmod_list *entry) +{ + struct kmod_module_info *info; + + if (entry == NULL) + return NULL; + + info = entry->data; + return info->key; +} + +KMOD_EXPORT const char *kmod_module_info_get_value(const struct kmod_list *entry) +{ + struct kmod_module_info *info; + + if (entry == NULL) + return NULL; + + info = entry->data; + return info->value; +} + +KMOD_EXPORT void kmod_module_info_free_list(struct kmod_list *list) +{ + kmod_list_release(list, kmod_module_info_free); +} + +struct kmod_module_version { + uint64_t crc; + char symbol[]; +}; + +static struct kmod_module_version *kmod_module_versions_new(uint64_t crc, + const char *symbol) +{ + struct kmod_module_version *mv; + size_t symbollen = strlen(symbol) + 1; + + mv = malloc(sizeof(struct kmod_module_version) + symbollen); + if (mv == NULL) + return NULL; + + mv->crc = crc; + memcpy(mv->symbol, symbol, symbollen); + return mv; +} + +static void kmod_module_version_free(struct kmod_module_version *version) +{ + free(version); +} + +KMOD_EXPORT int kmod_module_get_versions(const struct kmod_module *mod, + struct kmod_list **list) +{ + struct kmod_elf *elf; + struct kmod_modversion *versions; + int i, count, ret = 0; + + if (mod == NULL || list == NULL) + return -ENOENT; + + assert(*list == NULL); + + elf = kmod_module_get_elf(mod); + if (elf == NULL) + return -errno; + + count = kmod_elf_get_modversions(elf, &versions); + if (count < 0) + return count; + + for (i = 0; i < count; i++) { + struct kmod_module_version *mv; + struct kmod_list *n; + + mv = kmod_module_versions_new(versions[i].crc, versions[i].symbol); + if (mv == NULL) { + ret = -errno; + kmod_module_versions_free_list(*list); + *list = NULL; + goto list_error; + } + + n = kmod_list_append(*list, mv); + if (n != NULL) + *list = n; + else { + kmod_module_version_free(mv); + kmod_module_versions_free_list(*list); + *list = NULL; + ret = -ENOMEM; + goto list_error; + } + } + ret = count; + +list_error: + free(versions); + return ret; +} + +KMOD_EXPORT const char *kmod_module_version_get_symbol(const struct kmod_list *entry) +{ + struct kmod_module_version *version; + + if (entry == NULL || entry->data == NULL) + return NULL; + + version = entry->data; + return version->symbol; +} + +KMOD_EXPORT uint64_t kmod_module_version_get_crc(const struct kmod_list *entry) +{ + struct kmod_module_version *version; + + if (entry == NULL || entry->data == NULL) + return 0; + + version = entry->data; + return version->crc; +} + +KMOD_EXPORT void kmod_module_versions_free_list(struct kmod_list *list) +{ + kmod_list_release(list, kmod_module_version_free); +} + +struct kmod_module_symbol { + uint64_t crc; + char symbol[]; +}; + +static struct kmod_module_symbol *kmod_module_symbols_new(uint64_t crc, const char *symbol) +{ + struct kmod_module_symbol *mv; + size_t symbollen = strlen(symbol) + 1; + + mv = malloc(sizeof(struct kmod_module_symbol) + symbollen); + if (mv == NULL) + return NULL; + + mv->crc = crc; + memcpy(mv->symbol, symbol, symbollen); + return mv; +} + +static void kmod_module_symbol_free(struct kmod_module_symbol *symbol) +{ + free(symbol); +} + +KMOD_EXPORT int kmod_module_get_symbols(const struct kmod_module *mod, + struct kmod_list **list) +{ + struct kmod_elf *elf; + struct kmod_modversion *symbols; + int i, count, ret = 0; + + if (mod == NULL || list == NULL) + return -ENOENT; + + assert(*list == NULL); + + elf = kmod_module_get_elf(mod); + if (elf == NULL) + return -errno; + + count = kmod_elf_get_symbols(elf, &symbols); + if (count < 0) + return count; + + for (i = 0; i < count; i++) { + struct kmod_module_symbol *mv; + struct kmod_list *n; + + mv = kmod_module_symbols_new(symbols[i].crc, symbols[i].symbol); + if (mv == NULL) { + ret = -errno; + kmod_module_symbols_free_list(*list); + *list = NULL; + goto list_error; + } + + n = kmod_list_append(*list, mv); + if (n != NULL) + *list = n; + else { + kmod_module_symbol_free(mv); + kmod_module_symbols_free_list(*list); + *list = NULL; + ret = -ENOMEM; + goto list_error; + } + } + ret = count; + +list_error: + free(symbols); + return ret; +} + +KMOD_EXPORT const char *kmod_module_symbol_get_symbol(const struct kmod_list *entry) +{ + struct kmod_module_symbol *symbol; + + if (entry == NULL || entry->data == NULL) + return NULL; + + symbol = entry->data; + return symbol->symbol; +} + +KMOD_EXPORT uint64_t kmod_module_symbol_get_crc(const struct kmod_list *entry) +{ + struct kmod_module_symbol *symbol; + + if (entry == NULL || entry->data == NULL) + return 0; + + symbol = entry->data; + return symbol->crc; +} + +KMOD_EXPORT void kmod_module_symbols_free_list(struct kmod_list *list) +{ + kmod_list_release(list, kmod_module_symbol_free); +} + +struct kmod_module_dependency_symbol { + uint64_t crc; + uint8_t bind; + char symbol[]; +}; + +// clang-format off +static struct kmod_module_dependency_symbol *kmod_module_dependency_symbols_new(uint64_t crc, uint8_t bind, const char *symbol) +// clang-format on +{ + struct kmod_module_dependency_symbol *mv; + size_t symbollen = strlen(symbol) + 1; + + mv = malloc(sizeof(struct kmod_module_dependency_symbol) + symbollen); + if (mv == NULL) + return NULL; + + mv->crc = crc; + mv->bind = bind; + memcpy(mv->symbol, symbol, symbollen); + return mv; +} + +static void kmod_module_dependency_symbol_free( + struct kmod_module_dependency_symbol *dependency_symbol) +{ + free(dependency_symbol); +} + +KMOD_EXPORT int kmod_module_get_dependency_symbols(const struct kmod_module *mod, + struct kmod_list **list) +{ + struct kmod_elf *elf; + struct kmod_modversion *symbols; + int i, count, ret = 0; + + if (mod == NULL || list == NULL) + return -ENOENT; + + assert(*list == NULL); + + elf = kmod_module_get_elf(mod); + if (elf == NULL) + return -errno; + + count = kmod_elf_get_dependency_symbols(elf, &symbols); + if (count < 0) + return count; + + for (i = 0; i < count; i++) { + struct kmod_module_dependency_symbol *mv; + struct kmod_list *n; + + mv = kmod_module_dependency_symbols_new(symbols[i].crc, symbols[i].bind, + symbols[i].symbol); + if (mv == NULL) { + ret = -errno; + kmod_module_dependency_symbols_free_list(*list); + *list = NULL; + goto list_error; + } + + n = kmod_list_append(*list, mv); + if (n != NULL) + *list = n; + else { + kmod_module_dependency_symbol_free(mv); + kmod_module_dependency_symbols_free_list(*list); + *list = NULL; + ret = -ENOMEM; + goto list_error; + } + } + ret = count; + +list_error: + free(symbols); + return ret; +} + +// clang-format off +KMOD_EXPORT const char *kmod_module_dependency_symbol_get_symbol(const struct kmod_list *entry) +// clang-format on +{ + struct kmod_module_dependency_symbol *dependency_symbol; + + if (entry == NULL || entry->data == NULL) + return NULL; + + dependency_symbol = entry->data; + return dependency_symbol->symbol; +} + +KMOD_EXPORT uint64_t kmod_module_dependency_symbol_get_crc(const struct kmod_list *entry) +{ + struct kmod_module_dependency_symbol *dependency_symbol; + + if (entry == NULL || entry->data == NULL) + return 0; + + dependency_symbol = entry->data; + return dependency_symbol->crc; +} + +KMOD_EXPORT int kmod_module_dependency_symbol_get_bind(const struct kmod_list *entry) +{ + struct kmod_module_dependency_symbol *dependency_symbol; + + if (entry == NULL || entry->data == NULL) + return 0; + + dependency_symbol = entry->data; + return dependency_symbol->bind; +} + +KMOD_EXPORT void kmod_module_dependency_symbols_free_list(struct kmod_list *list) +{ + kmod_list_release(list, kmod_module_dependency_symbol_free); +} diff --git a/libkmod/libkmod-signature.c b/libkmod/libkmod-signature.c new file mode 100644 index 0000000..4f5023e --- /dev/null +++ b/libkmod/libkmod-signature.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2013 Michal Marek, SUSE + */ + +#include +#include +#if ENABLE_OPENSSL +#include +#include +#endif +#include +#include +#include + +#include +#include + +#include "libkmod-internal.h" + +/* These types and tables were copied from the 3.7 kernel sources. + * As this is just description of the signature format, it should not be + * considered derived work (so libkmod can use the LGPL license). + */ +enum pkey_algo { + PKEY_ALGO_DSA, + PKEY_ALGO_RSA, + PKEY_ALGO__LAST, +}; + +static const char *const pkey_algo[PKEY_ALGO__LAST] = { + [PKEY_ALGO_DSA] = "DSA", + [PKEY_ALGO_RSA] = "RSA", +}; + +enum pkey_hash_algo { + PKEY_HASH_MD4, + PKEY_HASH_MD5, + PKEY_HASH_SHA1, + PKEY_HASH_RIPE_MD_160, + PKEY_HASH_SHA256, + PKEY_HASH_SHA384, + PKEY_HASH_SHA512, + PKEY_HASH_SHA224, + PKEY_HASH_SM3, + PKEY_HASH__LAST, +}; + +const char *const pkey_hash_algo[PKEY_HASH__LAST] = { + // clang-format off + [PKEY_HASH_MD4] = "md4", + [PKEY_HASH_MD5] = "md5", + [PKEY_HASH_SHA1] = "sha1", + [PKEY_HASH_RIPE_MD_160] = "rmd160", + [PKEY_HASH_SHA256] = "sha256", + [PKEY_HASH_SHA384] = "sha384", + [PKEY_HASH_SHA512] = "sha512", + [PKEY_HASH_SHA224] = "sha224", + [PKEY_HASH_SM3] = "sm3", + // clang-format on +}; + +enum pkey_id_type { + PKEY_ID_PGP, /* OpenPGP generated key ID */ + PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */ + PKEY_ID_PKCS7, /* Signature in PKCS#7 message */ + PKEY_ID_TYPE__LAST, +}; + +const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = { + [PKEY_ID_PGP] = "PGP", + [PKEY_ID_X509] = "X509", + [PKEY_ID_PKCS7] = "PKCS#7", +}; + +/* + * Module signature information block. + */ +struct module_signature { + uint8_t algo; /* Public-key crypto algorithm [enum pkey_algo] */ + uint8_t hash; /* Digest algorithm [enum pkey_hash_algo] */ + uint8_t id_type; /* Key identifier type [enum pkey_id_type] */ + uint8_t signer_len; /* Length of signer's name */ + uint8_t key_id_len; /* Length of key identifier */ + uint8_t __pad[3]; + uint32_t sig_len; /* Length of signature data (big endian) */ +}; + +static bool fill_default(const char *mem, off_t size, + const struct module_signature *modsig, size_t sig_len, + struct kmod_signature_info *sig_info) +{ + size -= sig_len; + sig_info->sig = mem + size; + sig_info->sig_len = sig_len; + + size -= modsig->key_id_len; + sig_info->key_id = mem + size; + sig_info->key_id_len = modsig->key_id_len; + + size -= modsig->signer_len; + sig_info->signer = mem + size; + sig_info->signer_len = modsig->signer_len; + + sig_info->algo = pkey_algo[modsig->algo]; + sig_info->hash_algo = pkey_hash_algo[modsig->hash]; + sig_info->id_type = pkey_id_type[modsig->id_type]; + + return true; +} + +#if ENABLE_OPENSSL + +struct pkcs7_private { + PKCS7 *pkcs7; + unsigned char *key_id; + BIGNUM *sno; + char *hash_algo; +}; + +static void pkcs7_free(void *s) +{ + struct kmod_signature_info *si = s; + struct pkcs7_private *pvt = si->private; + + PKCS7_free(pvt->pkcs7); + BN_free(pvt->sno); + free(pvt->key_id); + free(pvt->hash_algo); + free(pvt); + si->private = NULL; +} + +static const char *x509_name_to_str(X509_NAME *name) +{ + int i; + X509_NAME_ENTRY *e; + ASN1_STRING *d; + ASN1_OBJECT *o; + int nid = -1; + const char *str; + + for (i = 0; i < X509_NAME_entry_count(name); i++) { + e = X509_NAME_get_entry(name, i); + o = X509_NAME_ENTRY_get_object(e); + nid = OBJ_obj2nid(o); + if (nid == NID_commonName) + break; + } + if (nid == -1) + return NULL; + + d = X509_NAME_ENTRY_get_data(e); + str = (const char *)ASN1_STRING_get0_data(d); + + return str; +} + +static bool fill_pkcs7(const char *mem, off_t size, const struct module_signature *modsig, + size_t sig_len, struct kmod_signature_info *sig_info) +{ + const char *pkcs7_raw; + PKCS7 *pkcs7; + STACK_OF(PKCS7_SIGNER_INFO) * sis; + PKCS7_SIGNER_INFO *si; + PKCS7_ISSUER_AND_SERIAL *is; + X509_NAME *issuer; + ASN1_INTEGER *sno; + ASN1_OCTET_STRING *sig; + BIGNUM *sno_bn; + X509_ALGOR *dig_alg; + X509_ALGOR *sig_alg; + const ASN1_OBJECT *o; + BIO *in; + int len; + unsigned char *key_id_str; + struct pkcs7_private *pvt; + const char *issuer_str; + char *hash_algo; + int hash_algo_len; + + size -= sig_len; + pkcs7_raw = mem + size; + + in = BIO_new_mem_buf(pkcs7_raw, sig_len); + + pkcs7 = d2i_PKCS7_bio(in, NULL); + if (pkcs7 == NULL) { + BIO_free(in); + return false; + } + + BIO_free(in); + + sis = PKCS7_get_signer_info(pkcs7); + if (sis == NULL) + goto err; + + si = sk_PKCS7_SIGNER_INFO_value(sis, 0); + if (si == NULL) + goto err; + + is = si->issuer_and_serial; + if (is == NULL) + goto err; + issuer = is->issuer; + sno = is->serial; + + sig = si->enc_digest; + if (sig == NULL) + goto err; + + PKCS7_SIGNER_INFO_get0_algs(si, NULL, &dig_alg, &sig_alg); + + sig_info->sig = (const char *)ASN1_STRING_get0_data(sig); + sig_info->sig_len = ASN1_STRING_length(sig); + + sno_bn = ASN1_INTEGER_to_BN(sno, NULL); + if (sno_bn == NULL) + goto err; + + len = BN_num_bytes(sno_bn); + key_id_str = malloc(len); + if (key_id_str == NULL) + goto err2; + BN_bn2bin(sno_bn, key_id_str); + + sig_info->key_id = (const char *)key_id_str; + sig_info->key_id_len = len; + + issuer_str = x509_name_to_str(issuer); + if (issuer_str != NULL) { + sig_info->signer = issuer_str; + sig_info->signer_len = strlen(issuer_str); + } + + X509_ALGOR_get0(&o, NULL, NULL, dig_alg); + + // Use OBJ_obj2txt to calculate string length + hash_algo_len = OBJ_obj2txt(NULL, 0, o, 0); + if (hash_algo_len < 0) + goto err3; + hash_algo = malloc(hash_algo_len + 1); + if (hash_algo == NULL) + goto err3; + hash_algo_len = OBJ_obj2txt(hash_algo, hash_algo_len + 1, o, 0); + if (hash_algo_len < 0) + goto err4; + + // Assign libcrypto hash algo string or number + sig_info->hash_algo = hash_algo; + + sig_info->id_type = pkey_id_type[modsig->id_type]; + + pvt = malloc(sizeof(*pvt)); + if (pvt == NULL) + goto err4; + + pvt->pkcs7 = pkcs7; + pvt->key_id = key_id_str; + pvt->sno = sno_bn; + pvt->hash_algo = hash_algo; + sig_info->private = pvt; + + sig_info->free = pkcs7_free; + + return true; +err4: + free(hash_algo); +err3: + free(key_id_str); +err2: + BN_free(sno_bn); +err: + PKCS7_free(pkcs7); + return false; +} + +#else /* ENABLE OPENSSL */ + +static bool fill_pkcs7(const char *mem, off_t size, const struct module_signature *modsig, + size_t sig_len, struct kmod_signature_info *sig_info) +{ + sig_info->hash_algo = "unknown"; + sig_info->id_type = pkey_id_type[modsig->id_type]; + return true; +} + +#endif /* ENABLE OPENSSL */ + +#define SIG_MAGIC "~Module signature appended~\n" + +/* + * A signed module has the following layout: + * + * [ module ] + * [ signer's name ] + * [ key identifier ] + * [ signature data ] + * [ struct module_signature ] + * [ SIG_MAGIC ] + */ + +bool kmod_module_signature_info(const struct kmod_file *file, + struct kmod_signature_info *sig_info) +{ + const char *mem; + off_t size; + struct module_signature modsig; + size_t sig_len; + + size = kmod_file_get_size(file); + mem = kmod_file_get_contents(file); + if (size < (off_t)strlen(SIG_MAGIC)) + return false; + size -= strlen(SIG_MAGIC); + if (memcmp(SIG_MAGIC, mem + size, strlen(SIG_MAGIC)) != 0) + return false; + + if (size < (off_t)sizeof(struct module_signature)) + return false; + size -= sizeof(struct module_signature); + memcpy(&modsig, mem + size, sizeof(struct module_signature)); + if (modsig.algo >= PKEY_ALGO__LAST || modsig.hash >= PKEY_HASH__LAST || + modsig.id_type >= PKEY_ID_TYPE__LAST) + return false; + sig_len = be32toh(modsig.sig_len); + if (sig_len == 0 || + size < (int64_t)sig_len + modsig.signer_len + modsig.key_id_len) + return false; + + switch (modsig.id_type) { + case PKEY_ID_PKCS7: + return fill_pkcs7(mem, size, &modsig, sig_len, sig_info); + default: + return fill_default(mem, size, &modsig, sig_len, sig_info); + } +} + +void kmod_module_signature_info_free(struct kmod_signature_info *sig_info) +{ + if (sig_info->free) + sig_info->free(sig_info); +} diff --git a/libkmod/libkmod.c b/libkmod/libkmod.c new file mode 100644 index 0000000..f4510d0 --- /dev/null +++ b/libkmod/libkmod.c @@ -0,0 +1,856 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2011-2013 ProFUSION embedded systems + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libkmod.h" +#include "libkmod-internal.h" +#include "libkmod-index.h" + +#define KMOD_HASH_SIZE (256) +#define KMOD_LRU_MAX (128) +#define _KMOD_INDEX_MODULES_SIZE KMOD_INDEX_MODULES_BUILTIN + 1 + +static const struct { + const char *fn; + bool alias_prefix; +} index_files[] = { + // clang-format off + [KMOD_INDEX_MODULES_DEP] = { .fn = "modules.dep" }, + [KMOD_INDEX_MODULES_ALIAS] = { .fn = "modules.alias", .alias_prefix = true }, + [KMOD_INDEX_MODULES_SYMBOL] = { .fn = "modules.symbols", .alias_prefix = true }, + [KMOD_INDEX_MODULES_BUILTIN_ALIAS] = { .fn = "modules.builtin.alias" }, + [KMOD_INDEX_MODULES_BUILTIN] = { .fn = "modules.builtin" }, + // clang-format on +}; + +static const char *const default_config_paths[] = { + // clang-format off + SYSCONFDIR "/modprobe.d", + "/run/modprobe.d", + "/usr/local/lib/modprobe.d", + DISTCONFDIR "/modprobe.d", + "/lib/modprobe.d", + NULL, + // clang-format on +}; + +struct kmod_ctx { + int refcount; + int log_priority; + void (*log_fn)(void *data, int priority, const char *file, int line, + const char *fn, const char *format, va_list args); + void *log_data; + const void *userdata; + char *dirname; + enum kmod_file_compression_type kernel_compression; + struct kmod_config *config; + struct hash *modules_by_name; + struct index_mm *indexes[_KMOD_INDEX_MODULES_SIZE]; + unsigned long long indexes_stamp[_KMOD_INDEX_MODULES_SIZE]; +}; + +void kmod_log(const struct kmod_ctx *ctx, int priority, const char *file, int line, + const char *fn, const char *format, ...) +{ + va_list args; + + if (ctx->log_fn == NULL) + return; + + va_start(args, format); + ctx->log_fn(ctx->log_data, priority, file, line, fn, format, args); + va_end(args); +} + +_printf_format_(6, 0) static void log_filep(void *data, int priority, const char *file, + int line, const char *fn, const char *format, + va_list args) +{ + FILE *fp = data; + char buf[16]; + const char *priname; + switch (priority) { + case LOG_EMERG: + priname = "EMERGENCY"; + break; + case LOG_ALERT: + priname = "ALERT"; + break; + case LOG_CRIT: + priname = "CRITICAL"; + break; + case LOG_ERR: + priname = "ERROR"; + break; + case LOG_WARNING: + priname = "WARNING"; + break; + case LOG_NOTICE: + priname = "NOTICE"; + break; + case LOG_INFO: + priname = "INFO"; + break; + case LOG_DEBUG: + priname = "DEBUG"; + break; + default: + snprintf(buf, sizeof(buf), "L:%d", priority); + priname = buf; + } + if (ENABLE_DEBUG == 1) + fprintf(fp, "libkmod: %s %s:%d %s: ", priname, file, line, fn); + else + fprintf(fp, "libkmod: %s: %s: ", priname, fn); + vfprintf(fp, format, args); +} + +KMOD_EXPORT const char *kmod_get_dirname(const struct kmod_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + return ctx->dirname; +} + +KMOD_EXPORT void *kmod_get_userdata(const struct kmod_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + return (void *)ctx->userdata; +} + +KMOD_EXPORT void kmod_set_userdata(struct kmod_ctx *ctx, const void *userdata) +{ + if (ctx == NULL) + return; + ctx->userdata = userdata; +} + +static int log_priority(const char *priority) +{ + char *endptr; + int prio; + + prio = strtol(priority, &endptr, 10); + if (endptr[0] == '\0' || isspace(endptr[0])) + return prio; + if (strncmp(priority, "err", 3) == 0) + return LOG_ERR; + if (strncmp(priority, "info", 4) == 0) + return LOG_INFO; + if (strncmp(priority, "debug", 5) == 0) + return LOG_DEBUG; + return 0; +} + +static const char *dirname_default_prefix = MODULE_DIRECTORY; + +static char *get_kernel_release(const char *dirname) +{ + struct utsname u; + char *p; + + if (dirname != NULL) + return path_make_absolute_cwd(dirname); + + if (uname(&u) < 0) + return NULL; + + if (asprintf(&p, "%s/%s", dirname_default_prefix, u.release) < 0) + return NULL; + + return p; +} + +static enum kmod_file_compression_type get_kernel_compression(struct kmod_ctx *ctx) +{ + const char *path = "/sys/module/compression"; + char buf[16]; + int fd; + int err; + + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + /* Not having the file is not an error: kernel may be too old */ + DBG(ctx, "could not open '%s' for reading: %m\n", path); + return KMOD_FILE_COMPRESSION_NONE; + } + + err = read_str_safe(fd, buf, sizeof(buf)); + close(fd); + if (err < 0) { + ERR(ctx, "could not read from '%s': %s\n", path, strerror(-err)); + return KMOD_FILE_COMPRESSION_NONE; + } + + if (streq(buf, "zstd\n")) + return KMOD_FILE_COMPRESSION_ZSTD; + else if (streq(buf, "xz\n")) + return KMOD_FILE_COMPRESSION_XZ; + else if (streq(buf, "gzip\n")) + return KMOD_FILE_COMPRESSION_ZLIB; + + ERR(ctx, "unknown kernel compression %s", buf); + + return KMOD_FILE_COMPRESSION_NONE; +} + +KMOD_EXPORT struct kmod_ctx *kmod_new(const char *dirname, const char *const *config_paths) +{ + const char *env; + struct kmod_ctx *ctx; + int err; + + ctx = calloc(1, sizeof(struct kmod_ctx)); + if (!ctx) + return NULL; + + ctx->refcount = 1; + ctx->log_fn = log_filep; + ctx->log_data = stderr; + ctx->log_priority = LOG_ERR; + + ctx->dirname = get_kernel_release(dirname); + if (ctx->dirname == NULL) { + ERR(ctx, "could not retrieve directory\n"); + goto fail; + } + + /* environment overwrites config */ + env = secure_getenv("KMOD_LOG"); + if (env != NULL) + kmod_set_log_priority(ctx, log_priority(env)); + + ctx->kernel_compression = get_kernel_compression(ctx); + + if (config_paths == NULL) + config_paths = default_config_paths; + err = kmod_config_new(ctx, &ctx->config, config_paths); + if (err < 0) { + ERR(ctx, "could not create config\n"); + goto fail; + } + + ctx->modules_by_name = hash_new(KMOD_HASH_SIZE, NULL); + if (ctx->modules_by_name == NULL) { + ERR(ctx, "could not create by-name hash\n"); + goto fail; + } + + INFO(ctx, "ctx %p created\n", ctx); + DBG(ctx, "log_priority=%d\n", ctx->log_priority); + + return ctx; + +fail: + free(ctx->modules_by_name); + free(ctx->dirname); + free(ctx); + return NULL; +} + +KMOD_EXPORT struct kmod_ctx *kmod_ref(struct kmod_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + ctx->refcount++; + return ctx; +} + +KMOD_EXPORT struct kmod_ctx *kmod_unref(struct kmod_ctx *ctx) +{ + if (ctx == NULL) + return NULL; + + if (--ctx->refcount > 0) + return ctx; + + INFO(ctx, "context %p released\n", ctx); + + kmod_unload_resources(ctx); + hash_free(ctx->modules_by_name); + free(ctx->dirname); + if (ctx->config) + kmod_config_free(ctx->config); + + free(ctx); + return NULL; +} + +KMOD_EXPORT void kmod_set_log_fn(struct kmod_ctx *ctx, + void (*log_fn)(void *data, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args), + const void *data) +{ + if (ctx == NULL) + return; + ctx->log_fn = log_fn; + ctx->log_data = (void *)data; + INFO(ctx, "custom logging function %p registered\n", log_fn); +} + +KMOD_EXPORT int kmod_get_log_priority(const struct kmod_ctx *ctx) +{ + if (ctx == NULL) + return -1; + return ctx->log_priority; +} + +KMOD_EXPORT void kmod_set_log_priority(struct kmod_ctx *ctx, int priority) +{ + if (ctx == NULL) + return; + ctx->log_priority = priority; +} + +struct kmod_module *kmod_pool_get_module(struct kmod_ctx *ctx, const char *key) +{ + struct kmod_module *mod; + + mod = hash_find(ctx->modules_by_name, key); + + DBG(ctx, "get module name='%s' found=%p\n", key, mod); + + return mod; +} + +int kmod_pool_add_module(struct kmod_ctx *ctx, struct kmod_module *mod, const char *key) +{ + DBG(ctx, "add %p key='%s'\n", mod, key); + + return hash_add(ctx->modules_by_name, key, mod); +} + +void kmod_pool_del_module(struct kmod_ctx *ctx, struct kmod_module *mod, const char *key) +{ + DBG(ctx, "del %p key='%s'\n", mod, key); + + hash_del(ctx->modules_by_name, key); +} + +static int kmod_lookup_alias_from_alias_bin(struct kmod_ctx *ctx, + enum kmod_index index_number, + const char *name, struct kmod_list **list) +{ + int err, nmatch = 0; + struct index_file *idx; + struct index_value *realnames, *realname; + + assert(*list == NULL); + + if (ctx->indexes[index_number] != NULL) { + DBG(ctx, "use mmapped index '%s' for name=%s\n", + index_files[index_number].fn, name); + realnames = index_mm_searchwild(ctx->indexes[index_number], name); + } else { + char fn[PATH_MAX]; + + snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, + index_files[index_number].fn); + + DBG(ctx, "file=%s name=%s\n", fn, name); + + idx = index_file_open(fn); + if (idx == NULL) + return -ENOSYS; + + realnames = index_searchwild(idx, name); + index_file_close(idx); + } + + for (realname = realnames; realname; realname = realname->next) { + struct kmod_module *mod; + struct kmod_list *node; + + err = kmod_module_new_from_alias(ctx, name, realname->value, &mod); + if (err < 0) { + ERR(ctx, "Could not create module for alias=%s realname=%s: %s\n", + name, realname->value, strerror(-err)); + goto fail; + } + + node = kmod_list_append(*list, mod); + if (node == NULL) { + ERR(ctx, "out of memory\n"); + kmod_module_unref(mod); + err = -ENOMEM; + goto fail; + } + *list = node; + nmatch++; + } + + index_values_free(realnames); + return nmatch; + +fail: + kmod_list_release(*list, kmod_module_unref); + index_values_free(realnames); + return err; +} + +int kmod_lookup_alias_from_symbols_file(struct kmod_ctx *ctx, const char *name, + struct kmod_list **list) +{ + if (!strstartswith(name, "symbol:")) + return 0; + + return kmod_lookup_alias_from_alias_bin(ctx, KMOD_INDEX_MODULES_SYMBOL, name, + list); +} + +int kmod_lookup_alias_from_aliases_file(struct kmod_ctx *ctx, const char *name, + struct kmod_list **list) +{ + return kmod_lookup_alias_from_alias_bin(ctx, KMOD_INDEX_MODULES_ALIAS, name, list); +} + +static char *lookup_file(struct kmod_ctx *ctx, enum kmod_index index_number, + const char *name) +{ + char *line; + + if (ctx->indexes[index_number]) { + DBG(ctx, "use mmapped index '%s' modname=%s\n", + index_files[index_number].fn, name); + line = index_mm_search(ctx->indexes[index_number], name); + } else { + struct index_file *idx; + char fn[PATH_MAX]; + + snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, + index_files[index_number].fn); + DBG(ctx, "file=%s modname=%s\n", fn, name); + + idx = index_file_open(fn); + if (idx == NULL) { + DBG(ctx, "could not open builtin file '%s'\n", fn); + return NULL; + } + + line = index_search(idx, name); + index_file_close(idx); + } + + return line; +} + +static bool lookup_builtin_file(struct kmod_ctx *ctx, const char *name) +{ + _cleanup_free_ char *line = lookup_file(ctx, KMOD_INDEX_MODULES_BUILTIN, name); + + return line; +} + +int kmod_lookup_alias_from_kernel_builtin_file(struct kmod_ctx *ctx, const char *name, + struct kmod_list **list) +{ + struct kmod_list *l; + int ret; + + ret = kmod_lookup_alias_from_alias_bin(ctx, KMOD_INDEX_MODULES_BUILTIN_ALIAS, + name, list); + + kmod_list_foreach(l, *list) { + struct kmod_module *mod = l->data; + kmod_module_set_builtin(mod, true); + } + + return ret; +} + +int kmod_lookup_alias_from_builtin_file(struct kmod_ctx *ctx, const char *name, + struct kmod_list **list) +{ + assert(*list == NULL); + + if (lookup_builtin_file(ctx, name)) { + struct kmod_module *mod; + struct kmod_list *node; + int err; + + err = kmod_module_new_from_name(ctx, name, &mod); + if (err < 0) { + ERR(ctx, "Could not create module from name %s: %s\n", name, + strerror(-err)); + return err; + } + + /* already mark it as builtin since it's being created from + * this index */ + kmod_module_set_builtin(mod, true); + node = kmod_list_append(*list, mod); + if (node == NULL) { + ERR(ctx, "out of memory\n"); + kmod_module_unref(mod); + return -ENOMEM; + } + *list = node; + } + + return 0; +} + +bool kmod_lookup_alias_is_builtin(struct kmod_ctx *ctx, const char *name) +{ + return lookup_builtin_file(ctx, name); +} + +char *kmod_search_moddep(struct kmod_ctx *ctx, const char *name) +{ + return lookup_file(ctx, KMOD_INDEX_MODULES_DEP, name); +} + +int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name, + struct kmod_list **list) +{ + char *line; + int n = 0; + + assert(*list == NULL); + + /* + * Module names do not contain ':'. Return early if we know it will + * not be found. + */ + if (strchr(name, ':')) + return 0; + + line = kmod_search_moddep(ctx, name); + if (line != NULL) { + struct kmod_module *mod; + struct kmod_list *node; + + n = kmod_module_new_from_name(ctx, name, &mod); + if (n < 0) { + ERR(ctx, "Could not create module from name %s: %s\n", name, + strerror(-n)); + goto finish; + } + + node = kmod_list_append(*list, mod); + if (node == NULL) { + ERR(ctx, "out of memory\n"); + kmod_module_unref(mod); + n = -ENOMEM; + goto finish; + } + *list = node; + kmod_module_parse_depline(mod, line); + } + +finish: + free(line); + + return n; +} + +int kmod_lookup_alias_from_config(struct kmod_ctx *ctx, const char *name, + struct kmod_list **list) +{ + struct kmod_config *config = ctx->config; + struct kmod_list *l; + int err, nmatch = 0; + + assert(*list == NULL); + + kmod_list_foreach(l, config->aliases) { + const char *aliasname = kmod_alias_get_name(l); + const char *modname = kmod_alias_get_modname(l); + + if (fnmatch(aliasname, name, 0) == 0) { + struct kmod_module *mod; + struct kmod_list *node; + + err = kmod_module_new_from_alias(ctx, aliasname, modname, &mod); + if (err < 0) { + ERR(ctx, + "Could not create module for alias=%s modname=%s: %s\n", + name, modname, strerror(-err)); + goto fail; + } + + node = kmod_list_append(*list, mod); + if (node == NULL) { + ERR(ctx, "out of memory\n"); + kmod_module_unref(mod); + err = -ENOMEM; + goto fail; + } + *list = node; + nmatch++; + } + } + + return nmatch; + +fail: + kmod_list_release(*list, kmod_module_unref); + return err; +} + +int kmod_lookup_alias_from_commands(struct kmod_ctx *ctx, const char *name, + struct kmod_list **list) +{ + struct kmod_config *config = ctx->config; + struct kmod_list *l, *node; + int err, nmatch = 0; + + assert(*list == NULL); + + kmod_list_foreach(l, config->install_commands) { + const char *modname = kmod_command_get_modname(l); + + if (streq(modname, name)) { + const char *cmd = kmod_command_get_command(l); + struct kmod_module *mod; + + err = kmod_module_new_from_name(ctx, modname, &mod); + if (err < 0) { + ERR(ctx, "Could not create module from name %s: %s\n", + modname, strerror(-err)); + return err; + } + + node = kmod_list_append(*list, mod); + if (node == NULL) { + ERR(ctx, "out of memory\n"); + kmod_module_unref(mod); + return -ENOMEM; + } + + *list = node; + nmatch = 1; + + kmod_module_set_install_commands(mod, cmd); + + /* + * match only the first one, like modprobe from + * module-init-tools does + */ + break; + } + } + + if (nmatch) + return nmatch; + + kmod_list_foreach(l, config->remove_commands) { + const char *modname = kmod_command_get_modname(l); + + if (streq(modname, name)) { + const char *cmd = kmod_command_get_command(l); + struct kmod_module *mod; + + err = kmod_module_new_from_name(ctx, modname, &mod); + if (err < 0) { + ERR(ctx, "Could not create module from name %s: %s\n", + modname, strerror(-err)); + return err; + } + + node = kmod_list_append(*list, mod); + if (node == NULL) { + ERR(ctx, "out of memory\n"); + kmod_module_unref(mod); + return -ENOMEM; + } + + *list = node; + nmatch = 1; + + kmod_module_set_remove_commands(mod, cmd); + + /* + * match only the first one, like modprobe from + * module-init-tools does + */ + break; + } + } + + return nmatch; +} + +void kmod_set_modules_visited(struct kmod_ctx *ctx, bool visited) +{ + struct hash_iter iter; + const void *v; + + hash_iter_init(ctx->modules_by_name, &iter); + while (hash_iter_next(&iter, NULL, &v)) + kmod_module_set_visited((struct kmod_module *)v, visited); +} + +void kmod_set_modules_required(struct kmod_ctx *ctx, bool required) +{ + struct hash_iter iter; + const void *v; + + hash_iter_init(ctx->modules_by_name, &iter); + while (hash_iter_next(&iter, NULL, &v)) + kmod_module_set_required((struct kmod_module *)v, required); +} + +static bool is_cache_invalid(const char *path, unsigned long long stamp) +{ + struct stat st; + + if (stat(path, &st) < 0) + return true; + + if (stamp != stat_mstamp(&st)) + return true; + + return false; +} + +KMOD_EXPORT int kmod_validate_resources(struct kmod_ctx *ctx) +{ + struct kmod_list *l; + size_t i; + + if (ctx == NULL || ctx->config == NULL) + return KMOD_RESOURCES_MUST_RECREATE; + + kmod_list_foreach(l, ctx->config->paths) { + struct kmod_config_path *cf = l->data; + + if (is_cache_invalid(cf->path, cf->stamp)) + return KMOD_RESOURCES_MUST_RECREATE; + } + + for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) { + char path[PATH_MAX]; + + if (ctx->indexes[i] == NULL) + continue; + + snprintf(path, sizeof(path), "%s/%s.bin", ctx->dirname, index_files[i].fn); + + if (is_cache_invalid(path, ctx->indexes_stamp[i])) + return KMOD_RESOURCES_MUST_RELOAD; + } + + return KMOD_RESOURCES_OK; +} + +KMOD_EXPORT int kmod_load_resources(struct kmod_ctx *ctx) +{ + int ret = 0; + size_t i; + + if (ctx == NULL) + return -ENOENT; + + for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) { + char path[PATH_MAX]; + + if (ctx->indexes[i] != NULL) { + INFO(ctx, "Index %s already loaded\n", index_files[i].fn); + continue; + } + + snprintf(path, sizeof(path), "%s/%s.bin", ctx->dirname, index_files[i].fn); + ret = index_mm_open(ctx, path, &ctx->indexes_stamp[i], &ctx->indexes[i]); + + /* + * modules.builtin.alias are considered optional since it's + * recently added and older installations may not have it; + * we allow failing for any reason + */ + if (ret) { + if (i != KMOD_INDEX_MODULES_BUILTIN_ALIAS) + break; + ret = 0; + } + } + + if (ret) + kmod_unload_resources(ctx); + + return ret; +} + +KMOD_EXPORT void kmod_unload_resources(struct kmod_ctx *ctx) +{ + size_t i; + + if (ctx == NULL) + return; + + for (i = 0; i < _KMOD_INDEX_MODULES_SIZE; i++) { + if (ctx->indexes[i] != NULL) { + index_mm_close(ctx->indexes[i]); + ctx->indexes[i] = NULL; + ctx->indexes_stamp[i] = 0; + } + } +} + +KMOD_EXPORT int kmod_dump_index(struct kmod_ctx *ctx, enum kmod_index type, int fd) +{ + if (ctx == NULL) + return -ENOSYS; + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-unsigned-enum-zero-compare" +#endif + if (type < 0 || type >= _KMOD_INDEX_MODULES_SIZE) + return -ENOENT; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + if (ctx->indexes[type] != NULL) { + DBG(ctx, "use mmapped index '%s'\n", index_files[type].fn); + index_mm_dump(ctx->indexes[type], fd, index_files[type].alias_prefix); + } else { + char fn[PATH_MAX]; + struct index_file *idx; + + snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname, index_files[type].fn); + + DBG(ctx, "file=%s\n", fn); + + idx = index_file_open(fn); + if (idx == NULL) + return -ENOSYS; + + index_dump(idx, fd, index_files[type].alias_prefix); + index_file_close(idx); + } + + return 0; +} + +const struct kmod_config *kmod_get_config(const struct kmod_ctx *ctx) +{ + return ctx->config; +} + +enum kmod_file_compression_type kmod_get_kernel_compression(const struct kmod_ctx *ctx) +{ + return ctx->kernel_compression; +} diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h new file mode 100644 index 0000000..1c0d118 --- /dev/null +++ b/libkmod/libkmod.h @@ -0,0 +1,1466 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2011-2013 ProFUSION embedded systems + */ + +#pragma once +#ifndef _LIBKMOD_H_ +#define _LIBKMOD_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * SECTION:libkmod + * @short_description: libkmod context + * + * The context contains the default values for the library user, + * and is passed to all library operations. + */ + +/** + * kmod_ctx: + * + * Opaque object representing the library context. + */ +struct kmod_ctx; + +/** + * kmod_new: + * @dirname: what to consider as linux module's directory, if NULL + * defaults to $MODULE_DIRECTORY/`uname -r`. If it's relative, + * it's treated as relative to the current working directory. + * Otherwise, give an absolute dirname. + * @config_paths: ordered array of paths (directories or files) where + * to load from user-defined configuration parameters such as + * alias, blacklists, commands (install, remove). If NULL + * defaults to /etc/modprobe.d, /run/modprobe.d, + * /usr/local/lib/modprobe.d, DISTCONFDIR/modprobe.d, and + * /lib/modprobe.d. Give an empty vector if configuration should + * not be read. This array must be null terminated. + * + * Create kmod library context. This reads the kmod configuration + * and fills in the default values. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the kmod library context. + * + * Returns: a new kmod library context + * + * Since: 1 + */ +struct kmod_ctx *kmod_new(const char *dirname, const char *const *config_paths); + +/** + * kmod_ref: + * @ctx: kmod library context + * + * Take a reference of the kmod library context. + * + * Returns: the passed kmod library context + * + * Since: 1 + */ +struct kmod_ctx *kmod_ref(struct kmod_ctx *ctx); + +/** + * kmod_unref: + * @ctx: kmod library context + * + * Drop a reference of the kmod library context. If the refcount + * reaches zero, the resources of the context will be released. + * + * Returns: the passed kmod library context or NULL if it's freed + * + * Since: 1 + */ +struct kmod_ctx *kmod_unref(struct kmod_ctx *ctx); + +/** + * kmod_load_resources: + * @ctx: kmod library context + * + * Load indexes and keep them open in @ctx. This way it's faster to lookup + * information within the indexes. If this function is not called before a + * search, the necessary index is always opened and closed. + * + * If user will do more than one or two lookups, insertions, deletions, most + * likely it's good to call this function first. Particularly in a daemon like + * udev that on boot issues hundreds of calls to lookup the index, calling + * this function will speedup the searches. + * + * Returns: 0 on success or < 0 otherwise. + * + * Since: 1 + */ +int kmod_load_resources(struct kmod_ctx *ctx); + +/** + * kmod_unload_resources: + * @ctx: kmod library context + * + * Unload all the indexes. This will free the resources to maintain the index + * open and all subsequent searches will need to open and close the index. + * + * User is free to call kmod_load_resources() and kmod_unload_resources() as + * many times as wanted during the lifecycle of @ctx. For example, if a daemon + * knows that when starting up it will lookup a lot of modules, it could call + * kmod_load_resources() and after the first burst of searches is gone, it + * could free the resources by calling kmod_unload_resources(). + * + * Returns: 0 on success or < 0 otherwise. + * + * Since: 1 + */ +void kmod_unload_resources(struct kmod_ctx *ctx); + +/** + * kmod_resources: + * @KMOD_RESOURCES_OK: resources are valid + * @KMOD_RESOURCES_MUST_RELOAD: resources are not valid; to resolve call + * kmod_unload_resources() and kmod_load_resources() + * @KMOD_RESOURCES_MUST_RECREATE: resources are not valid; to resolve @ctx must + * be re-created. + * + * The validity state of the current libkmod resources, returned by + * kmod_validate_resources(). + */ +enum kmod_resources { + KMOD_RESOURCES_OK = 0, + KMOD_RESOURCES_MUST_RELOAD = 1, + KMOD_RESOURCES_MUST_RECREATE = 2, +}; + +/** + * kmod_validate_resources: + * @ctx: kmod library context + * + * Check if indexes and configuration files changed on disk and the current + * context is not valid anymore. + * + * Returns: the resources state, valid states are #kmod_resources. + * + * Since: 3 + */ +int kmod_validate_resources(struct kmod_ctx *ctx); + +/** + * kmod_index: + * @KMOD_INDEX_MODULES_DEP: index of module dependencies + * @KMOD_INDEX_MODULES_ALIAS: index of module aliases + * @KMOD_INDEX_MODULES_SYMBOL: index of symbol aliases + * @KMOD_INDEX_MODULES_BUILTIN_ALIAS: index of builtin module aliases + * @KMOD_INDEX_MODULES_BUILTIN: index of builtin module + * @_KMOD_INDEX_PAD: DO NOT USE; padding to make sure enum is not mapped to char + * + * The (module) index type, used by kmod_dump_index(). + */ +enum kmod_index { + KMOD_INDEX_MODULES_DEP = 0, + KMOD_INDEX_MODULES_ALIAS, + KMOD_INDEX_MODULES_SYMBOL, + KMOD_INDEX_MODULES_BUILTIN_ALIAS, + KMOD_INDEX_MODULES_BUILTIN, + _KMOD_INDEX_PAD = 1U << 31, +}; + +/** + * kmod_dump_index: + * @ctx: kmod library context + * @type: index to dump, valid indexes are #kmod_index + * @fd: file descriptor to dump index to + * + * Dump index to file descriptor. Note that this function doesn't use stdio.h + * so call fflush() before calling this function to be sure data is written in + * order. + * + * Returns: 0 on success or < 0 otherwise. + * + * Since: 4 + */ +int kmod_dump_index(struct kmod_ctx *ctx, enum kmod_index type, int fd); + +/** + * kmod_set_log_priority: + * @ctx: kmod library context + * @priority: the new logging priority + * + * Set the current logging priority, as defined in syslog.h(0P). The value + * controls which messages are logged. + * + * Since: 1 + */ +void kmod_set_log_priority(struct kmod_ctx *ctx, int priority); + +/** + * kmod_get_log_priority: + * @ctx: kmod library context + * + * Get the current logging priority, as defined in syslog.h(0P). + * + * Returns: the current logging priority + * + * Since: 1 + */ +int kmod_get_log_priority(const struct kmod_ctx *ctx); + +/** + * kmod_set_log_fn: + * @ctx: kmod library context + * @log_fn: function to be called for logging messages + * @data: data to pass to log function + * + * The built-in logging writes to stderr. It can be + * overridden by a custom function, to plug log messages + * into the user's logging functionality. + * + * Since: 1 + */ +void kmod_set_log_fn(struct kmod_ctx *ctx, + void (*log_fn)(void *log_data, int priority, const char *file, + int line, const char *fn, const char *format, + va_list args), + const void *data); + +/** + * kmod_set_userdata: + * @ctx: kmod library context + * @userdata: data pointer + * + * Store custom @userdata in the library context. + * + * Since: 1 + */ +void kmod_set_userdata(struct kmod_ctx *ctx, const void *userdata); + +/** + * kmod_get_userdata: + * @ctx: kmod library context + * + * Retrieve stored data pointer from library context. This might be useful + * to access from callbacks. + * + * Returns: stored userdata + * + * Since: 1 + */ +void *kmod_get_userdata(const struct kmod_ctx *ctx); + +/** + * kmod_get_dirname: + * @ctx: kmod library context + * + * Retrieve the absolute path used for linux modules in this context. The path + * is computed from the arguments to kmod_new(). + * + * Since: 22 + */ +const char *kmod_get_dirname(const struct kmod_ctx *ctx); + +/** + * SECTION:libkmod-list + * @short_description: general purpose list + * + * Access to kmod generated lists. + */ + +/** + * kmod_list: + * + * Opaque object for a circular (doubly linked) list. + */ +struct kmod_list; + +/** + * kmod_list_foreach: + * @curr: the current node in the list + * @list: the head of the list + * + * Iterate over the list @list. + */ +#define kmod_list_foreach(curr, list) \ + for (curr = list; curr != NULL; curr = kmod_list_next(list, curr)) + +/** + * kmod_list_foreach_reverse: + * @curr: the current node in the list + * @list: the head of the list + * + * Iterate in reverse over the list @list. + */ +#define kmod_list_foreach_reverse(curr, list) \ + for (curr = kmod_list_last(list); curr != NULL; curr = kmod_list_prev(list, curr)) + +/** + * kmod_list_last: + * @list: the head of the list + * + * Get the last element of the @list. As @list is a circular list, + * this is a cheap operation O(1) with the last element being the + * previous element. + * + * If the list has a single element it will return the list itself (as + * expected, and this is what differentiates from kmod_list_prev()). + * + * Returns: last node at @list or NULL if the list is empty. + * + * Since: 2 + */ +struct kmod_list *kmod_list_last(const struct kmod_list *list); + +/** + * kmod_list_next: + * @list: the head of the list + * @curr: the current node in the list + * + * Get the next node in @list relative to @curr as if @list was not a circular + * list. I.e. calling this function in the last node of the list returns + * NULL.. It can be used to iterate a list by checking for NULL return to know + * when all elements were iterated. + * + * Returns: node next to @curr or NULL if either this node is the last of or + * list is empty. + * + * Since: 1 + */ +struct kmod_list *kmod_list_next(const struct kmod_list *list, + const struct kmod_list *curr); + +/** + * kmod_list_prev: + * @list: the head of the list + * @curr: the current node in the list + * + * Get the previous node in @list relative to @curr as if @list was not a + * circular list. I.e.: the previous of the head is NULL. It can be used to + * iterate a list by checking for NULL return to know when all elements were + * iterated. + * + * Returns: node previous to @curr or NULL if either this node is the head of + * the list or the list is empty. + * + * Since: 1 + */ +struct kmod_list *kmod_list_prev(const struct kmod_list *list, + const struct kmod_list *curr); + +/** + * SECTION:libkmod-config + * @short_description: retrieve current libkmod configuration + * + * Access to configuration lists - it allows to get each configuration's + * key/value stored by kmod. + */ + +/** + * kmod_config_iter: + * + * Opaque object for iterating and retrieving configuration information. + */ +struct kmod_config_iter; + +/** + * kmod_config_get_blacklists: + * @ctx: kmod library context + * + * Retrieve an iterator to deal with the blacklist maintained inside the + * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and + * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must + * be made to initialize the iterator and check if it's valid. + * + * Returns: a new iterator over the blacklists or NULL on failure. Free it + * with kmod_config_iter_free_iter(). + * + * Since: 4 + */ +struct kmod_config_iter *kmod_config_get_blacklists(const struct kmod_ctx *ctx); + +/** + * kmod_config_get_install_commands: + * @ctx: kmod library context + * + * Retrieve an iterator to deal with the install commands maintained inside the + * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and + * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must + * be made to initialize the iterator and check if it's valid. + * + * Returns: a new iterator over the install commands or NULL on failure. Free + * it with kmod_config_iter_free_iter(). + * + * Since: 4 + */ +struct kmod_config_iter *kmod_config_get_install_commands(const struct kmod_ctx *ctx); + +/** + * kmod_config_get_remove_commands: + * @ctx: kmod library context + * + * Retrieve an iterator to deal with the remove commands maintained inside the + * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and + * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must + * be made to initialize the iterator and check if it's valid. + * + * Returns: a new iterator over the remove commands or NULL on failure. Free + * it with kmod_config_iter_free_iter(). + * + * Since: 4 + */ +struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *ctx); + +/** + * kmod_config_get_aliases: + * @ctx: kmod library context + * + * Retrieve an iterator to deal with the aliases maintained inside the + * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and + * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must + * be made to initialize the iterator and check if it's valid. + * + * Returns: a new iterator over the aliases or NULL on failure. Free it with + * kmod_config_iter_free_iter(). + * + * Since: 4 + */ +struct kmod_config_iter *kmod_config_get_aliases(const struct kmod_ctx *ctx); + +/** + * kmod_config_get_options: + * @ctx: kmod library context + * + * Retrieve an iterator to deal with the options maintained inside the + * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and + * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must + * be made to initialize the iterator and check if it's valid. + * + * Returns: a new iterator over the options or NULL on failure. Free it with + * kmod_config_iter_free_iter(). + * + * Since: 4 + */ +struct kmod_config_iter *kmod_config_get_options(const struct kmod_ctx *ctx); + +/** + * kmod_config_get_softdeps: + * @ctx: kmod library context + * + * Retrieve an iterator to deal with the softdeps maintained inside the + * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and + * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must + * be made to initialize the iterator and check if it's valid. + * + * Returns: a new iterator over the softdeps or NULL on failure. Free it with + * kmod_config_iter_free_iter(). + * + * Since: 4 + */ +struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_ctx *ctx); + +/** + * kmod_config_get_weakdeps: + * @ctx: kmod library context + * + * Retrieve an iterator to deal with the weakdeps maintained inside the + * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and + * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must + * be made to initialize the iterator and check if it's valid. + * + * Returns: a new iterator over the weakdeps or NULL on failure. Free it with + * kmod_config_iter_free_iter(). + * + * Since: 33 + */ +struct kmod_config_iter *kmod_config_get_weakdeps(const struct kmod_ctx *ctx); + +/** + * kmod_config_iter_get_key: + * @iter: iterator over a certain configuration + * + * When using a new allocated iterator, user must perform a call to + * kmod_config_iter_next() to initialize iterator's position and check if it's + * valid. + * + * Returns: the key of the current configuration pointed by @iter. + * + * Since: 4 + */ +const char *kmod_config_iter_get_key(const struct kmod_config_iter *iter); + +/** + * kmod_config_iter_get_value: + * @iter: iterator over a certain configuration + * + * When using a new allocated iterator, user must perform a call to + * kmod_config_iter_next() to initialize iterator's position and check if it's + * valid. + * + * Returns: the value of the current configuration pointed by @iter. + * + * Since: 4 + */ +const char *kmod_config_iter_get_value(const struct kmod_config_iter *iter); + +/** + * kmod_config_iter_next: + * @iter: iterator over a certain configuration + * + * Make @iter point to the next item of a certain configuration. It's an + * automatically recycling iterator. When it reaches the end, false is + * returned; then if user wants to iterate again, it's sufficient to call this + * function once more. + * + * Returns: true if next position of @iter is valid or false if its end is + * reached. + * + * Since: 4 + */ +bool kmod_config_iter_next(struct kmod_config_iter *iter); + +/** + * kmod_config_iter_free_iter: + * @iter: iterator over a certain configuration + * + * Free resources used by the iterator. + * + * Since: 4 + */ +void kmod_config_iter_free_iter(struct kmod_config_iter *iter); + +/** + * SECTION:libkmod-module + * @short_description: operate on kernel modules + * + * Wide range of function to operate on kernel modules - loading, unloading, + * reference counting, retrieving a list of module dependencies and more. + */ + +/** + * kmod_module: + * + * Opaque object representing a module. + */ +struct kmod_module; + +/** + * kmod_module_new_from_lookup: + * @ctx: kmod library context + * @given_alias: alias to look for + * @list: an empty list where to save the list of modules matching + * @given_alias + * + * Create a new list of kmod modules using an alias or module name and lookup + * libkmod's configuration files and indexes in order to find the module. + * Once it's found in one of the places, it stops searching and create the + * list of modules that is saved in @list. + * + * The search order is: 1. aliases in configuration file; 2. module names in + * modules.dep index; 3. symbol aliases in modules.symbols index; 4. aliases + * from install commands; 5. builtin indexes from kernel. + * + * The initial refcount is 1, and needs to be decremented to release the + * resources of the kmod_module. The returned @list must be released by + * calling kmod_module_unref_list(). Since libkmod keeps track of all + * kmod_modules created, they are all released upon @ctx destruction too. Do + * not unref @ctx before all the desired operations with the returned list are + * completed. + * + * Returns: 0 on success or < 0 otherwise. It fails if any of the lookup + * methods failed, which is basically due to memory allocation fail. If module + * is not found, it still returns 0, but @list is an empty list. + * + * Since: 1 + */ +int kmod_module_new_from_lookup(struct kmod_ctx *ctx, const char *given_alias, + struct kmod_list **list); + +/** + * kmod_module_new_from_name_lookup: + * @ctx: kmod library context + * @modname: module name to look for + * @mod: returned module on success + * + * Lookup by module name, without considering possible aliases. This is similar + * to kmod_module_new_from_lookup(), but don't consider as source indexes and + * configurations that work with aliases. When successful, this always resolves + * to one and only one module. + * + * The search order is: 1. module names in modules.dep index; + * 2. builtin indexes from kernel. + * + * The initial refcount is 1, and needs to be decremented to release the + * resources of the kmod_module. Since libkmod keeps track of all + * kmod_modules created, they are all released upon @ctx destruction too. Do + * not unref @ctx before all the desired operations with the returned list are + * completed. + * + * Returns: 0 on success or < 0 otherwise. It fails if any of the lookup + * methods failed, which is basically due to memory allocation failure. If + * module is not found, it still returns 0, but @mod is left untouched. + * + * Since: 30 + */ +int kmod_module_new_from_name_lookup(struct kmod_ctx *ctx, const char *modname, + struct kmod_module **mod); + +/** + * kmod_module_new_from_name: + * @ctx: kmod library context + * @name: name of the module + * @mod: where to save the created struct kmod_module + * + * Create a new struct kmod_module using the module name. @name can not be an + * alias, file name or anything else; it must be a module name. There's no + * check if the module exists in the system. + * + * This function is also used internally by many others that return a new + * struct kmod_module or a new list of modules. + * + * The initial refcount is 1, and needs to be decremented to release the + * resources of the kmod_module. Since libkmod keeps track of all + * kmod_modules created, they are all released upon @ctx destruction too. Do + * not unref @ctx before all the desired operations with the returned + * kmod_module are done. + * + * Returns: 0 on success or < 0 otherwise. It fails if name is not a valid + * module name or if memory allocation failed. + * + * Since: 1 + */ +int kmod_module_new_from_name(struct kmod_ctx *ctx, const char *name, + struct kmod_module **mod); + +/** + * kmod_module_new_from_path: + * @ctx: kmod library context + * @path: path where to find the given module + * @mod: where to save the created struct kmod_module + * + * Create a new struct kmod_module using the module path. @path must be an + * existent file within the filesystem and must be accessible to libkmod. + * + * The initial refcount is 1, and needs to be decremented to release the + * resources of the kmod_module. Since libkmod keeps track of all + * kmod_modules created, they are all released upon @ctx destruction too. Do + * not unref @ctx before all the desired operations with the returned + * kmod_module are done. + * + * If @path is relative, it's treated as relative to the current working + * directory. Otherwise, give an absolute path. + * + * Returns: 0 on success or < 0 otherwise. It fails if file does not exist, if + * it's not a valid file for a kmod_module or if memory allocation failed. + * + * Since: 1 + */ +int kmod_module_new_from_path(struct kmod_ctx *ctx, const char *path, + struct kmod_module **mod); + +/** + * kmod_module_ref: + * @mod: kmod module + * + * Take a reference of the kmod module, incrementing its refcount. + * + * Returns: the passed @module with its refcount incremented. + * + * Since: 1 + */ +struct kmod_module *kmod_module_ref(struct kmod_module *mod); + +/** + * kmod_module_unref: + * @mod: kmod module + * + * Drop a reference of the kmod module. If the refcount reaches zero, its + * resources are released. + * + * Returns: NULL if @mod is NULL or if the module was released. Otherwise it + * returns the passed @mod with its refcount decremented. + * + * Since: 1 + */ +struct kmod_module *kmod_module_unref(struct kmod_module *mod); + +/** + * kmod_module_unref_list: + * @list: list of kmod modules + * + * Drop a reference of each kmod module in @list and releases the resources + * taken by the list itself. + * + * Returns: 0 + * + * Since: 1 + */ +int kmod_module_unref_list(struct kmod_list *list); + +/** + * kmod_insert: + * @KMOD_INSERT_FORCE_VERMAGIC: ignore kernel version magic + * @KMOD_INSERT_FORCE_MODVERSION: ignore symbol version hashes + * + * Insertion flags, used by kmod_module_insert_module(). + */ +enum kmod_insert { + KMOD_INSERT_FORCE_VERMAGIC = 0x1, + KMOD_INSERT_FORCE_MODVERSION = 0x2, +}; + +/** + * kmod_module_insert_module: + * @mod: kmod module + * @flags: flags are not passed to the kernel, but instead they dictate the + * behavior of this function, valid flags #kmod_insert + * @options: module's options to pass to the kernel. + * + * Insert a module in the kernel. It opens the file pointed by @mod, + * mmap'ing it and passing to kernel. + * + * Returns: 0 on success or < 0 on failure. If module is already loaded it + * returns -EEXIST. + * + * Since: 1 + */ +int kmod_module_insert_module(struct kmod_module *mod, unsigned int flags, + const char *options); + +/** + * kmod_probe: + * @KMOD_PROBE_FORCE_VERMAGIC: ignore kernel version magic + * @KMOD_PROBE_FORCE_MODVERSION: ignore symbol version hashes + * @KMOD_PROBE_IGNORE_COMMAND: ignore install commands and softdeps configured + * in the system + * @KMOD_PROBE_IGNORE_LOADED: do not check whether the module is already + * live in the kernel or not + * @KMOD_PROBE_DRY_RUN: dry run, do not insert module, just call the + * associated callback function + * @KMOD_PROBE_FAIL_ON_LOADED: probe will fail if KMOD_PROBE_IGNORE_LOADED is + * not specified and the module is already live in the kernel + * @KMOD_PROBE_APPLY_BLACKLIST_ALL: prior to probe, apply KMOD_FILTER_BLACKLIST + * filter to this module and its dependencies. If any of them are blacklisted + * and the blacklisted module is not live in the kernel, the function returns + * early with thus enum + * @KMOD_PROBE_APPLY_BLACKLIST: probe will return early with this enum, if the + * module is blacklisted + * @KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY: probe will return early with this + * enum, if the module is an alias and is blacklisted + * + * Bitmask which defines the behaviour of kmod_module_probe_insert_module(). + */ +enum kmod_probe { + KMOD_PROBE_FORCE_VERMAGIC = 0x00001, + KMOD_PROBE_FORCE_MODVERSION = 0x00002, + KMOD_PROBE_IGNORE_COMMAND = 0x00004, + KMOD_PROBE_IGNORE_LOADED = 0x00008, + KMOD_PROBE_DRY_RUN = 0x00010, + KMOD_PROBE_FAIL_ON_LOADED = 0x00020, + + /* codes below can be used in return value, too */ + KMOD_PROBE_APPLY_BLACKLIST_ALL = 0x10000, + KMOD_PROBE_APPLY_BLACKLIST = 0x20000, + KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY = 0x40000, +}; + +/** + * kmod_module_probe_insert_module: + * @mod: kmod module + * @flags: flags are not passed to the kernel, but instead they dictate the + * behavior of this function, valid flags are #kmod_probe + * @extra_options: module's options to pass to the kernel. It applies only + * to @mod, not to its dependencies. + * @run_install: function to run when @mod is backed by an install command. + * @data: data to give back to @run_install callback + * @print_action: function to call with the action being taken (install or + * insmod). It's useful for tools like modprobe when running with verbose + * output or in dry-run mode. + * + * Insert a module in the kernel resolving dependencies, soft dependencies, + * install commands and applying blacklist. + * + * If @run_install is NULL, this function will fork and exec by calling + * system(3). Don't pass a NULL argument in @run_install if your binary is + * setuid/setgid (see warning in system(3)). If you need control over the + * execution of an install command, give a callback function instead. + * + * Returns: 0 on success, > 0 if stopped by a reason given in @flags or < 0 on + * failure. + * + * Since: 3 + */ +int kmod_module_probe_insert_module( + struct kmod_module *mod, unsigned int flags, const char *extra_options, + int (*run_install)(struct kmod_module *m, const char *cmdline, void *data), + const void *data, + void (*print_action)(struct kmod_module *m, bool install, const char *options)); + +/** + * kmod_remove: + * @KMOD_REMOVE_FORCE: force remove module regardless if it's still in + * use by a kernel subsystem or other process; passed directly to the kernel + * @KMOD_REMOVE_NOWAIT: always set, pass O_NONBLOCK to delete_module(2); + * passed directly to the kernel + * @KMOD_REMOVE_NOLOG: when module removal fails, do not log anything; not + * passed to the kernel + * + * Removal flags, used by kmod_module_remove_module(). + */ +enum kmod_remove { + KMOD_REMOVE_FORCE = O_TRUNC, + KMOD_REMOVE_NOWAIT = O_NONBLOCK, + KMOD_REMOVE_NOLOG = 1, +}; + +/** + * kmod_module_remove_module: + * @mod: kmod module + * @flags: flags used when removing the module, valid flags are #kmod_remove + * + * Remove a module from the kernel. + * + * Returns: 0 on success or < 0 on failure. + * + * Since: 1 + */ +int kmod_module_remove_module(struct kmod_module *mod, unsigned int flags); + +/** + * kmod_module_get_module: + * @entry: an entry in a list of kmod modules. + * + * Get the kmod module of this @entry in the list, increasing its refcount. + * After it's used, unref it. Since the refcount is incremented upon return, + * you still have to call kmod_module_unref_list() to release the list of kmod + * modules. + * + * Returns: NULL on failure or the kmod_module contained in this list entry + * with its refcount incremented. + * + * Since: 1 + */ +struct kmod_module *kmod_module_get_module(const struct kmod_list *entry); + +/** + * kmod_module_get_dependencies: + * @mod: kmod module + * + * Search the modules.dep index to find the dependencies of the given @mod. + * The result is cached in @mod, so subsequent calls to this function will + * return the already searched list of modules. + * + * Returns: NULL on failure. Otherwise it returns a list of kmod modules + * that can be released by calling kmod_module_unref_list(). + * + * Since: 1 + */ +struct kmod_list *kmod_module_get_dependencies(const struct kmod_module *mod); + +/** + * kmod_module_get_softdeps: + * @mod: kmod module + * @pre: where to save the list of preceding soft dependencies. + * @post: where to save the list of post soft dependencies. + * + * Get soft dependencies for this kmod module. Soft dependencies come + * from configuration file and are not cached in @mod because it may include + * dependency cycles that would make we leak kmod_module. Any call + * to this function will search for this module in configuration, allocate a + * list and return the result. + * + * Both @pre and @post are newly created list of kmod_module and + * should be unreferenced with kmod_module_unref_list(). + * + * Returns: 0 on success or < 0 otherwise. + * + * Since: 2 + */ +int kmod_module_get_softdeps(const struct kmod_module *mod, struct kmod_list **pre, + struct kmod_list **post); + +/** + * kmod_module_get_weakdeps: + * @mod: kmod module + * @weak: where to save the list of weak dependencies. + * + * Get weak dependencies for this kmod module. Weak dependencies come + * from configuration file and are not cached in @mod because it may include + * dependency cycles that would make we leak kmod_module. Any call + * to this function will search for this module in configuration, allocate a + * list and return the result. + * + * @weak is newly created list of kmod_module and + * should be unreferenced with kmod_module_unref_list(). + * + * Returns: 0 on success or < 0 otherwise. + * + * Since: 33 + */ +int kmod_module_get_weakdeps(const struct kmod_module *mod, struct kmod_list **weak); + +/** + * kmod_filter: + * @KMOD_FILTER_BLACKLIST: filter modules in blacklist out + * @KMOD_FILTER_BUILTIN: filter builtin modules out + * + * Bitmask defining what gets filtered out, used by kmod_module_apply_filter(). + */ +enum kmod_filter { + KMOD_FILTER_BLACKLIST = 0x00001, + KMOD_FILTER_BUILTIN = 0x00002, +}; + +/** + * kmod_module_apply_filter: + * @ctx: kmod library context + * @filter_type: bitmask to filter modules out, valid types are #kmod_filter + * @input: list of kmod_module to be filtered + * @output: where to save the new list + * + * Given a list @input, this function filter it out by the filter mask + * and save it in @output. + * + * Returns: 0 on success or < 0 otherwise. @output is saved with the updated + * list. + * + * Since: 6 + */ +int kmod_module_apply_filter(const struct kmod_ctx *ctx, enum kmod_filter filter_type, + const struct kmod_list *input, struct kmod_list **output); + +/** + * kmod_module_get_filtered_blacklist: + * @ctx: kmod library context + * @input: list of kmod_module to be filtered with blacklist + * @output: where to save the new list + * + * This function should not be used. Use kmod_module_apply_filter instead. + * + * Given a list @input, this function filter it out with config's blacklist + * and save it in @output. + * + * Returns: 0 on success or < 0 otherwise. @output is saved with the updated + * list. + * + * Since: 1 + * Deprecated: 6: Use #kmod_module_apply_filter instead. + */ +int kmod_module_get_filtered_blacklist(const struct kmod_ctx *ctx, + const struct kmod_list *input, + struct kmod_list **output) + __attribute__((deprecated)); + +/** + * kmod_module_get_install_commands: + * @mod: kmod module + * + * Get install commands for this kmod module. Install commands come from the + * configuration file and are cached in @mod. The first call to this function + * will search for this module in configuration and subsequent calls return + * the cached string. The install commands are returned as they were in the + * configuration, concatenated by ';'. No other processing is made in this + * string. + * + * Returns: a string with all install commands separated by semicolons. This + * string is owned by @mod, do not free it. + * + * Since: 1 + */ +const char *kmod_module_get_install_commands(const struct kmod_module *mod); + +/** + * kmod_module_get_remove_commands: + * @mod: kmod module + * + * Get remove commands for this kmod module. Remove commands come from the + * configuration file and are cached in @mod. The first call to this function + * will search for this module in configuration and subsequent calls return + * the cached string. The remove commands are returned as they were in the + * configuration, concatenated by ';'. No other processing is made in this + * string. + * + * Returns: a string with all remove commands separated by semicolons. This + * string is owned by @mod, do not free it. + * + * Since: 1 + */ +const char *kmod_module_get_remove_commands(const struct kmod_module *mod); + +/** + * kmod_module_get_name: + * @mod: kmod module + * + * Get the name of this kmod module. Name is always available, independently + * if it was created by kmod_module_new_from_name() or another function and + * it's always normalized (dashes are replaced with underscores). + * + * Returns: the name of this kmod module. + * + * Since: 1 + */ +const char *kmod_module_get_name(const struct kmod_module *mod); + +/** + * kmod_module_get_options: + * @mod: kmod module + * + * Get options of this kmod module. Options come from the configuration file + * and are cached in @mod. The first call to this function will search for + * this module in configuration and subsequent calls return the cached string. + * + * Returns: a string with all the options separated by spaces. This string is + * owned by @mod, do not free it. + * + * Since: 1 + */ +const char *kmod_module_get_options(const struct kmod_module *mod); + +/** + * kmod_module_get_path: + * @mod: kmod module + * + * Get the path of this kmod module. If this kmod module was not created by + * path, it can search the modules.dep index in order to find out the module + * under context's dirname. + * + * Returns: the path of this kmod module or NULL if such information is not + * available. + * + * Since: 1 + */ +const char *kmod_module_get_path(const struct kmod_module *mod); + +/** + * kmod_module_get_dependency_symbols: + * @mod: kmod module + * @list: where to return list of module dependency_symbols + * + * Get a list of entries in ELF section ".symtab" or "__ksymtab_strings". + * + * The structure contained in this list is internal to libkmod and its fields + * can be obtainsed by calling kmod_module_dependency_symbol_get_crc() and + * kmod_module_dependency_symbol_get_symbol(). + * + * After use, free the @list by calling + * kmod_module_dependency_symbols_free_list(). + * + * Returns: 0 on success or < 0 otherwise. + * + * Since: 3 + */ +int kmod_module_get_dependency_symbols(const struct kmod_module *mod, + struct kmod_list **list); + +/** + * kmod_symbol_bind: + * @KMOD_SYMBOL_NONE: no or unknown symbol type + * @KMOD_SYMBOL_LOCAL: local symbol, accessible only within the module + * @KMOD_SYMBOL_GLOBAL: global symbol, accessible by all modules + * @KMOD_SYMBOL_WEAK: weak symbol, a lower precedence global symbols + * @KMOD_SYMBOL_UNDEF: undefined or not yet resolved symbol + * + * The symbol bind type, see kmod_module_dependency_symbol_get_bind(). + */ +enum kmod_symbol_bind { + KMOD_SYMBOL_NONE = '\0', + KMOD_SYMBOL_LOCAL = 'L', + KMOD_SYMBOL_GLOBAL = 'G', + KMOD_SYMBOL_WEAK = 'W', + KMOD_SYMBOL_UNDEF = 'U', +}; + +/** + * kmod_module_dependency_symbol_get_bind: + * @entry: a list entry representing a kmod module dependency_symbol + * + * Get the bind type of a kmod module dependency_symbol. + * + * Returns: the bind of this kmod module dependency_symbol on success, + * or < 0 on failure. Valid bind types are #kmod_symbol_bind. + * + * Since: 3 + */ +int kmod_module_dependency_symbol_get_bind(const struct kmod_list *entry); + +/** + * kmod_module_dependency_symbol_get_crc: + * @entry: a list entry representing a kmod module dependency_symbol + * + * Get the crc of a kmod module dependency_symbol. + * + * Returns: the crc of this kmod module dependency_symbol if available, otherwise default to 0. + * + * Since: 3 + */ +uint64_t kmod_module_dependency_symbol_get_crc(const struct kmod_list *entry); + +/** + * kmod_module_dependency_symbol_get_symbol: + * @entry: a list entry representing a kmod module dependency_symbols + * + * Get the dependency symbol of a kmod module + * + * Returns: the symbol of this kmod module dependency_symbols on success or NULL + * on failure. The string is owned by the dependency_symbols, do not free it. + * + * Since: 3 + */ +const char *kmod_module_dependency_symbol_get_symbol(const struct kmod_list *entry); + +/** + * kmod_module_dependency_symbols_free_list: + * @list: kmod module dependency_symbols list + * + * Release the resources taken by @list + * + * Since: 3 + */ +void kmod_module_dependency_symbols_free_list(struct kmod_list *list); + +/** + * kmod_module_get_sections: + * @mod: kmod module + * + * Get a list of kmod sections of this @mod, as returned by the kernel. + * + * The structure contained in this list is internal to libkmod and its fields + * can be obtained by calling kmod_module_section_get_name() and + * kmod_module_section_get_address(). + * + * After use, free the @list by calling kmod_module_section_free_list(). + * + * Returns: a new list of kmod module sections on success or NULL on failure. + * + * Since: 1 + */ +struct kmod_list *kmod_module_get_sections(const struct kmod_module *mod); + +/** + * kmod_module_section_get_address: + * @entry: a list entry representing a kmod module section + * + * Get the address of a kmod module section. + * + * Returns: the address of this kmod module section on success or ULONG_MAX + * on failure. + * + * Since: 1 + */ +unsigned long kmod_module_section_get_address(const struct kmod_list *entry); + +/** + * kmod_module_section_get_name: + * @entry: a list entry representing a kmod module section + * + * Get the name of a kmod module section. + * + * Returns: the name of this kmod module section on success or NULL on + * failure. The string is owned by the section, do not free it. + * + * Since: 1 + */ +const char *kmod_module_section_get_name(const struct kmod_list *entry); + +/** + * kmod_module_section_free_list: + * @list: kmod module section list + * + * Release the resources taken by @list + * + * Since: 1 + */ +void kmod_module_section_free_list(struct kmod_list *list); + +/** + * kmod_module_get_symbols: + * @mod: kmod module + * @list: where to return list of module symbols + * + * Get a list of entries in ELF section ".symtab" or "__ksymtab_strings". + * + * The structure contained in this list is internal to libkmod and its fields + * can be obtainsed by calling kmod_module_symbol_get_crc() and + * kmod_module_symbol_get_symbol(). + * + * After use, free the @list by calling kmod_module_symbols_free_list(). + * + * Returns: 0 on success or < 0 otherwise. + * + * Since: 3 + */ +int kmod_module_get_symbols(const struct kmod_module *mod, struct kmod_list **list); + +/** + * kmod_module_symbol_get_crc: + * @entry: a list entry representing a kmod module symbol + * + * Get the crc of a kmod module symbol. + * + * Returns: the crc of this kmod module symbol if available, otherwise default to 0. + * + * Since: 3 + */ +uint64_t kmod_module_symbol_get_crc(const struct kmod_list *entry); + +/** + * kmod_module_symbol_get_symbol: + * @entry: a list entry representing a kmod module symbols + * + * Get the symbol of a kmod module symbols. + * + * Returns: the symbol of this kmod module symbols on success or NULL + * on failure. The string is owned by the symbols, do not free it. + * + * Since: 3 + */ +const char *kmod_module_symbol_get_symbol(const struct kmod_list *entry); + +/** + * kmod_module_symbols_free_list: + * @list: kmod module symbols list + * + * Release the resources taken by @list + * + * Since: 3 + */ +void kmod_module_symbols_free_list(struct kmod_list *list); + +/** + * kmod_module_get_versions: + * @mod: kmod module + * @list: where to return list of module versions + * + * Get a list of entries in ELF section "__versions". + * + * The structure contained in this list is internal to libkmod and its fields + * can be obtainsed by calling kmod_module_version_get_crc() and + * kmod_module_version_get_symbol(). + * + * After use, free the @list by calling kmod_module_versions_free_list(). + * + * Returns: 0 on success or < 0 otherwise. + * + * Since: 2 + */ +int kmod_module_get_versions(const struct kmod_module *mod, struct kmod_list **list); + +/** + * kmod_module_version_get_crc: + * @entry: a list entry representing a kmod module version + * + * Get the crc of a kmod module version. + * + * Returns: the crc of this kmod module version if available, otherwise default to 0. + * + * Since: 2 + */ +uint64_t kmod_module_version_get_crc(const struct kmod_list *entry); + +/** + * kmod_module_version_get_symbol: + * @entry: a list entry representing a kmod module versions + * + * Get the symbol of a kmod module versions. + * + * Returns: the symbol of this kmod module versions on success or NULL + * on failure. The string is owned by the versions, do not free it. + * + * Since: 2 + */ +const char *kmod_module_version_get_symbol(const struct kmod_list *entry); + +/** + * kmod_module_versions_free_list: + * @list: kmod module versions list + * + * Release the resources taken by @list + * + * Since: 2 + */ +void kmod_module_versions_free_list(struct kmod_list *list); + +/** + * kmod_module_get_info: + * @mod: kmod module + * @list: where to return list of module information + * + * Get a list of entries in ELF section ".modinfo", these contain + * alias, license, depends, vermagic and other keys with respective + * values. If the module is signed (CONFIG_MODULE_SIG), information + * about the module signature is included as well: signer, + * sig_key and sig_hashalgo. + * + * The structure contained in this list is internal to libkmod and its fields + * can be obtainsed by calling kmod_module_info_get_key() and + * kmod_module_info_get_value(). + * + * After use, free the @list by calling kmod_module_info_free_list(). + * + * Returns: number of entries in @list on success or < 0 otherwise. + * + * Since: 2 + */ +int kmod_module_get_info(const struct kmod_module *mod, struct kmod_list **list); + +/** + * kmod_module_info_get_key: + * @entry: a list entry representing a kmod module info + * + * Get the key of a kmod module info. + * + * Returns: the key of this kmod module info on success or NULL on + * failure. The string is owned by the info, do not free it. + * + * Since: 2 + */ +const char *kmod_module_info_get_key(const struct kmod_list *entry); + +/** + * kmod_module_info_get_value: + * @entry: a list entry representing a kmod module info + * + * Get the value of a kmod module info. + * + * Returns: the value of this kmod module info on success or NULL on + * failure. The string is owned by the info, do not free it. + * + * Since: 2 + */ +const char *kmod_module_info_get_value(const struct kmod_list *entry); + +/** + * kmod_module_info_free_list: + * @list: kmod module info list + * + * Release the resources taken by @list + * + * Since: 2 + */ +void kmod_module_info_free_list(struct kmod_list *list); + +/** + * SECTION:libkmod-loaded + * @short_description: currently loaded modules + * + * Information about currently loaded modules, as reported by the kernel. + * These information are not cached by libkmod and are always read from /sys + * and /proc/modules. + */ + +/** + * kmod_module_new_from_loaded: + * @ctx: kmod library context + * @list: where to save the list of loaded modules + * + * Create a new list of kmod modules with all modules currently loaded in + * kernel. It uses /proc/modules to get the names of loaded modules and to + * create kmod modules by calling kmod_module_new_from_name() in each of them. + * They are put in @list in no particular order. + * + * The initial refcount is 1, and needs to be decremented to release the + * resources of the kmod_module. The returned @list must be released by + * calling kmod_module_unref_list(). Since libkmod keeps track of all + * kmod_modules created, they are all released upon @ctx destruction too. Do + * not unref @ctx before all the desired operations with the returned list are + * completed. + * + * Returns: 0 on success or < 0 on error. + * + * Since: 1 + */ +int kmod_module_new_from_loaded(struct kmod_ctx *ctx, struct kmod_list **list); + +/** + * kmod_module_initstate: + * @KMOD_MODULE_BUILTIN: module is builtin + * @KMOD_MODULE_LIVE: module is live in the kernel + * @KMOD_MODULE_COMING: module is being loaded + * @KMOD_MODULE_GOING: module is being unloaded + * @_KMOD_MODULE_PAD: DO NOT USE; padding to make sure enum is not mapped to char + * + * The module "live information" as reported by the kernel, see + * kmod_module_get_initstate(). + */ +enum kmod_module_initstate { + KMOD_MODULE_BUILTIN = 0, + KMOD_MODULE_LIVE, + KMOD_MODULE_COMING, + KMOD_MODULE_GOING, + _KMOD_MODULE_PAD = 1U << 31, +}; + +/** + * kmod_module_get_initstate: + * @mod: kmod module + * + * Get the initstate of this @mod, as returned by the kernel, by reading + * /sys filesystem. + * + * Returns: < 0 on error or module state if module is found in the kernel, valid + * states are #kmod_module_initstate. + * + * Since: 1 + */ +int kmod_module_get_initstate(const struct kmod_module *mod); + +/** + * kmod_module_initstate_str: + * @state: the state as returned by kmod_module_get_initstate() + * + * Translate a initstate to a string. + * + * Returns: the string associated to the @state. This string is statically + * allocated, do not free it. + * + * Since: 1 + */ +const char *kmod_module_initstate_str(enum kmod_module_initstate state); + +/** + * kmod_module_get_size: + * @mod: kmod module + * + * Get the size of this kmod module as returned by the kernel. If supported, + * the size is read from the coresize attribute in /sys/module. For older + * kernels, this falls back on /proc/modules and searches for the specified + * module to get its size. + * + * Returns: the size of this kmod module. + * + * Since: 1 + */ +long kmod_module_get_size(const struct kmod_module *mod); + +/** + * kmod_module_get_refcnt: + * @mod: kmod module + * + * Get the ref count of this @mod, as returned by the kernel, by reading + * /sys filesystem. + * + * Returns: the reference count on success or < 0 on failure. + * + * Since: 1 + */ +int kmod_module_get_refcnt(const struct kmod_module *mod); + +/** + * kmod_module_get_holders: + * @mod: kmod module + * + * Get a list of kmod modules that are holding this @mod, as returned by Linux + * Kernel. After use, free the @list by calling kmod_module_unref_list(). + * + * Returns: a new list of kmod modules on success or NULL on failure. + * + * Since: 1 + */ +struct kmod_list *kmod_module_get_holders(const struct kmod_module *mod); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif diff --git a/libkmod/libkmod.pc.in b/libkmod/libkmod.pc.in new file mode 100644 index 0000000..3acca6a --- /dev/null +++ b/libkmod/libkmod.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libkmod +Description: Library to deal with kernel modules +Version: @VERSION@ +Libs: -L${libdir} -lkmod +Libs.private: @libzstd_LIBS@ @liblzma_LIBS@ @zlib_LIBS@ +Cflags: -I${includedir} diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym new file mode 100644 index 0000000..ea1cb44 --- /dev/null +++ b/libkmod/libkmod.sym @@ -0,0 +1,107 @@ +# With kmod v5 we bumped the DSO major and re-tagged all symbols. +# +# NOTE: Do not change the existing sections. Add new symbols to new sections. +LIBKMOD_5 { +global: + kmod_get_log_priority; + kmod_get_userdata; + kmod_new; + kmod_ref; + kmod_set_log_fn; + kmod_set_log_priority; + kmod_set_userdata; + kmod_unref; + kmod_list_next; + kmod_list_prev; + kmod_list_last; + + kmod_load_resources; + kmod_unload_resources; + kmod_validate_resources; + kmod_config_get_blacklists; + kmod_config_get_install_commands; + kmod_config_get_remove_commands; + kmod_config_get_aliases; + kmod_config_get_options; + kmod_config_get_softdeps; + kmod_config_iter_get_key; + kmod_config_iter_get_value; + kmod_config_iter_next; + kmod_config_iter_free_iter; + kmod_dump_index; + + kmod_module_new_from_name; + kmod_module_new_from_path; + kmod_module_new_from_lookup; + kmod_module_new_from_loaded; + kmod_module_ref; + kmod_module_unref; + kmod_module_unref_list; + kmod_module_get_module; + kmod_module_remove_module; + kmod_module_insert_module; + kmod_module_probe_insert_module; + + kmod_module_get_dependencies; + kmod_module_get_softdeps; + kmod_module_get_filtered_blacklist; + + kmod_module_get_name; + kmod_module_get_path; + + kmod_module_initstate_str; + kmod_module_get_initstate; + kmod_module_get_refcnt; + kmod_module_get_sections; + kmod_module_section_free_list; + kmod_module_section_get_name; + kmod_module_section_get_address; + kmod_module_get_holders; + kmod_module_get_size; + + kmod_module_get_options; + kmod_module_get_install_commands; + kmod_module_get_remove_commands; + + kmod_module_get_info; + kmod_module_info_get_key; + kmod_module_info_get_value; + kmod_module_info_free_list; + kmod_module_get_versions; + kmod_module_version_get_symbol; + kmod_module_version_get_crc; + kmod_module_versions_free_list; + kmod_module_get_symbols; + kmod_module_symbol_get_symbol; + kmod_module_symbol_get_crc; + kmod_module_symbols_free_list; + + kmod_module_get_dependency_symbols; + kmod_module_dependency_symbol_get_symbol; + kmod_module_dependency_symbol_get_crc; + kmod_module_dependency_symbol_get_bind; + kmod_module_dependency_symbols_free_list; +local: + *; +}; + +LIBKMOD_6 { +global: + kmod_module_apply_filter; +} LIBKMOD_5; + +LIBKMOD_22 { +global: + kmod_get_dirname; +} LIBKMOD_6; + +LIBKMOD_30 { +global: + kmod_module_new_from_name_lookup; +} LIBKMOD_22; + +LIBKMOD_33 { +global: + kmod_config_get_weakdeps; + kmod_module_get_weakdeps; +} LIBKMOD_30; diff --git a/m4/.gitignore b/m4/.gitignore new file mode 100644 index 0000000..d59889a --- /dev/null +++ b/m4/.gitignore @@ -0,0 +1,6 @@ +libtool.m4 +ltoptions.m4 +ltsugar.m4 +ltversion.m4 +lt~obsolete.m4 +gtk-doc.m4 diff --git a/m4/attributes.m4 b/m4/attributes.m4 new file mode 100644 index 0000000..a22bb58 --- /dev/null +++ b/m4/attributes.m4 @@ -0,0 +1,277 @@ +# SPDX-License-Identifier: GPL-2.0-or-later WITH Autoconf-exception-macro +# +# Copyright (c) 2006-2008 Diego Pettenò +# Copyright (c) 2006-2008 xine project +# Copyright (c) 2012 Lucas De Marchi + +# Check if FLAG in ENV-VAR is supported by compiler and append it +# to WHERE-TO-APPEND variable. Note that we invert -Wno-* checks to +# -W* as gcc cannot test for negated warnings. +# CC_CHECK_FLAG_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG]) + +AC_DEFUN([CC_CHECK_FLAG_APPEND], [ + AC_CACHE_CHECK([if $CC supports flag $3 in envvar $2], + AS_TR_SH([cc_cv_$2_$3]), + [eval "AS_TR_SH([cc_save_$2])='${$2}'" + eval "AS_TR_SH([$2])='-Werror `echo "$3" | sed 's/^-Wno-/-W/'`'" + AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; } ])], + [eval "AS_TR_SH([cc_cv_$2_$3])='yes'"], + [eval "AS_TR_SH([cc_cv_$2_$3])='no'"]) + eval "AS_TR_SH([$2])='$cc_save_$2'"]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_$2_$3])[ = xyes], + [eval "$1='${$1} $3'"]) +]) + +dnl CC_CHECK_FLAGS_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG1 FLAG2]) +AC_DEFUN([CC_CHECK_FLAGS_APPEND], [ + for flag in $3; do + CC_CHECK_FLAG_APPEND($1, $2, $flag) + done +]) + +dnl Check if the flag is supported by linker (cacheable) +dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) + + +dnl Check if the flag is supported by linker +dnl CC_CHECK_LDFLAGS_SILENT([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) +AC_DEFUN([CC_CHECK_LDFLAGS_SILENT], [ + AC_CACHE_VAL(AS_TR_SH([cc_cv_ldflags_$1]), + [ac_save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $1" + AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 1; }])], + [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_ldflags_$1])="]) + LDFLAGS="$ac_save_LDFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes], + [$2], [$3]) +]) + +dnl Check if the flag is supported by linker (cacheable) +dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) + +AC_DEFUN([CC_CHECK_LDFLAGS], [ + AC_CACHE_CHECK([if $CC supports $1 flag], + AS_TR_SH([cc_cv_ldflags_$1]), + CC_CHECK_LDFLAGS_SILENT([$1]) dnl Don't execute actions here! + ) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes], + [$2], [$3]) +]) + +dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for +dnl the current linker to avoid undefined references in a shared object. +AC_DEFUN([CC_NOUNDEFINED], [ + dnl We check $host for which systems to enable this for. + AC_REQUIRE([AC_CANONICAL_HOST]) + + case $host in + dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads + dnl are requested, as different implementations are present; to avoid problems + dnl use -Wl,-z,defs only for those platform not behaving this way. + *-freebsd* | *-openbsd*) ;; + *) + dnl First of all check for the --no-undefined variant of GNU ld. This allows + dnl for a much more readable command line, so that people can understand what + dnl it does without going to look for what the heck -z defs does. + for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do + CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"]) + break + done + ;; + esac + + AC_SUBST([LDFLAGS_NOUNDEFINED]) +]) + +dnl Check for a -Werror flag or equivalent. -Werror is the GCC +dnl and ICC flag that tells the compiler to treat all the warnings +dnl as fatal. We usually need this option to make sure that some +dnl constructs (like attributes) are not simply ignored. +dnl +dnl Other compilers don't support -Werror per se, but they support +dnl an equivalent flag: +dnl - Sun Studio compiler supports -errwarn=%all +AC_DEFUN([CC_CHECK_WERROR], [ + AC_CACHE_CHECK( + [for $CC way to treat warnings as errors], + [cc_cv_werror], + [CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror], + [CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])]) + ]) +]) + +AC_DEFUN([CC_CHECK_ATTRIBUTE], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))], + AS_TR_SH([cc_cv_attribute_$1]), + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])], + [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"]) + CFLAGS="$ac_save_CFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes], + [AC_DEFINE( + AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1, + [Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))] + ) + $4], + [$5]) +]) + +AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [ + CC_CHECK_ATTRIBUTE( + [constructor],, + [void __attribute__((constructor)) ctor() { int a; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_FORMAT], [ + CC_CHECK_ATTRIBUTE( + [format], [format(printf, n, n)], + [void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [ + CC_CHECK_ATTRIBUTE( + [format_arg], [format_arg(printf)], + [char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [ + CC_CHECK_ATTRIBUTE( + [visibility_$1], [visibility("$1")], + [void __attribute__((visibility("$1"))) $1_function() { }], + [$2], [$3]) +]) + +AC_DEFUN([CC_ATTRIBUTE_NONNULL], [ + CC_CHECK_ATTRIBUTE( + [nonnull], [nonnull()], + [void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_UNUSED], [ + CC_CHECK_ATTRIBUTE( + [unused], , + [void some_function(void *foo, __attribute__((unused)) void *bar);], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [ + CC_CHECK_ATTRIBUTE( + [sentinel], , + [void some_function(void *foo, ...) __attribute__((sentinel));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [ + CC_CHECK_ATTRIBUTE( + [deprecated], , + [void some_function(void *foo, ...) __attribute__((deprecated));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_ALIAS], [ + CC_CHECK_ATTRIBUTE( + [alias], [weak, alias], + [void other_function(void *foo) { } + void some_function(void *foo) __attribute__((weak, alias("other_function")));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_MALLOC], [ + CC_CHECK_ATTRIBUTE( + [malloc], , + [void * __attribute__((malloc)) my_alloc(int n);], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_PACKED], [ + CC_CHECK_ATTRIBUTE( + [packed], , + [struct astructure { char a; int b; long c; void *d; } __attribute__((packed));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_CONST], [ + CC_CHECK_ATTRIBUTE( + [const], , + [int __attribute__((const)) twopow(int n) { return 1 << n; } ], + [$1], [$2]) +]) + +AC_DEFUN([CC_FLAG_VISIBILITY], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if $CC supports -fvisibility=hidden], + [cc_cv_flag_visibility], + [cc_flag_visibility_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden], + cc_cv_flag_visibility='yes', + cc_cv_flag_visibility='no') + CFLAGS="$cc_flag_visibility_save_CFLAGS"]) + + AS_IF([test "x$cc_cv_flag_visibility" = "xyes"], + [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1, + [Define this if the compiler supports the -fvisibility flag]) + $1], + [$2]) +]) + +AC_DEFUN([CC_CHECK_FUNC_BUILTIN], [ + m4_pushdef([UPNAME], m4_translit([$1], [-a-z], [_A-Z])) + AC_CACHE_CHECK([if compiler has $1 builtin function], + [cc_cv_have_$1], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([], [ + m4_case([$1], + [__builtin_clz], [$1(0)], + [__builtin_types_compatible_p], [$1(int, int)], + [__builtin_uaddl_overflow], [$1(0UL, 0UL, (void*)0)], + [__builtin_uaddll_overflow], [$1(0ULL, 0ULL, (void*)0)], + [__builtin_expect], [$1(0, 0)] + )])], + [cc_cv_have_$1=yes], + [cc_cv_have_$1=no])]) + + AS_IF([test "x$cc_cv_have_$1" = "xyes"], + [AC_DEFINE([HAVE_]m4_defn([UPNAME]), 1, [Define to 1 if compiler has $1() builtin function]) + $2], + [AS_IF([test "x$3" = "x"], [AC_MSG_ERROR([*** builtin function not found: $1()])], + [AC_DEFINE([HAVE_]m4_defn([UPNAME]), 0) + $3])] + ) + m4_popdef([UPNAME]) +]) + +AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported], + [cc_cv_attribute_aligned], + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + for cc_attribute_align_try in 64 32 16 8 4 2; do + AC_COMPILE_IFELSE([AC_LANG_SOURCE([ + int main() { + static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0; + return c; + }])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break]) + done + CFLAGS="$ac_save_CFLAGS" + ]) + + if test "x$cc_cv_attribute_aligned" != "x"; then + AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned], + [Define the highest alignment supported]) + fi +]) diff --git a/m4/features.m4 b/m4/features.m4 new file mode 100644 index 0000000..6384a05 --- /dev/null +++ b/m4/features.m4 @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0-or-later WITH Autoconf-exception-macro +# +# Copyright (c) 2015 Lucas De Marchi + +# CC_FEATURE_APPEND([FLAGS], [ENV-TO-CHECK], [FLAG-NAME]) +AC_DEFUN([CC_FEATURE_APPEND], [ + AS_VAR_PUSHDEF([FLAGS], [$1])dnl + AS_VAR_PUSHDEF([ENV_CHECK], [$2])dnl + AS_VAR_PUSHDEF([FLAG_NAME], [$3])dnl + + AS_CASE([" AS_VAR_GET(FLAGS) " ], + [*" FLAG_NAME "*], [AC_RUN_LOG([: FLAGS already contains FLAG_NAME])], + [ + AS_IF([test "x$FLAGS" != "x"], [AS_VAR_APPEND(FLAGS, " ")]) + AS_IF([test "x$ENV_CHECK" = "xyes"], + [AS_VAR_APPEND(FLAGS, "+FLAG_NAME")], + [AS_VAR_APPEND(FLAGS, "-FLAG_NAME")]) + ] + ) + + AS_VAR_POPDEF([FLAG_NAME])dnl + AS_VAR_POPDEF([ENV_CHECK])dnl + AS_VAR_POPDEF([FLAGS])dnl +]) diff --git a/man/.gitignore b/man/.gitignore new file mode 100644 index 0000000..a229b2f --- /dev/null +++ b/man/.gitignore @@ -0,0 +1,4 @@ +*.5 +*.8 +Makefile +Makefile.in diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 0000000..6356d87 --- /dev/null +++ b/man/Makefile.am @@ -0,0 +1,28 @@ +MAN5 = depmod.d.5 modprobe.d.5 modules.dep.5 +MAN8 = kmod.8 depmod.8 insmod.8 lsmod.8 rmmod.8 modprobe.8 modinfo.8 +MAN_STUB = modules.dep.bin.5 + +AM_V_SCDOC = $(AM_V_SCDOC_$(V)) +AM_V_SCDOC_ = $(AM_V_SCDOC_$(AM_DEFAULT_VERBOSITY)) +AM_V_SCDOC_0 = @echo " SCDOC " $@; + +if BUILD_TOOLS +dist_man_MANS = $(MAN5) $(MAN8) $(MAN_STUB) +endif + +EXTRA_DIST = $(MAN5:%.5=%.5.scd) $(MAN8:%.8=%.8.scd) +CLEANFILES = $(filter-out $(MAN_STUB), $(dist_man_MANS)) + +define generate_manpage + $(AM_V_SCDOC)cat $< | \ + sed -e 's|@SYSCONFDIR@|$(sysconfdir)|g' | \ + sed -e 's|@DISTCONFDIR@|$(distconfdir)|g' | \ + sed -e 's|@MODULE_DIRECTORY@|$(module_directory)|g' | \ + $(SCDOC) > $@ +endef + +%.5: %.5.scd + $(generate_manpage) + +%.8: %.8.scd + $(generate_manpage) diff --git a/man/depmod.8.scd b/man/depmod.8.scd new file mode 100644 index 0000000..4a4b51a --- /dev/null +++ b/man/depmod.8.scd @@ -0,0 +1,181 @@ +DEPMOD(8) "kmod" "depmod" + +# NAME + +depmod - Generate modules.dep and map files. + +# SYNOPSIS + +*depmod* [*-b* _basedir_] [*-m* _moduledir_] [*-o* _outdir_] [*-e*] [*-E* _Module.symvers_] +\ \ \ \ \ \ \ \[*-F* _System.map_] [*-n*] [*-v*] [*-A*] [*-P* _prefix_] [*-w*] [_version_] + +*depmod* [*-e*] [*-E* _Module.symvers_] [*-F* _System.map_] [*-n*] [*-v*] [*-P* _prefix_] +\ \ \ \ \ \ \ \[*-w*] [_version_] [_filename_...] + +# DESCRIPTION + +Linux kernel modules can provide services (called "symbols") for other modules +to use (using one of the EXPORT_SYMBOL variants in the code). If a second module +uses this symbol, that second module clearly depends on the first module. These +dependencies can get quite complex. + +*depmod* creates a list of module dependencies by reading each module under +//_version_. By default is @MODULE_DIRECTORY@ +and is empty. See options below to override when needed. It determines +what symbols each module exports and needs. This list is written to +*modules.dep*, and a binary hashed version named modules.dep.bin, in the same +directory. If filenames are given on the command line, only those modules are +examined (which is rarely useful unless all modules are listed). *depmod* also +creates a list of symbols provided by modules in the file named modules.symbols +and its binary hashed version, modules.symbols.bin. Finally, *depmod* will +output a file named modules.devname if modules supply special device names +(devname) that should be populated in /dev on boot (by a utility such as +systemd-tmpfiles). + +If a _version_ is provided, then that kernel version's module directory is used +rather than the current kernel version (as returned by *uname -r*). + +# OPTIONS + +*-a*, *--all* + Probe all modules. This option is enabled by default if no file names + are given in the command-line. + +*-A*, *--quick* + This option scans to see if any modules are newer than the + *modules.dep* file before any work is done: if not, it silently exits + rather than regenerating the files. + +*-b* _basedir_, *--basedir*=_basedir_ + Override the base directory where modules are located. + If your modules are not currently in the (normal) directory + @MODULE_DIRECTORY@/_version_, but in a staging area, you can specify a + _basedir_ which is prepended to the directory name. This _basedir_ is + stripped from the resulting *modules.dep* file, so it is ready to be + moved into the normal location. Use this option if you are a + distribution vendor who needs to pre-generate the meta-data files rather + than running *depmod* again later. + + If a relative path is given, it's relative to the current working + directory. + + Example: + depmod -b /my/build/staging/dir/ + + This expects all input files under + _/my/build/staging/dir@MODULE_DIRECTORY@/$(uname -r)_ and generates + index files under that same directory. + +*-m* _moduledir_, *--moduledir*=_moduledir_ + Override the module directory , which defaults to + @MODULE_DIRECTORY@ prefix set at build time. This is useful when + building *modules.dep* file in _basedir_ for a system that uses a + different prefix, e.g. _/usr/lib/modules_ vs _/lib/modules_. + + Relative and absolute paths are accepted, but they are always relative + to the _basedir_. + + Examples: + depmod -b /tmp/build -m /kernel-modules++ +depmod -b /tmp/build -m kernel-modules + + This expects all input files under + _/tmp/build/kernel-modules/$(uname -r)_ and generates index files under + that same directory. + + Without an accompanying *-b* argument, the moduledir is relative to _/_. + Example: + + depmod -m foo/bar + + This expects all input files under _/foo/bar/$(uname -r)_ and generates + index files under the same directory. Unless libkmod is prepared to + handle that arbitrary location, it won't work in runtime. + + +*-o* _outdir_, *--outdir*=_outdir_ + Set the output directory where *depmod* will store any generated file. + _outdir_ serves as a root to that location, similar to how _basedir_ is + used. Also this setting takes precedence and if used together with + _basedir_ it will result in the input being that directory, but the output + being the one set by _outdir_. + + If a relative path is given, it's relative to the current working + directory. + + Example: + depmod -o /my/build/staging/dir/ + + This expects all input files under + _@MODULE_DIRECTORY@/$(uname -r)_ and generates index files under + _/my/build/staging/dir@MODULE_DIRECTORY@/$(uname -r)_. + +*-C* _file_ _or_ _directory_, *--config*=_file_ _or_ _directory_ + This option overrides the default configuration files. See + *depmod.d*(5). + +*-e*, *--errsyms* + When combined with the *-F* option, this reports any symbols which a + module needs which are not supplied by other modules or the kernel. + Normally, any symbols not provided by modules are assumed to be provided + by the kernel (which should be true in a perfect world), but this + assumption can break especially when additionally updated third party + drivers are not correctly installed or were built incorrectly. + +*-E* _Module.symvers_, *--symvers*=_Module.symvers_ + When combined with the *-e* option, this reports any symbol versions + supplied by modules that do not match with the symbol versions provided + by the kernel in its _Module.symvers_. This option is mutually + incompatible with *-F*. + +*-F* _System.map_, *--filesyms*=_System.map_ + Supplied with the _System.map_ produced when the kernel was built, this + allows the *-e* option to report unresolved symbols. This option is + mutually incompatible with *-E*. + +*-h*, *--help* + Print the help message and exit. + +*-n*, *--show*, *--dry-run* + This sends the resulting *modules.dep* and the various map files to + standard output rather than writing them into the module directory. + +*-P* + Some architectures prefix symbols with an extraneous character. This + specifies a prefix character (for example '\_') to ignore. + +*-v*, *--verbose* + In verbose mode, *depmod* will print (to stdout) all the symbols each + module depends on and the module's file name which provides that symbol. + +*-V*, *--version* + Show version of program and exit. See below for caveats when run on + older kernels. + +*-w* + Warn on duplicate dependencies, aliases, symbol versions, etc. + +# COPYRIGHT + +This manual page originally Copyright 2002, Rusty Russell, IBM Corporation. +Portions Copyright Jon Masters, and others. + +# SEE ALSO + +*depmod.d*(5), *modprobe*(8), *modules.dep*(5) + +# BUGS + +Please direct any bug reports to kmod's issue tracker at +https://github.com/kmod-project/kmod/issues/ alongside with version used, steps +to reproduce the problem and the expected outcome. + +# AUTHORS + +Numerous contributions have come from the linux-modules mailing list + and Github. If you have a clone of kmod.git +itself, the output of *git-shortlog*(1) and *git-blame*(1) can show you the +authors for specific parts of the project. + +*Lucas De Marchi* is the current maintainer of the +project. diff --git a/man/depmod.d.5.scd b/man/depmod.d.5.scd new file mode 100644 index 0000000..cb8de4d --- /dev/null +++ b/man/depmod.d.5.scd @@ -0,0 +1,118 @@ +DEPMOD.D(5) "kmod" "depmod.d" + +# NAME + +depmod.d - Configuration directory for depmod + +# SYNOPSIS + +@SYSCONFDIR@/depmod.d/\*.conf + +/run/depmod.d/\*.conf + +/usr/local/lib/depmod.d/\*.conf + +@DISTCONFDIR@/depmod.d/\*.conf + +/lib/depmod.d/\*.conf + +# DESCRIPTION + +On execution *depmod* reads the configuration files from the above location and +based on that it processes the available modules and their dependencies. For +example: one can change the search order, exclude folders, override specific +module's location and more. + +This is typically useful in cases where built-in kernel modules are complemented +by custom built versions of the same and the user wishes to affect the priority +of processing in order to override the module version supplied by the kernel. + +# CONFIGURATION FORMAT + +The configuration files contain one command per line, with blank lines and lines +starting with '#' ignored (useful for adding comments). A '\\' at the end of a +line causes it to continue on the next line, which makes the files a bit neater. + +See the COMMANDS section below for more. + +# CONFIGURATION DIRECTORIES AND PRECEDENCE + +Configuration files are read from directories in listed in SYNOPSIS in that +order of precedence. Once a file of a given filename is loaded, any file of the +same name in subsequent directories is ignored. + +All configuration files are sorted in lexicographic order, regardless of the +directory they reside in. Configuration files can either be completely replaced +(by having a new configuration file with the same name in a directory of higher +priority) or partially replaced (by having a configuration file that is ordered +later). + +# COMMANDS + +search _subdirectory..._ + This allows you to specify the order in which @MODULE_DIRECTORY@ (or + other configured module location) subdirectories will be processed by + *depmod*. Directories are listed in order, with the highest priority + given to the first listed directory and the lowest priority given to the + last directory listed. The special keyword *built-in* refers to the + standard module directories installed by the kernel. Another special + keyword *external* refers to the list of external directories, defined + by the *external* command. + + By default, depmod will give a higher priority to a directory with the + name *updates* using this built-in search string: "updates built-in" but + more complex arrangements are possible and are used in several popular + distributions. + +override _modulename_ _kernelversion_ _modulesubdirectory_ + This command allows you to override which version of a specific module + will be used when more than one module sharing the same name is + processed by the *depmod* command. It is possible to specify one kernel + or all kernels using the \* wildcard. _modulesubdirectory_ is the name + of the subdirectory under @MODULE_DIRECTORY@ (or other module location) + where the target module is installed. + + For example, it is possible to override the priority of an updated test + module called *kmod* by specifying the following command: "override + kmod \* extra". This will ensure that any matching module name installed + under the *extra* subdirectory within @MODULE_DIRECTORY@ (or other + module location) will take priority over any likenamed module already + provided by the kernel. + +external _kernelversion_ _absolutemodulesdirectory..._ + This specifies a list of directories, which will be checked according to + the priorities in the *search* command. The order matters also, the + first directory has the higher priority. + + The _kernelversion_ is a POSIX regular expression or \* wildcard, like + in the *override*. + +exclude _excludedir_ + This specifies the trailing directories that will be excluded during the + search for kernel modules. + + The _excludedir_ is the trailing directory to exclude. + +# COPYRIGHT + +This manual page Copyright 2006-2010, Jon Masters, Red Hat, Inc. + +# SEE ALSO + +*depmod*(8) + +# BUGS + +Please direct any bug reports to kmod's issue tracker at +https://github.com/kmod-project/kmod/issues/ alongside with version used, steps +to reproduce the problem and the expected outcome. + +# AUTHORS + +Numerous contributions have come from the linux-modules mailing list + and Github. If you have a clone of kmod.git +itself, the output of *git-shortlog*(1) and *git-blame*(1) can show you the +authors for specific parts of the project. + +*Lucas De Marchi* is the current maintainer of the +project. diff --git a/man/insmod.8.scd b/man/insmod.8.scd new file mode 100644 index 0000000..a5c4264 --- /dev/null +++ b/man/insmod.8.scd @@ -0,0 +1,64 @@ +INSMOD(8) "kmod" "insmod" + +# NAME + +insmod - Simple program to insert a module into the Linux Kernel + +# SYNOPSIS + +*insmod* [_OPTIONS_] [_filename_] [_module options_] + +# DESCRIPTION + +*insmod* is a trivial program to insert a module into the kernel. Most users +will want to use *modprobe*(8) instead, which is more clever and can handle +module dependencies. + +Only the most general of error messages are reported: as the work of trying to +link the module is now done inside the kernel, the *dmesg*(1) usually gives more +information about errors. + +# OPTIONS + +*-f*, *--force* + This option can be extremely dangerous: it tells the kernel to ignore + the module version and vermagic fields when loading. With this option, + you can load modules build locally or by third parties, although this + can lead to memory corruption, system crashes and data loss. + +*-s*, *--syslog* + Send errors to syslog instead of standard error. + +*-v*, *--verbose* + Print messages about what the program is doing. Usually *insmod* prints + messages only if something goes wrong. + +*-V*, *--version* + Show version of program and exit. + +*-h*, *--help* + Print the help message and exit. + +# COPYRIGHT + +This manual page originally Copyright 2002, Rusty Russell, IBM Corporation. + +# SEE ALSO + +*modprobe*(8), *rmmod*(8), *lsmod*(8), *modinfo*(8), *depmod*(8) + +# BUGS + +Please direct any bug reports to kmod's issue tracker at +https://github.com/kmod-project/kmod/issues/ alongside with version used, steps +to reproduce the problem and the expected outcome. + +# AUTHORS + +Numerous contributions have come from the linux-modules mailing list + and Github. If you have a clone of kmod.git +itself, the output of *git-shortlog*(1) and *git-blame*(1) can show you the +authors for specific parts of the project. + +*Lucas De Marchi* is the current maintainer of the +project. diff --git a/man/kmod.8.scd b/man/kmod.8.scd new file mode 100644 index 0000000..b196bc1 --- /dev/null +++ b/man/kmod.8.scd @@ -0,0 +1,62 @@ +KMOD(8) "kmod" "kmod" + +# NAME + +kmod - Program to manage Linux Kernel modules + +# SYNOPSIS + +*kmod* [*OPTIONS...*] [_COMMAND_] [*COMMAND_OPTIONS...*] + +# DESCRIPTION + +*kmod* is a multi-call binary which implements the programs used to control +Linux Kernel modules. Most users will only run it using its other names. + +# OPTIONS + +*-V*, *--version* + Show the program version and exit. + +*-h*, *--help* + Show the help message. + +# COMMANDS + +*help* + Show the help message. + +*list* + List the currently loaded modules. + +*static-nodes* + Output the static device nodes information provided by the modules of + the currently running kernel version. + +# COPYRIGHT + +This manual page originally Copyright 2014, Marco d'Itri. + +# SEE ALSO + +*lsmod*(8), *rmmod*(8), *insmod*(8), *modinfo*(8), *modprobe*(8), *depmod*(8) + +# BUGS + +Please direct any bug reports to kmod's issue tracker at +https://github.com/kmod-project/kmod/issues/ alongside with version used, steps +to reproduce the problem and the expected outcome. + +# AUTHOR + +*kmod* project was started by Lucas De Marchi as a drop-in replacement to +module-init-tools that was maintained by Jon Masters, adding a library (libkmod) +and additional features. + +Numerous contributions have come from the linux-modules mailing list + and Github. If you have a clone of kmod.git +itself, the output of *git-shortlog*(1) and *git-blame*(1) can show you the +authors for specific parts of the project. + +*Lucas De Marchi* is the current maintainer of the +project. diff --git a/man/lsmod.8.scd b/man/lsmod.8.scd new file mode 100644 index 0000000..339526d --- /dev/null +++ b/man/lsmod.8.scd @@ -0,0 +1,53 @@ +LSMOD(8) "kmod" "lsmod" + +# NAME + +lsmod - Show the status of modules in the Linux Kernel + +# SYNOPSIS + +*lsmod* [_OPTIONS_] + +# DESCRIPTION + +*lsmod* is a trivial program which nicely formats the contents of the +_/proc/modules_, showing what kernel modules are currently loaded. + +# OPTIONS + +*-s*, *--syslog* + Send errors to syslog instead of standard error. + +*-v*, *--verbose* + Print messages about what the program is doing. Usually *lsmod* prints + messages only if something goes wrong. + +*-V*, *--version* + Show version of program and exit. + +*-h*, *--help* + Print the help message and exit. + +# COPYRIGHT + +This manual page originally Copyright 2002, Rusty Russell, IBM Corporation. + +# SEE ALSO + +*insmod*(8), *modprobe*(8), *modinfo*(8), *depmod*(8) + +# BUGS + +Please direct any bug reports to kmod's issue tracker at +https://github.com/kmod-project/kmod/issues/ alongside with version used, steps +to reproduce the problem and the expected outcome. + +# AUTHORS + +Numerous contributions have come from the linux-modules mailing list + and Github. If you have a clone of kmod.git +itself, the output of *git-shortlog*(1) and *git-blame*(1) can show you the +authors for specific parts of the project. + +*Lucas De Marchi* is the current maintainer of the +project. diff --git a/man/meson.build b/man/meson.build new file mode 100644 index 0000000..a9eb3cf --- /dev/null +++ b/man/meson.build @@ -0,0 +1,57 @@ +scdoc = find_program('scdoc') + +_man_pages = [ + ['5', 'depmod.d'], + ['5', 'modprobe.d'], + ['5', 'modules.dep'], + ['8', 'depmod'], + ['8', 'insmod'], + ['8', 'kmod'], + ['8', 'lsmod'], + ['8', 'modinfo'], + ['8', 'modprobe'], + ['8', 'rmmod'], +] +foreach tuple : _man_pages + section = tuple[0] + man = tuple[1] + + custom_target( + 'man_@0@_@1@'.format(section, man), + command : [ + build_scdoc, + scdoc, + '@INPUT@', + 's|@SYSCONFDIR@|@0@|g;'.format(sysconfdir) + + 's|@DISTCONFDIR@|@0@|g;'.format(distconfdir) + + 's|@MODULE_DIRECTORY@|@0@|g;'.format(moduledir), + ], + input : '@0@.@1@.scd'.format(man, section), + output : '@0@.@1@'.format(man, section), + capture : true, + build_by_default : get_option('manpages'), + install : true, + install_dir : get_option('mandir') / 'man@0@'.format(section) + ) +endforeach + +_man_aliases = [ + ['5', 'modprobe.conf', 'modprobe.d.5'], + ['5', 'modules.dep.bin', 'modules.dep.5'], +] + +foreach tuple : _man_aliases + section = tuple[0] + man = tuple[1] + dest = tuple[2] + + custom_target( + 'man_@0@_@1@'.format(section, man), + command : ['echo', '.so @0@'.format(dest)], + output : '@0@.@1@'.format(man, section), + capture : true, + build_by_default : get_option('manpages'), + install : true, + install_dir : get_option('mandir') / 'man@0@'.format(section) + ) +endforeach diff --git a/man/modinfo.8.scd b/man/modinfo.8.scd new file mode 100644 index 0000000..9601ad5 --- /dev/null +++ b/man/modinfo.8.scd @@ -0,0 +1,87 @@ +MODINFO(8) "kmod" "modinfo" + +# NAME + +modinfo - Show information about a Linux Kernel module + +# SYNOPSIS + +*modinfo* [*-0*] [*-F* _field_] [*-k* _kernel_] [modulename|filename...] + +*modinfo* *-V* + +*modinfo* *-h* + +# DESCRIPTION + +*modinfo* extracts information from the Linux Kernel modules given on the +command line. If the module name is not a filename, then the @MODULE_DIRECTORY@/ +_version_ directory is searched, as is also done by *modprobe*(8) when loading +kernel modules. + +*modinfo* by default lists each attribute of the module in form _fieldname_ : +_value_, for easy reading. The filename is listed the same way (although it's +not really an attribute). + +This version of *modinfo* can understand modules of any Linux Kernel +architecture. + +# OPTIONS + +*-V*, *--version* + Print the *modinfo* version. + +*-F* _field_, *--field*=_field_ + Only print this _field_ value, one per line. This is most useful for + scripts. Field names are case-insensitive. Common fields (which may not + be in every module) include author, description, license, parm, depends, + and alias. There are often multiple parm, alias and depends fields. The + special _field_ filename lists the filename of the module. + +*-b* _basedir_, *--basedir*=_basedir_ + Root directory for modules, / by default. + +*-k* _kernel_ + Provide information about a kernel other than the running one. This is + particularly useful for distributions needing to extract information + from a newly installed (but not yet running) set of kernel modules. For + example, you wish to find which firmware files are needed by various + modules in a new kernel for which you must make an initrd/initramfs + image prior to booting. + +*-0*, *--null* + Use the ASCII zero character to separate _field_ values, instead of a new + line. This is useful for scripts, since a new line can theoretically + appear inside a _field_. + +*-a* *--author*, *-d* *--description*, *-l* *--license*, *-p* *--parameters*, *-n* *--filename* + These are shortcuts for the *--field* flag's author, description, + license, parm and filename arguments, to ease the transition from the + old modutils *modinfo*. + +*-h*, *--help* + Print the help message and exit. + +# COPYRIGHT + +This manual page originally Copyright 2003, Rusty Russell, IBM Corporation. + +# SEE ALSO + +*modprobe*(8) + +# BUGS + +Please direct any bug reports to kmod's issue tracker at +https://github.com/kmod-project/kmod/issues/ alongside with version used, steps +to reproduce the problem and the expected outcome. + +# AUTHORS + +Numerous contributions have come from the linux-modules mailing list + and Github. If you have a clone of kmod.git +itself, the output of *git-shortlog*(1) and *git-blame*(1) can show you the +authors for specific parts of the project. + +*Lucas De Marchi* is the current maintainer of the +project. diff --git a/man/modprobe.8.scd b/man/modprobe.8.scd new file mode 100644 index 0000000..13738c1 --- /dev/null +++ b/man/modprobe.8.scd @@ -0,0 +1,223 @@ +MODPROBE(8) "kmod" "modprobe" + +# NAME + +modprobe - Add and remove modules from the Linux Kernel + +# SYNOPSIS + +*modprobe* [*-v*] [*-V*] [*-C* _config-file_] [*-n*] [*-i*] [*-q*] [*-b*] [_modulename_] +\ \ \ \ \ \ \ \ \ \[_module parameters_...] + +*modprobe* [*-r*] [*-v*] [*-n*] [*-i*] [_modulename_...] + +*modprobe* [*-c*] + +*modprobe* [*--dump-modversions*] [_filename_] + +# DESCRIPTION + +*modprobe* intelligently adds or removes a module from the Linux kernel: note +that for convenience, there is no difference between \_ and - in module names +(automatic underscore conversion is performed). *modprobe* looks in the module +directory @DISTCONFDIR@/`uname -r` for all the modules and other files, except +for the optional configuration files (see *modprobe.d*(5)). *modprobe* will also +use module options specified on the kernel command line in the form of +.