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

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