From: Lennart Poettering Date: Fri, 22 Oct 2021 13:49:00 +0000 (+0200) Subject: fs-util: add helper that can split CIFS services names X-Git-Tag: v250-rc1~396^2~5 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=68def5a975fd8c6dd2061250a0f7aeb8c149ea7b;p=thirdparty%2Fsystemd.git fs-util: add helper that can split CIFS services names --- diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index a60ac240ec8..65468b51b52 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -12,6 +12,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hostname-util.h" #include "log.h" #include "macro.h" #include "missing_fcntl.h" @@ -952,3 +953,78 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) { return -EINTR; } + +int parse_cifs_service( + const char *s, + char **ret_host, + char **ret_service, + char **ret_path) { + + _cleanup_free_ char *h = NULL, *ss = NULL, *x = NULL; + const char *p, *e, *d; + char delimiter; + + /* Parses a CIFS service in form of //host/service/path… and splitting it in three parts. The last + * part is optional, in which case NULL is returned there. To maximize compatibility syntax with + * backslashes instead of slashes is accepted too. */ + + if (!s) + return -EINVAL; + + p = startswith(s, "//"); + if (!p) { + p = startswith(s, "\\\\"); + if (!p) + return -EINVAL; + } + + delimiter = s[0]; + e = strchr(p, delimiter); + if (!e) + return -EINVAL; + + h = strndup(p, e - p); + if (!h) + return -ENOMEM; + + if (!hostname_is_valid(h, 0)) + return -EINVAL; + + e++; + + d = strchrnul(e, delimiter); + + ss = strndup(e, d - e); + if (!ss) + return -ENOMEM; + + if (!filename_is_valid(ss)) + return -EINVAL; + + if (!isempty(d)) { + x = strdup(skip_leading_chars(d, CHAR_TO_STR(delimiter))); + if (!x) + return -EINVAL; + + /* Make sure to convert Windows-style "\" → Unix-style / */ + for (char *i = x; *i; i++) + if (*i == delimiter) + *i = '/'; + + if (!path_is_valid(x)) + return -EINVAL; + + path_simplify(x); + if (!path_is_normalized(x)) + return -EINVAL; + } + + if (ret_host) + *ret_host = TAKE_PTR(h); + if (ret_service) + *ret_service = TAKE_PTR(ss); + if (ret_path) + *ret_path = TAKE_PTR(x); + + return 0; +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index f8a7657a074..0d69fa6edb8 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -106,3 +106,5 @@ static inline int conservative_rename(const char *oldpath, const char *newpath) } int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size); + +int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index cd024ef7f18..41ddec4783a 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -921,6 +921,36 @@ static void test_rmdir_parents(void) { assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); } +static void test_parse_cifs_service_one(const char *f, const char *h, const char *s, const char *d, int ret) { + _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL; + + assert_se(parse_cifs_service(f, &a, &b, &c) == ret); + assert_se(streq_ptr(a, h)); + assert_se(streq_ptr(b, s)); + assert_se(streq_ptr(c, d)); +} + +static void test_parse_cifs_service(void) { + log_info("/* %s */", __func__); + + test_parse_cifs_service_one("//foo/bar/baz", "foo", "bar", "baz", 0); + test_parse_cifs_service_one("\\\\foo\\bar\\baz", "foo", "bar", "baz", 0); + test_parse_cifs_service_one("//foo/bar", "foo", "bar", NULL, 0); + test_parse_cifs_service_one("\\\\foo\\bar", "foo", "bar", NULL, 0); + test_parse_cifs_service_one("//foo/bar/baz/uuu", "foo", "bar", "baz/uuu", 0); + test_parse_cifs_service_one("\\\\foo\\bar\\baz\\uuu", "foo", "bar", "baz/uuu", 0); + + test_parse_cifs_service_one(NULL, NULL, NULL, NULL, -EINVAL); + test_parse_cifs_service_one("", NULL, NULL, NULL, -EINVAL); + test_parse_cifs_service_one("abc", NULL, NULL, NULL, -EINVAL); + test_parse_cifs_service_one("abc/cde/efg", NULL, NULL, NULL, -EINVAL); + test_parse_cifs_service_one("//foo/bar/baz/..", NULL, NULL, NULL, -EINVAL); + test_parse_cifs_service_one("//foo///", NULL, NULL, NULL, -EINVAL); + test_parse_cifs_service_one("//foo/.", NULL, NULL, NULL, -EINVAL); + test_parse_cifs_service_one("//foo/a/.", NULL, NULL, NULL, -EINVAL); + test_parse_cifs_service_one("//./a", NULL, NULL, NULL, -EINVAL); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_INFO); @@ -940,6 +970,7 @@ int main(int argc, char *argv[]) { test_chmod_and_chown(); test_conservative_rename(); test_rmdir_parents(); + test_parse_cifs_service(); return 0; }