diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index 2c14b6f0cf..0c440db691 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -27,6 +27,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra assert(p); assert(ret); + assert(!FLAGS_SET(flags, EXTRACT_KEEP_QUOTE | EXTRACT_UNQUOTE)); /* Bail early if called after last value or with no input */ if (!*p) @@ -123,25 +124,30 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra return -EINVAL; } else if (c == quote) { /* found the end quote */ quote = 0; - break; + if (flags & EXTRACT_UNQUOTE) + break; } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { backslash = true; break; - } else { - if (!GREEDY_REALLOC(s, sz+2)) - return -ENOMEM; - - s[sz++] = c; } + + if (!GREEDY_REALLOC(s, sz+2)) + return -ENOMEM; + + s[sz++] = c; + + if (quote == 0) + break; } } else { for (;; (*p)++, c = **p) { if (c == 0) goto finish_force_terminate; - else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_UNQUOTE)) { + else if (IN_SET(c, '\'', '"') && (flags & (EXTRACT_KEEP_QUOTE | EXTRACT_UNQUOTE))) { quote = c; - break; + if (flags & EXTRACT_UNQUOTE) + break; } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { backslash = true; break; @@ -159,12 +165,15 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra } goto finish; - } else { - if (!GREEDY_REALLOC(s, sz+2)) - return -ENOMEM; - - s[sz++] = c; } + + if (!GREEDY_REALLOC(s, sz+2)) + return -ENOMEM; + + s[sz++] = c; + + if (quote != 0) + break; } } } diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h index 0e9e77e93d..f415872fac 100644 --- a/src/basic/extract-word.h +++ b/src/basic/extract-word.h @@ -8,9 +8,10 @@ typedef enum ExtractFlags { EXTRACT_CUNESCAPE = 1 << 1, /* Unescape known escape sequences. */ EXTRACT_UNESCAPE_RELAX = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */ EXTRACT_UNESCAPE_SEPARATORS = 1 << 3, /* Unescape separators (those specified, or whitespace by default). */ - EXTRACT_UNQUOTE = 1 << 4, /* Remove quoting with "" and ''. */ - EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, /* Don't treat multiple adjacent separators as one */ - EXTRACT_RETAIN_ESCAPE = 1 << 6, /* Treat escape character '\' as any other character without special meaning */ + EXTRACT_KEEP_QUOTE = 1 << 4, /* Ignore separators in quoting with "" and ''. */ + EXTRACT_UNQUOTE = 1 << 5, /* Ignore separators in quoting with "" and '', and remove the quotes. */ + EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 6, /* Don't treat multiple adjacent separators as one */ + EXTRACT_RETAIN_ESCAPE = 1 << 7, /* Treat escape character '\' as any other character without special meaning */ /* Note that if no flags are specified, escaped escape characters will be silently stripped. */ } ExtractFlags; diff --git a/src/basic/meson.build b/src/basic/meson.build index 846301fec5..d1aa86e7b5 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -144,6 +144,7 @@ basic_sources = files(''' missing_keyctl.h missing_magic.h missing_mman.h + missing_mount.h missing_network.h missing_prctl.h missing_random.h diff --git a/src/basic/missing_mount.h b/src/basic/missing_mount.h new file mode 100644 index 0000000000..c60acf041a --- /dev/null +++ b/src/basic/missing_mount.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +/* dab741e0e02bd3c4f5e2e97be74b39df2523fc6e (5.10) */ +#ifndef MS_NOSYMFOLLOW +#define MS_NOSYMFOLLOW 256 +#endif diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index c28e429935..60f956da0f 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -398,7 +398,7 @@ int tmpfs_patch_options( #if HAVE_SELINUX if (selinux_apifs_context) - if (!strextend_with_separator(&buf, ",", "context=\"", selinux_apifs_context, "\"")) + if (strextendf_with_separator(&buf, ",", "context=\"%s\"", selinux_apifs_context) < 0) return -ENOMEM; #endif diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index 34e0d53bc6..c4261b2494 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -17,6 +17,7 @@ #include "fs-util.h" #include "hashmap.h" #include "libmount-util.h" +#include "missing_mount.h" #include "missing_syscall.h" #include "mkdir.h" #include "mount-util.h" @@ -530,71 +531,52 @@ int mode_to_inaccessible_node( return 0; } -#define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "") -static char* mount_flags_to_string(long unsigned flags) { - char *x; - _cleanup_free_ char *y = NULL; - long unsigned overflow; +static int mount_flags_to_string(long unsigned flags, char **ret) { + static const struct { + long unsigned flag; + const char *name; + } map[] = { + { .flag = MS_RDONLY, .name = "MS_RDONLY", }, + { .flag = MS_NOSUID, .name = "MS_NOSUID", }, + { .flag = MS_NODEV, .name = "MS_NODEV", }, + { .flag = MS_NOEXEC, .name = "MS_NOEXEC", }, + { .flag = MS_SYNCHRONOUS, .name = "MS_SYNCHRONOUS", }, + { .flag = MS_REMOUNT, .name = "MS_REMOUNT", }, + { .flag = MS_MANDLOCK, .name = "MS_MANDLOCK", }, + { .flag = MS_DIRSYNC, .name = "MS_DIRSYNC", }, + { .flag = MS_NOSYMFOLLOW, .name = "MS_NOSYMFOLLOW", }, + { .flag = MS_NOATIME, .name = "MS_NOATIME", }, + { .flag = MS_NODIRATIME, .name = "MS_NODIRATIME", }, + { .flag = MS_BIND, .name = "MS_BIND", }, + { .flag = MS_MOVE, .name = "MS_MOVE", }, + { .flag = MS_REC, .name = "MS_REC", }, + { .flag = MS_SILENT, .name = "MS_SILENT", }, + { .flag = MS_POSIXACL, .name = "MS_POSIXACL", }, + { .flag = MS_UNBINDABLE, .name = "MS_UNBINDABLE", }, + { .flag = MS_PRIVATE, .name = "MS_PRIVATE", }, + { .flag = MS_SLAVE, .name = "MS_SLAVE", }, + { .flag = MS_SHARED, .name = "MS_SHARED", }, + { .flag = MS_RELATIME, .name = "MS_RELATIME", }, + { .flag = MS_KERNMOUNT, .name = "MS_KERNMOUNT", }, + { .flag = MS_I_VERSION, .name = "MS_I_VERSION", }, + { .flag = MS_STRICTATIME, .name = "MS_STRICTATIME", }, + { .flag = MS_LAZYTIME, .name = "MS_LAZYTIME", }, + }; + _cleanup_free_ char *str = NULL; - overflow = flags & ~(MS_RDONLY | - MS_NOSUID | - MS_NODEV | - MS_NOEXEC | - MS_SYNCHRONOUS | - MS_REMOUNT | - MS_MANDLOCK | - MS_DIRSYNC | - MS_NOATIME | - MS_NODIRATIME | - MS_BIND | - MS_MOVE | - MS_REC | - MS_SILENT | - MS_POSIXACL | - MS_UNBINDABLE | - MS_PRIVATE | - MS_SLAVE | - MS_SHARED | - MS_RELATIME | - MS_KERNMOUNT | - MS_I_VERSION | - MS_STRICTATIME | - MS_LAZYTIME); + for (size_t i = 0; i < ELEMENTSOF(map); i++) + if (flags & map[i].flag) { + if (!strextend_with_separator(&str, "|", map[i].name)) + return -ENOMEM; + flags &= ~map[i].flag; + } - if (flags == 0 || overflow != 0) - if (asprintf(&y, "%lx", overflow) < 0) - return NULL; + if (!str || flags != 0) + if (strextendf_with_separator(&str, "|", "%lx", flags) < 0) + return -ENOMEM; - x = strjoin(FLAG(MS_RDONLY), - FLAG(MS_NOSUID), - FLAG(MS_NODEV), - FLAG(MS_NOEXEC), - FLAG(MS_SYNCHRONOUS), - FLAG(MS_REMOUNT), - FLAG(MS_MANDLOCK), - FLAG(MS_DIRSYNC), - FLAG(MS_NOATIME), - FLAG(MS_NODIRATIME), - FLAG(MS_BIND), - FLAG(MS_MOVE), - FLAG(MS_REC), - FLAG(MS_SILENT), - FLAG(MS_POSIXACL), - FLAG(MS_UNBINDABLE), - FLAG(MS_PRIVATE), - FLAG(MS_SLAVE), - FLAG(MS_SHARED), - FLAG(MS_RELATIME), - FLAG(MS_KERNMOUNT), - FLAG(MS_I_VERSION), - FLAG(MS_STRICTATIME), - FLAG(MS_LAZYTIME), - y); - if (!x) - return NULL; - if (!y) - x[strlen(x) - 1] = '\0'; /* truncate the last | */ - return x; + *ret = TAKE_PTR(str); + return 0; } int mount_verbose_full( @@ -616,7 +598,7 @@ int mount_verbose_full( "Failed to mangle mount options %s: %m", strempty(options)); - fl = mount_flags_to_string(f); + (void) mount_flags_to_string(f, &fl); if ((f & MS_REMOUNT) && !what && !type) log_debug("Remounting %s (%s \"%s\")...", @@ -669,7 +651,6 @@ int mount_option_mangle( const struct libmnt_optmap *map; _cleanup_free_ char *ret = NULL; - const char *p; int r; /* This extracts mount flags from the mount options, and store @@ -694,12 +675,11 @@ int mount_option_mangle( if (!map) return -EINVAL; - p = options; - for (;;) { + for (const char *p = options;;) { _cleanup_free_ char *word = NULL; const struct libmnt_optmap *ent; - r = extract_first_word(&p, &word, ",", EXTRACT_UNQUOTE); + r = extract_first_word(&p, &word, ",", EXTRACT_KEEP_QUOTE); if (r < 0) return r; if (r == 0) diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c index 391968463f..a392ec7588 100644 --- a/src/test/test-extract-word.c +++ b/src/test/test-extract-word.c @@ -442,6 +442,46 @@ static void test_extract_first_word(void) { assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0); assert_se(streq(t, "baldo")); free(t); + + p = original = "mode=\"1777\",size=\"10%\",nr_inodes=\"400\"k,uid=\"496,,107\"520,gi\"\"'d=49610,'\"\"7520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""; + assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0); + assert_se(streq(t, "mode=\"1777\"")); + free(t); + assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0); + assert_se(streq(t, "size=\"10%\"")); + free(t); + assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0); + assert_se(streq(t, "nr_inodes=\"400\"k")); + free(t); + assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0); + assert_se(streq(t, "uid=\"496,,107\"520")); + free(t); + assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0); + assert_se(streq(t, "gi\"\"'d=49610,'\"\"7520")); + free(t); + assert_se(extract_first_word(&p, &t, ",", EXTRACT_KEEP_QUOTE) > 0); + assert_se(streq(t, "context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"")); + free(t); + + p = original = "mode=\"1777\",size=\"10%\",nr_inodes=\"400\"k,uid=\"496,,107\"520,gi\"\"'d=49610,'\"\"7520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""; + assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0); + assert_se(streq(t, "mode=1777")); + free(t); + assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0); + assert_se(streq(t, "size=10%")); + free(t); + assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0); + assert_se(streq(t, "nr_inodes=400k")); + free(t); + assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0); + assert_se(streq(t, "uid=496,,107520")); + free(t); + assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0); + assert_se(streq(t, "gid=49610,7520")); + free(t); + assert_se(extract_first_word(&p, &t, ",", EXTRACT_UNQUOTE) > 0); + assert_se(streq(t, "context=system_u:object_r:svirt_sandbox_file_t:s0:c0,c1")); + free(t); } static void test_extract_first_word_and_warn(void) { diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c index ab5184121c..ac64e75059 100644 --- a/src/test/test-mount-util.c +++ b/src/test/test-mount-util.c @@ -69,6 +69,11 @@ static void test_mount_option_mangle(void) { opts = mfree(opts); assert_se(mount_option_mangle("rw,relatime,fmask=0022,dmask=0022,\"hogehoge", MS_RDONLY, &f, &opts) < 0); + + assert_se(mount_option_mangle("mode=1777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"", 0, &f, &opts) == 0); + assert_se(f == 0); + assert_se(streq(opts, "mode=1777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"")); + opts = mfree(opts); } static void test_bind_remount_recursive(void) {