[PATCH 3/3] efi_loader: adjust sorting of capsules

The UEFI specification requires to apply capsules in alphabetical order. The current implementation only sorted by the first letter due to using strcmp() which will stop at the first zero byte.
* Provide function u16_strcasecmp() for case insensitive comparision of UTF-16 strings * Provide a unit test for u16_strcasecmp() * Use the function to sort capsules.
We do not implement the special handling of the rightmost period of capsule names yet.
Heinrich Schuchardt (3): lib: add function u16_strcasecmp() test: unit test for u16_strcasecmp() efi_loader: adjust sorting of capsules
include/charset.h | 13 +++++++++++++ lib/charset.c | 27 +++++++++++++++++++++++++++ lib/efi_loader/efi_capsule.c | 9 ++++++--- test/unicode_ut.c | 25 +++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 3 deletions(-)

Provide a function for comparing UTF-16 strings in a case insensitive manner.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- include/charset.h | 13 +++++++++++++ lib/charset.c | 26 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+)
diff --git a/include/charset.h b/include/charset.h index e900fd789a..6e79d7152e 100644 --- a/include/charset.h +++ b/include/charset.h @@ -173,6 +173,19 @@ s32 utf_to_lower(const s32 code); */ s32 utf_to_upper(const s32 code);
+/** + * u16_strcasecmp() - compare two u16 strings case insensitively + * + * @s1: first string to compare + * @s2: second string to compare + * @n: maximum number of u16 to compare + * Return: 0 if the first n u16 are the same in s1 and s2 + * < 0 if the first different u16 in s1 is less than the + * corresponding u16 in s2 + * > 0 if the first different u16 in s1 is greater than the + */ +int u16_strcasecmp(const u16 *s1, const u16 *s2); + /** * u16_strncmp() - compare two u16 string * diff --git a/lib/charset.c b/lib/charset.c index bece4985bf..b1842755eb 100644 --- a/lib/charset.c +++ b/lib/charset.c @@ -350,6 +350,32 @@ s32 utf_to_upper(const s32 code) return ret; }
+/* + * u16_strcasecmp() - compare two u16 strings case insensitively + * + * @s1: first string to compare + * @s2: second string to compare + * @n: maximum number of u16 to compare + * Return: 0 if the first n u16 are the same in s1 and s2 + * < 0 if the first different u16 in s1 is less than the + * corresponding u16 in s2 + * > 0 if the first different u16 in s1 is greater than the + */ +int u16_strcasecmp(const u16 *s1, const u16 *s2) +{ + int ret = 0; + s32 c1, c2; + + for (;;) { + c1 = utf_to_upper(utf16_get(&s1)); + c2 = utf_to_upper(utf16_get(&s2)); + ret = c1 - c2; + if (ret || !c1 || c1 == -1 || c2 == -1) + break; + } + return ret; +} + /* * u16_strncmp() - compare two u16 string *

Provide a unit test for u16_strcasecmp().
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- test/unicode_ut.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
diff --git a/test/unicode_ut.c b/test/unicode_ut.c index 3547aeffe7..382b796516 100644 --- a/test/unicode_ut.c +++ b/test/unicode_ut.c @@ -624,6 +624,31 @@ static int unicode_test_utf_to_upper(struct unit_test_state *uts) } UNICODE_TEST(unicode_test_utf_to_upper);
+static int unicode_test_u16_strcasecmp(struct unit_test_state *uts) +{ + ut_assert(u16_strcasecmp(u"abcd", u"abcd") == 0); + ut_assert(u16_strcasecmp(u"aBcd", u"abcd") == 0); + ut_assert(u16_strcasecmp(u"abcd", u"abCd") == 0); + ut_assert(u16_strcasecmp(u"abcdE", u"abcd") > 0); + ut_assert(u16_strcasecmp(u"abcd", u"abcdE") < 0); + ut_assert(u16_strcasecmp(u"abcE", u"abcd") > 0); + ut_assert(u16_strcasecmp(u"abcd", u"abcE") < 0); + ut_assert(u16_strcasecmp(u"abcd", u"abcd") == 0); + ut_assert(u16_strcasecmp(u"abcd", u"abcd") == 0); + if (CONFIG_IS_ENABLED(EFI_UNICODE_CAPITALIZATION)) { + /* Cyrillic letters */ + ut_assert(u16_strcasecmp(u"\x043a\x043d\x0438\x0433\x0430", + u"\x041a\x041d\x0418\x0413\x0410") == 0); + ut_assert(u16_strcasecmp(u"\x043a\x043d\x0438\x0433\x0430", + u"\x041a\x041d\x0418\x0413\x0411") < 0); + ut_assert(u16_strcasecmp(u"\x043a\x043d\x0438\x0433\x0431", + u"\x041a\x041d\x0418\x0413\x0410") > 0); + } + + return 0; +} +UNICODE_TEST(unicode_test_u16_strcasecmp); + static int unicode_test_u16_strncmp(struct unit_test_state *uts) { ut_assert(u16_strncmp(u"abc", u"abc", 3) == 0);

Up to now we only compared the first letter of the capsule name to sort them alphabetically. Properly sort by the Unicode alphabet.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com --- lib/efi_loader/efi_capsule.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 1163a2ee30..0997cd248f 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -1108,10 +1108,13 @@ static efi_status_t efi_capsule_scan_dir(u16 ***files, unsigned int *num) /* ignore an error */ EFI_CALL((*dirh->close)(dirh));
- /* in ascii order */ - /* FIXME: u16 version of strcasecmp */ + /* + * Capsule files are applied in case insensitive alphabetic order + * + * TODO: special handling of rightmost period + */ qsort(tmp_files, count, sizeof(*tmp_files), - (int (*)(const void *, const void *))strcasecmp); + (int (*)(const void *, const void *))u16_strcasecmp); *files = tmp_files; *num = count; ret = EFI_SUCCESS;
participants (1)
-
Heinrich Schuchardt