5d016e
From 1677b88d01729f514dd17145e3aefaa5db6cdf95 Mon Sep 17 00:00:00 2001
5d016e
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
5d016e
Date: Wed, 10 Mar 2021 11:49:02 +0100
5d016e
Subject: [PATCH 1/9] fstab-generator: do not propagate error if we fail to
5d016e
 canonicalize
5d016e
5d016e
r is used for the return value of the function, so we shouldn't
5d016e
use it a non-fatal check.
5d016e
---
5d016e
 src/fstab-generator/fstab-generator.c | 6 +++---
5d016e
 1 file changed, 3 insertions(+), 3 deletions(-)
5d016e
5d016e
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
5d016e
index 0910a9aa61b..7cb4ea286dc 100644
5d016e
--- a/src/fstab-generator/fstab-generator.c
5d016e
+++ b/src/fstab-generator/fstab-generator.c
5d016e
@@ -611,11 +611,11 @@ static int parse_fstab(bool initrd) {
5d016e
                          * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
5d016e
                          * where a symlink refers to another mount target; this works assuming the sub-mountpoint
5d016e
                          * target is the final directory. */
5d016e
-                        r = chase_symlinks(where, initrd ? "/sysroot" : NULL,
5d016e
+                        k = chase_symlinks(where, initrd ? "/sysroot" : NULL,
5d016e
                                            CHASE_PREFIX_ROOT | CHASE_NONEXISTENT,
5d016e
                                            &canonical_where, NULL);
5d016e
-                        if (r < 0) /* If we can't canonicalize we continue on as if it wasn't a symlink */
5d016e
-                                log_debug_errno(r, "Failed to read symlink target for %s, ignoring: %m", where);
5d016e
+                        if (k < 0) /* If we can't canonicalize we continue on as if it wasn't a symlink */
5d016e
+                                log_debug_errno(k, "Failed to read symlink target for %s, ignoring: %m", where);
5d016e
                         else if (streq(canonical_where, where)) /* If it was fully canonicalized, suppress the change */
5d016e
                                 canonical_where = mfree(canonical_where);
5d016e
                         else
5d016e
5d016e
From 924f65030529d5a232c2be4ab6e2642dfdc2ea71 Mon Sep 17 00:00:00 2001
5d016e
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
5d016e
Date: Wed, 10 Mar 2021 13:20:47 +0100
5d016e
Subject: [PATCH 2/9] generators: warn but ignore failure to write timeouts
5d016e
5d016e
When we failed to split the options (because of disallowed quoting syntax, which
5d016e
might be a bug in its own), we would silently fail. Instead, let's emit a warning.
5d016e
Since we ignore the value if we cannot parse it anyway, let's ignore this error
5d016e
too.
5d016e
---
5d016e
 src/shared/generator.c | 8 ++++++--
5d016e
 1 file changed, 6 insertions(+), 2 deletions(-)
5d016e
5d016e
diff --git a/src/shared/generator.c b/src/shared/generator.c
5d016e
index 8b95c772db1..41922d67d8c 100644
5d016e
--- a/src/shared/generator.c
5d016e
+++ b/src/shared/generator.c
5d016e
@@ -216,8 +216,12 @@ int generator_write_timeouts(
5d016e
         r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
5d016e
                                        "x-systemd.device-timeout\0",
5d016e
                                  NULL, &timeout, filtered);
5d016e
-        if (r <= 0)
5d016e
-                return r;
5d016e
+        if (r < 0) {
5d016e
+                log_warning_errno(r, "Failed to parse fstab options, ignoring: %m");
5d016e
+                return 0;
5d016e
+        }
5d016e
+        if (r == 0)
5d016e
+                return 0;
5d016e
 
5d016e
         r = parse_sec_fix_0(timeout, &u);
5d016e
         if (r < 0) {
5d016e
5d016e
From 5fa2da125157a1beca508792ee5d9be833c2c0cb Mon Sep 17 00:00:00 2001
5d016e
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
5d016e
Date: Wed, 10 Mar 2021 13:35:26 +0100
5d016e
Subject: [PATCH 3/9] shared/fstab-util: immediately drop empty options again
5d016e
MIME-Version: 1.0
5d016e
Content-Type: text/plain; charset=UTF-8
5d016e
Content-Transfer-Encoding: 8bit
5d016e
5d016e
In the conversion from strv_split() to strv_split_full() done in
5d016e
7bb553bb98a57b4e03804f8192bdc5a534325582, EXTRACT_DONT_COALESCE_SEPARATORS was
5d016e
added. I think this was just by mistakeā€¦ We never look for "empty options", so
5d016e
whether we immediately ignore the extra separator or store the empty string in
5d016e
strv, should make no difference.
5d016e
---
5d016e
 src/shared/fstab-util.c    | 2 +-
5d016e
 src/test/test-fstab-util.c | 6 +++++-
5d016e
 2 files changed, 6 insertions(+), 2 deletions(-)
5d016e
5d016e
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
5d016e
index 292b97cd692..1ddcd371cfc 100644
5d016e
--- a/src/shared/fstab-util.c
5d016e
+++ b/src/shared/fstab-util.c
5d016e
@@ -140,7 +140,7 @@ int fstab_filter_options(const char *opts, const char *names,
5d016e
                                 break;
5d016e
                 }
5d016e
         } else {
5d016e
-                r = strv_split_full(&stor, opts, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
5d016e
+                r = strv_split_full(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS);
5d016e
                 if (r < 0)
5d016e
                         return r;
5d016e
 
5d016e
diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c
5d016e
index 222ffbb2a75..ebbdd05ca62 100644
5d016e
--- a/src/test/test-fstab-util.c
5d016e
+++ b/src/test/test-fstab-util.c
5d016e
@@ -91,9 +91,13 @@ static void test_fstab_filter_options(void) {
5d016e
         do_fstab_filter_options("opt =0", "x-opt\0opt\0noopt\0x-noopt\0", 0, NULL, NULL, NULL);
5d016e
         do_fstab_filter_options(" opt ", "opt\0x-opt\0", 0, NULL, NULL, NULL);
5d016e
 
5d016e
-        /* check function will NULL args */
5d016e
+        /* check function with NULL args */
5d016e
         do_fstab_filter_options(NULL, "opt\0", 0, NULL, NULL, "");
5d016e
         do_fstab_filter_options("", "opt\0", 0, NULL, NULL, "");
5d016e
+
5d016e
+        /* unnecessary comma separators */
5d016e
+        do_fstab_filter_options("opt=x,,,,", "opt\0", 1, "opt", "x", "");
5d016e
+        do_fstab_filter_options(",,,opt=x,,,,", "opt\0", 1, "opt", "x", "");
5d016e
 }
5d016e
 
5d016e
 static void test_fstab_find_pri(void) {
5d016e
5d016e
From 8723c716c795b4083372b11dd7065bca2aadbc70 Mon Sep 17 00:00:00 2001
5d016e
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
5d016e
Date: Wed, 10 Mar 2021 14:23:23 +0100
5d016e
Subject: [PATCH 4/9] basic/extract_word: try to explain what the various
5d016e
 options do
5d016e
5d016e
A test for stripping of escaped backslashes without any flags was explicitly
5d016e
added back in 4034a06ddb82ec9868cd52496fef2f5faa25575f. So it seems to be on
5d016e
purpose, though I would say that this is at least surprising and hence deserves
5d016e
a comment.
5d016e
5d016e
In test-extract-word, add tests for standalone EXTRACT_UNESCAPE_SEPARATORS.
5d016e
Only behaviour combined with EXTRACT_CUNESCAPE was tested.
5d016e
---
5d016e
 src/basic/extract-word.h     | 16 +++++++++-------
5d016e
 src/test/test-extract-word.c | 21 +++++++++++++++++++++
5d016e
 2 files changed, 30 insertions(+), 7 deletions(-)
5d016e
5d016e
diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h
5d016e
index d1de32e5806..3c1e7d98b64 100644
5d016e
--- a/src/basic/extract-word.h
5d016e
+++ b/src/basic/extract-word.h
5d016e
@@ -4,13 +4,15 @@
5d016e
 #include "macro.h"
5d016e
 
5d016e
 typedef enum ExtractFlags {
5d016e
-        EXTRACT_RELAX                    = 1 << 0,
5d016e
-        EXTRACT_CUNESCAPE                = 1 << 1,
5d016e
-        EXTRACT_CUNESCAPE_RELAX          = 1 << 2,
5d016e
-        EXTRACT_UNESCAPE_SEPARATORS      = 1 << 3,
5d016e
-        EXTRACT_UNQUOTE                  = 1 << 4,
5d016e
-        EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5,
5d016e
-        EXTRACT_RETAIN_ESCAPE            = 1 << 6,
5d016e
+        EXTRACT_RELAX                    = 1 << 0, /* Allow unbalanced quote and eat up trailing backslash. */
5d016e
+        EXTRACT_CUNESCAPE                = 1 << 1, /* Unescape known escape sequences. */
5d016e
+        EXTRACT_CUNESCAPE_RELAX          = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */
5d016e
+        EXTRACT_UNESCAPE_SEPARATORS      = 1 << 3, /* Unescape separators (those specified, or whitespace by default). */
5d016e
+        EXTRACT_UNQUOTE                  = 1 << 4, /* Remove quoting with "" and ''. */
5d016e
+        EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, /* Don't treat multiple adjacent separators as one */
5d016e
+        EXTRACT_RETAIN_ESCAPE            = 1 << 6, /* Treat escape character '\' as any other character without special meaning */
5d016e
+
5d016e
+        /* Note that if no flags are specified, escaped escape characters will be silently stripped. */
5d016e
 } ExtractFlags;
5d016e
 
5d016e
 int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
5d016e
diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c
5d016e
index 56b516fe40a..f718556d399 100644
5d016e
--- a/src/test/test-extract-word.c
5d016e
+++ b/src/test/test-extract-word.c
5d016e
@@ -344,6 +344,27 @@ static void test_extract_first_word(void) {
5d016e
         free(t);
5d016e
         assert_se(p == NULL);
5d016e
 
5d016e
+        p = "\\:";
5d016e
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
5d016e
+        assert_se(streq(t, ":"));
5d016e
+        free(t);
5d016e
+        assert_se(p == NULL);
5d016e
+
5d016e
+        p = "a\\:b";
5d016e
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
5d016e
+        assert_se(streq(t, "a:b"));
5d016e
+        free(t);
5d016e
+        assert_se(p == NULL);
5d016e
+
5d016e
+        p = "a\\ b:c";
5d016e
+        assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
5d016e
+        assert_se(streq(t, "a b"));
5d016e
+        free(t);
5d016e
+        assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
5d016e
+        assert_se(streq(t, "c"));
5d016e
+        free(t);
5d016e
+        assert_se(p == NULL);
5d016e
+
5d016e
         p = "\\:";
5d016e
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
5d016e
         assert_se(streq(t, ":"));
5d016e
5d016e
From 76c4e48ee603593822b3076c4cf5b63768ec55e3 Mon Sep 17 00:00:00 2001
5d016e
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
5d016e
Date: Wed, 10 Mar 2021 15:17:56 +0100
5d016e
Subject: [PATCH 5/9] basic/extract-word: allow escape character to be escaped
5d016e
5d016e
With EXTRACT_UNESCAPE_SEPARATORS, backslash is used to escape the separator.
5d016e
But it wasn't possible to insert the backslash itself. Let's allow this and
5d016e
add test.
5d016e
---
5d016e
 src/basic/extract-word.c     |  4 ++--
5d016e
 src/test/test-extract-word.c | 24 ++++++++++++++++++++++++
5d016e
 2 files changed, 26 insertions(+), 2 deletions(-)
5d016e
5d016e
diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c
5d016e
index 4e4e7e8ce93..06b813c031a 100644
5d016e
--- a/src/basic/extract-word.c
5d016e
+++ b/src/basic/extract-word.c
5d016e
@@ -102,8 +102,8 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
5d016e
                                         else
5d016e
                                                 sz += utf8_encode_unichar(s + sz, u);
5d016e
                                 } else if ((flags & EXTRACT_UNESCAPE_SEPARATORS) &&
5d016e
-                                           strchr(separators, **p))
5d016e
-                                        /* An escaped separator char */
5d016e
+                                           (strchr(separators, **p) || **p == '\\'))
5d016e
+                                        /* An escaped separator char or the escape char itself */
5d016e
                                         s[sz++] = c;
5d016e
                                 else if (flags & EXTRACT_CUNESCAPE_RELAX) {
5d016e
                                         s[sz++] = '\\';
5d016e
diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c
5d016e
index f718556d399..217c600dac6 100644
5d016e
--- a/src/test/test-extract-word.c
5d016e
+++ b/src/test/test-extract-word.c
5d016e
@@ -365,6 +365,18 @@ static void test_extract_first_word(void) {
5d016e
         free(t);
5d016e
         assert_se(p == NULL);
5d016e
 
5d016e
+        p = "a\\ b:c\\x";
5d016e
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == -EINVAL);
5d016e
+
5d016e
+        p = "a\\\\ b:c\\\\x";
5d016e
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
5d016e
+        assert_se(streq(t, "a\\ b"));
5d016e
+        free(t);
5d016e
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1);
5d016e
+        assert_se(streq(t, "c\\x"));
5d016e
+        free(t);
5d016e
+        assert_se(p == NULL);
5d016e
+
5d016e
         p = "\\:";
5d016e
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
5d016e
         assert_se(streq(t, ":"));
5d016e
@@ -386,6 +398,18 @@ static void test_extract_first_word(void) {
5d016e
         free(t);
5d016e
         assert_se(p == NULL);
5d016e
 
5d016e
+        p = "a\\ b:c\\x";
5d016e
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == -EINVAL);
5d016e
+
5d016e
+        p = "a\\\\ b:c\\\\x";
5d016e
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
5d016e
+        assert_se(streq(t, "a\\ b"));
5d016e
+        free(t);
5d016e
+        assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1);
5d016e
+        assert_se(streq(t, "c\\x"));
5d016e
+        free(t);
5d016e
+        assert_se(p == NULL);
5d016e
+
5d016e
         p = "\\:";
5d016e
         assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == -EINVAL);
5d016e
 
5d016e
5d016e
From 3141089f53274849ecedb84aacc6a35fd679d3c2 Mon Sep 17 00:00:00 2001
5d016e
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
5d016e
Date: Wed, 10 Mar 2021 15:39:47 +0100
5d016e
Subject: [PATCH 6/9] basic/extract-word: rename flag
5d016e
5d016e
The flag enables "relaxed mode" for all kinds of unescaping, not just c-unescaping.
5d016e
---
5d016e
 src/basic/extract-word.c     | 16 ++++++++--------
5d016e
 src/basic/extract-word.h     |  2 +-
5d016e
 src/resolve/resolved-conf.c  |  2 +-
5d016e
 src/test/test-extract-word.c | 22 +++++++++++-----------
5d016e
 src/test/test-strv.c         |  4 ++--
5d016e
 5 files changed, 23 insertions(+), 23 deletions(-)
5d016e
5d016e
diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c
5d016e
index 06b813c031a..d1af11318a8 100644
5d016e
--- a/src/basic/extract-word.c
5d016e
+++ b/src/basic/extract-word.c
5d016e
@@ -69,14 +69,14 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
5d016e
                                 return -ENOMEM;
5d016e
 
5d016e
                         if (c == 0) {
5d016e
-                                if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
5d016e
+                                if ((flags & EXTRACT_UNESCAPE_RELAX) &&
5d016e
                                     (quote == 0 || flags & EXTRACT_RELAX)) {
5d016e
                                         /* If we find an unquoted trailing backslash and we're in
5d016e
-                                         * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
5d016e
+                                         * EXTRACT_UNESCAPE_RELAX mode, keep it verbatim in the
5d016e
                                          * output.
5d016e
                                          *
5d016e
                                          * Unbalanced quotes will only be allowed in EXTRACT_RELAX
5d016e
-                                         * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
5d016e
+                                         * mode, EXTRACT_UNESCAPE_RELAX mode does not allow them.
5d016e
                                          */
5d016e
                                         s[sz++] = '\\';
5d016e
                                         goto finish_force_terminate;
5d016e
@@ -105,7 +105,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
5d016e
                                            (strchr(separators, **p) || **p == '\\'))
5d016e
                                         /* An escaped separator char or the escape char itself */
5d016e
                                         s[sz++] = c;
5d016e
-                                else if (flags & EXTRACT_CUNESCAPE_RELAX) {
5d016e
+                                else if (flags & EXTRACT_UNESCAPE_RELAX) {
5d016e
                                         s[sz++] = '\\';
5d016e
                                         s[sz++] = c;
5d016e
                                 } else
5d016e
@@ -196,7 +196,7 @@ int extract_first_word_and_warn(
5d016e
                 const char *rvalue) {
5d016e
 
5d016e
         /* Try to unquote it, if it fails, warn about it and try again
5d016e
-         * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
5d016e
+         * but this time using EXTRACT_UNESCAPE_RELAX to keep the
5d016e
          * backslashes verbatim in invalid escape sequences. */
5d016e
 
5d016e
         const char *save;
5d016e
@@ -207,11 +207,11 @@ int extract_first_word_and_warn(
5d016e
         if (r >= 0)
5d016e
                 return r;
5d016e
 
5d016e
-        if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
5d016e
+        if (r == -EINVAL && !(flags & EXTRACT_UNESCAPE_RELAX)) {
5d016e
 
5d016e
-                /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
5d016e
+                /* Retry it with EXTRACT_UNESCAPE_RELAX. */
5d016e
                 *p = save;
5d016e
-                r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
5d016e
+                r = extract_first_word(p, ret, separators, flags|EXTRACT_UNESCAPE_RELAX);
5d016e
                 if (r >= 0) {
5d016e
                         /* It worked this time, hence it must have been an invalid escape sequence. */
5d016e
                         log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret);
5d016e
diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h
5d016e
index 3c1e7d98b64..0e9e77e93d3 100644
5d016e
--- a/src/basic/extract-word.h
5d016e
+++ b/src/basic/extract-word.h
5d016e
@@ -6,7 +6,7 @@
5d016e
 typedef enum ExtractFlags {
5d016e
         EXTRACT_RELAX                    = 1 << 0, /* Allow unbalanced quote and eat up trailing backslash. */
5d016e
         EXTRACT_CUNESCAPE                = 1 << 1, /* Unescape known escape sequences. */
5d016e
-        EXTRACT_CUNESCAPE_RELAX          = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */
5d016e
+        EXTRACT_UNESCAPE_RELAX           = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */
5d016e
         EXTRACT_UNESCAPE_SEPARATORS      = 1 << 3, /* Unescape separators (those specified, or whitespace by default). */
5d016e
         EXTRACT_UNQUOTE                  = 1 << 4, /* Remove quoting with "" and ''. */
5d016e
         EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, /* Don't treat multiple adjacent separators as one */
5d016e
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
5d016e
index f2a33162517..87d1794a741 100644
5d016e
--- a/src/resolve/resolved-conf.c
5d016e
+++ b/src/resolve/resolved-conf.c
5d016e
@@ -348,7 +348,7 @@ int config_parse_dnssd_txt(
5d016e
                 int r;
5d016e
 
5d016e
                 r = extract_first_word(&rvalue, &word, NULL,
5d016e
-                                       EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX);
5d016e
+                                       EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX);
5d016e
                 if (r == 0)
5d016e
                         break;
5d016e
                 if (r == -ENOMEM)
5d016e
diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c
5d016e
index 217c600dac6..f1085266df2 100644
5d016e
--- a/src/test/test-extract-word.c
5d016e
+++ b/src/test/test-extract-word.c
5d016e
@@ -172,19 +172,19 @@ static void test_extract_first_word(void) {
5d016e
         assert_se(isempty(p));
5d016e
 
5d016e
         p = original = "fooo\\";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX) > 0);
5d016e
         assert_se(streq(t, "fooo\\"));
5d016e
         free(t);
5d016e
         assert_se(isempty(p));
5d016e
 
5d016e
         p = original = "fooo\\";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
5d016e
         assert_se(streq(t, "fooo\\"));
5d016e
         free(t);
5d016e
         assert_se(isempty(p));
5d016e
 
5d016e
         p = original = "fooo\\";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
5d016e
         assert_se(streq(t, "fooo\\"));
5d016e
         free(t);
5d016e
         assert_se(isempty(p));
5d016e
@@ -230,17 +230,17 @@ static void test_extract_first_word(void) {
5d016e
         assert_se(isempty(p));
5d016e
 
5d016e
         p = original = "\"foo\\";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE_RELAX) == -EINVAL);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_UNESCAPE_RELAX) == -EINVAL);
5d016e
         assert_se(p == original + 5);
5d016e
 
5d016e
         p = original = "\"foo\\";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
5d016e
         assert_se(streq(t, "foo\\"));
5d016e
         free(t);
5d016e
         assert_se(isempty(p));
5d016e
 
5d016e
         p = original = "\"foo\\";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
5d016e
         assert_se(streq(t, "foo\\"));
5d016e
         free(t);
5d016e
         assert_se(isempty(p));
5d016e
@@ -252,13 +252,13 @@ static void test_extract_first_word(void) {
5d016e
         assert_se(p == original + 10);
5d016e
 
5d016e
         p = original = "fooo\\ bar quux";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX) > 0);
5d016e
         assert_se(streq(t, "fooo bar"));
5d016e
         free(t);
5d016e
         assert_se(p == original + 10);
5d016e
 
5d016e
         p = original = "fooo\\ bar quux";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0);
5d016e
         assert_se(streq(t, "fooo bar"));
5d016e
         free(t);
5d016e
         assert_se(p == original + 10);
5d016e
@@ -268,7 +268,7 @@ static void test_extract_first_word(void) {
5d016e
         assert_se(p == original + 5);
5d016e
 
5d016e
         p = original = "fooo\\ bar quux";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
5d016e
         assert_se(streq(t, "fooo\\ bar"));
5d016e
         free(t);
5d016e
         assert_se(p == original + 10);
5d016e
@@ -278,13 +278,13 @@ static void test_extract_first_word(void) {
5d016e
         assert_se(p == original + 1);
5d016e
 
5d016e
         p = original = "\\w+@\\K[\\d.]+";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
5d016e
         assert_se(streq(t, "\\w+@\\K[\\d.]+"));
5d016e
         free(t);
5d016e
         assert_se(isempty(p));
5d016e
 
5d016e
         p = original = "\\w+\\b";
5d016e
-        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
5d016e
+        assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0);
5d016e
         assert_se(streq(t, "\\w+\b"));
5d016e
         free(t);
5d016e
         assert_se(isempty(p));
5d016e
diff --git a/src/test/test-strv.c b/src/test/test-strv.c
5d016e
index 162d8bed951..039bb2c78af 100644
5d016e
--- a/src/test/test-strv.c
5d016e
+++ b/src/test/test-strv.c
5d016e
@@ -333,12 +333,12 @@ static void test_strv_split(void) {
5d016e
         l = strv_free_erase(l);
5d016e
 
5d016e
         assert_se(strv_split_full(&l, "    'one'  \"  two\t three \"' four  five", NULL,
5d016e
-                                     EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_CUNESCAPE_RELAX) == 2);
5d016e
+                                     EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_UNESCAPE_RELAX) == 2);
5d016e
         assert_se(strv_equal(l, (char**) input_table_quoted_joined));
5d016e
 
5d016e
         l = strv_free_erase(l);
5d016e
 
5d016e
-        assert_se(strv_split_full(&l, "\\", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_CUNESCAPE_RELAX) == 1);
5d016e
+        assert_se(strv_split_full(&l, "\\", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_UNESCAPE_RELAX) == 1);
5d016e
         assert_se(strv_equal(l, STRV_MAKE("\\")));
5d016e
 }
5d016e
 
5d016e
5d016e
From 0264b404b9f193b70a19db0f600cf6bab3a05368 Mon Sep 17 00:00:00 2001
5d016e
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
5d016e
Date: Wed, 10 Mar 2021 16:53:38 +0100
5d016e
Subject: [PATCH 7/9] shared/fstab-util: pass through the escape character
5d016e
MIME-Version: 1.0
5d016e
Content-Type: text/plain; charset=UTF-8
5d016e
Content-Transfer-Encoding: 8bit
5d016e
5d016e
ā€¦ when not used to escape the separator (,) or the escape character (\).
5d016e
This mostly restores behaviour from before 0645b83a40d1c782f173c4d8440ab2fc82a75006,
5d016e
but still allows "," to be escaped.
5d016e
5d016e
Partially fixes #18952.
5d016e
---
5d016e
 src/shared/fstab-util.c    | 20 ++++++++++++--------
5d016e
 src/test/test-fstab-util.c |  4 ++++
5d016e
 2 files changed, 16 insertions(+), 8 deletions(-)
5d016e
5d016e
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
5d016e
index 1ddcd371cfc..8dc1733c0de 100644
5d016e
--- a/src/shared/fstab-util.c
5d016e
+++ b/src/shared/fstab-util.c
5d016e
@@ -97,16 +97,18 @@ int fstab_filter_options(const char *opts, const char *names,
5d016e
                 for (const char *word = opts;;) {
5d016e
                         const char *end = word;
5d016e
 
5d016e
-                        /* Look for an *non-escaped* comma separator. Only commas can be escaped, so "\," is
5d016e
-                         * the only valid escape sequence, so we can do a very simple test here. */
5d016e
+                        /* Look for a *non-escaped* comma separator. Only commas and backslashes can be
5d016e
+                         * escaped, so "\," and "\\" are the only valid escape sequences, and we can do a
5d016e
+                         * very simple test here. */
5d016e
                         for (;;) {
5d016e
-                                size_t n = strcspn(end, ",");
5d016e
+                                end += strcspn(end, ",\\");
5d016e
 
5d016e
-                                end += n;
5d016e
-                                if (n > 0 && end[-1] == '\\')
5d016e
-                                        end++;
5d016e
-                                else
5d016e
+                                if (IN_SET(*end, ',', '\0'))
5d016e
                                         break;
5d016e
+                                assert(*end == '\\');
5d016e
+                                end ++;                 /* Skip the backslash */
5d016e
+                                if (*end != '\0')
5d016e
+                                        end ++;         /* Skip the escaped char, but watch out for a trailing commma */
5d016e
                         }
5d016e
 
5d016e
                         NULSTR_FOREACH(name, names) {
5d016e
@@ -140,7 +142,9 @@ int fstab_filter_options(const char *opts, const char *names,
5d016e
                                 break;
5d016e
                 }
5d016e
         } else {
5d016e
-                r = strv_split_full(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS);
5d016e
+                /* For backwards compatibility, we need to pass-through escape characters.
5d016e
+                 * The only ones we "consume" are the ones used as "\," or "\\". */
5d016e
+                r = strv_split_full(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS | EXTRACT_UNESCAPE_RELAX);
5d016e
                 if (r < 0)
5d016e
                         return r;
5d016e
 
5d016e
diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c
5d016e
index ebbdd05ca62..3a7ec170d65 100644
5d016e
--- a/src/test/test-fstab-util.c
5d016e
+++ b/src/test/test-fstab-util.c
5d016e
@@ -98,6 +98,10 @@ static void test_fstab_filter_options(void) {
5d016e
         /* unnecessary comma separators */
5d016e
         do_fstab_filter_options("opt=x,,,,", "opt\0", 1, "opt", "x", "");
5d016e
         do_fstab_filter_options(",,,opt=x,,,,", "opt\0", 1, "opt", "x", "");
5d016e
+
5d016e
+        /* escaped characters */
5d016e
+        do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt1\0", 1, "opt1", "\\", "opt2=\\xff");
5d016e
+        do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt2\0", 1, "opt2", "\\xff", "opt1=\\");
5d016e
 }
5d016e
 
5d016e
 static void test_fstab_find_pri(void) {
5d016e
5d016e
From ff0c31bc2722eed528eae6644a104e85ed97f2f1 Mon Sep 17 00:00:00 2001
5d016e
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
5d016e
Date: Thu, 11 Mar 2021 10:37:36 +0100
5d016e
Subject: [PATCH 8/9] shared/fstab-util: teach fstab_filter_options() a mode
5d016e
 where all values are returned
5d016e
5d016e
Apart from tests, the new argument isn't used anywhere, so there should be no
5d016e
functional change. Note that the two arms of the big conditional are switched, so the
5d016e
diff is artificially inflated. The actual code change is rather small. I dropped the
5d016e
path which extracts ret_value manually, because it wasn't supporting unescaping of the
5d016e
escape character properly.
5d016e
---
5d016e
 src/core/mount.c                      |   2 +-
5d016e
 src/cryptsetup/cryptsetup-generator.c |   9 +-
5d016e
 src/fstab-generator/fstab-generator.c |   2 +-
5d016e
 src/shared/fstab-util.c               | 124 +++++++++++++----------
5d016e
 src/shared/fstab-util.h               |  12 ++-
5d016e
 src/shared/generator.c                |   2 +-
5d016e
 src/test/test-fstab-util.c            | 139 +++++++++++++++-----------
5d016e
 7 files changed, 169 insertions(+), 121 deletions(-)
5d016e
5d016e
diff --git a/src/core/mount.c b/src/core/mount.c
5d016e
index 23b558859c2..ca5d0939a18 100644
5d016e
--- a/src/core/mount.c
5d016e
+++ b/src/core/mount.c
5d016e
@@ -1019,7 +1019,7 @@ static void mount_enter_mounting(Mount *m) {
5d016e
         if (p) {
5d016e
                 _cleanup_free_ char *opts = NULL;
5d016e
 
5d016e
-                r = fstab_filter_options(p->options, "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts);
5d016e
+                r = fstab_filter_options(p->options, "nofail\0" "noauto\0" "auto\0", NULL, NULL, NULL, &opts);
5d016e
                 if (r < 0)
5d016e
                         goto fail;
5d016e
 
5d016e
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
5d016e
index 74f739b5139..98c8408da54 100644
5d016e
--- a/src/cryptsetup/cryptsetup-generator.c
5d016e
+++ b/src/cryptsetup/cryptsetup-generator.c
5d016e
@@ -301,7 +301,9 @@ static int create_disk(
5d016e
         netdev = fstab_test_option(options, "_netdev\0");
5d016e
         attach_in_initrd = fstab_test_option(options, "x-initrd.attach\0");
5d016e
 
5d016e
-        keyfile_can_timeout = fstab_filter_options(options, "keyfile-timeout\0", NULL, &keyfile_timeout_value, NULL);
5d016e
+        keyfile_can_timeout = fstab_filter_options(options,
5d016e
+                                                   "keyfile-timeout\0",
5d016e
+                                                   NULL, &keyfile_timeout_value, NULL, NULL);
5d016e
         if (keyfile_can_timeout < 0)
5d016e
                 return log_error_errno(keyfile_can_timeout, "Failed to parse keyfile-timeout= option value: %m");
5d016e
 
5d016e
@@ -310,11 +312,12 @@ static int create_disk(
5d016e
                 "header\0",
5d016e
                 NULL,
5d016e
                 &header_path,
5d016e
+                NULL,
5d016e
                 headerdev ? &filtered_header : NULL);
5d016e
         if (detached_header < 0)
5d016e
                 return log_error_errno(detached_header, "Failed to parse header= option value: %m");
5d016e
 
5d016e
-        tmp = fstab_filter_options(options, "tmp\0", NULL, &tmp_fstype, NULL);
5d016e
+        tmp = fstab_filter_options(options, "tmp\0", NULL, &tmp_fstype, NULL, NULL);
5d016e
         if (tmp < 0)
5d016e
                 return log_error_errno(tmp, "Failed to parse tmp= option value: %m");
5d016e
 
5d016e
@@ -602,7 +605,7 @@ static int filter_header_device(const char *options,
5d016e
         assert(ret_headerdev);
5d016e
         assert(ret_filtered_headerdev_options);
5d016e
 
5d016e
-        r = fstab_filter_options(options, "header\0", NULL, &headerspec, &filtered_headerspec);
5d016e
+        r = fstab_filter_options(options, "header\0", NULL, &headerspec, NULL, &filtered_headerspec);
5d016e
         if (r < 0)
5d016e
                 return log_error_errno(r, "Failed to parse header= option value: %m");
5d016e
 
5d016e
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
5d016e
index 7cb4ea286dc..b454a5980d4 100644
5d016e
--- a/src/fstab-generator/fstab-generator.c
5d016e
+++ b/src/fstab-generator/fstab-generator.c
5d016e
@@ -200,7 +200,7 @@ static int write_timeout(
5d016e
         usec_t u;
5d016e
         int r;
5d016e
 
5d016e
-        r = fstab_filter_options(opts, filter, NULL, &timeout, NULL);
5d016e
+        r = fstab_filter_options(opts, filter, NULL, &timeout, NULL, NULL);
5d016e
         if (r < 0)
5d016e
                 return log_warning_errno(r, "Failed to parse options: %m");
5d016e
         if (r == 0)
5d016e
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
5d016e
index 8dc1733c0de..6674ed4a19f 100644
5d016e
--- a/src/shared/fstab-util.c
5d016e
+++ b/src/shared/fstab-util.c
5d016e
@@ -79,21 +79,80 @@ int fstab_is_mount_point(const char *mount) {
5d016e
         return false;
5d016e
 }
5d016e
 
5d016e
-int fstab_filter_options(const char *opts, const char *names,
5d016e
-                         const char **ret_namefound, char **ret_value, char **ret_filtered) {
5d016e
+int fstab_filter_options(
5d016e
+                const char *opts,
5d016e
+                const char *names,
5d016e
+                const char **ret_namefound,
5d016e
+                char **ret_value,
5d016e
+                char ***ret_values,
5d016e
+                char **ret_filtered) {
5d016e
+
5d016e
         const char *name, *namefound = NULL, *x;
5d016e
-        _cleanup_strv_free_ char **stor = NULL;
5d016e
-        _cleanup_free_ char *v = NULL, **strv = NULL;
5d016e
+        _cleanup_strv_free_ char **stor = NULL, **values = NULL;
5d016e
+        _cleanup_free_ char *value = NULL, **filtered = NULL;
5d016e
         int r;
5d016e
 
5d016e
         assert(names && *names);
5d016e
+        assert(!(ret_value && ret_values));
5d016e
 
5d016e
         if (!opts)
5d016e
                 goto answer;
5d016e
 
5d016e
-        /* If !ret_value and !ret_filtered, this function is not allowed to fail. */
5d016e
+        /* Finds any options matching 'names', and returns:
5d016e
+         * - the last matching option name in ret_namefound,
5d016e
+         * - the last matching value in ret_value,
5d016e
+         * - any matching values in ret_values,
5d016e
+         * - the rest of the option string in ret_filtered.
5d016e
+         *
5d016e
+         * If !ret_value and !ret_values and !ret_filtered, this function is not allowed to fail.
5d016e
+         *
5d016e
+         * Returns negative on error, true if any matching options were found, false otherwise. */
5d016e
+
5d016e
+        if (ret_filtered || ret_value || ret_values) {
5d016e
+                /* For backwards compatibility, we need to pass-through escape characters.
5d016e
+                 * The only ones we "consume" are the ones used as "\," or "\\". */
5d016e
+                r = strv_split_full(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS | EXTRACT_UNESCAPE_RELAX);
5d016e
+                if (r < 0)
5d016e
+                        return r;
5d016e
+
5d016e
+                filtered = memdup(stor, sizeof(char*) * (strv_length(stor) + 1));
5d016e
+                if (!filtered)
5d016e
+                        return -ENOMEM;
5d016e
+
5d016e
+                char **t = filtered;
5d016e
+                for (char **s = t; *s; s++) {
5d016e
+                        NULSTR_FOREACH(name, names) {
5d016e
+                                x = startswith(*s, name);
5d016e
+                                if (!x)
5d016e
+                                        continue;
5d016e
+                                /* Match name, but when ret_values, only when followed by assignment. */
5d016e
+                                if (*x == '=' || (!ret_values && *x == '\0'))
5d016e
+                                        goto found;
5d016e
+                        }
5d016e
+
5d016e
+                        *t = *s;
5d016e
+                        t++;
5d016e
+                        continue;
5d016e
+                found:
5d016e
+                        /* Keep the last occurrence found */
5d016e
+                        namefound = name;
5d016e
+
5d016e
+                        if (ret_value || ret_values) {
5d016e
+                                assert(IN_SET(*x, '=', '\0'));
5d016e
 
5d016e
-        if (!ret_filtered) {
5d016e
+                                if (ret_value) {
5d016e
+                                        r = free_and_strdup(&value, *x == '=' ? x + 1 : NULL);
5d016e
+                                        if (r < 0)
5d016e
+                                                return r;
5d016e
+                                } else if (*x) {
5d016e
+                                        r = strv_extend(&values, x + 1);
5d016e
+                                        if (r < 0)
5d016e
+                                                return r;
5d016e
+                                }
5d016e
+                        }
5d016e
+                }
5d016e
+                *t = NULL;
5d016e
+        } else
5d016e
                 for (const char *word = opts;;) {
5d016e
                         const char *end = word;
5d016e
 
5d016e
@@ -121,17 +180,6 @@ int fstab_filter_options(const char *opts, const char *names,
5d016e
                                 x = word + strlen(name);
5d016e
                                 if (IN_SET(*x, '\0', '=', ',')) {
5d016e
                                         namefound = name;
5d016e
-                                        if (ret_value) {
5d016e
-                                                bool eq = *x == '=';
5d016e
-                                                assert(eq || IN_SET(*x, ',', '\0'));
5d016e
-
5d016e
-                                                r = free_and_strndup(&v,
5d016e
-                                                                     eq ? x + 1 : NULL,
5d016e
-                                                                     eq ? end - x - 1 : 0);
5d016e
-                                                if (r < 0)
5d016e
-                                                        return r;
5d016e
-                                        }
5d016e
-
5d016e
                                         break;
5d016e
                                 }
5d016e
                         }
5d016e
@@ -141,40 +189,6 @@ int fstab_filter_options(const char *opts, const char *names,
5d016e
                         else
5d016e
                                 break;
5d016e
                 }
5d016e
-        } else {
5d016e
-                /* For backwards compatibility, we need to pass-through escape characters.
5d016e
-                 * The only ones we "consume" are the ones used as "\," or "\\". */
5d016e
-                r = strv_split_full(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS | EXTRACT_UNESCAPE_RELAX);
5d016e
-                if (r < 0)
5d016e
-                        return r;
5d016e
-
5d016e
-                strv = memdup(stor, sizeof(char*) * (strv_length(stor) + 1));
5d016e
-                if (!strv)
5d016e
-                        return -ENOMEM;
5d016e
-
5d016e
-                char **t = strv;
5d016e
-                for (char **s = strv; *s; s++) {
5d016e
-                        NULSTR_FOREACH(name, names) {
5d016e
-                                x = startswith(*s, name);
5d016e
-                                if (x && IN_SET(*x, '\0', '='))
5d016e
-                                        goto found;
5d016e
-                        }
5d016e
-
5d016e
-                        *t = *s;
5d016e
-                        t++;
5d016e
-                        continue;
5d016e
-                found:
5d016e
-                        /* Keep the last occurrence found */
5d016e
-                        namefound = name;
5d016e
-                        if (ret_value) {
5d016e
-                                assert(IN_SET(*x, '=', '\0'));
5d016e
-                                r = free_and_strdup(&v, *x == '=' ? x + 1 : NULL);
5d016e
-                                if (r < 0)
5d016e
-                                        return r;
5d016e
-                        }
5d016e
-                }
5d016e
-                *t = NULL;
5d016e
-        }
5d016e
 
5d016e
 answer:
5d016e
         if (ret_namefound)
5d016e
@@ -182,14 +196,16 @@ int fstab_filter_options(const char *opts, const char *names,
5d016e
         if (ret_filtered) {
5d016e
                 char *f;
5d016e
 
5d016e
-                f = strv_join_full(strv, ",", NULL, true);
5d016e
+                f = strv_join_full(filtered, ",", NULL, true);
5d016e
                 if (!f)
5d016e
                         return -ENOMEM;
5d016e
 
5d016e
                 *ret_filtered = f;
5d016e
         }
5d016e
         if (ret_value)
5d016e
-                *ret_value = TAKE_PTR(v);
5d016e
+                *ret_value = TAKE_PTR(value);
5d016e
+        if (ret_values)
5d016e
+                *ret_values = TAKE_PTR(values);
5d016e
 
5d016e
         return !!namefound;
5d016e
 }
5d016e
@@ -229,7 +245,7 @@ int fstab_find_pri(const char *options, int *ret) {
5d016e
 
5d016e
         assert(ret);
5d016e
 
5d016e
-        r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL);
5d016e
+        r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL, NULL);
5d016e
         if (r < 0)
5d016e
                 return r;
5d016e
         if (r == 0 || !opt)
5d016e
diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h
5d016e
index 1a602cb56b2..97f40221afb 100644
5d016e
--- a/src/shared/fstab-util.h
5d016e
+++ b/src/shared/fstab-util.h
5d016e
@@ -10,12 +10,18 @@ bool fstab_is_extrinsic(const char *mount, const char *opts);
5d016e
 int fstab_is_mount_point(const char *mount);
5d016e
 int fstab_has_fstype(const char *fstype);
5d016e
 
5d016e
-int fstab_filter_options(const char *opts, const char *names, const char **namefound, char **value, char **filtered);
5d016e
+int fstab_filter_options(
5d016e
+                const char *opts,
5d016e
+                const char *names,
5d016e
+                const char **ret_namefound,
5d016e
+                char **ret_value,
5d016e
+                char ***ret_values,
5d016e
+                char **ret_filtered);
5d016e
 
5d016e
 int fstab_extract_values(const char *opts, const char *name, char ***values);
5d016e
 
5d016e
 static inline bool fstab_test_option(const char *opts, const char *names) {
5d016e
-        return !!fstab_filter_options(opts, names, NULL, NULL, NULL);
5d016e
+        return !!fstab_filter_options(opts, names, NULL, NULL, NULL, NULL);
5d016e
 }
5d016e
 
5d016e
 int fstab_find_pri(const char *options, int *ret);
5d016e
@@ -26,7 +32,7 @@ static inline bool fstab_test_yes_no_option(const char *opts, const char *yes_no
5d016e
         /* If first name given is last, return 1.
5d016e
          * If second name given is last or neither is found, return 0. */
5d016e
 
5d016e
-        assert_se(fstab_filter_options(opts, yes_no, &opt, NULL, NULL) >= 0);
5d016e
+        assert_se(fstab_filter_options(opts, yes_no, &opt, NULL, NULL, NULL) >= 0);
5d016e
 
5d016e
         return opt == yes_no;
5d016e
 }
5d016e
diff --git a/src/shared/generator.c b/src/shared/generator.c
5d016e
index 41922d67d8c..5b9c4325271 100644
5d016e
--- a/src/shared/generator.c
5d016e
+++ b/src/shared/generator.c
5d016e
@@ -215,7 +215,7 @@ int generator_write_timeouts(
5d016e
 
5d016e
         r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
5d016e
                                        "x-systemd.device-timeout\0",
5d016e
-                                 NULL, &timeout, filtered);
5d016e
+                                 NULL, &timeout, NULL, filtered);
5d016e
         if (r < 0) {
5d016e
                 log_warning_errno(r, "Failed to parse fstab options, ignoring: %m");
5d016e
                 return 0;
5d016e
diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c
5d016e
index 3a7ec170d65..d2f20185265 100644
5d016e
--- a/src/test/test-fstab-util.c
5d016e
+++ b/src/test/test-fstab-util.c
5d016e
@@ -6,102 +6,125 @@
5d016e
 #include "fstab-util.h"
5d016e
 #include "log.h"
5d016e
 #include "string-util.h"
5d016e
+#include "strv.h"
5d016e
 
5d016e
 /*
5d016e
-int fstab_filter_options(const char *opts, const char *names,
5d016e
-                         const char **namefound, char **value, char **filtered);
5d016e
+int fstab_filter_options(
5d016e
+        const char *opts,
5d016e
+        const char *names,
5d016e
+        const char **ret_namefound,
5d016e
+        const char **ret_value,
5d016e
+        const char **ret_values,
5d016e
+        char **ret_filtered);
5d016e
 */
5d016e
 
5d016e
 static void do_fstab_filter_options(const char *opts,
5d016e
                                     const char *remove,
5d016e
                                     int r_expected,
5d016e
+                                    int r_values_expected,
5d016e
                                     const char *name_expected,
5d016e
                                     const char *value_expected,
5d016e
+                                    const char *values_expected,
5d016e
                                     const char *filtered_expected) {
5d016e
         int r;
5d016e
         const char *name;
5d016e
-        _cleanup_free_ char *value = NULL, *filtered = NULL;
5d016e
+        _cleanup_free_ char *value = NULL, *filtered = NULL, *joined = NULL;
5d016e
+        _cleanup_strv_free_ char **values = NULL;
5d016e
 
5d016e
-        r = fstab_filter_options(opts, remove, &name, &value, &filtered);
5d016e
-        log_info("\"%s\" ā†’ %d, \"%s\", \"%s\", \"%s\", expected %d, \"%s\", \"%s\", \"%s\"",
5d016e
-                 opts, r, name, value, filtered,
5d016e
+        /* test mode which returns the last value */
5d016e
+
5d016e
+        r = fstab_filter_options(opts, remove, &name, &value, NULL, &filtered);
5d016e
+        log_info("1: \"%s\" ā†’ %d, \"%s\", \"%s\", \"%s\", expected %d, \"%s\", \"%s\", \"%s\"",
5d016e
+                 opts, r, strnull(name), value, filtered,
5d016e
                  r_expected, name_expected, value_expected, filtered_expected ?: opts);
5d016e
         assert_se(r == r_expected);
5d016e
         assert_se(streq_ptr(name, name_expected));
5d016e
         assert_se(streq_ptr(value, value_expected));
5d016e
         assert_se(streq_ptr(filtered, filtered_expected ?: opts));
5d016e
 
5d016e
+        /* test mode which returns all the values */
5d016e
+
5d016e
+        r = fstab_filter_options(opts, remove, &name, NULL, &values, NULL);
5d016e
+        assert_se(joined = strv_join(values, ":"));
5d016e
+        log_info("2: \"%s\" ā†’ %d, \"%s\", \"%s\", expected %d, \"%s\", \"%s\"",
5d016e
+                 opts, r, strnull(name), joined,
5d016e
+                 r_values_expected, name_expected, values_expected);
5d016e
+        assert_se(r == r_values_expected);
5d016e
+        assert_se(streq_ptr(name, r_values_expected > 0 ? name_expected : NULL));
5d016e
+        assert_se(streq_ptr(joined, values_expected));
5d016e
+
5d016e
         /* also test the malloc-less mode */
5d016e
-        r = fstab_filter_options(opts, remove, &name, NULL, NULL);
5d016e
-        log_info("\"%s\" ā†’ %d, \"%s\", expected %d, \"%s\"\n-",
5d016e
-                 opts, r, name,
5d016e
+        r = fstab_filter_options(opts, remove, &name, NULL, NULL, NULL);
5d016e
+        log_info("3: \"%s\" ā†’ %d, \"%s\", expected %d, \"%s\"\n-",
5d016e
+                 opts, r, strnull(name),
5d016e
                  r_expected, name_expected);
5d016e
         assert_se(r == r_expected);
5d016e
         assert_se(streq_ptr(name, name_expected));
5d016e
 }
5d016e
 
5d016e
 static void test_fstab_filter_options(void) {
5d016e
-        do_fstab_filter_options("opt=0", "opt\0x-opt\0", 1, "opt", "0", "");
5d016e
-        do_fstab_filter_options("opt=0", "x-opt\0opt\0", 1, "opt", "0", "");
5d016e
-        do_fstab_filter_options("opt", "opt\0x-opt\0", 1, "opt", NULL, "");
5d016e
-        do_fstab_filter_options("opt", "x-opt\0opt\0", 1, "opt", NULL, "");
5d016e
-        do_fstab_filter_options("x-opt", "x-opt\0opt\0", 1, "x-opt", NULL, "");
5d016e
-
5d016e
-        do_fstab_filter_options("opt=0,other", "opt\0x-opt\0", 1, "opt", "0", "other");
5d016e
-        do_fstab_filter_options("opt=0,other", "x-opt\0opt\0", 1, "opt", "0", "other");
5d016e
-        do_fstab_filter_options("opt,other", "opt\0x-opt\0", 1, "opt", NULL, "other");
5d016e
-        do_fstab_filter_options("opt,other", "x-opt\0opt\0", 1, "opt", NULL, "other");
5d016e
-        do_fstab_filter_options("x-opt,other", "opt\0x-opt\0", 1, "x-opt", NULL, "other");
5d016e
-
5d016e
-        do_fstab_filter_options("opt=0\\,1,other", "opt\0x-opt\0", 1, "opt", "0,1", "other");
5d016e
-        do_fstab_filter_options("opt=0,other,x-opt\\,foobar", "x-opt\0opt\0", 1, "opt", "0", "other,x-opt\\,foobar");
5d016e
-        do_fstab_filter_options("opt,other,x-opt\\,part", "opt\0x-opt\0", 1, "opt", NULL, "other,x-opt\\,part");
5d016e
-        do_fstab_filter_options("opt,other,part\\,x-opt", "x-opt\0opt\0", 1, "opt", NULL, "other,part\\,x-opt");
5d016e
-        do_fstab_filter_options("opt,other\\,\\,\\,opt,x-part", "opt\0x-opt\0", 1, "opt", NULL, "other\\,\\,\\,opt,x-part");
5d016e
-
5d016e
-        do_fstab_filter_options("opto=0,other", "opt\0x-opt\0", 0, NULL, NULL, NULL);
5d016e
-        do_fstab_filter_options("opto,other", "opt\0x-opt\0", 0, NULL, NULL, NULL);
5d016e
-        do_fstab_filter_options("x-opto,other", "opt\0x-opt\0", 0, NULL, NULL, NULL);
5d016e
-
5d016e
-        do_fstab_filter_options("first,opt=0", "opt\0x-opt\0", 1, "opt", "0", "first");
5d016e
-        do_fstab_filter_options("first=1,opt=0", "opt\0x-opt\0", 1, "opt", "0", "first=1");
5d016e
-        do_fstab_filter_options("first,opt=", "opt\0x-opt\0", 1, "opt", "", "first");
5d016e
-        do_fstab_filter_options("first=1,opt", "opt\0x-opt\0", 1, "opt", NULL, "first=1");
5d016e
-        do_fstab_filter_options("first=1,x-opt", "opt\0x-opt\0", 1, "x-opt", NULL, "first=1");
5d016e
-
5d016e
-        do_fstab_filter_options("first,opt=0,last=1", "opt\0x-opt\0", 1, "opt", "0", "first,last=1");
5d016e
-        do_fstab_filter_options("first=1,opt=0,last=2", "x-opt\0opt\0", 1, "opt", "0", "first=1,last=2");
5d016e
-        do_fstab_filter_options("first,opt,last", "opt\0", 1, "opt", NULL, "first,last");
5d016e
-        do_fstab_filter_options("first=1,opt,last", "x-opt\0opt\0", 1, "opt", NULL, "first=1,last");
5d016e
-        do_fstab_filter_options("first=,opt,last", "opt\0noopt\0", 1, "opt", NULL, "first=,last");
5d016e
+        do_fstab_filter_options("opt=0", "opt\0x-opt\0", 1, 1, "opt", "0", "0", "");
5d016e
+        do_fstab_filter_options("opt=0", "x-opt\0opt\0", 1, 1, "opt", "0", "0", "");
5d016e
+        do_fstab_filter_options("opt", "opt\0x-opt\0", 1, 0, "opt", NULL, "", "");
5d016e
+        do_fstab_filter_options("opt", "x-opt\0opt\0", 1, 0, "opt", NULL, "", "");
5d016e
+        do_fstab_filter_options("x-opt", "x-opt\0opt\0", 1, 0, "x-opt", NULL, "", "");
5d016e
+
5d016e
+        do_fstab_filter_options("opt=0,other", "opt\0x-opt\0", 1, 1, "opt", "0", "0", "other");
5d016e
+        do_fstab_filter_options("opt=0,other", "x-opt\0opt\0", 1, 1, "opt", "0", "0", "other");
5d016e
+        do_fstab_filter_options("opt,other", "opt\0x-opt\0", 1, 0, "opt", NULL, "", "other");
5d016e
+        do_fstab_filter_options("opt,other", "x-opt\0opt\0", 1, 0, "opt", NULL, "", "other");
5d016e
+        do_fstab_filter_options("x-opt,other", "opt\0x-opt\0", 1, 0, "x-opt", NULL, "", "other");
5d016e
+
5d016e
+        do_fstab_filter_options("opt=0\\,1,other", "opt\0x-opt\0", 1, 1, "opt", "0,1", "0,1", "other");
5d016e
+        do_fstab_filter_options("opt=0,other,x-opt\\,foobar", "x-opt\0opt\0", 1, 1, "opt", "0", "0", "other,x-opt\\,foobar");
5d016e
+        do_fstab_filter_options("opt,other,x-opt\\,part", "opt\0x-opt\0", 1, 0, "opt", NULL, "", "other,x-opt\\,part");
5d016e
+        do_fstab_filter_options("opt,other,part\\,x-opt", "x-opt\0opt\0", 1, 0, "opt", NULL, "", "other,part\\,x-opt");
5d016e
+        do_fstab_filter_options("opt,other\\,\\,\\,opt,x-part", "opt\0x-opt\0", 1, 0, "opt", NULL, "", "other\\,\\,\\,opt,x-part");
5d016e
+
5d016e
+        do_fstab_filter_options("opto=0,other", "opt\0x-opt\0", 0, 0, NULL, NULL, "", NULL);
5d016e
+        do_fstab_filter_options("opto,other", "opt\0x-opt\0", 0, 0, NULL, NULL, "", NULL);
5d016e
+        do_fstab_filter_options("x-opto,other", "opt\0x-opt\0", 0, 0, NULL, NULL, "", NULL);
5d016e
+
5d016e
+        do_fstab_filter_options("first,opt=0", "opt\0x-opt\0", 1, 1, "opt", "0", "0", "first");
5d016e
+        do_fstab_filter_options("first=1,opt=0", "opt\0x-opt\0", 1, 1, "opt", "0", "0", "first=1");
5d016e
+        do_fstab_filter_options("first,opt=", "opt\0x-opt\0", 1, 1, "opt", "", "", "first");
5d016e
+        do_fstab_filter_options("first=1,opt", "opt\0x-opt\0", 1, 0, "opt", NULL, "", "first=1");
5d016e
+        do_fstab_filter_options("first=1,x-opt", "opt\0x-opt\0", 1, 0, "x-opt", NULL, "", "first=1");
5d016e
+
5d016e
+        do_fstab_filter_options("first,opt=0,last=1", "opt\0x-opt\0", 1, 1, "opt", "0", "0", "first,last=1");
5d016e
+        do_fstab_filter_options("first=1,opt=0,last=2", "x-opt\0opt\0", 1, 1, "opt", "0", "0", "first=1,last=2");
5d016e
+        do_fstab_filter_options("first,opt,last", "opt\0", 1, 0, "opt", NULL, "", "first,last");
5d016e
+        do_fstab_filter_options("first=1,opt,last", "x-opt\0opt\0", 1, 0, "opt", NULL, "", "first=1,last");
5d016e
+        do_fstab_filter_options("first=,opt,last", "opt\0noopt\0", 1, 0, "opt", NULL, "", "first=,last");
5d016e
 
5d016e
         /* check repeated options */
5d016e
-        do_fstab_filter_options("first,opt=0,noopt=1,last=1", "opt\0noopt\0", 1, "noopt", "1", "first,last=1");
5d016e
-        do_fstab_filter_options("first=1,opt=0,last=2,opt=1", "opt\0", 1, "opt", "1", "first=1,last=2");
5d016e
-        do_fstab_filter_options("x-opt=0,x-opt=1", "opt\0x-opt\0", 1, "x-opt", "1", "");
5d016e
-        do_fstab_filter_options("opt=0,x-opt=1", "opt\0x-opt\0", 1, "x-opt", "1", "");
5d016e
+        do_fstab_filter_options("first,opt=0,noopt=1,last=1", "opt\0noopt\0", 1, 1, "noopt", "1", "0:1", "first,last=1");
5d016e
+        do_fstab_filter_options("first=1,opt=0,last=2,opt=1", "opt\0", 1, 1, "opt", "1", "0:1", "first=1,last=2");
5d016e
+        do_fstab_filter_options("x-opt=0,x-opt=1", "opt\0x-opt\0", 1, 1, "x-opt", "1", "0:1", "");
5d016e
+        do_fstab_filter_options("opt=0,x-opt=1", "opt\0x-opt\0", 1, 1, "x-opt", "1", "0:1", "");
5d016e
+        do_fstab_filter_options("opt=0,opt=1,opt=,opt=,opt=2", "opt\0noopt\0", 1, 1, "opt", "2", "0:1:::2", "");
5d016e
 
5d016e
         /* check that semicolons are not misinterpreted */
5d016e
-        do_fstab_filter_options("opt=0;", "opt\0", 1, "opt", "0;", "");
5d016e
-        do_fstab_filter_options("opt;=0", "x-opt\0opt\0noopt\0x-noopt\0", 0, NULL, NULL, NULL);
5d016e
-        do_fstab_filter_options("opt;", "opt\0x-opt\0", 0, NULL, NULL, NULL);
5d016e
+        do_fstab_filter_options("opt=0;", "opt\0", 1, 1, "opt", "0;", "0;", "");
5d016e
+        do_fstab_filter_options("opt;=0", "x-opt\0opt\0noopt\0x-noopt\0", 0, 0, NULL, NULL, "", NULL);
5d016e
+        do_fstab_filter_options("opt;", "opt\0x-opt\0", 0, 0, NULL, NULL, "", NULL);
5d016e
 
5d016e
         /* check that spaces are not misinterpreted */
5d016e
-        do_fstab_filter_options("opt=0 ", "opt\0", 1, "opt", "0 ", "");
5d016e
-        do_fstab_filter_options("opt =0", "x-opt\0opt\0noopt\0x-noopt\0", 0, NULL, NULL, NULL);
5d016e
-        do_fstab_filter_options(" opt ", "opt\0x-opt\0", 0, NULL, NULL, NULL);
5d016e
+        do_fstab_filter_options("opt=0 ", "opt\0", 1, 1, "opt", "0 ", "0 ", "");
5d016e
+        do_fstab_filter_options("opt =0", "x-opt\0opt\0noopt\0x-noopt\0", 0, 0, NULL, NULL, "", NULL);
5d016e
+        do_fstab_filter_options(" opt ", "opt\0x-opt\0", 0, 0, NULL, NULL, "", NULL);
5d016e
 
5d016e
         /* check function with NULL args */
5d016e
-        do_fstab_filter_options(NULL, "opt\0", 0, NULL, NULL, "");
5d016e
-        do_fstab_filter_options("", "opt\0", 0, NULL, NULL, "");
5d016e
+        do_fstab_filter_options(NULL, "opt\0", 0, 0, NULL, NULL, "", "");
5d016e
+        do_fstab_filter_options("", "opt\0", 0, 0, NULL, NULL, "", "");
5d016e
 
5d016e
         /* unnecessary comma separators */
5d016e
-        do_fstab_filter_options("opt=x,,,,", "opt\0", 1, "opt", "x", "");
5d016e
-        do_fstab_filter_options(",,,opt=x,,,,", "opt\0", 1, "opt", "x", "");
5d016e
+        do_fstab_filter_options("opt=x,,,,", "opt\0", 1, 1, "opt", "x", "x", "");
5d016e
+        do_fstab_filter_options(",,,opt=x,,,,", "opt\0", 1, 1, "opt", "x", "x", "");
5d016e
 
5d016e
         /* escaped characters */
5d016e
-        do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt1\0", 1, "opt1", "\\", "opt2=\\xff");
5d016e
-        do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt2\0", 1, "opt2", "\\xff", "opt1=\\");
5d016e
+        do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt1\0", 1, 1, "opt1", "\\", "\\", "opt2=\\xff");
5d016e
+        do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt2\0", 1, 1, "opt2", "\\xff", "\\xff", "opt1=\\");
5d016e
 }
5d016e
 
5d016e
 static void test_fstab_find_pri(void) {
5d016e
5d016e
From d6cef552dcb4764a89269ce9603eb21f348d911a Mon Sep 17 00:00:00 2001
5d016e
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
5d016e
Date: Thu, 11 Mar 2021 11:10:32 +0100
5d016e
Subject: [PATCH 9/9] fstab-generator: get rid of fstab_extract_values()
5d016e
5d016e
This was a parallel implementation of option parsing that didn't
5d016e
support escaping of separators. Let's port this over to the common code.
5d016e
5d016e
Fixes #18952.
5d016e
---
5d016e
 src/fstab-generator/fstab-generator.c | 14 ++++++-------
5d016e
 src/shared/fstab-util.c               | 29 ---------------------------
5d016e
 src/shared/fstab-util.h               |  2 --
5d016e
 3 files changed, 7 insertions(+), 38 deletions(-)
5d016e
5d016e
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
5d016e
index b454a5980d4..8c1087a9a33 100644
5d016e
--- a/src/fstab-generator/fstab-generator.c
5d016e
+++ b/src/fstab-generator/fstab-generator.c
5d016e
@@ -241,7 +241,7 @@ static int write_dependency(
5d016e
         assert(f);
5d016e
         assert(opts);
5d016e
 
5d016e
-        r = fstab_extract_values(opts, filter, &names);
5d016e
+        r = fstab_filter_options(opts, filter, NULL, NULL, &names, NULL);
5d016e
         if (r < 0)
5d016e
                 return log_warning_errno(r, "Failed to parse options: %m");
5d016e
         if (r == 0)
5d016e
@@ -274,17 +274,17 @@ static int write_dependency(
5d016e
 
5d016e
 static int write_after(FILE *f, const char *opts) {
5d016e
         return write_dependency(f, opts,
5d016e
-                                "x-systemd.after", "After=%1$s\n");
5d016e
+                                "x-systemd.after\0", "After=%1$s\n");
5d016e
 }
5d016e
 
5d016e
 static int write_requires_after(FILE *f, const char *opts) {
5d016e
         return write_dependency(f, opts,
5d016e
-                                "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
5d016e
+                                "x-systemd.requires\0", "After=%1$s\nRequires=%1$s\n");
5d016e
 }
5d016e
 
5d016e
 static int write_before(FILE *f, const char *opts) {
5d016e
         return write_dependency(f, opts,
5d016e
-                                "x-systemd.before", "Before=%1$s\n");
5d016e
+                                "x-systemd.before\0", "Before=%1$s\n");
5d016e
 }
5d016e
 
5d016e
 static int write_requires_mounts_for(FILE *f, const char *opts) {
5d016e
@@ -295,7 +295,7 @@ static int write_requires_mounts_for(FILE *f, const char *opts) {
5d016e
         assert(f);
5d016e
         assert(opts);
5d016e
 
5d016e
-        r = fstab_extract_values(opts, "x-systemd.requires-mounts-for", &paths);
5d016e
+        r = fstab_filter_options(opts, "x-systemd.requires-mounts-for\0", NULL, NULL, &paths, NULL);
5d016e
         if (r < 0)
5d016e
                 return log_warning_errno(r, "Failed to parse options: %m");
5d016e
         if (r == 0)
5d016e
@@ -376,11 +376,11 @@ static int add_mount(
5d016e
             mount_point_ignore(where))
5d016e
                 return 0;
5d016e
 
5d016e
-        r = fstab_extract_values(opts, "x-systemd.wanted-by", &wanted_by);
5d016e
+        r = fstab_filter_options(opts, "x-systemd.wanted-by\0", NULL, NULL, &wanted_by, NULL);
5d016e
         if (r < 0)
5d016e
                 return r;
5d016e
 
5d016e
-        r = fstab_extract_values(opts, "x-systemd.required-by", &required_by);
5d016e
+        r = fstab_filter_options(opts, "x-systemd.required-by\0", NULL, NULL, &required_by, NULL);
5d016e
         if (r < 0)
5d016e
                 return r;
5d016e
 
5d016e
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
5d016e
index 6674ed4a19f..7fd3d9c2c34 100644
5d016e
--- a/src/shared/fstab-util.c
5d016e
+++ b/src/shared/fstab-util.c
5d016e
@@ -210,35 +210,6 @@ int fstab_filter_options(
5d016e
         return !!namefound;
5d016e
 }
5d016e
 
5d016e
-int fstab_extract_values(const char *opts, const char *name, char ***values) {
5d016e
-        _cleanup_strv_free_ char **optsv = NULL, **res = NULL;
5d016e
-        char **s;
5d016e
-
5d016e
-        assert(opts);
5d016e
-        assert(name);
5d016e
-        assert(values);
5d016e
-
5d016e
-        optsv = strv_split(opts, ",");
5d016e
-        if (!optsv)
5d016e
-                return -ENOMEM;
5d016e
-
5d016e
-        STRV_FOREACH(s, optsv) {
5d016e
-                char *arg;
5d016e
-                int r;
5d016e
-
5d016e
-                arg = startswith(*s, name);
5d016e
-                if (!arg || *arg != '=')
5d016e
-                        continue;
5d016e
-                r = strv_extend(&res, arg + 1);
5d016e
-                if (r < 0)
5d016e
-                        return r;
5d016e
-        }
5d016e
-
5d016e
-        *values = TAKE_PTR(res);
5d016e
-
5d016e
-        return !!*values;
5d016e
-}
5d016e
-
5d016e
 int fstab_find_pri(const char *options, int *ret) {
5d016e
         _cleanup_free_ char *opt = NULL;
5d016e
         int r, pri;
5d016e
diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h
5d016e
index 97f40221afb..6b596baafa1 100644
5d016e
--- a/src/shared/fstab-util.h
5d016e
+++ b/src/shared/fstab-util.h
5d016e
@@ -18,8 +18,6 @@ int fstab_filter_options(
5d016e
                 char ***ret_values,
5d016e
                 char **ret_filtered);
5d016e
 
5d016e
-int fstab_extract_values(const char *opts, const char *name, char ***values);
5d016e
-
5d016e
 static inline bool fstab_test_option(const char *opts, const char *names) {
5d016e
         return !!fstab_filter_options(opts, names, NULL, NULL, NULL, NULL);
5d016e
 }