]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
string-util: introduce STRING_FILENAME_PART flag for string_is_safe()
authorLennart Poettering <lennart@amutable.com>
Tue, 16 Jun 2026 14:49:38 +0000 (16:49 +0200)
committerLennart Poettering <lennart@amutable.com>
Mon, 22 Jun 2026 12:41:16 +0000 (14:41 +0200)
Whenever we are validating a string that shall appear in a filename
eventually we want to use filename_part_is_valid() rather than file
filename_is_valid(). Let's add explicit support for that to
string_is_safe(), since it's actually a really common case.

src/basic/string-util.c
src/basic/string-util.h
src/test/test-string-util.c

index cfeb2e3917afac2873cdbe3acecb1906c060d788..86d1f2da2c1a2cc0404dd0c08a22537ad66066c4 100644 (file)
@@ -1139,6 +1139,9 @@ bool string_is_safe(const char *p, StringSafeFlags flags) {
         if (FLAGS_SET(flags, STRING_FILENAME) && !filename_is_valid(p))
                 return false;
 
+        if (FLAGS_SET(flags, STRING_FILENAME_PART) && !filename_part_is_valid(p))
+                return false;
+
         return true;
 }
 
index 6242f5593129575a2d3d325ea42b0ed6bceebca3..68ab38c32113b192a431ac9a25a95ad065f9d915 100644 (file)
@@ -229,7 +229,8 @@ typedef enum StringSafeFlags {
         STRING_ALLOW_QUOTES        = 1 << 4, /* Allow quotes (" or ') */
         STRING_ALLOW_GLOBS         = 1 << 5, /* Allow globs (?, * or [) */
         STRING_FILENAME            = 1 << 6, /* Verify the string is valid as regular filename */
-        STRING_DISALLOW_WHITESPACE = 1 << 7, /* Refuse whitespace (space, tab, newline, …) */
+        STRING_FILENAME_PART       = 1 << 7, /* Verify the string is valid as part of a regular filename */
+        STRING_DISALLOW_WHITESPACE = 1 << 8, /* Refuse whitespace (space, tab, newline, …) */
 } StringSafeFlags;
 
 bool string_is_safe(const char *p, StringSafeFlags flags) _pure_;
index 648b8fa839d0d43800b826e2ae99cd57fa8ac232..368cd64b84aa1f5276df8d451246eb0663f0a505 100644 (file)
@@ -1622,6 +1622,20 @@ TEST(string_is_safe) {
         ASSERT_TRUE(string_is_safe("a\nb", STRING_ALLOW_NEWLINES));
         ASSERT_FALSE(string_is_safe("a\nb", STRING_ALLOW_NEWLINES | STRING_DISALLOW_WHITESPACE));
 
+        /* STRING_FILENAME_PART: like STRING_FILENAME, but "." and ".." are accepted; '/' still rejected. */
+        ASSERT_TRUE(string_is_safe("hello", STRING_FILENAME_PART));
+        ASSERT_TRUE(string_is_safe("hello.txt", STRING_FILENAME_PART));
+        ASSERT_TRUE(string_is_safe("...", STRING_FILENAME_PART));
+        ASSERT_TRUE(string_is_safe(".hidden", STRING_FILENAME_PART));
+        ASSERT_TRUE(string_is_safe(".", STRING_FILENAME_PART));             /* accepted, unlike STRING_FILENAME */
+        ASSERT_TRUE(string_is_safe("..", STRING_FILENAME_PART));            /* accepted, unlike STRING_FILENAME */
+        ASSERT_FALSE(string_is_safe("", STRING_FILENAME_PART));             /* empty still rejected by default */
+        ASSERT_TRUE(string_is_safe("", STRING_FILENAME_PART | STRING_ALLOW_EMPTY)); /* ... unless explicitly allowed */
+        ASSERT_FALSE(string_is_safe("/", STRING_FILENAME_PART));
+        ASSERT_FALSE(string_is_safe("/foo", STRING_FILENAME_PART));
+        ASSERT_FALSE(string_is_safe("foo/bar", STRING_FILENAME_PART));
+        ASSERT_FALSE(string_is_safe("foo/", STRING_FILENAME_PART));
+
         /* Pairwise combinations. */
         ASSERT_TRUE(string_is_safe("", STRING_ALLOW_EMPTY | STRING_ASCII));
         ASSERT_FALSE(string_is_safe("über", STRING_ALLOW_EMPTY | STRING_ASCII));