Blame SOURCES/kmod-0005-depmod-backport-external-directories-support.patch

ca9082
From e91ce48303e37b92f123a91acfa9cbda17035d7c Mon Sep 17 00:00:00 2001
ca9082
From: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
Date: Thu, 17 Aug 2017 15:09:06 +0300
ca9082
Subject: [PATCH] depmod: backport external directories support
ca9082
ca9082
Requires to backport scratchbuf implementation
ca9082
ca9082
Squashed commit of the following:
ca9082
ca9082
commit 92ab2321a4b761eb2822d77dc235dd543b021c69
ca9082
Author: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
Date:   Thu Jul 20 17:09:52 2017 +0300
ca9082
ca9082
    man/depmod.d: add external keyword description
ca9082
ca9082
    The commit 'depmod: implement external directories support' added
ca9082
    external directories support (see
ca9082
    7da6884e7357ac05772e90f6d7e63b1948103fc4).
ca9082
ca9082
    This patch documents the extention in the manpage.
ca9082
ca9082
    Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
ca9082
commit 01673c9b8dbe580634604808f831b735aa7fe298
ca9082
Author: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
Date:   Tue May 9 22:09:23 2017 +0300
ca9082
ca9082
    depmod: implement external directories support
ca9082
ca9082
    The idea is to add a configuration keyword, external, which
ca9082
    will list directories for scanning for particular kernel version
ca9082
    mask:
ca9082
ca9082
    external 4.10 /the/modules/dir /second/modules/dir
ca9082
ca9082
    And extend "search" keyword to set it's priority with pseudo dir
ca9082
    "external" (as it's done for built-in):
ca9082
ca9082
    search subdir external subdir2 built-in subdir3
ca9082
ca9082
    (actually, the version is the same as for override keyword: * or
ca9082
    posix regexp, so example above is a bit incorrect).
ca9082
ca9082
    All other logic left the same: if there are duplicates, only one
ca9082
    is under consideration and it is unloadable if it is bad.
ca9082
ca9082
    The resulting modules.dep will contain entries a-la:
ca9082
ca9082
    /the/modules/dir/module1.ko:
ca9082
    kernel/module2.ko: /the/modules/dir/module1.ko
ca9082
ca9082
    (here /lib/modules/$(uname -r)/kernel/module2.ko depends of
ca9082
    symbols, provided by /the/modules/dir/module1.ko and external has
ca9082
    higher priority).
ca9082
ca9082
    modprobe and modinfo understand it out of box.
ca9082
ca9082
    This is a pretty simple extention of existing logic, since now
ca9082
    depmod already is able to:
ca9082
ca9082
    a) scan modules with full path from command line without -a
ca9082
    switch;
ca9082
    b) detects broken symbol dependencies and broken modversions,
ca9082
    what assumes, that modules are already are not built for the
ca9082
    existing kernel.
ca9082
ca9082
    Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
ca9082
commit 5274ad44377ad5998b9f4b3c5c180d407ed68fb9
ca9082
Author: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
Date:   Tue May 9 22:09:22 2017 +0300
ca9082
ca9082
    depmod: rewrite depmod modules search with scratchbuf
ca9082
ca9082
    The recursive search code used used pretty big, PATH_MAX,
ca9082
    automatic storage buffer for the module directory scanning. Some
ca9082
    time ago there was scratchbuf implemented, which dynamically
ca9082
    reallocates its buffer on demand. The patch takes it in use for
ca9082
    the scanning code also. The initial size is hardcoded to 256
ca9082
    bytes which sounds good enough for most usecases so there should
ca9082
    be not many reallocations.
ca9082
ca9082
    (add #include <shared/scratchbuf.h> which in upstream
ca9082
    comes from 3b4c684c125d depmod: fix string overflow)
ca9082
ca9082
    Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
ca9082
commit 88f47f04df80dd80065d2811f3a7bae25dd98a79
ca9082
Author: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
Date:   Tue May 9 22:09:21 2017 +0300
ca9082
ca9082
    depmod: create depmod dir independent search function
ca9082
ca9082
    Prepare to implement external directories support.
ca9082
ca9082
    The patch splits depmod_modules_search() function to two
ca9082
    functions: depmod_modules_search(), called by the high level with
ca9082
    intention to search all possible modules, and
ca9082
    depmod_module_search_path(), which takes path as a parameter and
ca9082
    scans modules under the path only. Initially it is used to scan
ca9082
    the same depmod->cfg->dirname path only.
ca9082
ca9082
    Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
ca9082
commit 6488110e07ad49a0dc4da7bbf79dd00a50bd9cc5
ca9082
Author: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
Date:   Wed Nov 23 17:23:38 2016 +0200
ca9082
ca9082
    depmod: search key: move builtin detection under the add function
ca9082
ca9082
    Prepare to implement external directories support.
ca9082
ca9082
    It's better to isolate behaviour difference under the
ca9082
    cfg_search_add() call, then make the client code aware of it.
ca9082
ca9082
    In case of external modules/directories support, there will be
ca9082
    one more keyword added, so making the clients aware of it makes
ca9082
    even less sense.
ca9082
ca9082
    Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
ca9082
commit 183465f82e0157b7facba9e100d0e822163ca951
ca9082
Author: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
Date:   Wed Nov 9 08:52:26 2016 +0200
ca9082
ca9082
    shared: make scratchbuf_str static
ca9082
ca9082
    It fixes linking problem
ca9082
ca9082
    tools/depmod.o: In function `output_symbols_bin':
ca9082
    depmod.c:(.text.output_symbols_bin+0x135): undefined reference to `scratchbuf_str'
ca9082
ca9082
    for -O0 build, where gcc doesn't actually inline it.
ca9082
ca9082
    Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
ca9082
commit 4d202380a16b273a641b3c9e85a6d80d9f367c68
ca9082
Author: Lucas De Marchi <lucas.demarchi@intel.com>
ca9082
Date:   Wed Aug 10 14:51:57 2016 -0300
ca9082
ca9082
    testsuite: include stdio.h
ca9082
ca9082
    It's used in the log macros so include it.
ca9082
ca9082
commit c226d66c43c7550247c76e7285aeb338dce2ea34
ca9082
Author: Lucas De Marchi <lucas.demarchi@intel.com>
ca9082
Date:   Wed Aug 10 14:20:32 2016 -0300
ca9082
ca9082
    Add scratchbuf implementation
ca9082
ca9082
    This should fill the requirements for "we need to loop over a lot of
ca9082
    strings that usually are small enough to remain on stack, but we want to
ca9082
    protect ourselves against huge strings not fitting in the static
ca9082
    buffer we estimated as sufficient"
ca9082
ca9082
Signed-off-by: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
ca9082
---
ca9082
 Makefile.am                 |   5 +
ca9082
 man/depmod.d.xml            |  20 ++++
ca9082
 shared/scratchbuf.c         |  60 +++++++++++
ca9082
 shared/scratchbuf.h         |  31 ++++++
ca9082
 testsuite/.gitignore        |   3 +
ca9082
 testsuite/test-scratchbuf.c |  89 +++++++++++++++++
ca9082
 testsuite/testsuite.h       |   1 +
ca9082
 tools/depmod.c              | 239 ++++++++++++++++++++++++++++++++++++--------
ca9082
 8 files changed, 404 insertions(+), 44 deletions(-)
ca9082
 create mode 100644 shared/scratchbuf.c
ca9082
 create mode 100644 shared/scratchbuf.h
ca9082
 create mode 100644 testsuite/test-scratchbuf.c
ca9082
ca9082
diff --git a/Makefile.am b/Makefile.am
ca9082
index 896ae6366f45..407efe3f93c2 100644
ca9082
--- a/Makefile.am
ca9082
+++ b/Makefile.am
ca9082
@@ -51,6 +51,7 @@ shared_libshared_la_SOURCES = \
ca9082
 	shared/array.h \
ca9082
 	shared/hash.c \
ca9082
 	shared/hash.h \
ca9082
+	shared/scratchbuf.c \
ca9082
 	shared/strbuf.c \
ca9082
 	shared/strbuf.h \
ca9082
 	shared/util.c \
ca9082
@@ -307,6 +308,7 @@ testsuite_libtestsuite_la_LIBADD = -lrt
ca9082
 TESTSUITE = \
ca9082
 	testsuite/test-hash \
ca9082
 	testsuite/test-array \
ca9082
+	testsuite/test-scratchbuf \
ca9082
 	testsuite/test-strbuf \
ca9082
 	testsuite/test-init \
ca9082
 	testsuite/test-initstate \
ca9082
@@ -329,6 +331,9 @@ testsuite_test_hash_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
ca9082
 testsuite_test_array_LDADD = $(TESTSUITE_LDADD)
ca9082
 testsuite_test_array_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
ca9082
 
ca9082
+testsuite_test_scratchbuf_LDADD = $(TESTSUITE_LDADD)
ca9082
+testsuite_test_scratchbuf_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
ca9082
+
ca9082
 testsuite_test_strbuf_LDADD = $(TESTSUITE_LDADD)
ca9082
 testsuite_test_strbuf_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
ca9082
 
ca9082
diff --git a/man/depmod.d.xml b/man/depmod.d.xml
ca9082
index c30c06c5b605..4341a568e8a0 100644
ca9082
--- a/man/depmod.d.xml
ca9082
+++ b/man/depmod.d.xml
ca9082
@@ -75,6 +75,9 @@
ca9082
             first listed directory and the lowest priority given to the last
ca9082
             directory listed. The special keyword <command>built-in</command> 
ca9082
             refers to the standard module directories installed by the kernel.
ca9082
+            Another special keyword <command>external</command> refers to the
ca9082
+            list of external directories, defined by the
ca9082
+            <command>external</command> command.
ca9082
           </para>
ca9082
           <para>
ca9082
             By default, depmod will give a higher priority to 
ca9082
@@ -110,6 +113,23 @@
ca9082
           </para>
ca9082
         </listitem>
ca9082
       </varlistentry>
ca9082
+      <varlistentry>
ca9082
+        <term>external <replaceable>kernelversion</replaceable>
ca9082
+        <replaceable>absolutemodulesdirectory...</replaceable>
ca9082
+        </term>
ca9082
+        <listitem>
ca9082
+          <para>
ca9082
+            This specifies a list of directories, which will be checked
ca9082
+            according to the priorities in the <command>search</command>
ca9082
+            command. The order matters also, the first directory has the higher
ca9082
+            priority.
ca9082
+          </para>
ca9082
+          <para>
ca9082
+            The <replaceable>kernelversion</replaceable> is a POSIX regular
ca9082
+            expression or * wildcard, like in the <command>override</command>.
ca9082
+          </para>
ca9082
+        </listitem>
ca9082
+      </varlistentry>
ca9082
     </variablelist>
ca9082
   </refsect1>
ca9082
 
ca9082
diff --git a/shared/scratchbuf.c b/shared/scratchbuf.c
ca9082
new file mode 100644
ca9082
index 000000000000..8d9eb83f30c1
ca9082
--- /dev/null
ca9082
+++ b/shared/scratchbuf.c
ca9082
@@ -0,0 +1,60 @@
ca9082
+/*
ca9082
+ * kmod - interface to kernel module operations
ca9082
+ *
ca9082
+ * Copyright (C) 2016  Intel Corporation. All rights reserved.
ca9082
+ *
ca9082
+ * This library is free software; you can redistribute it and/or
ca9082
+ * modify it under the terms of the GNU Lesser General Public
ca9082
+ * License as published by the Free Software Foundation; either
ca9082
+ * version 2.1 of the License, or (at your option) any later version.
ca9082
+ *
ca9082
+ * This library is distributed in the hope that it will be useful,
ca9082
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
ca9082
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
ca9082
+ * Lesser General Public License for more details.
ca9082
+ *
ca9082
+ * You should have received a copy of the GNU Lesser General Public
ca9082
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
ca9082
+ */
ca9082
+#include "scratchbuf.h"
ca9082
+
ca9082
+#include <errno.h>
ca9082
+#include <string.h>
ca9082
+
ca9082
+void scratchbuf_init(struct scratchbuf *buf, char *stackbuf, size_t size)
ca9082
+{
ca9082
+	buf->bytes = stackbuf;
ca9082
+	buf->size = size;
ca9082
+	buf->need_free = false;
ca9082
+}
ca9082
+
ca9082
+int scratchbuf_alloc(struct scratchbuf *buf, size_t size)
ca9082
+{
ca9082
+	char *tmp;
ca9082
+
ca9082
+	if (size <= buf->size)
ca9082
+		return 0;
ca9082
+
ca9082
+	if (buf->need_free) {
ca9082
+		tmp = realloc(buf->bytes, size);
ca9082
+		if (tmp == NULL)
ca9082
+			return -ENOMEM;
ca9082
+	} else {
ca9082
+		tmp = malloc(size);
ca9082
+		if (tmp == NULL)
ca9082
+			return -ENOMEM;
ca9082
+		memcpy(tmp, buf->bytes, buf->size);
ca9082
+	}
ca9082
+
ca9082
+	buf->size = size;
ca9082
+	buf->bytes = tmp;
ca9082
+	buf->need_free = true;
ca9082
+
ca9082
+	return 0;
ca9082
+}
ca9082
+
ca9082
+void scratchbuf_release(struct scratchbuf *buf)
ca9082
+{
ca9082
+	if (buf->need_free)
ca9082
+		free(buf->bytes);
ca9082
+}
ca9082
diff --git a/shared/scratchbuf.h b/shared/scratchbuf.h
ca9082
new file mode 100644
ca9082
index 000000000000..27ea9d9f6008
ca9082
--- /dev/null
ca9082
+++ b/shared/scratchbuf.h
ca9082
@@ -0,0 +1,31 @@
ca9082
+#pragma once
ca9082
+
ca9082
+#include <stdbool.h>
ca9082
+#include <stdlib.h>
ca9082
+
ca9082
+#include <shared/macro.h>
ca9082
+
ca9082
+/*
ca9082
+ * Buffer abstract data type
ca9082
+ */
ca9082
+struct scratchbuf {
ca9082
+	char *bytes;
ca9082
+	size_t size;
ca9082
+	bool need_free;
ca9082
+};
ca9082
+
ca9082
+void scratchbuf_init(struct scratchbuf *buf, char *stackbuf, size_t size);
ca9082
+int scratchbuf_alloc(struct scratchbuf *buf, size_t sz);
ca9082
+void scratchbuf_release(struct scratchbuf *buf);
ca9082
+
ca9082
+/* Return a C string */
ca9082
+static inline char *scratchbuf_str(struct scratchbuf *buf)
ca9082
+{
ca9082
+	return buf->bytes;
ca9082
+}
ca9082
+
ca9082
+#define SCRATCHBUF_INITIALIZER(buf_) {			\
ca9082
+	.bytes = buf_,					\
ca9082
+	.size = sizeof(buf_) + _array_size_chk(buf_),	\
ca9082
+	.need_free = false,				\
ca9082
+}
ca9082
diff --git a/testsuite/test-scratchbuf.c b/testsuite/test-scratchbuf.c
ca9082
new file mode 100644
ca9082
index 000000000000..6d86957cefb8
ca9082
--- /dev/null
ca9082
+++ b/testsuite/test-scratchbuf.c
ca9082
@@ -0,0 +1,89 @@
ca9082
+/*
ca9082
+ * Copyright (C)  2016 Intel Corporation. All rights reserved.
ca9082
+ *
ca9082
+ * This program is free software; you can redistribute it and/or
ca9082
+ * modify it under the terms of the GNU Lesser General Public
ca9082
+ * License as published by the Free Software Foundation; either
ca9082
+ * version 2.1 of the License, or (at your option) any later version.
ca9082
+ *
ca9082
+ * This program is distributed in the hope that it will be useful,
ca9082
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
ca9082
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
ca9082
+ * Lesser General Public License for more details.
ca9082
+ *
ca9082
+ * You should have received a copy of the GNU Lesser General Public
ca9082
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
ca9082
+ */
ca9082
+
ca9082
+#include <errno.h>
ca9082
+#include <string.h>
ca9082
+#include <unistd.h>
ca9082
+
ca9082
+#include <shared/scratchbuf.h>
ca9082
+
ca9082
+#include "testsuite.h"
ca9082
+
ca9082
+static int test_scratchbuf_onlystack(const struct test *t)
ca9082
+{
ca9082
+	struct scratchbuf sbuf;
ca9082
+	const char *smallstr = "xyz";
ca9082
+	char buf[3 + 2];
ca9082
+	char buf2[3 + 1];
ca9082
+
ca9082
+	scratchbuf_init(&sbuf, buf, sizeof(buf));
ca9082
+	assert_return(scratchbuf_alloc(&sbuf, strlen(smallstr) + 1) == 0, EXIT_FAILURE);
ca9082
+	assert_return(sbuf.need_free == false, EXIT_FAILURE);
ca9082
+	scratchbuf_release(&sbuf);
ca9082
+
ca9082
+	scratchbuf_init(&sbuf, buf2, sizeof(buf2));
ca9082
+	assert_return(scratchbuf_alloc(&sbuf, strlen(smallstr) + 1) == 0, EXIT_FAILURE);
ca9082
+	assert_return(sbuf.need_free == false, EXIT_FAILURE);
ca9082
+	scratchbuf_release(&sbuf);
ca9082
+
ca9082
+	memcpy(scratchbuf_str(&sbuf), smallstr, strlen(smallstr) + 1);
ca9082
+	assert_return(strcmp(scratchbuf_str(&sbuf), smallstr) == 0, EXIT_FAILURE);
ca9082
+
ca9082
+	return 0;
ca9082
+}
ca9082
+DEFINE_TEST(test_scratchbuf_onlystack,
ca9082
+		.description = "test scratchbuf for buffer on stack only");
ca9082
+
ca9082
+
ca9082
+static int test_scratchbuf_heap(const struct test *t)
ca9082
+{
ca9082
+	struct scratchbuf sbuf;
ca9082
+	const char *smallstr = "xyz";
ca9082
+	const char *largestr = "xyzxyzxyz";
ca9082
+	const char *largestr2 = "xyzxyzxyzxyzxyz";
ca9082
+	char buf[3 + 1];
ca9082
+
ca9082
+	scratchbuf_init(&sbuf, buf, sizeof(buf));
ca9082
+
ca9082
+	/* Initially only on stack */
ca9082
+	assert_return(scratchbuf_alloc(&sbuf, strlen(smallstr) + 1) == 0, EXIT_FAILURE);
ca9082
+	assert_return(sbuf.need_free == false, EXIT_FAILURE);
ca9082
+	memcpy(scratchbuf_str(&sbuf), smallstr, strlen(smallstr) + 1);
ca9082
+
ca9082
+	/* Grow once to heap */
ca9082
+	assert_return(scratchbuf_alloc(&sbuf, strlen(largestr) + 1) == 0, EXIT_FAILURE);
ca9082
+	assert_return(sbuf.need_free == true, EXIT_FAILURE);
ca9082
+	assert_return(sbuf.size == strlen(largestr) + 1, EXIT_FAILURE);
ca9082
+	assert_return(strcmp(scratchbuf_str(&sbuf), smallstr) == 0, EXIT_FAILURE);
ca9082
+	memcpy(scratchbuf_str(&sbuf), largestr, strlen(largestr) + 1);
ca9082
+	assert_return(strcmp(scratchbuf_str(&sbuf), largestr) == 0, EXIT_FAILURE);
ca9082
+
ca9082
+	/* Grow again - realloc should take place */
ca9082
+	assert_return(scratchbuf_alloc(&sbuf, strlen(largestr2) + 1) == 0, EXIT_FAILURE);
ca9082
+	assert_return(sbuf.need_free == true, EXIT_FAILURE);
ca9082
+	assert_return(sbuf.size == strlen(largestr2) + 1, EXIT_FAILURE);
ca9082
+	memcpy(scratchbuf_str(&sbuf), largestr2, strlen(largestr2) + 1);
ca9082
+	assert_return(strcmp(scratchbuf_str(&sbuf), largestr2) == 0, EXIT_FAILURE);
ca9082
+
ca9082
+	scratchbuf_release(&sbuf);
ca9082
+
ca9082
+	return 0;
ca9082
+}
ca9082
+DEFINE_TEST(test_scratchbuf_heap,
ca9082
+		.description = "test scratchbuf for buffer on that grows to heap");
ca9082
+
ca9082
+TESTSUITE_MAIN();
ca9082
diff --git a/testsuite/testsuite.h b/testsuite/testsuite.h
ca9082
index 3bd74f34ca7e..bb0eb507ac2f 100644
ca9082
--- a/testsuite/testsuite.h
ca9082
+++ b/testsuite/testsuite.h
ca9082
@@ -19,6 +19,7 @@
ca9082
 
ca9082
 #include <stdbool.h>
ca9082
 #include <stdarg.h>
ca9082
+#include <stdio.h>
ca9082
 
ca9082
 #include <shared/macro.h>
ca9082
 
ca9082
diff --git a/tools/depmod.c b/tools/depmod.c
ca9082
index 7b9583249018..79fd14354266 100644
ca9082
--- a/tools/depmod.c
ca9082
+++ b/tools/depmod.c
ca9082
@@ -35,6 +35,7 @@
ca9082
 #include <shared/hash.h>
ca9082
 #include <shared/macro.h>
ca9082
 #include <shared/util.h>
ca9082
+#include <shared/scratchbuf.h>
ca9082
 
ca9082
 #include <libkmod/libkmod.h>
ca9082
 
ca9082
@@ -44,6 +45,7 @@
ca9082
 static int verbose = DEFAULT_VERBOSE;
ca9082
 
ca9082
 static const char CFG_BUILTIN_KEY[] = "built-in";
ca9082
+static const char CFG_EXTERNAL_KEY[] = "external";
ca9082
 static const char *default_cfg_paths[] = {
ca9082
 	"/run/depmod.d",
ca9082
 	SYSCONFDIR "/depmod.d",
ca9082
@@ -432,9 +434,21 @@ struct cfg_override {
ca9082
 	char path[];
ca9082
 };
ca9082
 
ca9082
+enum search_type {
ca9082
+	SEARCH_PATH,
ca9082
+	SEARCH_BUILTIN,
ca9082
+	SEARCH_EXTERNAL
ca9082
+};
ca9082
+
ca9082
 struct cfg_search {
ca9082
 	struct cfg_search *next;
ca9082
-	uint8_t builtin;
ca9082
+	enum search_type type;
ca9082
+	size_t len;
ca9082
+	char path[];
ca9082
+};
ca9082
+
ca9082
+struct cfg_external {
ca9082
+	struct cfg_external *next;
ca9082
 	size_t len;
ca9082
 	char path[];
ca9082
 };
ca9082
@@ -449,14 +463,27 @@ struct cfg {
ca9082
 	uint8_t warn_dups;
ca9082
 	struct cfg_override *overrides;
ca9082
 	struct cfg_search *searches;
ca9082
+	struct cfg_external *externals;
ca9082
 };
ca9082
 
ca9082
-static int cfg_search_add(struct cfg *cfg, const char *path, uint8_t builtin)
ca9082
+static enum search_type cfg_define_search_type(const char *path)
ca9082
+{
ca9082
+	if (streq(path, CFG_BUILTIN_KEY))
ca9082
+		return SEARCH_BUILTIN;
ca9082
+	if (streq(path, CFG_EXTERNAL_KEY))
ca9082
+		return SEARCH_EXTERNAL;
ca9082
+	return SEARCH_PATH;
ca9082
+}
ca9082
+
ca9082
+static int cfg_search_add(struct cfg *cfg, const char *path)
ca9082
 {
ca9082
 	struct cfg_search *s;
ca9082
 	size_t len;
ca9082
+	enum search_type type;
ca9082
 
ca9082
-	if (builtin)
ca9082
+	type = cfg_define_search_type(path);
ca9082
+
ca9082
+	if (type != SEARCH_PATH)
ca9082
 		len = 0;
ca9082
 	else
ca9082
 		len = strlen(path) + 1;
ca9082
@@ -466,15 +493,15 @@ static int cfg_search_add(struct cfg *cfg, const char *path, uint8_t builtin)
ca9082
 		ERR("search add: out of memory\n");
ca9082
 		return -ENOMEM;
ca9082
 	}
ca9082
-	s->builtin = builtin;
ca9082
-	if (builtin)
ca9082
+	s->type = type;
ca9082
+	if (type != SEARCH_PATH)
ca9082
 		s->len = 0;
ca9082
 	else {
ca9082
 		s->len = len - 1;
ca9082
 		memcpy(s->path, path, len);
ca9082
 	}
ca9082
 
ca9082
-	DBG("search add: %s, builtin=%hhu\n", path, builtin);
ca9082
+	DBG("search add: %s, search type=%hhu\n", path, type);
ca9082
 
ca9082
 	s->next = cfg->searches;
ca9082
 	cfg->searches = s;
ca9082
@@ -522,6 +549,32 @@ static void cfg_override_free(struct cfg_override *o)
ca9082
 	free(o);
ca9082
 }
ca9082
 
ca9082
+static int cfg_external_add(struct cfg *cfg, const char *path)
ca9082
+{
ca9082
+	struct cfg_external *ext;
ca9082
+	size_t len = strlen(path);
ca9082
+
ca9082
+	ext = malloc(sizeof(struct cfg_external) + len + 1);
ca9082
+	if (ext == NULL) {
ca9082
+		ERR("external add: out of memory\n");
ca9082
+		return -ENOMEM;
ca9082
+	}
ca9082
+
ca9082
+	strcpy(ext->path, path);
ca9082
+	ext->len = len;
ca9082
+
ca9082
+	DBG("external add: %s\n", ext->path);
ca9082
+
ca9082
+	ext->next = cfg->externals;
ca9082
+	cfg->externals = ext;
ca9082
+	return 0;
ca9082
+}
ca9082
+
ca9082
+static void cfg_external_free(struct cfg_external *ext)
ca9082
+{
ca9082
+	free(ext);
ca9082
+}
ca9082
+
ca9082
 static int cfg_kernel_matches(const struct cfg *cfg, const char *pattern)
ca9082
 {
ca9082
 	regex_t re;
ca9082
@@ -567,8 +620,7 @@ static int cfg_file_parse(struct cfg *cfg, const char *filename)
ca9082
 		if (streq(cmd, "search")) {
ca9082
 			const char *sp;
ca9082
 			while ((sp = strtok_r(NULL, "\t ", &saveptr)) != NULL) {
ca9082
-				uint8_t builtin = streq(sp, CFG_BUILTIN_KEY);
ca9082
-				cfg_search_add(cfg, sp, builtin);
ca9082
+				cfg_search_add(cfg, sp);
ca9082
 			}
ca9082
 		} else if (streq(cmd, "override")) {
ca9082
 			const char *modname = strtok_r(NULL, "\t ", &saveptr);
ca9082
@@ -586,6 +638,20 @@ static int cfg_file_parse(struct cfg *cfg, const char *filename)
ca9082
 			}
ca9082
 
ca9082
 			cfg_override_add(cfg, modname, subdir);
ca9082
+		} else if (streq(cmd, "external")) {
ca9082
+			const char *version = strtok_r(NULL, "\t ", &saveptr);
ca9082
+			const char *dir = strtok_r(NULL, "\t ", &saveptr);
ca9082
+
ca9082
+			if (version == NULL || dir == NULL)
ca9082
+				goto syntax_error;
ca9082
+
ca9082
+			if (!cfg_kernel_matches(cfg, version)) {
ca9082
+				INF("%s:%u: external directory did not match %s\n",
ca9082
+				    filename, linenum, version);
ca9082
+				goto done_next;
ca9082
+			}
ca9082
+
ca9082
+			cfg_external_add(cfg, dir);
ca9082
 		} else if (streq(cmd, "include")
ca9082
 				|| streq(cmd, "make_map_files")) {
ca9082
 			INF("%s:%u: command %s not implemented yet\n",
ca9082
@@ -762,7 +828,7 @@ static int cfg_load(struct cfg *cfg, const char * const *cfg_paths)
ca9082
 	 * list here. But only if there was no "search" option specified.
ca9082
 	 */
ca9082
 	if (cfg->searches == NULL)
ca9082
-		cfg_search_add(cfg, "updates", 0);
ca9082
+		cfg_search_add(cfg, "updates");
ca9082
 
ca9082
 	return 0;
ca9082
 }
ca9082
@@ -780,6 +846,12 @@ static void cfg_free(struct cfg *cfg)
ca9082
 		cfg->searches = cfg->searches->next;
ca9082
 		cfg_search_free(tmp);
ca9082
 	}
ca9082
+
ca9082
+	while (cfg->externals) {
ca9082
+		struct cfg_external *tmp = cfg->externals;
ca9082
+		cfg->externals = cfg->externals->next;
ca9082
+		cfg_external_free(tmp);
ca9082
+	}
ca9082
 }
ca9082
 
ca9082
 
ca9082
@@ -987,6 +1059,33 @@ static int depmod_module_del(struct depmod *depmod, struct mod *mod)
ca9082
 	return 0;
ca9082
 }
ca9082
 
ca9082
+static const char *search_to_string(const struct cfg_search *s)
ca9082
+{
ca9082
+	switch(s->type) {
ca9082
+	case SEARCH_EXTERNAL:
ca9082
+		return "external";
ca9082
+	case SEARCH_BUILTIN:
ca9082
+		return "built-in";
ca9082
+	default:
ca9082
+		return s->path;
ca9082
+	}
ca9082
+}
ca9082
+
ca9082
+static bool depmod_is_path_starts_with(const char *path,
ca9082
+				       size_t pathlen,
ca9082
+				       const char *prefix,
ca9082
+				       size_t prefix_len)
ca9082
+{
ca9082
+	if (pathlen <= prefix_len)
ca9082
+		return false;
ca9082
+	if (path[prefix_len] != '/')
ca9082
+		return false;
ca9082
+	if (memcmp(path, prefix, prefix_len) != 0)
ca9082
+		return false;
ca9082
+
ca9082
+	return true;
ca9082
+}
ca9082
+
ca9082
 /* returns if existing module @mod is higher priority than newpath.
ca9082
  * note this is the inverse of module-init-tools is_higher_priority()
ca9082
  */
ca9082
@@ -995,6 +1094,7 @@ static int depmod_module_is_higher_priority(const struct depmod *depmod, const s
ca9082
 	const struct cfg *cfg = depmod->cfg;
ca9082
 	const struct cfg_override *ov;
ca9082
 	const struct cfg_search *se;
ca9082
+	const struct cfg_external *ext;
ca9082
 
ca9082
 	/* baselen includes the last '/' and mod->baselen doesn't. So it's
ca9082
 	 * actually correct to use modnamelen in the first and modnamesz in
ca9082
@@ -1003,35 +1103,55 @@ static int depmod_module_is_higher_priority(const struct depmod *depmod, const s
ca9082
 	size_t oldlen = mod->baselen + mod->modnamesz;
ca9082
 	const char *oldpath = mod->path;
ca9082
 	int i, bprio = -1, oldprio = -1, newprio = -1;
ca9082
-
ca9082
-	assert(strncmp(newpath, cfg->dirname, cfg->dirnamelen) == 0);
ca9082
-	assert(strncmp(oldpath, cfg->dirname, cfg->dirnamelen) == 0);
ca9082
-
ca9082
-	newpath += cfg->dirnamelen + 1;
ca9082
-	newlen -= cfg->dirnamelen + 1;
ca9082
-	oldpath += cfg->dirnamelen + 1;
ca9082
-	oldlen -= cfg->dirnamelen + 1;
ca9082
+	size_t relnewlen = 0;
ca9082
+	size_t reloldlen = 0;
ca9082
+	const char *relnewpath = NULL;
ca9082
+	const char *reloldpath = NULL;
ca9082
 
ca9082
 	DBG("comparing priorities of %s and %s\n",
ca9082
 	    oldpath, newpath);
ca9082
 
ca9082
+	if (strncmp(newpath, cfg->dirname, cfg->dirnamelen) == 0) {
ca9082
+		relnewpath = newpath + cfg->dirnamelen + 1;
ca9082
+		relnewlen = newlen - cfg->dirnamelen + 1;
ca9082
+	}
ca9082
+	if (strncmp(oldpath, cfg->dirname, cfg->dirnamelen) == 0) {
ca9082
+		reloldpath = oldpath + cfg->dirnamelen + 1;
ca9082
+		reloldlen = oldlen - cfg->dirnamelen + 1;
ca9082
+	}
ca9082
+
ca9082
 	for (ov = cfg->overrides; ov != NULL; ov = ov->next) {
ca9082
 		DBG("override %s\n", ov->path);
ca9082
-		if (newlen == ov->len && memcmp(ov->path, newpath, newlen) == 0)
ca9082
+		if (relnewlen == ov->len &&
ca9082
+		    memcmp(ov->path, relnewpath, relnewlen) == 0)
ca9082
 			return 0;
ca9082
-		if (oldlen == ov->len && memcmp(ov->path, oldpath, oldlen) == 0)
ca9082
+		if (reloldlen == ov->len &&
ca9082
+		    memcmp(ov->path, reloldpath, reloldlen) == 0)
ca9082
 			return 1;
ca9082
 	}
ca9082
 
ca9082
 	for (i = 0, se = cfg->searches; se != NULL; se = se->next, i++) {
ca9082
-		DBG("search %s\n", se->builtin ? "built-in" : se->path);
ca9082
-		if (se->builtin)
ca9082
+		DBG("search %s\n", search_to_string(se));
ca9082
+		if (se->type == SEARCH_BUILTIN)
ca9082
 			bprio = i;
ca9082
-		else if (newlen > se->len && newpath[se->len] == '/' &&
ca9082
-			 memcmp(se->path, newpath, se->len) == 0)
ca9082
+		else if (se->type == SEARCH_EXTERNAL) {
ca9082
+			for (ext = cfg->externals; ext != NULL; ext = ext->next, i++) {
ca9082
+				if (depmod_is_path_starts_with(newpath,
ca9082
+							       newlen,
ca9082
+							       ext->path,
ca9082
+							       ext->len))
ca9082
+					newprio = i;
ca9082
+				if (depmod_is_path_starts_with(oldpath,
ca9082
+							       oldlen,
ca9082
+							       ext->path,
ca9082
+							       ext->len))
ca9082
+					oldprio = i;
ca9082
+			}
ca9082
+		} else if (relnewlen > se->len && relnewpath[se->len] == '/' &&
ca9082
+			 memcmp(se->path, relnewpath, se->len) == 0)
ca9082
 			newprio = i;
ca9082
-		else if (oldlen > se->len && oldpath[se->len] == '/' &&
ca9082
-			 memcmp(se->path, oldpath, se->len) == 0)
ca9082
+		else if (reloldlen > se->len && reloldpath[se->len] == '/' &&
ca9082
+			 memcmp(se->path, reloldpath, se->len) == 0)
ca9082
 			oldprio = i;
ca9082
 	}
ca9082
 
ca9082
@@ -1102,10 +1222,11 @@ add:
ca9082
 	return 0;
ca9082
 }
ca9082
 
ca9082
-static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t baselen, char *path)
ca9082
+static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t baselen, struct scratchbuf *s_path)
ca9082
 {
ca9082
 	struct dirent *de;
ca9082
 	int err = 0, dfd = dirfd(d);
ca9082
+	char *path;
ca9082
 
ca9082
 	while ((de = readdir(d)) != NULL) {
ca9082
 		const char *name = de->d_name;
ca9082
@@ -1118,11 +1239,13 @@ static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t basel
ca9082
 		if (streq(name, "build") || streq(name, "source"))
ca9082
 			continue;
ca9082
 		namelen = strlen(name);
ca9082
-		if (baselen + namelen + 2 >= PATH_MAX) {
ca9082
-			path[baselen] = '\0';
ca9082
-			ERR("path is too long %s%s\n", path, name);
ca9082
+		if (scratchbuf_alloc(s_path, baselen + namelen + 2) < 0) {
ca9082
+			err = -ENOMEM;
ca9082
+			ERR("No memory\n");
ca9082
 			continue;
ca9082
 		}
ca9082
+
ca9082
+		path = scratchbuf_str(s_path);
ca9082
 		memcpy(path + baselen, name, namelen + 1);
ca9082
 
ca9082
 		if (de->d_type == DT_REG)
ca9082
@@ -1148,10 +1271,6 @@ static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t basel
ca9082
 		if (is_dir) {
ca9082
 			int fd;
ca9082
 			DIR *subdir;
ca9082
-			if (baselen + namelen + 2 + NAME_MAX >= PATH_MAX) {
ca9082
-				ERR("directory path is too long %s\n", path);
ca9082
-				continue;
ca9082
-			}
ca9082
 			fd = openat(dfd, name, O_RDONLY);
ca9082
 			if (fd < 0) {
ca9082
 				ERR("openat(%d, %s, O_RDONLY): %m\n",
ca9082
@@ -1168,7 +1287,7 @@ static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t basel
ca9082
 			path[baselen + namelen + 1] = '\0';
ca9082
 			err = depmod_modules_search_dir(depmod, subdir,
ca9082
 							baselen + namelen + 1,
ca9082
-							path);
ca9082
+							s_path);
ca9082
 			closedir(subdir);
ca9082
 		} else {
ca9082
 			err = depmod_modules_search_file(depmod, baselen,
ca9082
@@ -1181,33 +1300,65 @@ static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t basel
ca9082
 			err = 0; /* ignore errors */
ca9082
 		}
ca9082
 	}
ca9082
-
ca9082
 	return err;
ca9082
 }
ca9082
 
ca9082
-static int depmod_modules_search(struct depmod *depmod)
ca9082
+static int depmod_modules_search_path(struct depmod *depmod,
ca9082
+				      const char *path)
ca9082
 {
ca9082
-	char path[PATH_MAX];
ca9082
-	DIR *d = opendir(depmod->cfg->dirname);
ca9082
+	char buf[256];
ca9082
+	_cleanup_(scratchbuf_release) struct scratchbuf s_path_buf =
ca9082
+		SCRATCHBUF_INITIALIZER(buf);
ca9082
+	char *path_buf;
ca9082
+	DIR *d;
ca9082
 	size_t baselen;
ca9082
 	int err;
ca9082
+
ca9082
+	d = opendir(path);
ca9082
 	if (d == NULL) {
ca9082
 		err = -errno;
ca9082
-		ERR("could not open directory %s: %m\n", depmod->cfg->dirname);
ca9082
+		ERR("could not open directory %s: %m\n", path);
ca9082
 		return err;
ca9082
 	}
ca9082
 
ca9082
-	baselen = depmod->cfg->dirnamelen;
ca9082
-	memcpy(path, depmod->cfg->dirname, baselen);
ca9082
-	path[baselen] = '/';
ca9082
+	baselen = strlen(path);
ca9082
+
ca9082
+	if (scratchbuf_alloc(&s_path_buf, baselen + 2) < 0) {
ca9082
+		err = -ENOMEM;
ca9082
+		goto out;
ca9082
+	}
ca9082
+	path_buf = scratchbuf_str(&s_path_buf);
ca9082
+
ca9082
+	memcpy(path_buf, path, baselen);
ca9082
+	path_buf[baselen] = '/';
ca9082
 	baselen++;
ca9082
-	path[baselen] = '\0';
ca9082
+	path_buf[baselen] = '\0';
ca9082
 
ca9082
-	err = depmod_modules_search_dir(depmod, d, baselen, path);
ca9082
+	err = depmod_modules_search_dir(depmod, d, baselen, &s_path_buf);
ca9082
+out:
ca9082
 	closedir(d);
ca9082
 	return err;
ca9082
 }
ca9082
 
ca9082
+static int depmod_modules_search(struct depmod *depmod)
ca9082
+{
ca9082
+	int err;
ca9082
+	struct cfg_external *ext;
ca9082
+
ca9082
+	err = depmod_modules_search_path(depmod, depmod->cfg->dirname);
ca9082
+	if (err < 0)
ca9082
+		return err;
ca9082
+
ca9082
+	for (ext = depmod->cfg->externals; ext != NULL; ext = ext->next) {
ca9082
+		err = depmod_modules_search_path(depmod, ext->path);
ca9082
+		if (err < 0 && err == -ENOENT)
ca9082
+			/* ignore external dir absense */
ca9082
+			continue;
ca9082
+	}
ca9082
+
ca9082
+	return 0;
ca9082
+}
ca9082
+
ca9082
 static int mod_cmp(const void *pa, const void *pb) {
ca9082
 	const struct mod *a = *(const struct mod **)pa;
ca9082
 	const struct mod *b = *(const struct mod **)pb;
ca9082
-- 
ca9082
2.14.1
ca9082
ca9082
--- kmod-20/man/depmod.d.5.orig	2017-08-17 16:01:34.330058135 +0300
ca9082
+++ kmod-20/man/depmod.d.5	2017-08-17 16:01:37.901058086 +0300
ca9082
@@ -1,13 +1,13 @@
ca9082
 '\" t
ca9082
 .\"     Title: depmod.d
ca9082
 .\"    Author: Jon Masters <jcm@jonmasters.org>
ca9082
-.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
ca9082
-.\"      Date: 03/01/2015
ca9082
+.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
ca9082
+.\"      Date: 08/17/2017
ca9082
 .\"    Manual: depmod.d
ca9082
 .\"    Source: kmod
ca9082
 .\"  Language: English
ca9082
 .\"
ca9082
-.TH "DEPMOD\&.D" "5" "03/01/2015" "kmod" "depmod.d"
ca9082
+.TH "DEPMOD\&.D" "5" "08/17/2017" "kmod" "depmod.d"
ca9082
 .\" -----------------------------------------------------------------
ca9082
 .\" * Define some portability stuff
ca9082
 .\" -----------------------------------------------------------------
ca9082
@@ -52,7 +52,11 @@
ca9082
 This allows you to specify the order in which /lib/modules (or other configured module location) subdirectories will be processed by
ca9082
 \fBdepmod\fR\&. Directories are listed in order, with the highest priority given to the first listed directory and the lowest priority given to the last directory listed\&. The special keyword
ca9082
 \fBbuilt\-in\fR
ca9082
-refers to the standard module directories installed by the kernel\&.
ca9082
+refers to the standard module directories installed by the kernel\&. Another special keyword
ca9082
+\fBexternal\fR
ca9082
+refers to the list of external directories, defined by the
ca9082
+\fBexternal\fR
ca9082
+command\&.
ca9082
 .sp
ca9082
 By default, depmod will give a higher priority to a directory with the name
ca9082
 \fBupdates\fR
ca9082
@@ -73,6 +77,18 @@
ca9082
 \fBextra\fR
ca9082
 subdirectory within /lib/modules (or other module location) will take priority over any likenamed module already provided by the kernel\&.
ca9082
 .RE
ca9082
+.PP
ca9082
+external \fIkernelversion\fR \fIabsolutemodulesdirectory\&.\&.\&.\fR
ca9082
+.RS 4
ca9082
+This specifies a list of directories, which will be checked according to the priorities in the
ca9082
+\fBsearch\fR
ca9082
+command\&. The order matters also, the first directory has the higher priority\&.
ca9082
+.sp
ca9082
+The
ca9082
+\fIkernelversion\fR
ca9082
+is a POSIX regular expression or * wildcard, like in the
ca9082
+\fBoverride\fR\&.
ca9082
+.RE
ca9082
 .SH "COPYRIGHT"
ca9082
 .PP
ca9082
 This manual page Copyright 2006\-2010, Jon Masters, Red Hat, Inc\&.