All of lore.kernel.org
 help / color / mirror / Atom feed
From: Josh Law <objecting@objecting.org>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Brendan Higgins <brendan.higgins@linux.dev>,
	David Gow <david@davidgow.net>, Rae Moar <raemoar63@gmail.com>,
	linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com,
	linux-kernel@vger.kernel.org, Josh Law <objecting@objecting.org>
Subject: [PATCH v4 4/8] lib/glob: add case-insensitive glob_match_nocase()
Date: Sun, 15 Mar 2026 21:18:03 +0000	[thread overview]
Message-ID: <20260315211807.411173-5-objecting@objecting.org> (raw)
In-Reply-To: <20260315211807.411173-1-objecting@objecting.org>

Add a case-insensitive variant of glob_match() for callers that need
to match patterns regardless of case.

The immediate motivation is the ATA device quirk denylist in
drivers/ata/libata-core.c, which uses glob_match() to match drive
model and firmware revision strings.  ATA IDENTIFY DEVICE data is
space-padded ASCII and the specification does not mandate a particular
case, so a denylist entry could miss a drive that reports its model
string in unexpected casing.  Switching the denylist to
glob_match_nocase() (in a follow-up ATA patch) would make it robust
against such variation.

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           | 75 +++++++++++++++++++++++++++++++++++++-------
 2 files changed, 65 insertions(+), 11 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..172b8ba3cd8e 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.
  *
@@ -20,7 +21,7 @@ MODULE_LICENSE("Dual MIT/GPL");
  * succeeds, or false (0) if it fails.  Equivalent to !fnmatch(@pat, @str, 0).
  *
  * Pattern metacharacters are ?, *, [ and \.
- * (And, inside character classes, !, - and ].)
+ * (And, inside character classes, !, ^ - and ].)
  *
  * This is a small and simple implementation intended for device denylists
  * where a string is matched against a number of patterns.  Thus, it
@@ -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


  parent reply	other threads:[~2026-03-15 21:18 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-15 21:17 [PATCH v4 0/8] lib/glob: bug fixes, new features, and tests Josh Law
2026-03-15 21:18 ` [PATCH v4 1/8] lib/glob: normalize inverted character class ranges Josh Law
2026-03-15 21:18 ` [PATCH v4 2/8] lib/glob: treat trailing backslash as literal character Josh Law
2026-03-15 21:18 ` [PATCH v4 3/8] lib/glob: accept [^...] as character class negation syntax Josh Law
2026-03-15 21:18 ` Josh Law [this message]
2026-03-15 21:18 ` [PATCH v4 5/8] lib/glob: add glob_validate() for pattern syntax checking Josh Law
2026-03-15 21:18 ` [PATCH v4 6/8] lib/tests: add glob test cases for escapes, edge cases, and new features Josh Law
2026-03-15 21:18 ` [PATCH v4 7/8] lib/tests: add kunit tests for glob_match_nocase() and glob_validate() Josh Law
2026-03-15 21:18 ` [PATCH v4 8/8] kunit: validate glob filter patterns before use Josh Law
2026-03-17  7:19 ` [PATCH v4 0/8] lib/glob: bug fixes, new features, and tests David Gow
2026-03-17  7:30   ` Josh Law
2026-03-17 16:32   ` Josh Law
2026-03-18  7:19     ` David Gow
2026-03-18 15:54       ` Josh Law
2026-03-19 22:23       ` Josh Law

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260315211807.411173-5-objecting@objecting.org \
    --to=objecting@objecting.org \
    --cc=akpm@linux-foundation.org \
    --cc=brendan.higgins@linux.dev \
    --cc=david@davidgow.net \
    --cc=kunit-dev@googlegroups.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=raemoar63@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.