fs-util: add helper that can split CIFS services names

This commit is contained in:
Lennart Poettering 2021-10-22 15:49:00 +02:00
parent c9080dfb0b
commit 68def5a975
3 changed files with 109 additions and 0 deletions

View 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;
}

View 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);

View 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;
}