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

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