]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fs-util: add helper that can split CIFS services names
authorLennart Poettering <lennart@poettering.net>
Fri, 22 Oct 2021 13:49:00 +0000 (15:49 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 27 Oct 2021 20:37:56 +0000 (22:37 +0200)
src/basic/fs-util.c
src/basic/fs-util.h
src/test/test-fs-util.c

index a60ac240ec8bd0514f185e6ede3d23a74af0f3e1..65468b51b526305f9131efc750eb10813451bdb8 100644 (file)
@@ -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;
+}
index f8a7657a0748c2daa6dc2d30da49947b50052862..0d69fa6edb87dad62fa7887c12a552140ad86836 100644 (file)
@@ -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);
index cd024ef7f18a43d678cf0df8425cc52c5dcceea9..41ddec4783a462ef3a6d9e02a7ca8565497a7530 100644 (file)
@@ -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;
 }