summaryrefslogtreecommitdiffstats
path: root/libkmod
diff options
context:
space:
mode:
Diffstat (limited to 'libkmod')
-rw-r--r--libkmod/.gitignore6
-rw-r--r--libkmod/COPYING504
-rw-r--r--libkmod/Makefile13
-rw-r--r--libkmod/README58
-rw-r--r--libkmod/docs/.gitignore14
-rw-r--r--libkmod/docs/Makefile.am34
-rw-r--r--libkmod/docs/libkmod-docs.xml27
-rw-r--r--libkmod/docs/libkmod-sections.txt108
-rw-r--r--libkmod/docs/version.xml.in1
-rw-r--r--libkmod/libkmod-builtin.c330
-rw-r--r--libkmod/libkmod-config.c1252
-rw-r--r--libkmod/libkmod-elf.c1219
-rw-r--r--libkmod/libkmod-file.c541
-rw-r--r--libkmod/libkmod-index.c1082
-rw-r--r--libkmod/libkmod-index.h48
-rw-r--r--libkmod/libkmod-internal.h210
-rw-r--r--libkmod/libkmod-list.c314
-rw-r--r--libkmod/libkmod-module.c2986
-rw-r--r--libkmod/libkmod-signature.c358
-rw-r--r--libkmod/libkmod.c1024
-rw-r--r--libkmod/libkmod.h270
-rw-r--r--libkmod/libkmod.pc.in11
-rw-r--r--libkmod/libkmod.sym94
23 files changed, 10504 insertions, 0 deletions
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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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..c4f3d69
--- /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="__must_check|KMOD_EXPORT"
+
+MKDB_OPTIONS = --xml-mode --output-format=xml --name-space kmod --tmpl-dir=.
+
+MKTMPL_OPTIONS = --output-dir=.
+
+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-internal.h \
+ libkmod-index.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..fd17506
--- /dev/null
+++ b/libkmod/docs/libkmod-docs.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+ <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
+ <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index">
+ <bookinfo>
+ <title>libkmod Reference Manual</title>
+ <releaseinfo>for libkmod version &version;</releaseinfo>
+ </bookinfo>
+
+ <chapter>
+ <title>libkmod</title>
+ <xi:include href="xml/libkmod.xml"/>
+ <xi:include href="xml/libkmod-list.xml"/>
+ <xi:include href="xml/libkmod-config.xml"/>
+ <xi:include href="xml/libkmod-module.xml"/>
+ <xi:include href="xml/libkmod-loaded.xml"/>
+ </chapter>
+
+ <index id="api-index-full">
+ <title>API Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+</book>
diff --git a/libkmod/docs/libkmod-sections.txt b/libkmod/docs/libkmod-sections.txt
new file mode 100644
index 0000000..33d9eec
--- /dev/null
+++ b/libkmod/docs/libkmod-sections.txt
@@ -0,0 +1,108 @@
+<SECTION>
+<FILE>libkmod</FILE>
+kmod_ctx
+kmod_new
+kmod_ref
+kmod_unref
+
+kmod_load_resources
+kmod_unload_resources
+kmod_validate_resources
+kmod_dump_index
+
+kmod_set_log_priority
+kmod_get_log_priority
+kmod_set_log_fn
+kmod_get_userdata
+kmod_set_userdata
+kmod_get_dirname
+</SECTION>
+
+<SECTION>
+<FILE>libkmod-list</FILE>
+kmod_list
+kmod_list_foreach
+kmod_list_foreach_reverse
+kmod_list_last
+kmod_list_next
+kmod_list_prev
+</SECTION>
+
+<SECTION>
+<FILE>libkmod-config</FILE>
+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_iter_get_key
+kmod_config_iter_get_value
+kmod_config_iter_next
+kmod_config_iter_free_iter
+</SECTION>
+
+<SECTION>
+<FILE>libkmod-module</FILE>
+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_module_insert_module
+kmod_module_probe_insert_module
+kmod_module_remove_module
+
+kmod_module_get_module
+kmod_module_get_dependencies
+kmod_module_get_softdeps
+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_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_free_list
+kmod_module_section_get_address
+kmod_module_section_get_name
+
+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_free_list
+kmod_module_info_get_key
+kmod_module_info_get_value
+</SECTION>
+
+<SECTION>
+<FILE>libkmod-loaded</FILE>
+kmod_module_new_from_loaded
+kmod_module_get_initstate
+kmod_module_initstate_str
+kmod_module_get_size
+kmod_module_get_refcnt
+kmod_module_get_holders
+</SECTION>
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..65334a8
--- /dev/null
+++ b/libkmod/libkmod-builtin.c
@@ -0,0 +1,330 @@
+/*
+ * libkmod - interface to kernel built-in modules
+ *
+ * Copyright (C) 2019 Alexey Gladkov <gladkov.alexey@gmail.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+#define MODULES_BUILTIN_MODINFO "modules.builtin.modinfo"
+
+struct kmod_builtin_iter {
+ struct kmod_ctx *ctx;
+
+ // The file descriptor.
+ int file;
+
+ // The total size in bytes.
+ ssize_t size;
+
+ // The offset of current module.
+ off_t pos;
+
+ // The offset at which the next module is located.
+ off_t next;
+
+ // Number of strings in the current block.
+ ssize_t nstrings;
+
+ // Internal buffer and its size.
+ size_t bufsz;
+ char *buf;
+};
+
+static struct kmod_builtin_iter *kmod_builtin_iter_new(struct kmod_ctx *ctx)
+{
+ char path[PATH_MAX];
+ int file, sv_errno;
+ struct stat sb;
+ struct kmod_builtin_iter *iter = NULL;
+ const char *dirname = kmod_get_dirname(ctx);
+ size_t len = strlen(dirname);
+
+ file = -1;
+
+ if ((len + 1 + strlen(MODULES_BUILTIN_MODINFO) + 1) >= PATH_MAX) {
+ sv_errno = ENAMETOOLONG;
+ goto fail;
+ }
+
+ snprintf(path, PATH_MAX, "%s/%s", dirname, MODULES_BUILTIN_MODINFO);
+
+ file = open(path, O_RDONLY|O_CLOEXEC);
+ if (file < 0) {
+ sv_errno = errno;
+ goto fail;
+ }
+
+ if (fstat(file, &sb) < 0) {
+ sv_errno = errno;
+ goto fail;
+ }
+
+ iter = malloc(sizeof(*iter));
+ if (!iter) {
+ sv_errno = ENOMEM;
+ goto fail;
+ }
+
+ iter->ctx = ctx;
+ iter->file = file;
+ iter->size = sb.st_size;
+ iter->nstrings = 0;
+ iter->pos = 0;
+ iter->next = 0;
+ iter->bufsz = 0;
+ iter->buf = NULL;
+
+ return iter;
+fail:
+ if (file >= 0)
+ close(file);
+
+ errno = sv_errno;
+
+ return iter;
+}
+
+static void kmod_builtin_iter_free(struct kmod_builtin_iter *iter)
+{
+ close(iter->file);
+ free(iter->buf);
+ free(iter);
+}
+
+static off_t get_string(struct kmod_builtin_iter *iter, off_t offset,
+ char **line, size_t *size)
+{
+ int sv_errno;
+ char *nullp = NULL;
+ size_t linesz = 0;
+
+ while (!nullp) {
+ char buf[BUFSIZ];
+ ssize_t sz;
+ size_t partsz;
+
+ sz = pread(iter->file, buf, BUFSIZ, offset);
+ if (sz < 0) {
+ sv_errno = errno;
+ goto fail;
+ } else if (sz == 0) {
+ offset = 0;
+ break;
+ }
+
+ nullp = memchr(buf, '\0', (size_t) sz);
+ partsz = (size_t)((nullp) ? (nullp - buf) + 1 : sz);
+ offset += (off_t) partsz;
+
+ if (iter->bufsz < linesz + partsz) {
+ iter->bufsz = linesz + partsz;
+ iter->buf = realloc(iter->buf, iter->bufsz);
+
+ if (!iter->buf) {
+ sv_errno = errno;
+ goto fail;
+ }
+ }
+
+ strncpy(iter->buf + linesz, buf, partsz);
+ linesz += partsz;
+ }
+
+ if (linesz) {
+ *line = iter->buf;
+ *size = linesz;
+ }
+
+ return offset;
+fail:
+ errno = sv_errno;
+ return -1;
+}
+
+static bool kmod_builtin_iter_next(struct kmod_builtin_iter *iter)
+{
+ char *line, *modname;
+ size_t linesz;
+ off_t pos, offset, modlen;
+
+ modname = NULL;
+
+ iter->nstrings = 0;
+ offset = pos = iter->next;
+
+ while (offset < iter->size) {
+ char *dot;
+ off_t len;
+
+ offset = get_string(iter, pos, &line, &linesz);
+ if (offset <= 0) {
+ if (offset)
+ ERR(iter->ctx, "get_string: %s\n", strerror(errno));
+ pos = iter->size;
+ break;
+ }
+
+ dot = strchr(line, '.');
+ if (!dot) {
+ ERR(iter->ctx, "kmod_builtin_iter_next: unexpected string without modname prefix\n");
+ pos = iter->size;
+ break;
+ }
+
+ len = dot - line;
+
+ if (!modname) {
+ modname = strdup(line);
+ modlen = len;
+ } else if (modlen != len || strncmp(modname, line, len)) {
+ break;
+ }
+
+ iter->nstrings++;
+ pos = offset;
+ }
+
+ iter->pos = iter->next;
+ iter->next = pos;
+
+ free(modname);
+
+ return (iter->pos < iter->size);
+}
+
+static bool kmod_builtin_iter_get_modname(struct kmod_builtin_iter *iter,
+ char modname[static PATH_MAX])
+{
+ int sv_errno;
+ char *line, *dot;
+ size_t linesz, len;
+ off_t offset;
+
+ if (iter->pos == iter->size)
+ return false;
+
+ line = NULL;
+
+ offset = get_string(iter, iter->pos, &line, &linesz);
+ if (offset <= 0) {
+ sv_errno = errno;
+ if (offset)
+ ERR(iter->ctx, "get_string: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ dot = strchr(line, '.');
+ if (!dot) {
+ sv_errno = errno;
+ ERR(iter->ctx, "kmod_builtin_iter_get_modname: unexpected string without modname prefix\n");
+ goto fail;
+ }
+
+ len = dot - line;
+
+ if (len >= PATH_MAX) {
+ sv_errno = ENAMETOOLONG;
+ goto fail;
+ }
+
+ strncpy(modname, line, len);
+ modname[len] = '\0';
+
+ return true;
+fail:
+ errno = sv_errno;
+ return false;
+}
+
+/* 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)
+{
+ ssize_t count = 0;
+ char *s, *line = NULL;
+ size_t i, n, linesz, modlen, size;
+ off_t pos, offset;
+
+ char *name = NULL;
+ char buf[PATH_MAX];
+
+ struct kmod_builtin_iter *iter = kmod_builtin_iter_new(ctx);
+
+ if (!iter)
+ return -errno;
+
+ while (!name && kmod_builtin_iter_next(iter)) {
+ if (!kmod_builtin_iter_get_modname(iter, buf)) {
+ count = -errno;
+ goto fail;
+ }
+
+ if (strcmp(modname, buf))
+ continue;
+
+ name = buf;
+ }
+
+ if (!name) {
+ count = -ENOSYS;
+ goto fail;
+ }
+
+ modlen = strlen(modname) + 1;
+ count = iter->nstrings;
+ size = iter->next - iter->pos - (modlen * count);
+
+ *modinfo = malloc(size + sizeof(char *) * (count + 1));
+ if (!*modinfo) {
+ count = -errno;
+ goto fail;
+ }
+
+ s = (char *)(*modinfo + count + 1);
+ i = 0;
+
+ n = 0;
+ offset = pos = iter->pos;
+
+ while (offset < iter->next) {
+ offset = get_string(iter, pos, &line, &linesz);
+ if (offset <= 0) {
+ count = (offset) ? -errno : -EINVAL;
+ free(*modinfo);
+ goto fail;
+ }
+
+ strcpy(s + i, line + modlen);
+ (*modinfo)[n++] = s + i;
+ i += linesz - modlen;
+
+ pos = offset;
+ }
+fail:
+ kmod_builtin_iter_free(iter);
+ return count;
+}
diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c
new file mode 100644
index 0000000..e83621b
--- /dev/null
+++ b/libkmod/libkmod-config.c
@@ -0,0 +1,1252 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <shared/util.h>
+
+#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;
+};
+
+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;
+}
+
+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;
+
+ *list = l;
+ cmd = NULL;
+ return 0;
+}
+
+static void kmod_config_free_command(struct kmod_config *config,
+ struct kmod_list *l,
+ struct kmod_list **list)
+{
+ struct kmod_command *cmd = l->data;
+
+ free(cmd);
+ *list = kmod_list_remove(l);
+}
+
+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;
+
+ opt = NULL;
+ config->options = list;
+ return 0;
+}
+
+static void kmod_config_free_options(struct kmod_config *config,
+ struct kmod_list *l)
+{
+ struct kmod_options *opt = l->data;
+
+ free(opt);
+
+ config->options = kmod_list_remove(l);
+}
+
+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;
+
+ alias = NULL;
+ config->aliases = list;
+ return 0;
+}
+
+static void kmod_config_free_alias(struct kmod_config *config,
+ struct kmod_list *l)
+{
+ struct kmod_alias *alias = l->data;
+
+ free(alias);
+
+ config->aliases = kmod_list_remove(l);
+}
+
+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);
+
+ p = strdup(modname);
+ if (!p)
+ return -ENOMEM;
+
+ list = kmod_list_append(config->blacklists, p);
+ if (!list)
+ return -ENOMEM;
+
+ p = NULL;
+ config->blacklists = list;
+ return 0;
+}
+
+static void kmod_config_free_blacklist(struct kmod_config *config,
+ struct kmod_list *l)
+{
+ free(l->data);
+ config->blacklists = kmod_list_remove(l);
+}
+
+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;
+ 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;
+ n_pre++;
+ } else if (mode == S_POST) {
+ buflen += plen + 1;
+ n_post++;
+ }
+ }
+ p = s + 1;
+ if (*s == '\0')
+ break;
+ }
+
+ DBG(config->ctx, "%u pre, %u post\n", n_pre, n_post);
+
+ dep = malloc(sizeof(struct kmod_softdep) + modnamelen +
+ n_pre * sizeof(const char *) +
+ n_post * sizeof(const char *) +
+ buflen);
+ 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 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 void kmod_config_free_softdep(struct kmod_config *config,
+ struct kmod_list *l)
+{
+ free(l->data);
+ config->softdeps = kmod_list_remove(l);
+}
+
+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, "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)
+{
+ while (config->aliases)
+ kmod_config_free_alias(config, config->aliases);
+
+ while (config->blacklists)
+ kmod_config_free_blacklist(config, config->blacklists);
+
+ while (config->options)
+ kmod_config_free_options(config, config->options);
+
+ while (config->install_commands) {
+ kmod_config_free_command(config, config->install_commands,
+ &config->install_commands);
+ }
+
+ while (config->remove_commands) {
+ kmod_config_free_command(config, config->remove_commands,
+ &config->remove_commands);
+ }
+
+ while (config->softdeps)
+ kmod_config_free_softdep(config, config->softdeps);
+
+ for (; config->paths != NULL;
+ config->paths = kmod_list_remove(config->paths))
+ free(config->paths->data);
+
+ 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")
+ && !streq(&fn[len - 6], ".alias")))
+ return true;
+
+ fstatat(dirfd(d), fn, &st, 0);
+
+ 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");
+
+ 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:
+ for (; list != NULL; list = kmod_list_remove(list))
+ free(list->data);
+
+ for (; path_list != NULL; path_list = kmod_list_remove(path_list))
+ free(path_list->data);
+
+ 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,
+};
+
+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 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;
+ }
+
+ return iter;
+}
+
+/**
+ * SECTION:libkmod-config
+ * @short_description: retrieve current libkmod configuration
+ */
+
+/**
+ * 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().
+ */
+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);
+}
+
+/**
+ * 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().
+ */
+KMOD_EXPORT struct kmod_config_iter *kmod_config_get_install_commands(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;;
+
+ return kmod_config_iter_new(ctx, CONFIG_TYPE_INSTALL);
+}
+
+/**
+ * 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().
+ */
+KMOD_EXPORT struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;;
+
+ return kmod_config_iter_new(ctx, CONFIG_TYPE_REMOVE);
+}
+
+/**
+ * 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().
+ */
+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_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().
+ */
+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_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().
+ */
+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_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.
+ */
+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_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.
+ */
+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_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.
+ */
+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_config_iter_free_iter:
+ * @iter: iterator over a certain configuration
+ *
+ * Free resources used by the iterator.
+ */
+KMOD_EXPORT void kmod_config_iter_free_iter(struct kmod_config_iter *iter)
+{
+ free(iter->data);
+ free(iter);
+}
diff --git a/libkmod/libkmod-elf.c b/libkmod/libkmod-elf.c
new file mode 100644
index 0000000..933825b
--- /dev/null
+++ b/libkmod/libkmod-elf.c
@@ -0,0 +1,1219 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <elf.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/util.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+enum kmod_elf_class {
+ KMOD_ELF_32 = (1 << 1),
+ KMOD_ELF_64 = (1 << 2),
+ KMOD_ELF_LSB = (1 << 3),
+ KMOD_ELF_MSB = (1 << 4)
+};
+
+/* 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)];
+};
+
+struct kmod_elf {
+ const uint8_t *memory;
+ uint8_t *changed;
+ uint64_t size;
+ enum kmod_elf_class class;
+ struct kmod_elf_header {
+ 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;
+ uint32_t nameoff; /* offset in strings itself */
+ } strings;
+ uint16_t machine;
+ } header;
+};
+
+//#define ENABLE_ELFDBG 1
+
+#if defined(ENABLE_LOGGING) && defined(ENABLE_ELFDBG)
+#define ELFDBG(elf, ...) \
+ _elf_dbg(elf, __FILE__, __LINE__, __func__, __VA_ARGS__);
+
+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->class & KMOD_ELF_32) ? 32 : 64,
+ (elf->class & KMOD_ELF_MSB) ? 'M' : 'L',
+ fname, line, func);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+}
+#else
+#define ELFDBG(elf, ...)
+#endif
+
+
+static int elf_identify(const void *memory, uint64_t size)
+{
+ const uint8_t *p = memory;
+ int class = 0;
+
+ if (size <= EI_NIDENT || memcmp(p, ELFMAG, SELFMAG) != 0)
+ return -ENOEXEC;
+
+ switch (p[EI_CLASS]) {
+ case ELFCLASS32:
+ if (size <= sizeof(Elf32_Ehdr))
+ return -EINVAL;
+ class |= KMOD_ELF_32;
+ break;
+ case ELFCLASS64:
+ if (size <= sizeof(Elf64_Ehdr))
+ return -EINVAL;
+ class |= KMOD_ELF_64;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (p[EI_DATA]) {
+ case ELFDATA2LSB:
+ class |= KMOD_ELF_LSB;
+ break;
+ case ELFDATA2MSB:
+ class |= KMOD_ELF_MSB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return class;
+}
+
+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;
+ size_t i;
+
+ assert(size <= sizeof(uint64_t));
+ assert(offset + size <= elf->size);
+ if (offset + size > elf->size) {
+ ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
+ offset, size, offset + size, elf->size);
+ return (uint64_t)-1;
+ }
+
+ p = elf->memory + offset;
+ if (elf->class & KMOD_ELF_MSB) {
+ for (i = 0; i < size; i++)
+ ret = (ret << 8) | p[i];
+ } else {
+ for (i = 1; i <= size; i++)
+ ret = (ret << 8) | p[size - i];
+ }
+
+ ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64"\n",
+ size, offset, ret);
+
+ return ret;
+}
+
+static inline int elf_set_uint(struct kmod_elf *elf, uint64_t offset, uint64_t size, uint64_t value)
+{
+ uint8_t *p;
+ size_t i;
+
+ ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64" write memory=%p\n",
+ size, offset, value, elf->changed);
+
+ assert(size <= sizeof(uint64_t));
+ assert(offset + size <= elf->size);
+ if (offset + size > elf->size) {
+ ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
+ offset, size, offset + size, elf->size);
+ return -1;
+ }
+
+ if (elf->changed == NULL) {
+ elf->changed = malloc(elf->size);
+ if (elf->changed == NULL)
+ return -errno;
+ memcpy(elf->changed, elf->memory, elf->size);
+ elf->memory = elf->changed;
+ ELFDBG(elf, "copied memory to allow writing.\n");
+ }
+
+ p = elf->changed + offset;
+ if (elf->class & KMOD_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)
+{
+ assert(offset < elf->size);
+ if (offset >= elf->size) {
+ ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
+ offset, elf->size);
+ return NULL;
+ }
+ return elf->memory + offset;
+}
+
+static inline const void *elf_get_section_header(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 NULL;
+ }
+ return elf_get_mem(elf, 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, uint32_t *nameoff)
+{
+ const uint8_t *p = elf_get_section_header(elf, idx);
+ uint64_t min_size, off = p - elf->memory;
+
+ if (p == NULL) {
+ ELFDBG(elf, "no section at %"PRIu16"\n", idx);
+ *offset = 0;
+ *size = 0;
+ *nameoff = 0;
+ return -EINVAL;
+ }
+
+#define READV(field) \
+ elf_get_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field))
+
+ if (elf->class & KMOD_ELF_32) {
+ const Elf32_Shdr *hdr _unused_ = (const Elf32_Shdr *)p;
+ *size = READV(sh_size);
+ *offset = READV(sh_offset);
+ *nameoff = READV(sh_name);
+ } else {
+ const Elf64_Shdr *hdr _unused_ = (const Elf64_Shdr *)p;
+ *size = READV(sh_size);
+ *offset = READV(sh_offset);
+ *nameoff = READV(sh_name);
+ }
+#undef READV
+
+ if (addu64_overflow(*offset, *size, &min_size)
+ || min_size > elf->size) {
+ ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
+ min_size, elf->size);
+ return -EINVAL;
+ }
+
+ ELFDBG(elf, "section=%"PRIu16" is: offset=%"PRIu64" size=%"PRIu64" nameoff=%"PRIu32"\n",
+ idx, *offset, *size, *nameoff);
+
+ return 0;
+}
+
+static const char *elf_get_strings_section(const struct kmod_elf *elf, uint64_t *size)
+{
+ *size = elf->header.strings.size;
+ return elf_get_mem(elf, elf->header.strings.offset);
+}
+
+struct kmod_elf *kmod_elf_new(const void *memory, off_t size)
+{
+ struct kmod_elf *elf;
+ uint64_t min_size;
+ size_t shdrs_size, shdr_size;
+ int class;
+
+ 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;
+ }
+
+ class = elf_identify(memory, size);
+ if (class < 0) {
+ errno = -class;
+ return NULL;
+ }
+
+ elf = malloc(sizeof(struct kmod_elf));
+ if (elf == NULL) {
+ return NULL;
+ }
+
+ elf->memory = memory;
+ elf->changed = NULL;
+ elf->size = size;
+ elf->class = class;
+
+#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->class & KMOD_ELF_32) {
+ const Elf32_Ehdr *hdr _unused_ = elf_get_mem(elf, 0);
+ LOAD_HEADER;
+ shdr_size = sizeof(Elf32_Shdr);
+ } else {
+ const Elf64_Ehdr *hdr _unused_ = elf_get_mem(elf, 0);
+ LOAD_HEADER;
+ shdr_size = sizeof(Elf64_Shdr);
+ }
+#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 %"PRIu16"\n",
+ elf->header.section.entry_size, shdr_size);
+ goto invalid;
+ }
+ shdrs_size = shdr_size * elf->header.section.count;
+ if (addu64_overflow(shdrs_size, elf->header.section.offset, &min_size)
+ || min_size > elf->size) {
+ ELFDBG(elf, "file is too short to hold sections\n");
+ goto invalid;
+ }
+
+ if (elf_get_section_info(elf, elf->header.strings.section,
+ &elf->header.strings.offset,
+ &elf->header.strings.size,
+ &elf->header.strings.nameoff) < 0) {
+ ELFDBG(elf, "could not get strings section\n");
+ goto invalid;
+ } else {
+ uint64_t slen;
+ const char *s = elf_get_strings_section(elf, &slen);
+ if (slen == 0 || s[slen - 1] != '\0') {
+ ELFDBG(elf, "strings section does not ends with \\0\n");
+ goto invalid;
+ }
+ }
+
+ return elf;
+
+invalid:
+ free(elf);
+ errno = EINVAL;
+ return NULL;
+}
+
+void kmod_elf_unref(struct kmod_elf *elf)
+{
+ free(elf->changed);
+ free(elf);
+}
+
+const void *kmod_elf_get_memory(const struct kmod_elf *elf)
+{
+ return elf->memory;
+}
+
+static int elf_find_section(const struct kmod_elf *elf, const char *section)
+{
+ uint64_t nameslen;
+ const char *names = elf_get_strings_section(elf, &nameslen);
+ uint16_t i;
+
+ for (i = 1; i < elf->header.section.count; i++) {
+ uint64_t off, size;
+ uint32_t nameoff;
+ const char *n;
+ int err = elf_get_section_info(elf, i, &off, &size, &nameoff);
+ if (err < 0)
+ continue;
+ if (nameoff >= nameslen)
+ continue;
+ n = names + nameoff;
+ if (!streq(section, n))
+ continue;
+
+ return i;
+ }
+
+ return -ENODATA;
+}
+
+int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, const void **buf, uint64_t *buf_size)
+{
+ uint64_t nameslen;
+ const char *names = elf_get_strings_section(elf, &nameslen);
+ uint16_t i;
+
+ *buf = NULL;
+ *buf_size = 0;
+
+ for (i = 1; i < elf->header.section.count; i++) {
+ uint64_t off, size;
+ uint32_t nameoff;
+ const char *n;
+ int err = elf_get_section_info(elf, i, &off, &size, &nameoff);
+ if (err < 0)
+ continue;
+ if (nameoff >= nameslen)
+ continue;
+ n = names + nameoff;
+ if (!streq(section, n))
+ continue;
+
+ *buf = elf_get_mem(elf, off);
+ *buf_size = size;
+ return 0;
+ }
+
+ return -ENODATA;
+}
+
+/* array will be allocated with strings in a single malloc, just free *array */
+int kmod_elf_get_strings(const struct kmod_elf *elf, const char *section, char ***array)
+{
+ size_t i, j, count;
+ uint64_t size;
+ const void *buf;
+ const char *strings;
+ char *s, **a;
+ int err;
+
+ *array = NULL;
+
+ err = kmod_elf_get_section(elf, section, &buf, &size);
+ if (err < 0)
+ return err;
+
+ strings = buf;
+ if (strings == NULL || size == 0)
+ return 0;
+
+ /* skip zero padding */
+ while (strings[0] == '\0' && size > 1) {
+ 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++;
+
+ *array = a = malloc(size + 1 + sizeof(char *) * (count + 1));
+ 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 (strings[i] == '\0' && i < size)
+ i++;
+
+ a[j] = &s[i];
+ j++;
+ }
+
+ return count;
+}
+
+/* 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 off, offcrc, slen;
+ uint64_t size;
+ struct kmod_modversion *a;
+ const void *buf;
+ char *itr;
+ int i, count, err;
+#define MODVERSION_SEC_SIZE (sizeof(struct kmod_modversion64))
+
+ assert_cc(sizeof(struct kmod_modversion64) ==
+ sizeof(struct kmod_modversion32));
+
+ if (elf->class & KMOD_ELF_32)
+ offcrc = sizeof(uint32_t);
+ else
+ offcrc = sizeof(uint64_t);
+
+ *array = NULL;
+
+ err = kmod_elf_get_section(elf, "__versions", &buf, &size);
+ if (err < 0)
+ return err;
+
+ if (buf == NULL || size == 0)
+ return 0;
+
+ if (size % MODVERSION_SEC_SIZE != 0)
+ return -EINVAL;
+
+ count = size / MODVERSION_SEC_SIZE;
+
+ off = (const uint8_t *)buf - elf->memory;
+ slen = 0;
+
+ for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) {
+ const char *symbol = elf_get_mem(elf, off + offcrc);
+
+ if (symbol[0] == '.')
+ symbol++;
+
+ slen += strlen(symbol) + 1;
+ }
+
+ *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
+ if (*array == NULL)
+ return -errno;
+
+ itr = (char *)(a + count);
+ off = (const uint8_t *)buf - elf->memory;
+
+ for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) {
+ uint64_t crc = elf_get_uint(elf, off, offcrc);
+ const char *symbol = elf_get_mem(elf, off + offcrc);
+ size_t symbollen;
+
+ if (symbol[0] == '.')
+ symbol++;
+
+ a[i].crc = crc;
+ a[i].bind = KMOD_SYMBOL_UNDEF;
+ a[i].symbol = itr;
+ symbollen = strlen(symbol) + 1;
+ memcpy(itr, symbol, symbollen);
+ itr += symbollen;
+ }
+
+ return count;
+}
+
+int kmod_elf_strip_section(struct kmod_elf *elf, const char *section)
+{
+ uint64_t off, size;
+ const void *buf;
+ int idx = elf_find_section(elf, section);
+ uint64_t val;
+
+ if (idx < 0)
+ return idx;
+
+ buf = elf_get_section_header(elf, idx);
+ off = (const uint8_t *)buf - elf->memory;
+
+ if (elf->class & KMOD_ELF_32) {
+ 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);
+}
+
+int kmod_elf_strip_vermagic(struct kmod_elf *elf)
+{
+ uint64_t i, size;
+ const void *buf;
+ const char *strings;
+ int err;
+
+ err = kmod_elf_get_section(elf, ".modinfo", &buf, &size);
+ if (err < 0)
+ return err;
+ strings = buf;
+ if (strings == NULL || size == 0)
+ return 0;
+
+ /* skip zero padding */
+ while (strings[0] == '\0' && size > 1) {
+ 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;
+
+ if (elf->changed == NULL) {
+ elf->changed = malloc(elf->size);
+ if (elf->changed == NULL)
+ return -errno;
+ memcpy(elf->changed, elf->memory, elf->size);
+ elf->memory = elf->changed;
+ ELFDBG(elf, "copied memory to allow writing.\n");
+ }
+
+ len = strlen(s);
+ ELFDBG(elf, "clear .modinfo vermagic \"%s\" (%zd bytes)\n",
+ s, len);
+ memset(elf->changed + off, '\0', len);
+ return 0;
+ }
+
+ ELFDBG(elf, "no vermagic found in .modinfo\n");
+ return -ENODATA;
+}
+
+
+static int kmod_elf_get_symbols_symtab(const struct kmod_elf *elf, struct kmod_modversion **array)
+{
+ uint64_t i, last, size;
+ const void *buf;
+ const char *strings;
+ char *itr;
+ struct kmod_modversion *a;
+ int count, err;
+
+ *array = NULL;
+
+ err = kmod_elf_get_section(elf, "__ksymtab_strings", &buf, &size);
+ if (err < 0)
+ return err;
+ strings = buf;
+ if (strings == NULL || size == 0)
+ return 0;
+
+ /* skip zero padding */
+ while (strings[0] == '\0' && size > 1) {
+ strings++;
+ size--;
+ }
+ if (size <= 1)
+ return 0;
+
+ 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 (strings[i - 1] != '\0')
+ count++;
+
+ *array = a = malloc(size + 1 + sizeof(struct kmod_modversion) * count);
+ if (*array == NULL)
+ return -errno;
+
+ itr = (char *)(a + count);
+ last = 0;
+ for (i = 0, count = 0; i < size; i++) {
+ if (strings[i] == '\0') {
+ size_t slen = i - last;
+ if (last == i) {
+ last = i + 1;
+ continue;
+ }
+ a[count].crc = 0;
+ a[count].bind = KMOD_SYMBOL_GLOBAL;
+ a[count].symbol = itr;
+ memcpy(itr, strings + last, slen);
+ itr[slen] = '\0';
+ itr += slen + 1;
+ count++;
+ last = i + 1;
+ }
+ }
+ if (strings[i - 1] != '\0') {
+ size_t slen = i - last;
+ a[count].crc = 0;
+ a[count].bind = KMOD_SYMBOL_GLOBAL;
+ a[count].symbol = itr;
+ memcpy(itr, strings + last, slen);
+ itr[slen] = '\0';
+ count++;
+ }
+
+ 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;
+ uint32_t nameoff;
+
+ if (shndx == SHN_ABS || shndx == SHN_UNDEF)
+ return crc;
+
+ err = elf_get_section_info(elf, shndx, &off, &size, &nameoff);
+ if (err < 0) {
+ ELFDBG("Cound not find section index %"PRIu16" for crc", shndx);
+ return (uint64_t)-1;
+ }
+
+ if (crc > (size - sizeof(uint32_t))) {
+ ELFDBG("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_off, sym_off;
+ const void *strtab, *symtab;
+ struct kmod_modversion *a;
+ char *itr;
+ size_t slen, symlen;
+ int i, count, symcount, err;
+
+ err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen);
+ if (err < 0) {
+ ELFDBG(elf, "no .strtab found.\n");
+ goto fallback;
+ }
+
+ err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen);
+ if (err < 0) {
+ ELFDBG(elf, "no .symtab found.\n");
+ goto fallback;
+ }
+
+ if (elf->class & KMOD_ELF_32)
+ symlen = sizeof(Elf32_Sym);
+ else
+ symlen = sizeof(Elf64_Sym);
+
+ if (symtablen % symlen != 0) {
+ ELFDBG(elf, "unexpected .symtab of length %"PRIu64", not multiple of %"PRIu64" as expected.\n", symtablen, symlen);
+ goto fallback;
+ }
+
+ symcount = symtablen / symlen;
+ count = 0;
+ slen = 0;
+ str_off = (const uint8_t *)strtab - elf->memory;
+ sym_off = (const uint8_t *)symtab - elf->memory + 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->class & KMOD_ELF_32) {
+ 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 %d 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;
+ slen += strlen(name + crc_strlen) + 1;
+ count++;
+ }
+
+ if (count == 0)
+ goto fallback;
+
+ *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
+ if (*array == NULL)
+ return -errno;
+
+ itr = (char *)(a + count);
+ count = 0;
+ str_off = (const uint8_t *)strtab - elf->memory;
+ sym_off = (const uint8_t *)symtab - elf->memory + 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->class & KMOD_ELF_32) {
+ 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->class & KMOD_ELF_32)
+ 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 = itr;
+ slen = strlen(name);
+ memcpy(itr, name, slen);
+ itr[slen] = '\0';
+ itr += slen + 1;
+ 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, const void *versions, uint64_t versionslen, const char *name, uint64_t *crc)
+{
+ size_t verlen, crclen, off;
+ uint64_t i;
+
+ if (elf->class & KMOD_ELF_32) {
+ struct kmod_modversion32 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ } else {
+ struct kmod_modversion64 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ }
+
+ off = (const uint8_t *)versions - elf->memory;
+ for (i = 0; i < versionslen; i += verlen) {
+ const char *symbol = elf_get_mem(elf, off + i + crclen);
+ if (!streq(name, symbol))
+ 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;
+ const void *versions, *strtab, *symtab;
+ struct kmod_modversion *a;
+ char *itr;
+ size_t slen, verlen, symlen, crclen;
+ int i, count, symcount, vercount, err;
+ bool handle_register_symbols;
+ uint8_t *visited_versions;
+ uint64_t *symcrcs;
+
+ err = kmod_elf_get_section(elf, "__versions", &versions, &versionslen);
+ if (err < 0) {
+ versions = NULL;
+ versionslen = 0;
+ verlen = 0;
+ crclen = 0;
+ } else {
+ if (elf->class & KMOD_ELF_32) {
+ struct kmod_modversion32 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ } else {
+ struct kmod_modversion64 *mv;
+ verlen = sizeof(*mv);
+ crclen = sizeof(mv->crc);
+ }
+ if (versionslen % verlen != 0) {
+ ELFDBG(elf, "unexpected __versions of length %"PRIu64", not multiple of %zd as expected.\n", versionslen, verlen);
+ versions = NULL;
+ versionslen = 0;
+ }
+ }
+
+ err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen);
+ if (err < 0) {
+ ELFDBG(elf, "no .strtab found.\n");
+ return -EINVAL;
+ }
+
+ err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen);
+ if (err < 0) {
+ ELFDBG(elf, "no .symtab found.\n");
+ return -EINVAL;
+ }
+
+ if (elf->class & KMOD_ELF_32)
+ symlen = sizeof(Elf32_Sym);
+ else
+ symlen = sizeof(Elf64_Sym);
+
+ if (symtablen % symlen != 0) {
+ ELFDBG(elf, "unexpected .symtab of length %"PRIu64", not multiple of %"PRIu64" 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;
+ slen = 0;
+ str_off = (const uint8_t *)strtab - elf->memory;
+ sym_off = (const uint8_t *)symtab - elf->memory + 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->class & KMOD_ELF_32) {
+ 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->class & KMOD_ELF_32)
+ 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 %d 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 %"PRIu64"\n", i);
+ continue;
+ }
+
+ slen += strlen(name) + 1;
+ count++;
+
+ idx = kmod_elf_crc_find(elf, versions, 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 */
+ ver_off = (const uint8_t *)versions - elf->memory;
+ for (i = 0; i < vercount; i++) {
+ if (visited_versions[i] == 0) {
+ const char *name;
+ name = elf_get_mem(elf, ver_off + i * verlen + crclen);
+ slen += strlen(name) + 1;
+
+ count++;
+ }
+ }
+ }
+
+ if (count == 0) {
+ free(visited_versions);
+ free(symcrcs);
+ *array = NULL;
+ return 0;
+ }
+
+ *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
+ if (*array == NULL) {
+ free(visited_versions);
+ free(symcrcs);
+ return -errno;
+ }
+
+ itr = (char *)(a + count);
+ count = 0;
+ str_off = (const uint8_t *)strtab - elf->memory;
+ sym_off = (const uint8_t *)symtab - elf->memory + 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->class & KMOD_ELF_32) {
+ 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->class & KMOD_ELF_32)
+ 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 %"PRIu64"\n", i);
+ continue;
+ }
+
+ if (elf->class & KMOD_ELF_32)
+ bind = ELF32_ST_BIND(info);
+ else
+ bind = ELF64_ST_BIND(info);
+ if (bind == STB_WEAK)
+ bind = KMOD_SYMBOL_WEAK;
+ else
+ bind = KMOD_SYMBOL_UNDEF;
+
+ slen = strlen(name);
+ crc = symcrcs[i];
+
+ a[count].crc = crc;
+ a[count].bind = bind;
+ a[count].symbol = itr;
+ memcpy(itr, name, slen);
+ itr[slen] = '\0';
+ itr += slen + 1;
+
+ count++;
+ }
+
+ free(symcrcs);
+
+ if (visited_versions == NULL)
+ return count;
+
+ /* add unvisited (module_layout/struct_module) */
+ ver_off = (const uint8_t *)versions - elf->memory;
+ 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);
+ slen = strlen(name);
+ crc = elf_get_uint(elf, ver_off + i * verlen, crclen);
+
+ a[count].crc = crc;
+ a[count].bind = KMOD_SYMBOL_UNDEF;
+ a[count].symbol = itr;
+ memcpy(itr, name, slen);
+ itr[slen] = '\0';
+ itr += slen + 1;
+
+ count++;
+ }
+ free(visited_versions);
+ return count;
+}
diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c
new file mode 100644
index 0000000..b138e7e
--- /dev/null
+++ b/libkmod/libkmod-file.c
@@ -0,0 +1,541 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef ENABLE_ZSTD
+#include <zstd.h>
+#endif
+#ifdef ENABLE_XZ
+#include <lzma.h>
+#endif
+#ifdef ENABLE_ZLIB
+#include <zlib.h>
+#endif
+
+#include <shared/util.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+struct kmod_file;
+struct file_ops {
+ int (*load)(struct kmod_file *file);
+ void (*unload)(struct kmod_file *file);
+};
+
+struct kmod_file {
+#ifdef ENABLE_ZSTD
+ bool zstd_used;
+#endif
+#ifdef ENABLE_XZ
+ bool xz_used;
+#endif
+#ifdef ENABLE_ZLIB
+ gzFile gzf;
+#endif
+ int fd;
+ enum kmod_file_compression_type compression;
+ off_t size;
+ void *memory;
+ const struct file_ops *ops;
+ const struct kmod_ctx *ctx;
+ struct kmod_elf *elf;
+};
+
+#ifdef ENABLE_ZSTD
+static int zstd_read_block(struct kmod_file *file, size_t block_size,
+ ZSTD_inBuffer *input, size_t *input_capacity)
+{
+ ssize_t rdret;
+ int ret;
+
+ if (*input_capacity < block_size) {
+ free((void *)input->src);
+ input->src = malloc(block_size);
+ if (input->src == NULL) {
+ ret = -errno;
+ ERR(file->ctx, "zstd: %m\n");
+ return ret;
+ }
+ *input_capacity = block_size;
+ }
+
+ rdret = read(file->fd, (void *)input->src, block_size);
+ if (rdret < 0) {
+ ret = -errno;
+ ERR(file->ctx, "zstd: %m\n");
+ return ret;
+ }
+
+ input->pos = 0;
+ input->size = rdret;
+ return 0;
+}
+
+static int zstd_ensure_outbuffer_space(ZSTD_outBuffer *buffer, size_t min_free)
+{
+ uint8_t *old_buffer = buffer->dst;
+ int ret = 0;
+
+ if (buffer->size - buffer->pos >= min_free)
+ return 0;
+
+ buffer->size += min_free;
+ buffer->dst = realloc(buffer->dst, buffer->size);
+ if (buffer->dst == NULL) {
+ ret = -errno;
+ free(old_buffer);
+ }
+
+ return ret;
+}
+
+static int zstd_decompress_block(struct kmod_file *file, ZSTD_DStream *dstr,
+ ZSTD_inBuffer *input, ZSTD_outBuffer *output,
+ size_t *next_block_size)
+{
+ size_t out_buf_min_size = ZSTD_DStreamOutSize();
+ int ret = 0;
+
+ do {
+ ssize_t dsret;
+
+ ret = zstd_ensure_outbuffer_space(output, out_buf_min_size);
+ if (ret) {
+ ERR(file->ctx, "zstd: %s\n", strerror(-ret));
+ break;
+ }
+
+ dsret = ZSTD_decompressStream(dstr, output, input);
+ if (ZSTD_isError(dsret)) {
+ ret = -EINVAL;
+ ERR(file->ctx, "zstd: %s\n", ZSTD_getErrorName(dsret));
+ break;
+ }
+ if (dsret > 0)
+ *next_block_size = (size_t)dsret;
+ } while (input->pos < input->size
+ || output->pos > output->size
+ || output->size - output->pos < out_buf_min_size);
+
+ return ret;
+}
+
+static int load_zstd(struct kmod_file *file)
+{
+ ZSTD_DStream *dstr;
+ size_t next_block_size;
+ size_t zst_inb_capacity = 0;
+ ZSTD_inBuffer zst_inb = { 0 };
+ ZSTD_outBuffer zst_outb = { 0 };
+ int ret;
+
+ dstr = ZSTD_createDStream();
+ if (dstr == NULL) {
+ ret = -EINVAL;
+ ERR(file->ctx, "zstd: Failed to create decompression stream\n");
+ goto out;
+ }
+
+ next_block_size = ZSTD_initDStream(dstr);
+
+ while (true) {
+ ret = zstd_read_block(file, next_block_size, &zst_inb,
+ &zst_inb_capacity);
+ if (ret != 0)
+ goto out;
+ if (zst_inb.size == 0) /* EOF */
+ break;
+
+ ret = zstd_decompress_block(file, dstr, &zst_inb, &zst_outb,
+ &next_block_size);
+ if (ret != 0)
+ goto out;
+ }
+
+ ZSTD_freeDStream(dstr);
+ free((void *)zst_inb.src);
+ file->zstd_used = true;
+ file->memory = zst_outb.dst;
+ file->size = zst_outb.pos;
+ return 0;
+out:
+ if (dstr != NULL)
+ ZSTD_freeDStream(dstr);
+ free((void *)zst_inb.src);
+ free((void *)zst_outb.dst);
+ return ret;
+}
+
+static void unload_zstd(struct kmod_file *file)
+{
+ if (!file->zstd_used)
+ return;
+ free(file->memory);
+}
+
+static const char magic_zstd[] = {0x28, 0xB5, 0x2F, 0xFD};
+#endif
+
+#ifdef ENABLE_XZ
+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 = 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->xz_used = true;
+ file->memory = p;
+ file->size = total;
+ return 0;
+ out:
+ free(p);
+ return ret;
+}
+
+static int load_xz(struct kmod_file *file)
+{
+ lzma_stream strm = LZMA_STREAM_INIT;
+ lzma_ret lzret;
+ int ret;
+
+ lzret = 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);
+ lzma_end(&strm);
+ return ret;
+}
+
+static void unload_xz(struct kmod_file *file)
+{
+ if (!file->xz_used)
+ return;
+ free(file->memory);
+}
+
+static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
+#endif
+
+#ifdef ENABLE_ZLIB
+#define READ_STEP (4 * 1024 * 1024)
+static int load_zlib(struct kmod_file *file)
+{
+ int err = 0;
+ off_t did = 0, total = 0;
+ _cleanup_free_ unsigned char *p = NULL;
+
+ errno = 0;
+ file->gzf = gzdopen(file->fd, "rb");
+ if (file->gzf == NULL)
+ return -errno;
+ file->fd = -1; /* now owned by gzf due gzdopen() */
+
+ for (;;) {
+ int r;
+
+ if (did == total) {
+ void *tmp = realloc(p, total + READ_STEP);
+ if (tmp == NULL) {
+ err = -errno;
+ goto error;
+ }
+ total += READ_STEP;
+ p = tmp;
+ }
+
+ r = gzread(file->gzf, p + did, total - did);
+ if (r == 0)
+ break;
+ else if (r < 0) {
+ int gzerr;
+ const char *gz_errmsg = gzerror(file->gzf, &gzerr);
+
+ ERR(file->ctx, "gzip: %s\n", gz_errmsg);
+
+ /* gzip might not set errno here */
+ err = gzerr == Z_ERRNO ? -errno : -EINVAL;
+ goto error;
+ }
+ did += r;
+ }
+
+ file->memory = p;
+ file->size = did;
+ p = NULL;
+ return 0;
+
+error:
+ gzclose(file->gzf);
+ return err;
+}
+
+static void unload_zlib(struct kmod_file *file)
+{
+ if (file->gzf == NULL)
+ return;
+ free(file->memory);
+ gzclose(file->gzf); /* closes file->fd */
+}
+
+static const char magic_zlib[] = {0x1f, 0x8b};
+#endif
+
+static const struct comp_type {
+ size_t magic_size;
+ enum kmod_file_compression_type compression;
+ const char *magic_bytes;
+ const struct file_ops ops;
+} comp_types[] = {
+#ifdef ENABLE_ZSTD
+ {sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, {load_zstd, unload_zstd}},
+#endif
+#ifdef ENABLE_XZ
+ {sizeof(magic_xz), KMOD_FILE_COMPRESSION_XZ, magic_xz, {load_xz, unload_xz}},
+#endif
+#ifdef ENABLE_ZLIB
+ {sizeof(magic_zlib), KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, {load_zlib, unload_zlib}},
+#endif
+ {0, KMOD_FILE_COMPRESSION_NONE, NULL, {NULL, NULL}}
+};
+
+static int load_reg(struct kmod_file *file)
+{
+ struct stat st;
+
+ if (fstat(file->fd, &st) < 0)
+ return -errno;
+
+ file->size = st.st_size;
+ file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE,
+ file->fd, 0);
+ if (file->memory == MAP_FAILED)
+ return -errno;
+
+ return 0;
+}
+
+static void unload_reg(struct kmod_file *file)
+{
+ munmap(file->memory, file->size);
+}
+
+static const struct file_ops reg_ops = {
+ load_reg, unload_reg
+};
+
+struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
+{
+ if (file->elf)
+ return file->elf;
+
+ kmod_file_load_contents(file);
+ 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 = calloc(1, sizeof(struct kmod_file));
+ const struct comp_type *itr;
+ size_t magic_size_max = 0;
+ int err = 0;
+
+ if (file == NULL)
+ return NULL;
+
+ file->fd = open(filename, O_RDONLY|O_CLOEXEC);
+ if (file->fd < 0) {
+ err = -errno;
+ goto error;
+ }
+
+ for (itr = comp_types; itr->ops.load != NULL; itr++) {
+ if (magic_size_max < itr->magic_size)
+ magic_size_max = itr->magic_size;
+ }
+
+ if (magic_size_max > 0) {
+ char *buf = alloca(magic_size_max + 1);
+ ssize_t sz;
+
+ if (buf == NULL) {
+ err = -errno;
+ goto error;
+ }
+ sz = read_str_safe(file->fd, buf, magic_size_max + 1);
+ lseek(file->fd, 0, SEEK_SET);
+ if (sz != (ssize_t)magic_size_max) {
+ if (sz < 0)
+ err = sz;
+ else
+ err = -EINVAL;
+ goto error;
+ }
+
+ for (itr = comp_types; itr->ops.load != NULL; itr++) {
+ if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) {
+ file->ops = &itr->ops;
+ file->compression = itr->compression;
+ break;
+ }
+ }
+ }
+
+ if (file->ops == NULL) {
+ file->ops = &reg_ops;
+ file->compression = KMOD_FILE_COMPRESSION_NONE;
+ }
+
+ file->ctx = ctx;
+
+error:
+ if (err < 0) {
+ if (file->fd >= 0)
+ close(file->fd);
+ free(file);
+ errno = -err;
+ return NULL;
+ }
+
+ return file;
+}
+
+/*
+ * Callers should just check file->memory got updated
+ */
+void kmod_file_load_contents(struct kmod_file *file)
+{
+ if (file->memory)
+ return;
+
+ /* The load functions already log possible errors. */
+ file->ops->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->memory)
+ file->ops->unload(file);
+
+ if (file->fd >= 0)
+ close(file->fd);
+ free(file);
+}
diff --git a/libkmod/libkmod-index.c b/libkmod/libkmod-index.c
new file mode 100644
index 0000000..6a34c8d
--- /dev/null
+++ b/libkmod/libkmod-index.c
@@ -0,0 +1,1082 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/macro.h>
+#include <shared/strbuf.h>
+#include <shared/util.h>
+
+#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". Second one used on newer versioned binary files.
+ * #define INDEX_MAGIC_OLD 0xB007FA57
+ *
+ * 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 128
+
+/* 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
+ *
+ * char first;
+ * char 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 it's 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 Patrica 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 Patrica trie works on individiual 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 */
+};
+
+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, unsigned len, unsigned int 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 void read_error(void)
+{
+ fatal("Module index: unexpected error: %s\n"
+ "Try re-running depmod\n", errno ? strerror(errno) : "EOF");
+}
+
+static int read_char(FILE *in)
+{
+ int ch;
+
+ errno = 0;
+ ch = getc_unlocked(in);
+ if (ch == EOF)
+ read_error();
+ return ch;
+}
+
+static uint32_t read_long(FILE *in)
+{
+ uint32_t l;
+
+ errno = 0;
+ if (fread(&l, sizeof(uint32_t), 1, in) != sizeof(uint32_t))
+ read_error();
+ return ntohl(l);
+}
+
+static unsigned buf_freadchars(struct strbuf *buf, FILE *in)
+{
+ unsigned i = 0;
+ int ch;
+
+ while ((ch = read_char(in))) {
+ if (!strbuf_pushchar(buf, ch))
+ break;
+ i++;
+ }
+
+ return i;
+}
+
+/*
+ * Index file searching
+ */
+struct index_node_f {
+ FILE *file;
+ char *prefix; /* path compression */
+ struct index_value *values;
+ unsigned char first; /* range of child nodes */
+ unsigned char last;
+ uint32_t children[0];
+};
+
+static struct index_node_f *index_read(FILE *in, uint32_t offset)
+{
+ struct index_node_f *node;
+ char *prefix;
+ int i, child_count = 0;
+
+ if ((offset & INDEX_NODE_MASK) == 0)
+ return NULL;
+
+ if (fseek(in, offset & INDEX_NODE_MASK, SEEK_SET) < 0)
+ return NULL;
+
+ if (offset & INDEX_NODE_PREFIX) {
+ struct strbuf buf;
+ strbuf_init(&buf);
+ buf_freadchars(&buf, in);
+ prefix = strbuf_steal(&buf);
+ } else
+ prefix = NOFAIL(strdup(""));
+
+ if (offset & INDEX_NODE_CHILDS) {
+ char first = read_char(in);
+ char last = read_char(in);
+ child_count = last - first + 1;
+
+ node = NOFAIL(malloc(sizeof(struct index_node_f) +
+ sizeof(uint32_t) * child_count));
+
+ node->first = first;
+ node->last = last;
+
+ for (i = 0; i < child_count; i++)
+ node->children[i] = read_long(in);
+ } else {
+ node = NOFAIL(malloc(sizeof(struct index_node_f)));
+ node->first = INDEX_CHILDMAX;
+ node->last = 0;
+ }
+
+ node->values = NULL;
+ if (offset & INDEX_NODE_VALUES) {
+ int value_count;
+ struct strbuf buf;
+ const char *value;
+ unsigned int priority;
+
+ value_count = read_long(in);
+
+ strbuf_init(&buf);
+ while (value_count--) {
+ priority = read_long(in);
+ buf_freadchars(&buf, in);
+ value = strbuf_str(&buf);
+ add_value(&node->values, value, buf.used, priority);
+ strbuf_clear(&buf);
+ }
+ strbuf_release(&buf);
+ }
+
+ node->prefix = prefix;
+ node->file = in;
+ return node;
+}
+
+static void index_close(struct index_node_f *node)
+{
+ free(node->prefix);
+ index_values_free(node->values);
+ free(node);
+}
+
+struct index_file {
+ FILE *file;
+ uint32_t root_offset;
+};
+
+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;
+ errno = EINVAL;
+
+ magic = read_long(file);
+ if (magic != INDEX_MAGIC) {
+ fclose(file);
+ return NULL;
+ }
+
+ version = read_long(file);
+ if (version >> 16 != INDEX_VERSION_MAJOR) {
+ fclose(file);
+ return NULL;
+ }
+
+ new = NOFAIL(malloc(sizeof(struct index_file)));
+ new->file = file;
+ new->root_offset = read_long(new->file);
+
+ errno = 0;
+ return new;
+}
+
+void index_file_close(struct index_file *idx)
+{
+ fclose(idx->file);
+ free(idx);
+}
+
+static struct index_node_f *index_readroot(struct index_file *in)
+{
+ return index_read(in->file, in->root_offset);
+}
+
+static struct index_node_f *index_readchild(const struct index_node_f *parent,
+ int ch)
+{
+ if (parent->first <= ch && ch <= parent->last) {
+ return index_read(parent->file,
+ parent->children[ch - parent->first]);
+ }
+
+ return NULL;
+}
+
+static void index_dump_node(struct index_node_f *node, struct strbuf *buf,
+ int fd)
+{
+ struct index_value *v;
+ int ch, pushed;
+
+ pushed = strbuf_pushchars(buf, node->prefix);
+
+ for (v = node->values; v != NULL; v = v->next) {
+ write_str_safe(fd, buf->bytes, buf->used);
+ write_str_safe(fd, " ", 1);
+ write_str_safe(fd, v->value, strlen(v->value));
+ write_str_safe(fd, "\n", 1);
+ }
+
+ for (ch = node->first; ch <= node->last; ch++) {
+ struct index_node_f *child = index_readchild(node, ch);
+
+ if (!child)
+ continue;
+
+ strbuf_pushchar(buf, ch);
+ index_dump_node(child, buf, fd);
+ strbuf_popchar(buf);
+ }
+
+ strbuf_popchars(buf, pushed);
+ index_close(node);
+}
+
+void index_dump(struct index_file *in, int fd, const char *prefix)
+{
+ struct index_node_f *root;
+ struct strbuf buf;
+
+ root = index_readroot(in);
+ if (root == NULL)
+ return;
+
+ strbuf_init(&buf);
+ strbuf_pushchars(&buf, prefix);
+ index_dump_node(root, &buf, fd);
+ 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 ch;
+ int j;
+
+ while(node) {
+ for (j = 0; node->prefix[j]; j++) {
+ 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)
+{
+ int pushed = 0;
+ int ch;
+
+ while (node->prefix[j]) {
+ ch = node->prefix[j];
+
+ strbuf_pushchar(buf, ch);
+ pushed++;
+ j++;
+ }
+
+ for (ch = node->first; ch <= node->last; ch++) {
+ struct index_node_f *child = index_readchild(node, ch);
+
+ if (!child)
+ continue;
+
+ strbuf_pushchar(buf, ch);
+ index_searchwild__all(child, 0, buf, subkey, out);
+ strbuf_popchar(buf);
+ }
+
+ if (node->values) {
+ if (fnmatch(strbuf_str(buf), 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, int i,
+ struct index_value **out)
+{
+ struct index_node_f *child;
+ int j;
+ int ch;
+
+ while(node) {
+ for (j = 0; node->prefix[j]; j++) {
+ ch = node->prefix[j];
+
+ if (ch == '*' || ch == '?' || ch == '[') {
+ index_searchwild__all(node, j, buf,
+ &key[i+j], out);
+ return;
+ }
+
+ if (ch != key[i+j]) {
+ index_close(node);
+ return;
+ }
+ }
+
+ i += j;
+
+ child = index_readchild(node, '*');
+ if (child) {
+ strbuf_pushchar(buf, '*');
+ index_searchwild__all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ child = index_readchild(node, '?');
+ if (child) {
+ strbuf_pushchar(buf, '?');
+ index_searchwild__all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ child = index_readchild(node, '[');
+ if (child) {
+ strbuf_pushchar(buf, '[');
+ index_searchwild__all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ if (key[i] == '\0') {
+ index_searchwild__allvalues(node, out);
+
+ return;
+ }
+
+ child = index_readchild(node, key[i]);
+ index_close(node);
+ node = child;
+ i++;
+ }
+}
+
+/*
+ * 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, 0, &out);
+ strbuf_release(&buf);
+ return out;
+}
+
+/**************************************************************************/
+/*
+ * Alternative implementation, using mmap to map all the file to memory when
+ * starting
+ */
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static const char _idx_empty_str[] = "";
+
+struct index_mm {
+ const struct kmod_ctx *ctx;
+ void *mm;
+ uint32_t root_offset;
+ size_t size;
+};
+
+struct index_mm_value {
+ unsigned int priority;
+ unsigned int len;
+ const char *value;
+};
+
+struct index_mm_value_array {
+ struct index_mm_value *values;
+ unsigned int len;
+};
+
+struct index_mm_node {
+ struct index_mm *idx;
+ const char *prefix; /* mmape'd value */
+ struct index_mm_value_array values;
+ unsigned char first;
+ unsigned char last;
+ uint32_t children[];
+};
+
+static inline uint32_t read_long_mm(void **p)
+{
+ uint8_t *addr = *(uint8_t **)p;
+ uint32_t v;
+
+ /* addr may be unalined to uint32_t */
+ v = get_unaligned((uint32_t *) addr);
+
+ *p = addr + sizeof(uint32_t);
+ return ntohl(v);
+}
+
+static inline uint8_t read_char_mm(void **p)
+{
+ uint8_t *addr = *(uint8_t **)p;
+ uint8_t v = *addr;
+ *p = addr + sizeof(uint8_t);
+ return v;
+}
+
+static inline char *read_chars_mm(void **p, unsigned *rlen)
+{
+ char *addr = *(char **)p;
+ size_t len = *rlen = strlen(addr);
+ *p = addr + len + 1;
+ return addr;
+}
+
+static struct index_mm_node *index_mm_read_node(struct index_mm *idx,
+ uint32_t offset) {
+ void *p = idx->mm;
+ struct index_mm_node *node;
+ const char *prefix;
+ int i, child_count, value_count, children_padding;
+ uint32_t children[INDEX_CHILDMAX];
+ char first, last;
+
+ if ((offset & INDEX_NODE_MASK) == 0)
+ return NULL;
+
+ p = (char *)p + (offset & INDEX_NODE_MASK);
+
+ if (offset & INDEX_NODE_PREFIX) {
+ unsigned len;
+ prefix = read_chars_mm(&p, &len);
+ } else
+ prefix = _idx_empty_str;
+
+ if (offset & INDEX_NODE_CHILDS) {
+ first = read_char_mm(&p);
+ last = read_char_mm(&p);
+ child_count = last - first + 1;
+ for (i = 0; i < child_count; i++)
+ children[i] = read_long_mm(&p);
+ } else {
+ first = INDEX_CHILDMAX;
+ last = 0;
+ child_count = 0;
+ }
+
+ children_padding = (sizeof(struct index_mm_node) +
+ (sizeof(uint32_t) * child_count)) % sizeof(void *);
+
+ if (offset & INDEX_NODE_VALUES)
+ value_count = read_long_mm(&p);
+ else
+ value_count = 0;
+
+ node = malloc(sizeof(struct index_mm_node)
+ + sizeof(uint32_t) * child_count + children_padding
+ + sizeof(struct index_mm_value) * value_count);
+ if (node == NULL)
+ return NULL;
+
+ node->idx = idx;
+ node->prefix = prefix;
+ if (value_count == 0)
+ node->values.values = NULL;
+ else {
+ node->values.values = (struct index_mm_value *)
+ ((char *)node + sizeof(struct index_mm_node) +
+ sizeof(uint32_t) * child_count + children_padding);
+ }
+ node->values.len = value_count;
+ node->first = first;
+ node->last = last;
+ memcpy(node->children, children, sizeof(uint32_t) * child_count);
+
+ for (i = 0; i < value_count; i++) {
+ struct index_mm_value *v = node->values.values + i;
+ v->priority = read_long_mm(&p);
+ v->value = read_chars_mm(&p, &v->len);
+ }
+
+ return node;
+}
+
+static void index_mm_free_node(struct index_mm_node *node)
+{
+ free(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;
+ 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 || (size_t) st.st_size < sizeof(hdr)) {
+ err = -EINVAL;
+ 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, %d, MAP_PRIVATE, 0): %m\n",
+ st.st_size, fd);
+ err = -errno;
+ goto fail_nommap;
+ }
+
+ p = idx->mm;
+ hdr.magic = read_long_mm(&p);
+ hdr.version = read_long_mm(&p);
+ hdr.root_offset = read_long_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(struct index_mm *idx)
+{
+ return index_mm_read_node(idx, idx->root_offset);
+}
+
+static struct index_mm_node *index_mm_readchild(const struct index_mm_node *parent,
+ int ch)
+{
+ if (parent->first <= ch && ch <= parent->last) {
+ return index_mm_read_node(parent->idx,
+ parent->children[ch - parent->first]);
+ }
+
+ return NULL;
+}
+
+static void index_mm_dump_node(struct index_mm_node *node, struct strbuf *buf,
+ int fd)
+{
+ struct index_mm_value *itr, *itr_end;
+ int ch, pushed;
+
+ pushed = strbuf_pushchars(buf, node->prefix);
+
+ itr = node->values.values;
+ itr_end = itr + node->values.len;
+ for (; itr < itr_end; itr++) {
+ write_str_safe(fd, buf->bytes, buf->used);
+ write_str_safe(fd, " ", 1);
+ write_str_safe(fd, itr->value, itr->len);
+ write_str_safe(fd, "\n", 1);
+ }
+
+ for (ch = node->first; ch <= node->last; ch++) {
+ struct index_mm_node *child = index_mm_readchild(node, ch);
+
+ if (child == NULL)
+ continue;
+
+ strbuf_pushchar(buf, ch);
+ index_mm_dump_node(child, buf, fd);
+ strbuf_popchar(buf);
+ }
+
+ strbuf_popchars(buf, pushed);
+ index_mm_free_node(node);
+}
+
+void index_mm_dump(struct index_mm *idx, int fd, const char *prefix)
+{
+ struct index_mm_node *root;
+ struct strbuf buf;
+
+ root = index_mm_readroot(idx);
+ if (root == NULL)
+ return;
+
+ strbuf_init(&buf);
+ strbuf_pushchars(&buf, prefix);
+ index_mm_dump_node(root, &buf, fd);
+ strbuf_release(&buf);
+}
+
+static char *index_mm_search_node(struct index_mm_node *node, const char *key,
+ int i)
+{
+ char *value;
+ struct index_mm_node *child;
+ int ch;
+ int j;
+
+ while(node) {
+ for (j = 0; node->prefix[j]; j++) {
+ ch = node->prefix[j];
+
+ if (ch != key[i+j]) {
+ index_mm_free_node(node);
+ return NULL;
+ }
+ }
+
+ i += j;
+
+ if (key[i] == '\0') {
+ value = node->values.len > 0
+ ? strdup(node->values.values[0].value)
+ : NULL;
+
+ index_mm_free_node(node);
+ return value;
+ }
+
+ child = index_mm_readchild(node, key[i]);
+ index_mm_free_node(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_mm_search(struct index_mm *idx, const char *key)
+{
+// FIXME: return value by reference instead of strdup
+ struct index_mm_node *root;
+ char *value;
+
+ root = index_mm_readroot(idx);
+ value = index_mm_search_node(root, key, 0);
+
+ 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)
+{
+ struct index_mm_value *itr, *itr_end;
+
+ itr = node->values.values;
+ itr_end = itr + node->values.len;
+ for (; itr < itr_end; itr++)
+ add_value(out, itr->value, itr->len, itr->priority);
+
+ index_mm_free_node(node);
+}
+
+/*
+ * 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)
+{
+ int pushed = 0;
+ int ch;
+
+ while (node->prefix[j]) {
+ ch = node->prefix[j];
+
+ strbuf_pushchar(buf, ch);
+ pushed++;
+ j++;
+ }
+
+ for (ch = node->first; ch <= node->last; ch++) {
+ struct index_mm_node *child = index_mm_readchild(node, ch);
+
+ if (!child)
+ continue;
+
+ strbuf_pushchar(buf, ch);
+ index_mm_searchwild_all(child, 0, buf, subkey, out);
+ strbuf_popchar(buf);
+ }
+
+ if (node->values.len > 0) {
+ if (fnmatch(strbuf_str(buf), subkey, 0) == 0)
+ index_mm_searchwild_allvalues(node, out);
+ else
+ index_mm_free_node(node);
+ } else {
+ index_mm_free_node(node);
+ }
+
+ 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, int i,
+ struct index_value **out)
+{
+ struct index_mm_node *child;
+ int j;
+ int ch;
+
+ while(node) {
+ for (j = 0; node->prefix[j]; j++) {
+ ch = node->prefix[j];
+
+ if (ch == '*' || ch == '?' || ch == '[') {
+ index_mm_searchwild_all(node, j, buf,
+ &key[i+j], out);
+ return;
+ }
+
+ if (ch != key[i+j]) {
+ index_mm_free_node(node);
+ return;
+ }
+ }
+
+ i += j;
+
+ child = index_mm_readchild(node, '*');
+ if (child) {
+ strbuf_pushchar(buf, '*');
+ index_mm_searchwild_all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ child = index_mm_readchild(node, '?');
+ if (child) {
+ strbuf_pushchar(buf, '?');
+ index_mm_searchwild_all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ child = index_mm_readchild(node, '[');
+ if (child) {
+ strbuf_pushchar(buf, '[');
+ index_mm_searchwild_all(child, 0, buf, &key[i], out);
+ strbuf_popchar(buf);
+ }
+
+ if (key[i] == '\0') {
+ index_mm_searchwild_allvalues(node, out);
+
+ return;
+ }
+
+ child = index_mm_readchild(node, key[i]);
+ index_mm_free_node(node);
+ node = child;
+ i++;
+ }
+}
+
+/*
+ * 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(struct index_mm *idx, const char *key)
+{
+ struct index_mm_node *root = index_mm_readroot(idx);
+ struct strbuf buf;
+ struct index_value *out = NULL;
+
+ strbuf_init(&buf);
+ index_mm_searchwild_node(root, &buf, key, 0, &out);
+ strbuf_release(&buf);
+ return out;
+}
diff --git a/libkmod/libkmod-index.h b/libkmod/libkmod-index.h
new file mode 100644
index 0000000..db671b0
--- /dev/null
+++ b/libkmod/libkmod-index.h
@@ -0,0 +1,48 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+struct index_value {
+ struct index_value *next;
+ unsigned int priority;
+ unsigned int 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, const char *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(struct index_mm *idx, const char *key);
+struct index_value *index_mm_searchwild(struct index_mm *idx, const char *key);
+void index_mm_dump(struct index_mm *idx, int fd, const char *prefix);
diff --git a/libkmod/libkmod-internal.h b/libkmod/libkmod-internal.h
new file mode 100644
index 0000000..26a7e28
--- /dev/null
+++ b/libkmod/libkmod-internal.h
@@ -0,0 +1,210 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <limits.h>
+
+#include <shared/macro.h>
+#include <shared/missing.h>
+
+#include "libkmod.h"
+
+static _always_inline_ _printf_format_(2, 3) void
+ kmod_log_null(const struct kmod_ctx *ctx, const char *format, ...) {}
+
+#define kmod_log_cond(ctx, prio, arg...) \
+ do { \
+ if (kmod_get_log_priority(ctx) >= prio) \
+ kmod_log(ctx, prio, __FILE__, __LINE__, __func__, ## arg);\
+ } while (0)
+
+#ifdef ENABLE_LOGGING
+# ifdef ENABLE_DEBUG
+# define DBG(ctx, arg...) kmod_log_cond(ctx, LOG_DEBUG, ## arg)
+# else
+# define DBG(ctx, arg...) kmod_log_null(ctx, ## arg)
+# endif
+# 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)
+#else
+# define DBG(ctx, arg...) kmod_log_null(ctx, ## arg)
+# define NOTICE(ctx, arg...) kmod_log_null(ctx, ## arg)
+# define INFO(ctx, arg...) kmod_log_null(ctx, ## arg)
+# define ERR(ctx, arg...) kmod_log_null(ctx, ## arg)
+#endif
+
+#define KMOD_EXPORT __attribute__ ((visibility("default")))
+
+#define KCMD_LINE_SIZE 4096
+
+#ifndef HAVE_SECURE_GETENV
+# ifdef HAVE___SECURE_GETENV
+# define secure_getenv __secure_getenv
+# else
+# warning neither secure_getenv nor __secure_getenv is available
+# define secure_getenv getenv
+# endif
+#endif
+
+void kmod_log(const struct kmod_ctx *ctx,
+ int priority, const char *file, int line, const char *fn,
+ const char *format, ...) __attribute__((format(printf, 6, 7))) __attribute__((nonnull(1, 3, 5)));
+
+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,
+};
+
+struct kmod_list *kmod_list_append(struct kmod_list *list, const void *data) _must_check_ __attribute__((nonnull(2)));
+struct kmod_list *kmod_list_prepend(struct kmod_list *list, const void *data) _must_check_ __attribute__((nonnull(2)));
+struct kmod_list *kmod_list_remove(struct kmod_list *list) _must_check_;
+struct kmod_list *kmod_list_remove_data(struct kmod_list *list,
+ const void *data) _must_check_ __attribute__((nonnull(2)));
+struct kmod_list *kmod_list_remove_n_latest(struct kmod_list *list,
+ unsigned int n) _must_check_;
+struct kmod_list *kmod_list_insert_after(struct kmod_list *list, const void *data) __attribute__((nonnull(2)));
+struct kmod_list *kmod_list_insert_before(struct kmod_list *list, const void *data) __attribute__((nonnull(2)));
+struct kmod_list *kmod_list_append_list(struct kmod_list *list1, struct kmod_list *list2) _must_check_;
+
+#undef kmod_list_foreach
+#define kmod_list_foreach(list_entry, first_entry) \
+ for (list_entry = ((first_entry) == NULL) ? NULL : (first_entry); \
+ list_entry != NULL; \
+ list_entry = (list_entry->node.next == &((first_entry)->node)) ? NULL : \
+ container_of(list_entry->node.next, struct kmod_list, node))
+
+#undef kmod_list_foreach_reverse
+#define kmod_list_foreach_reverse(list_entry, first_entry) \
+ for (list_entry = (((first_entry) == NULL) ? NULL : container_of(first_entry->node.prev, struct kmod_list, node)); \
+ list_entry != NULL; \
+ list_entry = ((list_entry == first_entry) ? NULL : \
+ container_of(list_entry->node.prev, struct kmod_list, node)))
+
+/* libkmod.c */
+int kmod_lookup_alias_from_config(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alias_from_symbols_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alias_from_aliases_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alias_from_kernel_builtin_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+int kmod_lookup_alias_from_builtin_file(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+bool kmod_lookup_alias_is_builtin(struct kmod_ctx *ctx, const char *name) __attribute__((nonnull(1, 2)));
+int kmod_lookup_alias_from_commands(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+void kmod_set_modules_visited(struct kmod_ctx *ctx, bool visited) __attribute__((nonnull((1))));
+void kmod_set_modules_required(struct kmod_ctx *ctx, bool required) __attribute__((nonnull((1))));
+
+char *kmod_search_moddep(struct kmod_ctx *ctx, const char *name) __attribute__((nonnull(1,2)));
+
+struct kmod_module *kmod_pool_get_module(struct kmod_ctx *ctx, const char *key) __attribute__((nonnull(1,2)));
+void kmod_pool_add_module(struct kmod_ctx *ctx, struct kmod_module *mod, const char *key) __attribute__((nonnull(1, 2, 3)));
+void kmod_pool_del_module(struct kmod_ctx *ctx, struct kmod_module *mod, const char *key) __attribute__((nonnull(1, 2, 3)));
+
+const struct kmod_config *kmod_get_config(const struct kmod_ctx *ctx) __attribute__((nonnull(1)));
+enum kmod_file_compression_type kmod_get_kernel_compression(const struct kmod_ctx *ctx) __attribute__((nonnull(1)));
+
+/* 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 *paths;
+};
+
+int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **config, const char * const *config_paths) __attribute__((nonnull(1, 2,3)));
+void kmod_config_free(struct kmod_config *config) __attribute__((nonnull(1)));
+const char *kmod_blacklist_get_modname(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_alias_get_name(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_alias_get_modname(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_option_get_options(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_option_get_modname(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_command_get_command(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char *kmod_command_get_modname(const struct kmod_list *l) __attribute__((nonnull(1)));
+
+const char *kmod_softdep_get_name(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) __attribute__((nonnull(1, 2)));
+const char * const *kmod_softdep_get_post(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);
+int kmod_module_parse_depline(struct kmod_module *mod, char *line) __attribute__((nonnull(1, 2)));
+void kmod_module_set_install_commands(struct kmod_module *mod, const char *cmd) __attribute__((nonnull(1)));
+void kmod_module_set_remove_commands(struct kmod_module *mod, const char *cmd) __attribute__((nonnull(1)));
+void kmod_module_set_visited(struct kmod_module *mod, bool visited) __attribute__((nonnull(1)));
+void kmod_module_set_builtin(struct kmod_module *mod, bool builtin) __attribute__((nonnull((1))));
+void kmod_module_set_required(struct kmod_module *mod, bool required) __attribute__((nonnull(1)));
+bool kmod_module_is_builtin(struct kmod_module *mod) __attribute__((nonnull(1)));
+
+/* libkmod-file.c */
+struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx, const char *filename) _must_check_ __attribute__((nonnull(1,2)));
+struct kmod_elf *kmod_file_get_elf(struct kmod_file *file) __attribute__((nonnull(1)));
+void kmod_file_load_contents(struct kmod_file *file) __attribute__((nonnull(1)));
+void *kmod_file_get_contents(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
+off_t kmod_file_get_size(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
+enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
+int kmod_file_get_fd(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
+void kmod_file_unref(struct kmod_file *file) __attribute__((nonnull(1)));
+
+/* libkmod-elf.c */
+struct kmod_elf;
+struct kmod_modversion {
+ uint64_t crc;
+ enum kmod_symbol_bind bind;
+ char *symbol;
+};
+
+struct kmod_elf *kmod_elf_new(const void *memory, off_t size) _must_check_;
+void kmod_elf_unref(struct kmod_elf *elf) __attribute__((nonnull(1)));
+const void *kmod_elf_get_memory(const struct kmod_elf *elf) _must_check_ __attribute__((nonnull(1)));
+int kmod_elf_get_strings(const struct kmod_elf *elf, const char *section, char ***array) _must_check_ __attribute__((nonnull(1,2,3)));
+int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array) _must_check_ __attribute__((nonnull(1,2)));
+int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array) _must_check_ __attribute__((nonnull(1,2)));
+int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf, struct kmod_modversion **array) _must_check_ __attribute__((nonnull(1,2)));
+int kmod_elf_strip_section(struct kmod_elf *elf, const char *section) _must_check_ __attribute__((nonnull(1,2)));
+int kmod_elf_strip_vermagic(struct kmod_elf *elf) _must_check_ __attribute__((nonnull(1)));
+
+/*
+ * Debug mock lib need to find section ".gnu.linkonce.this_module" in order to
+ * get modname
+ */
+int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, const void **buf, uint64_t *buf_size) _must_check_ __attribute__((nonnull(1,2,3,4)));
+
+/* 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;
+};
+bool kmod_module_signature_info(const struct kmod_file *file, struct kmod_signature_info *sig_info) _must_check_ __attribute__((nonnull(1, 2)));
+void kmod_module_signature_info_free(struct kmod_signature_info *sig_info) __attribute__((nonnull));
+
+/* libkmod-builtin.c */
+ssize_t kmod_builtin_get_modinfo(struct kmod_ctx *ctx, const char *modname, char ***modinfo) __attribute__((nonnull(1, 2, 3)));
diff --git a/libkmod/libkmod-list.c b/libkmod/libkmod-list.c
new file mode 100644
index 0000000..7623693
--- /dev/null
+++ b/libkmod/libkmod-list.c
@@ -0,0 +1,314 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+/**
+ * SECTION:libkmod-list
+ * @short_description: general purpose list
+ */
+
+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);
+}
+
+/*
+ * n must be greater to or equal the number of elements (we don't check the
+ * condition)
+ */
+struct kmod_list *kmod_list_remove_n_latest(struct kmod_list *list,
+ unsigned int n)
+{
+ struct kmod_list *l = list;
+ unsigned int i;
+
+ for (i = 0; i < n; i++) {
+ l = kmod_list_last(l);
+ l = kmod_list_remove(l);
+ }
+
+ return l;
+}
+
+/**
+ * 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.
+ */
+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_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.
+ */
+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_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.
+ */
+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..585da41
--- /dev/null
+++ b/libkmod/libkmod-module.c
@@ -0,0 +1,2986 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifdef HAVE_LINUX_MODULE_H
+#include <linux/module.h>
+#endif
+
+#include <shared/util.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+/**
+ * SECTION:libkmod-module
+ * @short_description: operate on kernel modules
+ */
+
+enum kmod_module_builtin {
+ KMOD_MODULE_BUILTIN_UNKNOWN,
+ KMOD_MODULE_BUILTIN_NO,
+ KMOD_MODULE_BUILTIN_YES,
+};
+
+/**
+ * kmod_module:
+ *
+ * Opaque object representing a module.
+ */
+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 n_dep;
+ 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;
+}
+
+int 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;
+ int err = 0, n = 0;
+ size_t dirnamelen;
+
+ if (mod->init.dep)
+ return mod->n_dep;
+ assert(mod->dep == NULL);
+ mod->init.dep = true;
+
+ p = strchr(line, ':');
+ if (p == NULL)
+ return 0;
+
+ *p = '\0';
+ dirname = kmod_get_dirname(mod->ctx);
+ dirnamelen = strlen(dirname);
+ if (dirnamelen + 2 >= PATH_MAX)
+ return 0;
+
+ 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 0;
+ mod->path = strdup(str);
+ if (mod->path == NULL)
+ return 0;
+ }
+
+ p++;
+ for (p = strtok_r(p, " \t", &saveptr); p != NULL;
+ p = strtok_r(NULL, " \t", &saveptr)) {
+ struct kmod_module *depmod = NULL;
+ const char *path;
+
+ 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);
+
+ list = kmod_list_prepend(list, depmod);
+ n++;
+ }
+
+ DBG(ctx, "%d dependencies for %s\n", n, mod->name);
+
+ mod->dep = list;
+ mod->n_dep = n;
+ return n;
+
+fail:
+ kmod_module_unref_list(list);
+ mod->init.dep = false;
+ return err;
+}
+
+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;
+
+ 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;
+ kmod_pool_add_module(ctx, m, m->hashkey);
+ *mod = m;
+
+ return 0;
+}
+
+/**
+ * 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.
+ */
+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)
+{
+ int err;
+ 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] = '\\';
+
+ err = kmod_module_new(ctx, key, name, namelen, alias, aliaslen, mod);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/**
+ * 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 with in 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.
+ */
+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;
+ }
+
+ m = kmod_pool_get_module(ctx, name);
+ if (m != NULL) {
+ if (m->path == NULL)
+ m->path = abspath;
+ else if (streq(m->path, abspath))
+ free(abspath);
+ else {
+ 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;
+ }
+
+ kmod_module_ref(m);
+ } else {
+ err = kmod_module_new(ctx, name, name, namelen, NULL, 0, &m);
+ if (err < 0) {
+ free(abspath);
+ return err;
+ }
+
+ m->path = abspath;
+ }
+
+ m->builtin = KMOD_MODULE_BUILTIN_NO;
+ *mod = m;
+
+ return 0;
+}
+
+/**
+ * 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.
+ */
+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_module_ref:
+ * @mod: kmod module
+ *
+ * Take a reference of the kmod module, incrementing its refcount.
+ *
+ * Returns: the passed @module with its refcount incremented.
+ */
+KMOD_EXPORT struct kmod_module *kmod_module_ref(struct kmod_module *mod)
+{
+ if (mod == NULL)
+ return NULL;
+
+ mod->refcount++;
+
+ return mod;
+}
+
+typedef int (*lookup_func)(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3)));
+
+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_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.
+ */
+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_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 succesful, 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.
+ */
+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_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
+ */
+KMOD_EXPORT int kmod_module_unref_list(struct kmod_list *list)
+{
+ for (; list != NULL; list = kmod_list_remove(list))
+ kmod_module_unref(list->data);
+
+ return 0;
+}
+
+/**
+ * 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.
+ */
+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 const struct kmod_list *module_get_dependencies_noref(const struct kmod_module *mod)
+{
+ if (!mod->init.dep) {
+ /* lazy init */
+ char *line = kmod_search_moddep(mod->ctx, mod->name);
+
+ if (line == NULL)
+ return NULL;
+
+ kmod_module_parse_depline((struct kmod_module *)mod, line);
+ free(line);
+
+ if (!mod->init.dep)
+ return NULL;
+ }
+
+ return mod->dep;
+}
+
+/**
+ * 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().
+ */
+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(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_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.
+ */
+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_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.
+ */
+KMOD_EXPORT const char *kmod_module_get_name(const struct kmod_module *mod)
+{
+ if (mod == NULL)
+ return NULL;
+
+ return mod->name;
+}
+
+/**
+ * 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.
+ */
+KMOD_EXPORT const char *kmod_module_get_path(const struct kmod_module *mod)
+{
+ char *line;
+
+ 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 */
+ line = kmod_search_moddep(mod->ctx, mod->name);
+ if (line == NULL)
+ return NULL;
+
+ kmod_module_parse_depline((struct kmod_module *) mod, line);
+ free(line);
+
+ return mod->path;
+}
+
+
+extern long delete_module(const char *name, unsigned int flags);
+
+/**
+ * kmod_module_remove_module:
+ * @mod: kmod module
+ * @flags: flags used when removing the module.
+ * KMOD_REMOVE_FORCE: force remove module regardless if it's still in
+ * use by a kernel subsystem or other process; passed directly to Linux kernel
+ * KMOD_REMOVE_NOWAIT: is always enforced, causing us to pass O_NONBLOCK to
+ * delete_module(2).
+ * KMOD_REMOVE_NOLOG: when module removal fails, do not log anything as the
+ * caller may want to handle retries and log when appropriate.
+ *
+ * Remove a module from Linux kernel.
+ *
+ * Returns: 0 on success or < 0 on failure.
+ */
+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, re-use 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)
+{
+ struct kmod_elf *elf;
+ const void *mem;
+ off_t size;
+ int err;
+
+ kmod_file_load_contents(mod->file);
+
+ if (flags & (KMOD_INSERT_FORCE_VERMAGIC | KMOD_INSERT_FORCE_MODVERSION)) {
+ elf = kmod_file_get_elf(mod->file);
+ if (elf == NULL) {
+ err = -errno;
+ return err;
+ }
+
+ if (flags & KMOD_INSERT_FORCE_MODVERSION) {
+ err = kmod_elf_strip_section(elf, "__versions");
+ if (err < 0)
+ INFO(mod->ctx, "Failed to strip modversion: %s\n", strerror(-err));
+ }
+
+ if (flags & KMOD_INSERT_FORCE_VERMAGIC) {
+ err = kmod_elf_strip_vermagic(elf);
+ if (err < 0)
+ INFO(mod->ctx, "Failed to strip vermagic: %s\n", strerror(-err));
+ }
+
+ mem = kmod_elf_get_memory(elf);
+ } else {
+ 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_module_insert_module:
+ * @mod: kmod module
+ * @flags: flags are not passed to Linux Kernel, but instead they dictate the
+ * behavior of this function, valid flags are
+ * KMOD_INSERT_FORCE_VERMAGIC: ignore kernel version magic;
+ * KMOD_INSERT_FORCE_MODVERSION: ignore symbol version hashes.
+ * @options: module's options to pass to Linux Kernel.
+ *
+ * Insert a module in Linux 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.
+ */
+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(struct kmod_module *mod)
+{
+ 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_module_apply_filter
+ * @ctx: kmod library context
+ * @filter_type: bitmask to filter modules out, valid types are
+ * KMOD_FILTER_BLACKLIST: filter modules in blacklist out;
+ * KMOD_FILTER_BUILTIN: filter builtin modules out.
+ * @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.
+ */
+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;
+ 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_module_probe_insert_module:
+ * @mod: kmod module
+ * @flags: flags are not passed to Linux Kernel, but instead they dictate the
+ * behavior of this function, valid flags are
+ * KMOD_PROBE_FORCE_VERMAGIC: ignore kernel version magic;
+ * KMOD_PROBE_FORCE_MODVERSION: ignore symbol version hashes;
+ * KMOD_PROBE_IGNORE_COMMAND: whether the probe should ignore install
+ * commands and softdeps configured in the system;
+ * KMOD_PROBE_IGNORE_LOADED: do not check whether the module is already
+ * live in kernel or not;
+ * KMOD_PROBE_DRY_RUN: dry run, do not insert module, just call the
+ * associated callback function;
+ * KMOD_PROBE_FAIL_ON_LOADED: if KMOD_PROBE_IGNORE_LOADED is not specified
+ * and the module is already live in kernel, the function will fail if this
+ * flag is specified;
+ * KMOD_PROBE_APPLY_BLACKLIST_ALL: probe will apply KMOD_FILTER_BLACKLIST
+ * filter to this module and its dependencies. If any of the dependencies (or
+ * the module) is blacklisted, the probe will fail, unless the blacklisted
+ * module is already live in kernel;
+ * KMOD_PROBE_APPLY_BLACKLIST: probe will fail if the module is blacklisted;
+ * KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY: probe will fail if the module is an
+ * alias and is blacklisted.
+ * @extra_options: module's options to pass to Linux 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 Linux 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.
+ */
+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;
+ }
+
+ /*
+ * Ugly assignement + check. We need to check if we were told to check
+ * blacklist and also return the reason why we failed.
+ * KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY will take effect only if the
+ * module is an alias, so we also need to check it
+ */
+ if ((mod->alias != NULL && ((err = flags & KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY)))
+ || (err = flags & KMOD_PROBE_APPLY_BLACKLIST_ALL)
+ || (err = flags & KMOD_PROBE_APPLY_BLACKLIST)) {
+ if (module_is_blacklisted(mod))
+ return err;
+ }
+
+ 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_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.
+ */
+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_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.
+ */
+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_softdep(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 soft dependency '%s', continuing anyway.\n", depname);
+ continue;
+ } else if (lst != NULL)
+ ret = kmod_list_append_list(ret, lst);
+ }
+ return ret;
+}
+
+/**
+ * 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.
+ */
+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_softdep(mod->ctx, array, count);
+ array = kmod_softdep_get_post(l, &count);
+ *post = lookup_softdep(mod->ctx, array, count);
+
+ /*
+ * find only the first command, as modprobe from
+ * module-init-tools does
+ */
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * 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.
+ */
+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;
+}
+
+/**
+ * SECTION:libkmod-loaded
+ * @short_description: currently loaded modules
+ *
+ * Information about currently loaded modules, as reported by Linux 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.
+ */
+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 (line[len - 1] != '\n' && fgets(line, sizeof(line), fp))
+ len = strlen(line);
+ }
+
+ fclose(fp);
+ *list = l;
+
+ return 0;
+}
+
+/**
+ * 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.
+ */
+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_module_get_initstate:
+ * @mod: kmod module
+ *
+ * Get the initstate of this @mod, as returned by Linux Kernel, by reading
+ * /sys filesystem.
+ *
+ * Returns: < 0 on error or module state if module is found in kernel, valid states are
+ * KMOD_MODULE_BUILTIN: module is builtin;
+ * KMOD_MODULE_LIVE: module is live in kernel;
+ * KMOD_MODULE_COMING: module is being loaded;
+ * KMOD_MODULE_GOING: module is being unloaded.
+ */
+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_module_get_size:
+ * @mod: kmod module
+ *
+ * Get the size of this kmod module as returned by Linux 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.
+ */
+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 (line[len - 1] != '\n' && fgets(line, sizeof(line), fp))
+ len = strlen(line);
+ }
+ fclose(fp);
+
+done:
+ close(dfd);
+ return size;
+}
+
+/**
+ * kmod_module_get_refcnt:
+ * @mod: kmod module
+ *
+ * Get the ref count of this @mod, as returned by Linux Kernel, by reading
+ * /sys filesystem.
+ *
+ * Returns: the reference count on success or < 0 on failure.
+ */
+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_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.
+ */
+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_module_get_sections:
+ * @mod: kmod module
+ *
+ * Get a list of kmod sections of this @mod, as returned by Linux Kernel. The
+ * structure contained in this list is internal to libkmod and their 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.
+ */
+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_module_section_get_module_name:
+ * @entry: a list entry representing a kmod module section
+ *
+ * Get the name of a kmod module section.
+ *
+ * After use, free the @list by calling kmod_module_section_free_list().
+ *
+ * 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.
+ */
+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_module_section_get_address:
+ * @entry: a list entry representing a kmod module section
+ *
+ * Get the address of a kmod module section.
+ *
+ * After use, free the @list by calling kmod_module_section_free_list().
+ *
+ * Returns: the address of this kmod module section on success or ULONG_MAX
+ * on failure.
+ */
+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_module_section_free_list:
+ * @list: kmod module section list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_section_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_section_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}
+
+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)
+{
+ char *str;
+ int i;
+ int j;
+ const size_t line_limit = 20;
+ size_t str_len;
+
+ str_len = len * 3; /* XX: or XX\0 */
+ str_len += ((str_len + line_limit - 1) / line_limit - 1) * 3; /* \n\t\t */
+
+ str = malloc(str_len);
+ if (str == NULL)
+ return NULL;
+
+ for (i = 0, j = 0; i < (int)len; i++) {
+ j += sprintf(str + j, "%02X", (unsigned char)hex[i]);
+ if (i < (int)len - 1) {
+ str[j++] = ':';
+
+ if ((i + 1) % line_limit == 0)
+ j += sprintf(str + j, "\n\t\t");
+ }
+ }
+ return str;
+}
+
+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_module_get_info:
+ * @mod: kmod module
+ * @list: where to return list of module information. Use
+ * kmod_module_info_get_key() and
+ * kmod_module_info_get_value(). Release this list with
+ * kmod_module_info_free_list()
+ *
+ * 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.
+ *
+ * After use, free the @list by calling kmod_module_info_free_list().
+ *
+ * Returns: number of entries in @list on success or < 0 otherwise.
+ */
+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_strings(elf, ".modinfo", &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_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.
+ */
+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_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.
+ */
+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_module_info_free_list:
+ * @list: kmod module info list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_info_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_info_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}
+
+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_module_get_versions:
+ * @mod: kmod module
+ * @list: where to return list of module versions. Use
+ * kmod_module_version_get_symbol() and
+ * kmod_module_version_get_crc(). Release this list with
+ * kmod_module_versions_free_list()
+ *
+ * Get a list of entries in ELF section "__versions".
+ *
+ * After use, free the @list by calling kmod_module_versions_free_list().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+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_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.
+ */
+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_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.
+ */
+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_module_versions_free_list:
+ * @list: kmod module versions list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_versions_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_version_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}
+
+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_module_get_symbols:
+ * @mod: kmod module
+ * @list: where to return list of module symbols. Use
+ * kmod_module_symbol_get_symbol() and
+ * kmod_module_symbol_get_crc(). Release this list with
+ * kmod_module_symbols_free_list()
+ *
+ * Get a list of entries in ELF section ".symtab" or "__ksymtab_strings".
+ *
+ * After use, free the @list by calling kmod_module_symbols_free_list().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+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_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.
+ */
+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_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.
+ */
+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_module_symbols_free_list:
+ * @list: kmod module symbols list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_symbols_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_symbol_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}
+
+struct kmod_module_dependency_symbol {
+ uint64_t crc;
+ uint8_t bind;
+ char symbol[];
+};
+
+static struct kmod_module_dependency_symbol *kmod_module_dependency_symbols_new(uint64_t crc, uint8_t bind, const char *symbol)
+{
+ 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_module_get_dependency_symbols:
+ * @mod: kmod module
+ * @list: where to return list of module dependency_symbols. Use
+ * kmod_module_dependency_symbol_get_symbol() and
+ * kmod_module_dependency_symbol_get_crc(). Release this list with
+ * kmod_module_dependency_symbols_free_list()
+ *
+ * Get a list of entries in ELF section ".symtab" or "__ksymtab_strings".
+ *
+ * After use, free the @list by calling
+ * kmod_module_dependency_symbols_free_list().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+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;
+}
+
+/**
+ * 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.
+ */
+KMOD_EXPORT const char *kmod_module_dependency_symbol_get_symbol(const struct kmod_list *entry)
+{
+ struct kmod_module_dependency_symbol *dependency_symbol;
+
+ if (entry == NULL || entry->data == NULL)
+ return NULL;
+
+ dependency_symbol = entry->data;
+ return dependency_symbol->symbol;
+}
+
+/**
+ * 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.
+ */
+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_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.
+ */
+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_module_dependency_symbols_free_list:
+ * @list: kmod module dependency_symbols list
+ *
+ * Release the resources taken by @list
+ */
+KMOD_EXPORT void kmod_module_dependency_symbols_free_list(struct kmod_list *list)
+{
+ while (list) {
+ kmod_module_dependency_symbol_free(list->data);
+ list = kmod_list_remove(list);
+ }
+}
diff --git a/libkmod/libkmod-signature.c b/libkmod/libkmod-signature.c
new file mode 100644
index 0000000..80f6447
--- /dev/null
+++ b/libkmod/libkmod-signature.c
@@ -0,0 +1,358 @@
+/*
+ * libkmod - module signature display
+ *
+ * Copyright (C) 2013 Michal Marek, SUSE
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <endian.h>
+#include <inttypes.h>
+#ifdef ENABLE_OPENSSL
+#include <openssl/pkcs7.h>
+#include <openssl/ssl.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/missing.h>
+#include <shared/util.h>
+
+#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] = {
+ [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",
+};
+
+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;
+}
+
+#ifdef 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;
+ const 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);
+ modsig = (struct module_signature *)(mem + size);
+ if (modsig->algo >= PKEY_ALGO__LAST ||
+ modsig->hash >= PKEY_HASH__LAST ||
+ modsig->id_type >= PKEY_ID_TYPE__LAST)
+ return false;
+ sig_len = be32toh(get_unaligned(&modsig->sig_len));
+ if (sig_len == 0 ||
+ size < (int64_t)(modsig->signer_len + modsig->key_id_len + sig_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..213b424
--- /dev/null
+++ b/libkmod/libkmod.c
@@ -0,0 +1,1024 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+#include <shared/hash.h>
+#include <shared/util.h>
+
+#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
+
+/**
+ * SECTION:libkmod
+ * @short_description: libkmod context
+ *
+ * The context contains the default values for the library user,
+ * and is passed to all library operations.
+ */
+
+static const struct {
+ const char *fn;
+ const char *prefix;
+} index_files[] = {
+ [KMOD_INDEX_MODULES_DEP] = { .fn = "modules.dep", .prefix = "" },
+ [KMOD_INDEX_MODULES_ALIAS] = { .fn = "modules.alias", .prefix = "alias " },
+ [KMOD_INDEX_MODULES_SYMBOL] = { .fn = "modules.symbols", .prefix = "alias "},
+ [KMOD_INDEX_MODULES_BUILTIN_ALIAS] = { .fn = "modules.builtin.alias", .prefix = "" },
+ [KMOD_INDEX_MODULES_BUILTIN] = { .fn = "modules.builtin", .prefix = ""},
+};
+
+static const char *const default_config_paths[] = {
+ SYSCONFDIR "/modprobe.d",
+ "/run/modprobe.d",
+ "/usr/local/lib/modprobe.d",
+ DISTCONFDIR "/modprobe.d",
+ "/lib/modprobe.d",
+ NULL
+};
+
+/**
+ * kmod_ctx:
+ *
+ * Opaque object representing the library context.
+ */
+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;
+#ifdef ENABLE_DEBUG
+ 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;
+ }
+ fprintf(fp, "libkmod: %s %s:%d %s: ", priname, file, line, fn);
+#else
+ fprintf(fp, "libkmod: %s: ", fn);
+#endif
+ vfprintf(fp, format, args);
+}
+
+
+/**
+ * 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().
+ */
+KMOD_EXPORT const char *kmod_get_dirname(const struct kmod_ctx *ctx)
+{
+ return ctx->dirname;
+}
+
+/**
+ * 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
+ */
+KMOD_EXPORT void *kmod_get_userdata(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;
+ return (void *)ctx->userdata;
+}
+
+/**
+ * kmod_set_userdata:
+ * @ctx: kmod library context
+ * @userdata: data pointer
+ *
+ * Store custom @userdata in the library context.
+ */
+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_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
+ */
+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);
+
+ /* 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_ref:
+ * @ctx: kmod library context
+ *
+ * Take a reference of the kmod library context.
+ *
+ * Returns: the passed kmod library context
+ */
+KMOD_EXPORT struct kmod_ctx *kmod_ref(struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;
+ ctx->refcount++;
+ return 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
+ */
+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_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.
+ */
+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_get_log_priority:
+ * @ctx: kmod library context
+ *
+ * Returns: the current logging priority
+ */
+KMOD_EXPORT int kmod_get_log_priority(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return -1;
+ return ctx->log_priority;
+}
+
+/**
+ * kmod_set_log_priority:
+ * @ctx: kmod library context
+ * @priority: the new logging priority
+ *
+ * Set the current logging priority. The value controls which messages
+ * are logged.
+ */
+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;
+}
+
+void kmod_pool_add_module(struct kmod_ctx *ctx, struct kmod_module *mod,
+ const char *key)
+{
+ DBG(ctx, "add %p key='%s'\n", mod, key);
+
+ 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;
+
+ if (ctx->indexes[index_number] != NULL) {
+ DBG(ctx, "use mmaped 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;
+
+ 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;
+ }
+
+ *list = kmod_list_append(*list, mod);
+ nmatch++;
+ }
+
+ index_values_free(realnames);
+ return nmatch;
+
+fail:
+ *list = kmod_list_remove_n_latest(*list, nmatch);
+ 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_builtin_file(struct kmod_ctx *ctx, const char *name)
+{
+ char *line;
+
+ if (ctx->indexes[KMOD_INDEX_MODULES_BUILTIN]) {
+ DBG(ctx, "use mmaped index '%s' modname=%s\n",
+ index_files[KMOD_INDEX_MODULES_BUILTIN].fn,
+ name);
+ line = index_mm_search(ctx->indexes[KMOD_INDEX_MODULES_BUILTIN],
+ name);
+ } else {
+ struct index_file *idx;
+ char fn[PATH_MAX];
+
+ snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname,
+ index_files[KMOD_INDEX_MODULES_BUILTIN].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;
+}
+
+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;
+
+ assert(*list == NULL);
+
+ 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)
+{
+ char *line;
+ int err = 0;
+
+ assert(*list == NULL);
+
+ line = lookup_builtin_file(ctx, name);
+ if (line != NULL) {
+ struct kmod_module *mod;
+
+ 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));
+ goto finish;
+ }
+
+ /* already mark it as builtin since it's being created from
+ * this index */
+ kmod_module_set_builtin(mod, true);
+ *list = kmod_list_append(*list, mod);
+ if (*list == NULL)
+ err = -ENOMEM;
+ }
+
+finish:
+ free(line);
+ return err;
+}
+
+bool kmod_lookup_alias_is_builtin(struct kmod_ctx *ctx, const char *name)
+{
+ _cleanup_free_ char *line;
+
+ line = lookup_builtin_file(ctx, name);
+
+ return line != NULL;
+}
+
+char *kmod_search_moddep(struct kmod_ctx *ctx, const char *name)
+{
+ struct index_file *idx;
+ char fn[PATH_MAX];
+ char *line;
+
+ if (ctx->indexes[KMOD_INDEX_MODULES_DEP]) {
+ DBG(ctx, "use mmaped index '%s' modname=%s\n",
+ index_files[KMOD_INDEX_MODULES_DEP].fn, name);
+ return index_mm_search(ctx->indexes[KMOD_INDEX_MODULES_DEP],
+ name);
+ }
+
+ snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname,
+ index_files[KMOD_INDEX_MODULES_DEP].fn);
+
+ DBG(ctx, "file=%s modname=%s\n", fn, name);
+
+ idx = index_file_open(fn);
+ if (idx == NULL) {
+ DBG(ctx, "could not open moddep file '%s'\n", fn);
+ return NULL;
+ }
+
+ line = index_search(idx, name);
+ index_file_close(idx);
+
+ return line;
+}
+
+int kmod_lookup_alias_from_moddep_file(struct kmod_ctx *ctx, const char *name,
+ struct kmod_list **list)
+{
+ char *line;
+ int n = 0;
+
+ /*
+ * 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;
+
+ 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;
+ }
+
+ *list = kmod_list_append(*list, mod);
+ 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;
+
+ 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;
+
+ 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;
+ }
+
+ *list = kmod_list_append(*list, mod);
+ nmatch++;
+ }
+ }
+
+ return nmatch;
+
+fail:
+ *list = kmod_list_remove_n_latest(*list, nmatch);
+ 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;
+
+ 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");
+ 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");
+ 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_validate_resources:
+ * @ctx: kmod library context
+ *
+ * Check if indexes and configuration files changed on disk and the current
+ * context is not valid anymore.
+ *
+ * Returns: KMOD_RESOURCES_OK if resources are still valid,
+ * KMOD_RESOURCES_MUST_RELOAD if it's sufficient to call
+ * kmod_unload_resources() and kmod_load_resources() or
+ * KMOD_RESOURCES_MUST_RECREATE if @ctx must be re-created.
+ */
+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_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 bootup issues hundreds of calls to lookup the index, calling
+ * this function will speedup the searches.
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+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_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.
+ */
+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_dump_index:
+ * @ctx: kmod library context
+ * @type: index to dump, valid indexes are
+ * 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: index of builtin module.
+ * @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.
+ */
+KMOD_EXPORT int kmod_dump_index(struct kmod_ctx *ctx, enum kmod_index type,
+ int fd)
+{
+ if (ctx == NULL)
+ return -ENOSYS;
+
+ if (type < 0 || type >= _KMOD_INDEX_MODULES_SIZE)
+ return -ENOENT;
+
+ if (ctx->indexes[type] != NULL) {
+ DBG(ctx, "use mmaped index '%s'\n", index_files[type].fn);
+ index_mm_dump(ctx->indexes[type], fd,
+ index_files[type].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].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..7251aa7
--- /dev/null
+++ b/libkmod/libkmod.h
@@ -0,0 +1,270 @@
+/*
+ * libkmod - interface to kernel module operations
+ *
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#ifndef _LIBKMOD_H_
+#define _LIBKMOD_H_
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * kmod_ctx
+ *
+ * library user context - reads the config and system
+ * environment, user variables, allows custom logging
+ */
+struct kmod_ctx;
+struct kmod_ctx *kmod_new(const char *dirname, const char * const *config_paths);
+struct kmod_ctx *kmod_ref(struct kmod_ctx *ctx);
+struct kmod_ctx *kmod_unref(struct kmod_ctx *ctx);
+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);
+int kmod_get_log_priority(const struct kmod_ctx *ctx);
+void kmod_set_log_priority(struct kmod_ctx *ctx, int priority);
+void *kmod_get_userdata(const struct kmod_ctx *ctx);
+void kmod_set_userdata(struct kmod_ctx *ctx, const void *userdata);
+
+const char *kmod_get_dirname(const struct kmod_ctx *ctx);
+
+/*
+ * Management of libkmod's resources
+ */
+int kmod_load_resources(struct kmod_ctx *ctx);
+void kmod_unload_resources(struct kmod_ctx *ctx);
+
+enum kmod_resources {
+ KMOD_RESOURCES_OK = 0,
+ KMOD_RESOURCES_MUST_RELOAD = 1,
+ KMOD_RESOURCES_MUST_RECREATE = 2,
+};
+int kmod_validate_resources(struct kmod_ctx *ctx);
+
+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,
+ /* Padding to make sure enum is not mapped to char */
+ _KMOD_INDEX_PAD = 1U << 31,
+};
+int kmod_dump_index(struct kmod_ctx *ctx, enum kmod_index type, int fd);
+
+/*
+ * kmod_list
+ *
+ * access to kmod generated lists
+ */
+struct kmod_list;
+struct kmod_list *kmod_list_next(const struct kmod_list *list,
+ const struct kmod_list *curr);
+struct kmod_list *kmod_list_prev(const struct kmod_list *list,
+ const struct kmod_list *curr);
+struct kmod_list *kmod_list_last(const struct kmod_list *list);
+
+#define kmod_list_foreach(list_entry, first_entry) \
+ for (list_entry = first_entry; \
+ list_entry != NULL; \
+ list_entry = kmod_list_next(first_entry, list_entry))
+
+#define kmod_list_foreach_reverse(list_entry, first_entry) \
+ for (list_entry = kmod_list_last(first_entry); \
+ list_entry != NULL; \
+ list_entry = kmod_list_prev(first_entry, list_entry))
+
+/*
+ * kmod_config_iter
+ *
+ * access to configuration lists - it allows to get each configuration's
+ * key/value stored by kmod
+ */
+struct kmod_config_iter;
+struct kmod_config_iter *kmod_config_get_blacklists(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_install_commands(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_aliases(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_options(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_ctx *ctx);
+const char *kmod_config_iter_get_key(const struct kmod_config_iter *iter);
+const char *kmod_config_iter_get_value(const struct kmod_config_iter *iter);
+bool kmod_config_iter_next(struct kmod_config_iter *iter);
+void kmod_config_iter_free_iter(struct kmod_config_iter *iter);
+
+/*
+ * kmod_module
+ *
+ * Operate on kernel modules
+ */
+struct kmod_module;
+int kmod_module_new_from_name(struct kmod_ctx *ctx, const char *name,
+ struct kmod_module **mod);
+int kmod_module_new_from_path(struct kmod_ctx *ctx, const char *path,
+ struct kmod_module **mod);
+int kmod_module_new_from_lookup(struct kmod_ctx *ctx, const char *given_alias,
+ struct kmod_list **list);
+int kmod_module_new_from_name_lookup(struct kmod_ctx *ctx,
+ const char *modname,
+ struct kmod_module **mod);
+int kmod_module_new_from_loaded(struct kmod_ctx *ctx,
+ struct kmod_list **list);
+
+struct kmod_module *kmod_module_ref(struct kmod_module *mod);
+struct kmod_module *kmod_module_unref(struct kmod_module *mod);
+int kmod_module_unref_list(struct kmod_list *list);
+struct kmod_module *kmod_module_get_module(const struct kmod_list *entry);
+
+
+/* Removal flags */
+enum kmod_remove {
+ KMOD_REMOVE_FORCE = O_TRUNC,
+ KMOD_REMOVE_NOWAIT = O_NONBLOCK, /* always set */
+ /* libkmod-only defines, not passed to kernel */
+ KMOD_REMOVE_NOLOG = 1,
+};
+
+/* Insertion flags */
+enum kmod_insert {
+ KMOD_INSERT_FORCE_VERMAGIC = 0x1,
+ KMOD_INSERT_FORCE_MODVERSION = 0x2,
+};
+
+/* Flags to 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,
+};
+
+/* Flags to kmod_module_apply_filter() */
+enum kmod_filter {
+ KMOD_FILTER_BLACKLIST = 0x00001,
+ KMOD_FILTER_BUILTIN = 0x00002,
+};
+
+int kmod_module_remove_module(struct kmod_module *mod, unsigned int flags);
+int kmod_module_insert_module(struct kmod_module *mod, unsigned int flags,
+ const char *options);
+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));
+
+
+const char *kmod_module_get_name(const struct kmod_module *mod);
+const char *kmod_module_get_path(const struct kmod_module *mod);
+const char *kmod_module_get_options(const struct kmod_module *mod);
+const char *kmod_module_get_install_commands(const struct kmod_module *mod);
+const char *kmod_module_get_remove_commands(const struct kmod_module *mod);
+struct kmod_list *kmod_module_get_dependencies(const struct kmod_module *mod);
+int kmod_module_get_softdeps(const struct kmod_module *mod,
+ struct kmod_list **pre, struct kmod_list **post);
+int kmod_module_get_filtered_blacklist(const struct kmod_ctx *ctx,
+ const struct kmod_list *input,
+ struct kmod_list **output) __attribute__ ((deprecated));
+int kmod_module_apply_filter(const struct kmod_ctx *ctx,
+ enum kmod_filter filter_type,
+ const struct kmod_list *input,
+ struct kmod_list **output);
+
+
+
+/*
+ * Information regarding "live information" from module's state, as returned
+ * by kernel
+ */
+
+enum kmod_module_initstate {
+ KMOD_MODULE_BUILTIN = 0,
+ KMOD_MODULE_LIVE,
+ KMOD_MODULE_COMING,
+ KMOD_MODULE_GOING,
+ /* Padding to make sure enum is not mapped to char */
+ _KMOD_MODULE_PAD = 1U << 31,
+};
+const char *kmod_module_initstate_str(enum kmod_module_initstate state);
+int kmod_module_get_initstate(const struct kmod_module *mod);
+int kmod_module_get_refcnt(const struct kmod_module *mod);
+struct kmod_list *kmod_module_get_holders(const struct kmod_module *mod);
+struct kmod_list *kmod_module_get_sections(const struct kmod_module *mod);
+const char *kmod_module_section_get_name(const struct kmod_list *entry);
+unsigned long kmod_module_section_get_address(const struct kmod_list *entry);
+void kmod_module_section_free_list(struct kmod_list *list);
+long kmod_module_get_size(const struct kmod_module *mod);
+
+
+
+/*
+ * Information retrieved from ELF headers and sections
+ */
+
+int kmod_module_get_info(const struct kmod_module *mod, struct kmod_list **list);
+const char *kmod_module_info_get_key(const struct kmod_list *entry);
+const char *kmod_module_info_get_value(const struct kmod_list *entry);
+void kmod_module_info_free_list(struct kmod_list *list);
+
+int kmod_module_get_versions(const struct kmod_module *mod, struct kmod_list **list);
+const char *kmod_module_version_get_symbol(const struct kmod_list *entry);
+uint64_t kmod_module_version_get_crc(const struct kmod_list *entry);
+void kmod_module_versions_free_list(struct kmod_list *list);
+
+int kmod_module_get_symbols(const struct kmod_module *mod, struct kmod_list **list);
+const char *kmod_module_symbol_get_symbol(const struct kmod_list *entry);
+uint64_t kmod_module_symbol_get_crc(const struct kmod_list *entry);
+void kmod_module_symbols_free_list(struct kmod_list *list);
+
+enum kmod_symbol_bind {
+ KMOD_SYMBOL_NONE = '\0',
+ KMOD_SYMBOL_LOCAL = 'L',
+ KMOD_SYMBOL_GLOBAL = 'G',
+ KMOD_SYMBOL_WEAK = 'W',
+ KMOD_SYMBOL_UNDEF = 'U'
+};
+
+int kmod_module_get_dependency_symbols(const struct kmod_module *mod, struct kmod_list **list);
+const char *kmod_module_dependency_symbol_get_symbol(const struct kmod_list *entry);
+int kmod_module_dependency_symbol_get_bind(const struct kmod_list *entry);
+uint64_t kmod_module_dependency_symbol_get_crc(const struct kmod_list *entry);
+void kmod_module_dependency_symbols_free_list(struct kmod_list *list);
+
+#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..0c04fda
--- /dev/null
+++ b/libkmod/libkmod.sym
@@ -0,0 +1,94 @@
+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_name_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;