#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"
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;
+}
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);
test_chmod_and_chown();
test_conservative_rename();
test_rmdir_parents();
+ test_parse_cifs_service();
return 0;
}