[PATCH 4/7] lib/glob: add case-insensitive glob_match_nocase()

Josh Law
2026-03-15T17:11:12Z

Add a case-insensitive variant of glob_match() for callers that need
to match patterns regardless of case. For example, ATA device model
strings or ftrace function filters may benefit from case-insensitive
matching without requiring both the pattern and target string to be
lowercased beforehand.

Refactor the matching logic into a static __glob_match() helper that
takes a bool nocase parameter, and implement both glob_match() and
glob_match_nocase() as thin wrappers. When nocase is true, literal
characters and character class endpoints are folded through tolower()
before comparison.

Signed-off-by: Josh Law <objecting@objecting.org>
---
include/linux/glob.h | 1 +
lib/glob.c | 73 ++++++++++++++++++++++++++++++++++++++------
2 files changed, 64 insertions(+), 10 deletions(-)

diff --git a/include/linux/glob.h b/include/linux/glob.h
index 861327b33e41..36527ae89730 100644
--- a/include/linux/glob.h
+++ b/include/linux/glob.h
@@ -6,5 +6,6 @@
#include <linux/compiler.h> /* For __pure */

bool __pure glob_match(char const *pat, char const *str);
+bool __pure glob_match_nocase(char const *pat, char const *str);

#endif /* _LINUX_GLOB_H */
diff --git a/lib/glob.c b/lib/glob.c
index c5508be0d215..9c71ccc15abc 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/glob.h>
#include <linux/export.h>
@@ -11,8 +12,8 @@
MODULE_DESCRIPTION("glob(7) matching");
MODULE_LICENSE("Dual MIT/GPL");

-/**
- * glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0)
+/*
+ * __glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0)
* @pat: Shell-style pattern to match, e.g. "*.[ch]".
* @str: String to match. The pattern must match the entire string.
*
@@ -39,7 +40,7 @@ MODULE_LICENSE("Dual MIT/GPL");
*
* An opening bracket without a matching close is matched literally.
*/
-bool __pure glob_match(char const *pat, char const *str)
+static bool __pure __glob_match(char const *pat, char const *str, bool nocase)
{
/*
* Backtrack to previous * on mismatch and retry starting one
@@ -58,6 +59,11 @@ bool __pure glob_match(char const *pat, char const *str)
unsigned char c = *str++;
unsigned char d = *pat++;

+ if (nocase) {
+ c = tolower(c);
+ d = tolower(d);
+ }
+
switch (d) {
case '?': /* Wildcard: anything but nul */
if (c == '\0')
@@ -94,13 +100,17 @@ bool __pure glob_match(char const *pat, char const *str)
goto literal;

class += 2;
- /* Normalize inverted ranges like [z-a] */
- if (a > b) {
- unsigned char tmp = a;
+ }
+ if (nocase) {
+ a = tolower(a);
+ b = tolower(b);
+ }
+ /* Normalize inverted ranges like [z-a] */
+ if (a > b) {
+ unsigned char tmp = a;

- a = b;
- b = tmp;
- }
+ a = b;
+ b = tmp;
}
if (a <= c && c <= b)
match = true;
@@ -112,8 +122,11 @@ bool __pure glob_match(char const *pat, char const *str)
}
break;
case '\\':
- if (*pat != '\0')
+ if (*pat != '\0') {
d = *pat++;
+ if (nocase)
+ d = tolower(d);
+ }
fallthrough;
default: /* Literal character */
literal:
@@ -132,4 +145,44 @@ bool __pure glob_match(char const *pat, char const *str)
}
}
}
+
+/**
+ * glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0)
+ * @pat: Shell-style pattern to match, e.g. "*.[ch]".
+ * @str: String to match. The pattern must match the entire string.
+ *
+ * Perform shell-style glob matching, returning true (1) if the match
+ * succeeds, or false (0) if it fails. Equivalent to !fnmatch(@pat, @str, 0).
+ *
+ * Pattern metacharacters are ?, *, [ and \.
+ * (And, inside character classes, !, ^ - and ].)
+ *
+ * This is small and simple and non-recursive, with run-time at most
+ * quadratic: strlen(@str)*strlen(@pat). It does not preprocess the
+ * patterns. An opening bracket without a matching close is matched
+ * literally.
+ *
+ * Return: true if @str matches @pat, false otherwise.
+ */
+bool __pure glob_match(char const *pat, char const *str)
+{
+ return __glob_match(pat, str, false);
+}
EXPORT_SYMBOL(glob_match);
+
+/**
+ * glob_match_nocase - Case-insensitive shell-style pattern matching
+ * @pat: Shell-style pattern to match.
+ * @str: String to match. The pattern must match the entire string.
+ *
+ * Identical to glob_match(), but performs case-insensitive comparisons
+ * for literal characters and character class contents using tolower().
+ * Metacharacters (?, *, [, ]) are not affected by case folding.
+ *
+ * Return: true if @str matches @pat (case-insensitive), false otherwise.
+ */
+bool __pure glob_match_nocase(char const *pat, char const *str)
+{
+ return __glob_match(pat, str, true);
+}
+EXPORT_SYMBOL(glob_match_nocase);
--
2.34.1