From 023f88a6ab76b9784e9b6c6396227f1490c1a8c2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Apr 2026 14:56:15 +0200 Subject: [PATCH 1/4] parse-util: add safe_atou64_full() --- src/basic/parse-util.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index d92577c0fb..0ee4da2126 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -83,6 +83,11 @@ static inline int safe_atou64(const char *s, uint64_t *ret_u) { return safe_atollu(s, (unsigned long long*) ret_u); } +static inline int safe_atou64_full(const char *s, unsigned base, uint64_t *ret_u) { + assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); + return safe_atollu_full(s, base, (unsigned long long*) ret_u); +} + static inline int safe_atoi64(const char *s, int64_t *ret_i) { assert_cc(sizeof(int64_t) == sizeof(long long)); return safe_atolli(s, (long long*) ret_i); From 2561159d397d8cb70e14d7e2b4a7b722a02854d6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 15 Apr 2026 16:05:37 +0200 Subject: [PATCH 2/4] string-util: add new strrstr_no_case() call --- src/basic/string-util.c | 11 +++++++++++ src/basic/string-util.h | 4 ++++ src/test/test-string-util.c | 23 +++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 9b63516ce0..c3298bc8ee 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -1530,6 +1530,17 @@ char* strrstr_internal(const char *haystack, const char *needle) { return NULL; } +char* strrstr_no_case_internal(const char *haystack, const char *needle) { + if (!haystack || !needle) + return NULL; + + for (const char *p = strchr(haystack, 0); p > haystack; p--) + if (startswith_no_case(p, needle)) + return (char*) p; + + return startswith_no_case(haystack, needle) ? (char*) haystack : NULL; +} + size_t str_common_prefix(const char *a, const char *b) { assert(a); assert(b); diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 5ab4dd9016..65fe5072b3 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -312,4 +312,8 @@ char* strrstr_internal(const char *haystack, const char *needle) _pure_; #define strrstr(haystack, needle) \ const_generic(haystack, strrstr_internal(haystack, needle)) +char* strrstr_no_case_internal(const char *haystack, const char *needle) _pure_; +#define strrstr_no_case(haystack, needle) \ + const_generic(haystack, strrstr_no_case_internal(haystack, needle)) + size_t str_common_prefix(const char *a, const char *b) _pure_; diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 8490f34f69..5707569e7e 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -535,6 +535,29 @@ TEST(endswith_no_case) { assert_se(!endswith_no_case("foobar", "FOOBARFOOFOO")); } +TEST(strrstr_no_case) { + ASSERT_STREQ(strrstr_no_case("fooBARfoobar", "bar"), "bar"); + ASSERT_STREQ(strrstr_no_case("fooBARfoobar", "BAR"), "bar"); + ASSERT_STREQ(strrstr_no_case("fooBARfoobar", "bAR"), "bar"); + ASSERT_STREQ(strrstr_no_case("fooBARfoobar", "FOO"), "foobar"); + ASSERT_STREQ(strrstr_no_case("fooBARfoobar", "foo"), "foobar"); + ASSERT_STREQ(strrstr_no_case("fooBARfoobar", "FoO"), "foobar"); + ASSERT_STREQ(strrstr_no_case("aXaXa", "x"), "Xa"); + ASSERT_STREQ(strrstr_no_case("aXaXa", "X"), "Xa"); + ASSERT_STREQ(strrstr_no_case("xHello", "hello"), "Hello"); + ASSERT_STREQ(strrstr_no_case("Hello", "l"), "lo"); + ASSERT_STREQ(strrstr_no_case("Hello", ""), ""); + ASSERT_STREQ(strrstr_no_case("", ""), ""); + ASSERT_STREQ(strrstr_no_case("FOO", "foo"), "FOO"); + ASSERT_STREQ(strrstr_no_case("hello", "hello"), "hello"); + ASSERT_STREQ(strrstr_no_case("X", "x"), "X"); + + ASSERT_NULL(strrstr_no_case("hello", "xyz")); + ASSERT_NULL(strrstr_no_case("", "x")); + ASSERT_NULL(strrstr_no_case(NULL, "x")); + ASSERT_NULL(strrstr_no_case("x", NULL)); +} + TEST(delete_chars) { char *s, input[] = " hello, waldo. abc"; From d7b6d89d903758f60a9ead5349257f59b3184955 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 15 Apr 2026 16:06:04 +0200 Subject: [PATCH 3/4] string-util: add minor optimization to strrstr() --- src/basic/string-util.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/basic/string-util.c b/src/basic/string-util.c index c3298bc8ee..6168855a75 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -1519,13 +1519,17 @@ char* strrstr_internal(const char *haystack, const char *needle) { /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the * last char, not before. */ - if (*needle == 0) + if (needle[0] == 0) return (char*) strchr(haystack, 0); + /* Special case: for single character strings, just use optimized strrchr() */ + if (needle[1] == 0) + return (char*) strrchr(haystack, needle[0]); + for (const char *p = strstr(haystack, needle), *q; p; p = q) { q = strstr(p + 1, needle); if (!q) - return (char *) p; + return (char*) p; } return NULL; } From 7e8882a96fdfcfd863beffadac494038387453c2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Apr 2026 17:57:10 +0200 Subject: [PATCH 4/4] stat-util: add vfs_free_bytes() The casts and the right fields to multiply aren't entirely trivial, let's add a helper for it. --- src/basic/stat-util.c | 17 +++++++++++++++++ src/basic/stat-util.h | 2 ++ src/coredump/coredump-submit.c | 6 +++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 06634b7df6..e0dc59a863 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -898,3 +898,20 @@ mode_t inode_type_from_string(const char *s) { return MODE_INVALID; } + +int vfs_free_bytes(int fd, uint64_t *ret) { + assert(fd >= 0); + assert(ret); + + /* Safely returns the current available disk space (for root, i.e. including any space reserved for + * root) of the disk referenced by the fd, converted to bytes. */ + + struct statvfs sv; + if (fstatvfs(fd, &sv) < 0) + return -errno; + + if (!MUL_SAFE(ret, (uint64_t) sv.f_frsize, (uint64_t) sv.f_bfree)) + return -ERANGE; + + return 0; +} diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index ec04a2b80c..de9ee03f44 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -161,3 +161,5 @@ static inline bool inode_type_can_hardlink(mode_t m) { * type). */ return IN_SET(m & S_IFMT, S_IFSOCK, S_IFLNK, S_IFREG, S_IFBLK, S_IFCHR, S_IFIFO); } + +int vfs_free_bytes(int fd, uint64_t *ret); diff --git a/src/coredump/coredump-submit.c b/src/coredump/coredump-submit.c index 6ce03cdec0..42d92a32e9 100644 --- a/src/coredump/coredump-submit.c +++ b/src/coredump/coredump-submit.c @@ -305,7 +305,6 @@ static int save_external_coredump( if (storage_on_tmpfs && config->compress) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; uint64_t cgroup_limit = UINT64_MAX; - struct statvfs sv; /* If we can't get the cgroup limit, just ignore it, but don't fail, * try anyway with the config settings. */ @@ -335,8 +334,9 @@ static int save_external_coredump( /* tmpfs might get full quickly, so check the available space too. But don't worry about * errors here, failing to access the storage location will be better logged when writing to * it. */ - if (fstatvfs(fd, &sv) >= 0) - max_size = MIN((uint64_t)sv.f_frsize * (uint64_t)sv.f_bfree, max_size); + uint64_t free_bytes; + if (vfs_free_bytes(fd, &free_bytes) >= 0) + max_size = MIN(free_bytes, max_size); /* Impose a lower minimum, otherwise we will miss the basic headers. */ max_size = MAX(PROCESS_SIZE_MIN, max_size); /* Ensure we can always switch to compressing on the fly in case we are running out of space