Unverified Commit 8aa60255 by Stéphane Graber Committed by GitHub

Merge pull request #3762 from brauner/2021-03-31/fixes

fixes: Makefile, lxc-user-nic, simplify get_network_config_ops()
parents b405dec6 7707b0e0
...@@ -233,10 +233,10 @@ struct alloted_s { ...@@ -233,10 +233,10 @@ struct alloted_s {
struct alloted_s *next; struct alloted_s *next;
}; };
static struct alloted_s *append_alloted(struct alloted_s **head, char *name, static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int n)
int n)
{ {
struct alloted_s *cur, *al; __do_free struct alloted_s *al = NULL;
struct alloted_s *cur;
if (!head || !name) { if (!head || !name) {
/* Sanity check. Parameters should not be null. */ /* Sanity check. Parameters should not be null. */
...@@ -244,32 +244,29 @@ static struct alloted_s *append_alloted(struct alloted_s **head, char *name, ...@@ -244,32 +244,29 @@ static struct alloted_s *append_alloted(struct alloted_s **head, char *name,
return NULL; return NULL;
} }
al = malloc(sizeof(struct alloted_s)); al = zalloc(sizeof(struct alloted_s));
if (!al) { if (!al) {
CMD_SYSERROR("Failed to allocate memory\n"); CMD_SYSERROR("Failed to allocate memory\n");
return NULL; return NULL;
} }
al->name = strdup(name); al->name = strdup(name);
if (!al->name) { if (!al->name)
free(al);
return NULL; return NULL;
}
al->allowed = n; al->allowed = n;
al->next = NULL; al->next = NULL;
if (!*head) { if (*head) {
*head = al;
return al;
}
cur = *head; cur = *head;
while (cur->next) while (cur->next)
cur = cur->next; cur = cur->next;
cur->next = al; cur->next = al;
} else {
*head = al;
}
return al; return move_ptr(al);
} }
static void free_alloted(struct alloted_s **head) static void free_alloted(struct alloted_s **head)
...@@ -321,10 +318,10 @@ static int get_alloted(char *me, char *intype, char *link, ...@@ -321,10 +318,10 @@ static int get_alloted(char *me, char *intype, char *link,
if (ret != 4) if (ret != 4)
continue; continue;
if (strlen(name) == 0) if (is_empty_string(name))
continue; continue;
if (strcmp(name, me)) { if (!strequal(name, me)) {
if (name[0] != '@') if (name[0] != '@')
continue; continue;
...@@ -332,17 +329,17 @@ static int get_alloted(char *me, char *intype, char *link, ...@@ -332,17 +329,17 @@ static int get_alloted(char *me, char *intype, char *link,
continue; continue;
} }
if (strcmp(type, intype)) if (!strequal(type, intype))
continue; continue;
if (strcmp(link, br)) if (!strequal(link, br))
continue; continue;
/* Found the user or group with the appropriate settings, /*
* therefore finish the search. What to do if there are more * Found the user or group with the appropriate settings,
* than one applicable lines? not specified in the docs. Since * therefore finish the search. What to do if there are is more
* getline is implemented with realloc, we don't need to free * than one applicable line? Currently this is not specified in
* line until exiting func. * the docs.
* *
* If append_alloted returns NULL, e.g. due to a malloc error, * If append_alloted returns NULL, e.g. due to a malloc error,
* we set count to 0 and break the loop, allowing cleanup and * we set count to 0 and break the loop, allowing cleanup and
......
...@@ -95,6 +95,7 @@ lxc_config_define(init_cwd); ...@@ -95,6 +95,7 @@ lxc_config_define(init_cwd);
lxc_config_define(init_gid); lxc_config_define(init_gid);
lxc_config_define(init_uid); lxc_config_define(init_uid);
lxc_config_define(init_groups); lxc_config_define(init_groups);
lxc_config_define(jump_table_net);
lxc_config_define(keyring_session); lxc_config_define(keyring_session);
lxc_config_define(log_file); lxc_config_define(log_file);
lxc_config_define(log_level); lxc_config_define(log_level);
...@@ -123,7 +124,6 @@ lxc_config_define(net_ipvlan_mode); ...@@ -123,7 +124,6 @@ lxc_config_define(net_ipvlan_mode);
lxc_config_define(net_ipvlan_isolation); lxc_config_define(net_ipvlan_isolation);
lxc_config_define(net_mtu); lxc_config_define(net_mtu);
lxc_config_define(net_name); lxc_config_define(net_name);
lxc_config_define(net_nic);
lxc_config_define(net_script_down); lxc_config_define(net_script_down);
lxc_config_define(net_script_up); lxc_config_define(net_script_up);
lxc_config_define(net_type); lxc_config_define(net_type);
...@@ -229,7 +229,7 @@ static struct lxc_config_t config_jump_table[] = { ...@@ -229,7 +229,7 @@ static struct lxc_config_t config_jump_table[] = {
{ "lxc.namespace.share", true, set_config_namespace_share, get_config_namespace_share, clr_config_namespace_share, }, { "lxc.namespace.share", true, set_config_namespace_share, get_config_namespace_share, clr_config_namespace_share, },
{ "lxc.time.offset.boot", true, set_config_time_offset_boot, get_config_time_offset_boot, clr_config_time_offset_boot, }, { "lxc.time.offset.boot", true, set_config_time_offset_boot, get_config_time_offset_boot, clr_config_time_offset_boot, },
{ "lxc.time.offset.monotonic", true, set_config_time_offset_monotonic, get_config_time_offset_monotonic, clr_config_time_offset_monotonic, }, { "lxc.time.offset.monotonic", true, set_config_time_offset_monotonic, get_config_time_offset_monotonic, clr_config_time_offset_monotonic, },
{ "lxc.net.", false, set_config_net_nic, get_config_net_nic, clr_config_net_nic, }, { "lxc.net.", false, set_config_jump_table_net, get_config_jump_table_net, clr_config_jump_table_net, },
{ "lxc.net", true, set_config_net, get_config_net, clr_config_net, }, { "lxc.net", true, set_config_net, get_config_net, clr_config_net, },
{ "lxc.no_new_privs", true, set_config_no_new_privs, get_config_no_new_privs, clr_config_no_new_privs, }, { "lxc.no_new_privs", true, set_config_no_new_privs, get_config_no_new_privs, clr_config_no_new_privs, },
{ "lxc.prlimit", false, set_config_prlimit, get_config_prlimit, clr_config_prlimit, }, { "lxc.prlimit", false, set_config_prlimit, get_config_prlimit, clr_config_prlimit, },
...@@ -257,7 +257,11 @@ static struct lxc_config_t config_jump_table[] = { ...@@ -257,7 +257,11 @@ static struct lxc_config_t config_jump_table[] = {
{ "lxc.proc", false, set_config_proc, get_config_proc, clr_config_proc, }, { "lxc.proc", false, set_config_proc, get_config_proc, clr_config_proc, },
}; };
static struct lxc_config_t config_jump_table_net[] = { struct lxc_config_net_t {
LXC_CONFIG_MEMBERS;
};
static struct lxc_config_net_t config_jump_table_net[] = {
/* If a longer key is added please update. */ /* If a longer key is added please update. */
#define NETWORK_SUBKEY_SIZE_MAX (STRLITERALLEN("veth.vlan.tagged.id") * 2) #define NETWORK_SUBKEY_SIZE_MAX (STRLITERALLEN("veth.vlan.tagged.id") * 2)
{ "flags", true, set_config_net_flags, get_config_net_flags, clr_config_net_flags, }, { "flags", true, set_config_net_flags, get_config_net_flags, clr_config_net_flags, },
...@@ -318,12 +322,20 @@ struct lxc_config_t *lxc_get_config(const char *key) ...@@ -318,12 +322,20 @@ struct lxc_config_t *lxc_get_config(const char *key)
return NULL; return NULL;
} }
static struct lxc_config_t *lxc_get_config_net(const char *key) static inline bool match_config_net_item(const struct lxc_config_net_t *entry,
const char *key)
{
if (entry->strict)
return strequal(entry->name, key);
return strnequal(entry->name, key, strlen(entry->name));
}
static struct lxc_config_net_t *lxc_get_config_net(const char *key)
{ {
for (size_t i = 0; i < ARRAY_SIZE(config_jump_table_net); i++) { for (size_t i = 0; i < ARRAY_SIZE(config_jump_table_net); i++) {
struct lxc_config_t *cur = &config_jump_table_net[i]; struct lxc_config_net_t *cur = &config_jump_table_net[i];
if (!match_config_item(cur, key)) if (!match_config_net_item(cur, key))
continue; continue;
return cur; return cur;
...@@ -5204,97 +5216,94 @@ static int get_config_includefiles(const char *key, char *retv, int inlen, ...@@ -5204,97 +5216,94 @@ static int get_config_includefiles(const char *key, char *retv, int inlen,
return ret_errno(ENOSYS); return ret_errno(ENOSYS);
} }
static struct lxc_config_t *get_network_config_ops(const char *key, struct config_net_info {
struct lxc_conf *lxc_conf, char buf[NETWORK_SUBKEY_SIZE_MAX];
ssize_t *idx, const char *subkey;
const char **const subkey) const struct lxc_config_net_t *ops;
struct lxc_netdev *netdev;
};
static int get_network_config_ops(const char *key, struct lxc_conf *lxc_conf,
struct config_net_info *info, bool allocate)
{ {
struct lxc_config_t *config = NULL;
int ret; int ret;
int64_t tmpidx; int64_t tmpidx;
const char *idx_start, *subkey_start; unsigned int idx;
char buf[NETWORK_SUBKEY_SIZE_MAX]; const char *idx_start;
if (!idx)
return ret_set_errno(NULL, EINVAL);
if (is_empty_string(key)) if (is_empty_string(key))
return ret_set_errno(NULL, EINVAL); return ret_errno(EINVAL);
/* check that this is a sensible network key */ /* check that this is a sensible network key */
if (!strnequal("lxc.net.", key, STRLITERALLEN("lxc.net."))) if (!strnequal("lxc.net.", key, STRLITERALLEN("lxc.net.")))
return log_error_errno(NULL, EINVAL, "Invalid network configuration key \"%s\"", key); return syserror_set(-EINVAL, "Invalid network configuration key \"%s\"", key);
/* lxc.net.<n> */ /* lxc.net.<n> */
/* beginning of index string */ /* beginning of index string */
idx_start = key + STRLITERALLEN("lxc.net."); idx_start = key + STRLITERALLEN("lxc.net.");
if (!isdigit(*idx_start)) if (!isdigit(*idx_start))
return log_error_errno(NULL, EINVAL, "Failed to detect digit in string \"%s\"", key + 8); return syserror_set(-EINVAL, "Failed to detect digit in string \"%s\"", key + 8);
ret = lxc_safe_int64_residual(idx_start, &tmpidx, 10, buf, sizeof(buf)); ret = lxc_safe_int64_residual(idx_start, &tmpidx, 10, info->buf, sizeof(info->buf));
if (ret) if (ret)
return log_error_errno(NULL, -ret, "Failed to parse network index"); return syserror("Failed to parse network index");
if (tmpidx < 0 || tmpidx >= INT_MAX) if (tmpidx < 0 || tmpidx >= INT_MAX)
return log_error_errno(NULL, ERANGE, "Number of configured networks would overflow the counter"); return syserror_set(-ERANGE, "Number of configured networks would overflow the counter");
*idx = (ssize_t)tmpidx; idx = (unsigned int)tmpidx;
if (!subkey) info->netdev = lxc_get_netdev_by_idx(lxc_conf, idx, allocate);
return NULL; if (!info->netdev)
return ret_errno(EINVAL);
/* Make sure subkey points to the empty string. */
info->subkey = info->buf;
if (is_empty_string(info->subkey))
return ret_errno(ENOENT);
subkey_start = &buf[1]; if (info->subkey[0] != '.')
if (is_empty_string(subkey_start)) return syserror_set(-EINVAL, "Invalid subkey");
return log_error_errno(NULL, EINVAL, "No network subkey specified"); info->subkey++;
/* lxc.net.<idx>.<subkey> */ /* lxc.net.<idx>.<subkey> */
*subkey = subkey_start; info->ops = lxc_get_config_net(info->subkey);
config = lxc_get_config_net(*subkey); if (!info->ops)
if (!config) return syserror_set(-ENOENT, "Unknown network configuration key \"%s\"", key);
return log_error_errno(NULL, ENOENT, "Unknown network configuration key \"%s\"", key);
return config; return 0;
} }
/* Config entry is something like "lxc.net.0.ipv4" the key 'lxc.net.' was /* Config entry is something like "lxc.net.0.ipv4" the key 'lxc.net.' was
* found. So we make sure next comes an integer, find the right callback (by * found. So we make sure next comes an integer, find the right callback (by
* rewriting the key), and call it. * rewriting the key), and call it.
*/ */
static int set_config_net_nic(const char *key, const char *value, static int set_config_jump_table_net(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data) struct lxc_conf *lxc_conf, void *data)
{ {
const char *subkey = NULL; struct config_net_info info = {};
ssize_t idx = -1; int ret;
const char *idxstring; const char *idxstring;
struct lxc_config_t *config;
struct lxc_netdev *netdev;
idxstring = key + STRLITERALLEN("lxc.net."); idxstring = key + STRLITERALLEN("lxc.net.");
if (!isdigit(*idxstring)) if (!isdigit(*idxstring))
return ret_errno(EINVAL); return ret_errno(EINVAL);
if (lxc_config_value_empty(value)) if (lxc_config_value_empty(value))
return clr_config_net_nic(key, lxc_conf, data); return clr_config_jump_table_net(key, lxc_conf, data);
config = get_network_config_ops(key, lxc_conf, &idx, &subkey);
if (!config || idx < 0)
return -errno;
netdev = lxc_get_netdev_by_idx(lxc_conf, (unsigned int)idx, true); ret = get_network_config_ops(key, lxc_conf, &info, true);
if (!netdev) if (ret)
return ret_errno(EINVAL); return ret;
return config->set(subkey, value, lxc_conf, netdev); return info.ops->set(info.subkey, value, lxc_conf, info.netdev);
} }
static int clr_config_net_nic(const char *key, struct lxc_conf *lxc_conf, static int clr_config_jump_table_net(const char *key, struct lxc_conf *lxc_conf,
void *data) void *data)
{ {
const char *subkey = NULL; struct config_net_info info = {};
ssize_t idx = -1;
int ret; int ret;
const char *idxstring; const char *idxstring;
struct lxc_config_t *config;
struct lxc_netdev *netdev;
idxstring = key + 8; idxstring = key + 8;
if (!isdigit(*idxstring)) if (!isdigit(*idxstring))
...@@ -5317,15 +5326,11 @@ static int clr_config_net_nic(const char *key, struct lxc_conf *lxc_conf, ...@@ -5317,15 +5326,11 @@ static int clr_config_net_nic(const char *key, struct lxc_conf *lxc_conf,
return 0; return 0;
} }
config = get_network_config_ops(key, lxc_conf, &idx, &subkey); ret = get_network_config_ops(key, lxc_conf, &info, false);
if (!config || idx < 0) if (ret)
return -errno; return ret;
netdev = lxc_get_netdev_by_idx(lxc_conf, (unsigned int)idx, false);
if (!netdev)
return ret_errno(EINVAL);
return config->clr(subkey, lxc_conf, netdev); return info.ops->clr(info.subkey, lxc_conf, info.netdev);
} }
static int clr_config_net_type(const char *key, struct lxc_conf *lxc_conf, static int clr_config_net_type(const char *key, struct lxc_conf *lxc_conf,
...@@ -5683,28 +5688,22 @@ static int clr_config_net_veth_ipv6_route(const char *key, ...@@ -5683,28 +5688,22 @@ static int clr_config_net_veth_ipv6_route(const char *key,
return 0; return 0;
} }
static int get_config_net_nic(const char *key, char *retv, int inlen, static int get_config_jump_table_net(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data) struct lxc_conf *c, void *data)
{ {
const char *subkey = NULL; struct config_net_info info = {};
ssize_t idx = -1; int ret;
const char *idxstring; const char *idxstring;
struct lxc_config_t *config;
struct lxc_netdev *netdev;
idxstring = key + STRLITERALLEN("lxc.net."); idxstring = key + STRLITERALLEN("lxc.net.");
if (!isdigit(*idxstring)) if (!isdigit(*idxstring))
return ret_errno(EINVAL); return ret_errno(EINVAL);
config = get_network_config_ops(key, c, &idx, &subkey); ret = get_network_config_ops(key, c, &info, false);
if (!config || idx < 0) if (ret)
return -errno; return ret;
netdev = lxc_get_netdev_by_idx(c, (unsigned int)idx, false);
if (!netdev)
return ret_errno(EINVAL);
return config->get(subkey, retv, inlen, c, netdev); return info.ops->get(info.subkey, retv, inlen, c, info.netdev);
} }
static int get_config_net_type(const char *key, char *retv, int inlen, static int get_config_net_type(const char *key, char *retv, int inlen,
...@@ -6427,23 +6426,22 @@ int lxc_list_subkeys(struct lxc_conf *conf, const char *key, char *retv, ...@@ -6427,23 +6426,22 @@ int lxc_list_subkeys(struct lxc_conf *conf, const char *key, char *retv,
int lxc_list_net(struct lxc_conf *c, const char *key, char *retv, int inlen) int lxc_list_net(struct lxc_conf *c, const char *key, char *retv, int inlen)
{ {
int len; struct config_net_info info = {};
const char *idxstring;
struct lxc_netdev *netdev; struct lxc_netdev *netdev;
int len, ret;
const char *idxstring;
int fulllen = 0; int fulllen = 0;
ssize_t idx = -1;
idxstring = key + STRLITERALLEN("lxc.net."); idxstring = key + STRLITERALLEN("lxc.net.");
if (!isdigit(*idxstring)) if (!isdigit(*idxstring))
return ret_errno(EINVAL); return ret_errno(EINVAL);
(void)get_network_config_ops(key, c, &idx, NULL); ret = get_network_config_ops(key, c, &info, false);
if (idx < 0) if (ret) {
return ret_errno(EINVAL); if (ret != -ENOENT)
netdev = lxc_get_netdev_by_idx(c, (unsigned int)idx, false);
if (!netdev)
return ret_errno(EINVAL); return ret_errno(EINVAL);
}
netdev = info.netdev;
if (!retv) if (!retv)
inlen = 0; inlen = 0;
......
...@@ -32,12 +32,15 @@ typedef int (*config_get_cb)(const char *key, char *value, int inlen, ...@@ -32,12 +32,15 @@ typedef int (*config_get_cb)(const char *key, char *value, int inlen,
typedef int (*config_clr_cb)(const char *key, struct lxc_conf *conf, typedef int (*config_clr_cb)(const char *key, struct lxc_conf *conf,
void *data); void *data);
#define LXC_CONFIG_MEMBERS \
char *name; \
bool strict; \
config_set_cb set; \
config_get_cb get; \
config_clr_cb clr
struct lxc_config_t { struct lxc_config_t {
char *name; LXC_CONFIG_MEMBERS;
bool strict;
config_set_cb set;
config_get_cb get;
config_clr_cb clr;
}; };
struct new_config_item { struct new_config_item {
......
...@@ -192,41 +192,6 @@ char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix) ...@@ -192,41 +192,6 @@ char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix)
return result; return result;
} }
char **lxc_normalize_path(const char *path)
{
char **components;
size_t components_len = 0;
size_t pos = 0;
components = lxc_string_split(path, '/');
if (!components)
return NULL;
/* resolve '.' and '..' */
for (pos = 0; pos < components_len;) {
if (strequal(components[pos], ".") ||
(strequal(components[pos], "..") && pos == 0)) {
/* eat this element */
free(components[pos]);
memmove(&components[pos], &components[pos + 1],
sizeof(char *) * (components_len - pos));
components_len--;
} else if (strequal(components[pos], "..")) {
/* eat this and the previous element */
free(components[pos - 1]);
free(components[pos]);
memmove(&components[pos - 1], &components[pos + 1],
sizeof(char *) * (components_len - pos));
components_len -= 2;
pos--;
} else {
pos++;
}
}
return components;
}
/* taken from systemd */ /* taken from systemd */
char *path_simplify(const char *path) char *path_simplify(const char *path)
{ {
...@@ -672,8 +637,9 @@ int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base) ...@@ -672,8 +637,9 @@ int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base)
return 0; return 0;
} }
int lxc_safe_int64_residual(const char *numstr, int64_t *converted, int base, char *residual, int lxc_safe_int64_residual(const char *restrict numstr,
size_t residual_len) int64_t *restrict converted, int base,
char *restrict residual, size_t residual_len)
{ {
char *remaining = NULL; char *remaining = NULL;
int64_t u; int64_t u;
...@@ -692,7 +658,7 @@ int lxc_safe_int64_residual(const char *numstr, int64_t *converted, int base, ch ...@@ -692,7 +658,7 @@ int lxc_safe_int64_residual(const char *numstr, int64_t *converted, int base, ch
errno = 0; errno = 0;
u = strtoll(numstr, &remaining, base); u = strtoll(numstr, &remaining, base);
if (errno == ERANGE && u == INT64_MAX) if (errno == ERANGE && u == INT64_MAX)
return -ERANGE; return ret_errno(ERANGE);
if (remaining == numstr) if (remaining == numstr)
return -EINVAL; return -EINVAL;
...@@ -705,11 +671,11 @@ int lxc_safe_int64_residual(const char *numstr, int64_t *converted, int base, ch ...@@ -705,11 +671,11 @@ int lxc_safe_int64_residual(const char *numstr, int64_t *converted, int base, ch
len = strlen(remaining); len = strlen(remaining);
if (len >= residual_len) if (len >= residual_len)
return -EINVAL; return ret_errno(EINVAL);
memcpy(residual, remaining, len); memcpy(residual, remaining, len);
} else if (*remaining != '\0') { } else if (*remaining != '\0') {
return -EINVAL; return ret_errno(EINVAL);
} }
out: out:
......
...@@ -30,21 +30,7 @@ __hidden extern char *lxc_string_replace(const char *needle, const char *replace ...@@ -30,21 +30,7 @@ __hidden extern char *lxc_string_replace(const char *needle, const char *replace
const char *haystack); const char *haystack);
__hidden extern bool lxc_string_in_array(const char *needle, const char **haystack); __hidden extern bool lxc_string_in_array(const char *needle, const char **haystack);
__hidden extern char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix); __hidden extern char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix);
/*
* Normalize and split path: Leading and trailing / are removed, multiple
* / are compactified, .. and . are resolved (.. on the top level is considered
* identical to .).
* Examples:
* / -> { NULL }
* foo/../bar -> { bar, NULL }
* ../../ -> { NULL }
* ./bar/baz/.. -> { bar, NULL }
* foo//bar -> { foo, bar, NULL }
*/
__hidden extern char **lxc_normalize_path(const char *path);
/* remove multiple slashes from the path, e.g. ///foo//bar -> /foo/bar */
__hidden extern char *lxc_deslashify(const char *path);
__hidden extern char *lxc_append_paths(const char *first, const char *second); __hidden extern char *lxc_append_paths(const char *first, const char *second);
/* /*
...@@ -78,8 +64,10 @@ __hidden extern int lxc_safe_long(const char *numstr, long int *converted); ...@@ -78,8 +64,10 @@ __hidden extern int lxc_safe_long(const char *numstr, long int *converted);
__hidden extern int lxc_safe_long_long(const char *numstr, long long int *converted); __hidden extern int lxc_safe_long_long(const char *numstr, long long int *converted);
__hidden extern int lxc_safe_ulong(const char *numstr, unsigned long *converted); __hidden extern int lxc_safe_ulong(const char *numstr, unsigned long *converted);
__hidden extern int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base); __hidden extern int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base);
__hidden extern int lxc_safe_int64_residual(const char *numstr, int64_t *converted, int base, __hidden extern int lxc_safe_int64_residual(const char *restrict numstr,
char *residual, size_t residual_len); int64_t *restrict converted,
int base, char *restrict residual,
size_t residual_len);
/* Handles B, kb, MB, GB. Detects overflows and reports -ERANGE. */ /* Handles B, kb, MB, GB. Detects overflows and reports -ERANGE. */
__hidden extern int parse_byte_size_string(const char *s, long long int *converted); __hidden extern int parse_byte_size_string(const char *s, long long int *converted);
......
...@@ -289,6 +289,41 @@ static int mk_rand_ovl_dirs(struct mnts *mnts, unsigned int num, struct lxc_argu ...@@ -289,6 +289,41 @@ static int mk_rand_ovl_dirs(struct mnts *mnts, unsigned int num, struct lxc_argu
return 0; return 0;
} }
static char **lxc_normalize_path(const char *path)
{
char **components;
size_t components_len = 0;
size_t pos = 0;
components = lxc_string_split(path, '/');
if (!components)
return NULL;
/* resolve '.' and '..' */
for (pos = 0; pos < components_len;) {
if (strequal(components[pos], ".") ||
(strequal(components[pos], "..") && pos == 0)) {
/* eat this element */
free(components[pos]);
memmove(&components[pos], &components[pos + 1],
sizeof(char *) * (components_len - pos));
components_len--;
} else if (strequal(components[pos], "..")) {
/* eat this and the previous element */
free(components[pos - 1]);
free(components[pos]);
memmove(&components[pos - 1], &components[pos + 1],
sizeof(char *) * (components_len - pos));
components_len -= 2;
pos--;
} else {
pos++;
}
}
return components;
}
static char *construct_path(char *path, bool as_prefix) static char *construct_path(char *path, bool as_prefix)
{ {
char **components = NULL; char **components = NULL;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment