From f62231a54abff6566415ea82aa8773e61f5688d6 Mon Sep 17 00:00:00 2001 From: Michal Sekletar Date: Tue, 15 Mar 2022 19:02:05 +0100 Subject: [PATCH] core: shorten long unit names that are based on paths and append path hash at the end Fixes #18077 (cherry picked from commit 1d0727e76fd5e9a07cc9991ec9a10ea1d78a99c7) Resolves: #1940973 --- src/basic/string-util.h | 23 +++++----- src/basic/unit-name.c | 88 ++++++++++++++++++++++++++++++++++++++- src/basic/unit-name.h | 3 ++ src/core/mount.c | 3 ++ src/test/test-unit-name.c | 25 ++++++++++- 5 files changed, 129 insertions(+), 13 deletions(-) diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 742b566932..0d406ff64a 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -9,17 +9,18 @@ #include "macro.h" /* What is interpreted as whitespace? */ -#define WHITESPACE " \t\n\r" -#define NEWLINE "\n\r" -#define QUOTES "\"\'" -#define COMMENTS "#;" -#define GLOB_CHARS "*?[" -#define DIGITS "0123456789" -#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" -#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS -#define ALPHANUMERICAL LETTERS DIGITS -#define HEXDIGITS DIGITS "abcdefABCDEF" +#define WHITESPACE " \t\n\r" +#define NEWLINE "\n\r" +#define QUOTES "\"\'" +#define COMMENTS "#;" +#define GLOB_CHARS "*?[" +#define DIGITS "0123456789" +#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" +#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS +#define ALPHANUMERICAL LETTERS DIGITS +#define HEXDIGITS DIGITS "abcdefABCDEF" +#define LOWERCASE_HEXDIGITS DIGITS "abcdef" #define streq(a,b) (strcmp((a),(b)) == 0) #define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index f9b3fafd4d..65ed979e39 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -6,11 +6,17 @@ #include #include +#include "sd-id128.h" + #include "alloc-util.h" #include "glob-util.h" #include "hexdecoct.h" #include "path-util.h" +#include "random-util.h" +#include "siphash24.h" +#include "sparse-endian.h" #include "special.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "unit-name.h" @@ -31,6 +37,9 @@ VALID_CHARS_WITH_AT \ "[]!-*?" +#define LONG_UNIT_NAME_HASH_KEY SD_ID128_MAKE(ec,f2,37,fb,58,32,4a,32,84,9f,06,9b,0d,21,eb,9a) +#define UNIT_NAME_HASH_LENGTH_CHARS 16 + bool unit_name_is_valid(const char *n, UnitNameFlags flags) { const char *e, *i, *at; @@ -513,6 +522,68 @@ int unit_name_template(const char *f, char **ret) { return 0; } +bool unit_name_is_hashed(const char *name) { + char *s; + + if (!unit_name_is_valid(name, UNIT_NAME_PLAIN)) + return false; + + assert_se(s = strrchr(name, '.')); + + if (s - name < UNIT_NAME_HASH_LENGTH_CHARS + 1) + return false; + + s -= UNIT_NAME_HASH_LENGTH_CHARS; + if (s[-1] != '_') + return false; + + for (size_t i = 0; i < UNIT_NAME_HASH_LENGTH_CHARS; i++) + if (!strchr(LOWERCASE_HEXDIGITS, s[i])) + return false; + + return true; +} + +int unit_name_hash_long(const char *name, char **ret) { + _cleanup_free_ char *n = NULL, *hash = NULL; + char *suffix; + le64_t h; + size_t len; + + if (strlen(name) < UNIT_NAME_MAX) + return -EMSGSIZE; + + suffix = strrchr(name, '.'); + if (!suffix) + return -EINVAL; + + if (unit_type_from_string(suffix+1) < 0) + return -EINVAL; + + h = htole64(siphash24(name, strlen(name) + 1, LONG_UNIT_NAME_HASH_KEY.bytes)); + + hash = hexmem(&h, sizeof(h)); + if (!hash) + return -ENOMEM; + + assert_se(strlen(hash) == UNIT_NAME_HASH_LENGTH_CHARS); + + len = UNIT_NAME_MAX - 1 - strlen(suffix+1) - UNIT_NAME_HASH_LENGTH_CHARS - 2; + assert(len > 0 && len < UNIT_NAME_MAX); + + n = strndup(name, len); + if (!n) + return -ENOMEM; + + if (!strextend(&n, "_", hash, suffix, NULL)) + return -ENOMEM; + assert_se(unit_name_is_valid(n, UNIT_NAME_PLAIN)); + + *ret = TAKE_PTR(n); + + return 0; +} + int unit_name_from_path(const char *path, const char *suffix, char **ret) { _cleanup_free_ char *p = NULL, *s = NULL; int r; @@ -532,7 +603,19 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret) { if (!s) return -ENOMEM; - /* Refuse this if this got too long or for some other reason didn't result in a valid name */ + if (strlen(s) >= UNIT_NAME_MAX) { + _cleanup_free_ char *n = NULL; + + log_debug("Unit name \"%s\" too long, falling back to hashed unit name.", s); + + r = unit_name_hash_long(s, &n); + if (r < 0) + return r; + + free_and_replace(s, n); + } + + /* Refuse if this for some other reason didn't result in a valid name */ if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) return -EINVAL; @@ -582,6 +665,9 @@ int unit_name_to_path(const char *name, char **ret) { if (r < 0) return r; + if (unit_name_is_hashed(name)) + return -ENAMETOOLONG; + return unit_name_path_unescape(prefix, ret); } diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index 61abcd585b..602295af8f 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -45,6 +45,9 @@ int unit_name_replace_instance(const char *f, const char *i, char **ret); int unit_name_template(const char *f, char **ret); +int unit_name_hash_long(const char *name, char **ret); +bool unit_name_is_hashed(const char *name); + int unit_name_from_path(const char *path, const char *suffix, char **ret); int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret); int unit_name_to_path(const char *name, char **ret); diff --git a/src/core/mount.c b/src/core/mount.c index d37b5731f8..e69ecb7ce3 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -572,6 +572,9 @@ static int mount_add_extras(Mount *m) { if (!m->where) { r = unit_name_to_path(u->id, &m->where); + if (r == -ENAMETOOLONG) + log_unit_error_errno(u, r, "Failed to derive mount point path from unit name, because unit name is hashed. " + "Set \"Where=\" in the unit file explicitly."); if (r < 0) return r; } diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 2b00ef8cb7..35cfaafd30 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -82,6 +82,7 @@ static void test_unit_name_replace_instance(void) { static void test_unit_name_from_path_one(const char *path, const char *suffix, const char *expected, int ret) { _cleanup_free_ char *t = NULL; + int r; assert_se(unit_name_from_path(path, suffix, &t) == ret); puts(strna(t)); @@ -89,12 +90,31 @@ static void test_unit_name_from_path_one(const char *path, const char *suffix, c if (t) { _cleanup_free_ char *k = NULL; - assert_se(unit_name_to_path(t, &k) == 0); + + /* We don't support converting hashed unit names back to paths */ + r = unit_name_to_path(t, &k); + if (r == -ENAMETOOLONG) + return; + assert(r == 0); + puts(strna(k)); assert_se(path_equal(k, empty_to_root(path))); } } +static void test_unit_name_is_hashed(void) { + assert_se(!unit_name_is_hashed("")); + assert_se(!unit_name_is_hashed("foo@bar.service")); + assert_se(!unit_name_is_hashed("foo@.service")); + assert_se(unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_7736d9ed33c2ec55.mount")); + assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_7736D9ED33C2EC55.mount")); + assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!7736d9ed33c2ec55.mount")); + assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_7736d9gd33c2ec55.mount")); + assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_.mount")); + assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_2103e1466b87f7f7@waldo.mount")); + assert_se(!unit_name_is_hashed("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_2103e1466b87f7f7@.mount")); +} + static void test_unit_name_from_path(void) { puts("-------------------------------------------------"); test_unit_name_from_path_one("/waldo", ".mount", "waldo.mount", 0); @@ -105,6 +125,8 @@ static void test_unit_name_from_path(void) { test_unit_name_from_path_one("///", ".mount", "-.mount", 0); test_unit_name_from_path_one("/foo/../bar", ".mount", NULL, -EINVAL); test_unit_name_from_path_one("/foo/./bar", ".mount", NULL, -EINVAL); + test_unit_name_from_path_one("/waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ".mount", + "waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_7736d9ed33c2ec55.mount", 0); } static void test_unit_name_from_path_instance_one(const char *pattern, const char *path, const char *suffix, const char *expected, int ret) { @@ -824,6 +846,7 @@ int main(int argc, char* argv[]) { test_unit_name_is_valid(); test_unit_name_replace_instance(); + test_unit_name_is_hashed(); test_unit_name_from_path(); test_unit_name_from_path_instance(); test_unit_name_mangle();